From 9377fcd5af7bc4ce1cb676c91459ad6b1045745a Mon Sep 17 00:00:00 2001 From: Tatenda Chipeperekwa Date: Wed, 25 Mar 2020 13:59:06 -0700 Subject: [PATCH 01/18] disp: msm: dp: refactor functions to reduce code complexity Refactor the mode validation and HDCP worker functions in order to reduce code complexity and improve readability. Change-Id: I6b33599d66b73d16a6cb9cc120605a032dada996 Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_display.c | 367 +++++++++++++++++++++++++++----------------- 1 file changed, 229 insertions(+), 138 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 92334bde6a8e..7db54515520d 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -362,35 +362,19 @@ static void dp_display_hdcp_deregister_stream(struct dp_display_private *dp, } } -static void dp_display_hdcp_cb_work(struct work_struct *work) +static void dp_display_hdcp_process_delayed_off(struct dp_display_private *dp) { - struct dp_display_private *dp; - struct delayed_work *dw = to_delayed_work(work); - struct sde_hdcp_ops *ops; - struct dp_link_hdcp_status *status; - 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_display_state_is(DP_STATE_ENABLED | DP_STATE_CONNECTED) || - dp_display_state_is(DP_STATE_ABORTED | DP_STATE_HDCP_ABORTED)) - return; - - if (dp_display_state_is(DP_STATE_SUSPENDED)) { - DP_DEBUG("System suspending. Delay HDCP operations\n"); - queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ); - return; - } - if (dp->hdcp_delayed_off) { if (dp->hdcp.ops && dp->hdcp.ops->off) dp->hdcp.ops->off(dp->hdcp.data); dp_display_update_hdcp_status(dp, true); dp->hdcp_delayed_off = false; } +} + +static int dp_display_hdcp_process_sink_sync(struct dp_display_private *dp) +{ + u8 sink_status = 0; if (dp->debug->hdcp_wait_sink_sync) { drm_dp_dpcd_readb(dp->aux->drm_aux, DP_SINK_STATUS, @@ -400,42 +384,59 @@ static void dp_display_hdcp_cb_work(struct work_struct *work) if (sink_status < 1) { DP_DEBUG("Sink not synchronized. Queuing again then exiting\n"); queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ); - return; + return -EAGAIN; } } - status = &dp->link->hdcp_status; + return 0; +} - if (status->hdcp_state == HDCP_STATE_INACTIVE) { - dp_display_check_source_hdcp_caps(dp); - dp_display_update_hdcp_info(dp); +static int dp_display_hdcp_start(struct dp_display_private *dp) +{ + if (dp->link->hdcp_status.hdcp_state != HDCP_STATE_INACTIVE) + return -EINVAL; - 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; - } - } else { + dp_display_check_source_hdcp_caps(dp); + 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; + return 0; } + } else { + dp_display_update_hdcp_status(dp, true); + return 0; } + return -EINVAL; +} + +static void dp_display_hdcp_print_auth_state(struct dp_display_private *dp) +{ + u32 hdcp_auth_state; + int rc; + rc = dp->catalog->ctrl.read_hdcp_status(&dp->catalog->ctrl); if (rc >= 0) { hdcp_auth_state = (rc >> 20) & 0x3; DP_DEBUG("hdcp auth state %d\n", hdcp_auth_state); } +} + +static void dp_display_hdcp_process_state(struct dp_display_private *dp) +{ + struct dp_link_hdcp_status *status; + struct sde_hdcp_ops *ops; + void *data; + int rc = 0; + + status = &dp->link->hdcp_status; ops = dp->hdcp.ops; data = dp->hdcp.data; - DP_DEBUG("%s: %s\n", sde_hdcp_version(status->hdcp_version), - sde_hdcp_state_name(status->hdcp_state)); - - dp_display_update_hdcp_status(dp, false); - if (status->hdcp_state != HDCP_STATE_AUTHENTICATED && dp->debug->force_encryption && ops && ops->force_encryption) ops->force_encryption(data, dp->debug->force_encryption); @@ -472,6 +473,46 @@ static void dp_display_hdcp_cb_work(struct work_struct *work) } } +static void dp_display_hdcp_cb_work(struct work_struct *work) +{ + struct dp_display_private *dp; + struct delayed_work *dw = to_delayed_work(work); + struct dp_link_hdcp_status *status; + int rc = 0; + + dp = container_of(dw, struct dp_display_private, hdcp_cb_work); + + if (!dp_display_state_is(DP_STATE_ENABLED | DP_STATE_CONNECTED) || + dp_display_state_is(DP_STATE_ABORTED | DP_STATE_HDCP_ABORTED)) + return; + + if (dp_display_state_is(DP_STATE_SUSPENDED)) { + DP_DEBUG("System suspending. Delay HDCP operations\n"); + queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ); + return; + } + + dp_display_hdcp_process_delayed_off(dp); + + rc = dp_display_hdcp_process_sink_sync(dp); + if (rc) + return; + + rc = dp_display_hdcp_start(dp); + if (!rc) + return; + + dp_display_hdcp_print_auth_state(dp); + + status = &dp->link->hdcp_status; + DP_DEBUG("%s: %s\n", sde_hdcp_version(status->hdcp_version), + sde_hdcp_state_name(status->hdcp_state)); + + dp_display_update_hdcp_status(dp, false); + + dp_display_hdcp_process_state(dp); +} + static void dp_display_notify_hdcp_status_cb(void *ptr, enum sde_hdcp_state state) { @@ -2065,25 +2106,150 @@ end: return 0; } +static int dp_display_validate_link_clock(struct dp_display_private *dp, + struct drm_display_mode *mode, struct dp_display_mode dp_mode) +{ + struct drm_dp_link *link_info; + u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0; + bool dsc_en; + int rate; + + link_info = &dp->panel->link_info; + + dsc_en = (dp_mode.timing.comp_info.comp_ratio > 1) ? true : false; + mode_bpp = dsc_en ? + DSC_BPP(dp_mode.timing.comp_info.dsc_info.config) + : dp_mode.timing.bpp; + + mode_rate_khz = mode->clock * mode_bpp; + rate = drm_dp_bw_code_to_link_rate(dp->link->link_params.bw_code); + supported_rate_khz = link_info->num_lanes * rate * 8; + + if (mode_rate_khz > supported_rate_khz) { + DP_DEBUG("mode_rate: %d kHz, supported_rate: %d kHz\n", + mode_rate_khz, supported_rate_khz); + return -EPERM; + } + + return 0; +} + +static int dp_display_validate_pixel_clock(struct dp_display_mode dp_mode, + u32 max_pclk_khz) +{ + u32 pclk_khz = dp_mode.timing.widebus_en ? + (dp_mode.timing.pixel_clk_khz >> 1) : + dp_mode.timing.pixel_clk_khz; + + if (pclk_khz > max_pclk_khz) { + DP_DEBUG("clk: %d kHz, max: %d kHz\n", pclk_khz, max_pclk_khz); + return -EPERM; + } + + return 0; +} + +static int dp_display_validate_mixers(struct msm_drm_private *priv, + struct drm_display_mode *mode, + const struct msm_resource_caps_info *avail_res) +{ + int rc; + u32 num_lm = 0; + + rc = msm_get_mixer_count(priv, mode, avail_res, &num_lm); + if (rc) { + DP_ERR("error getting mixer count. rc:%d\n", rc); + return rc; + } + + if (num_lm > avail_res->num_lm || + (num_lm == 2 && !avail_res->num_3dmux)) { + DP_DEBUG("num_lm:%d, req lm:%d 3dmux:%d\n", num_lm, + avail_res->num_lm, avail_res->num_3dmux); + return -EPERM; + } + + return 0; +} + +static void dp_display_validate_mst_connectors(struct dp_debug *debug, + struct dp_panel *dp_panel, struct drm_display_mode *mode, + enum drm_mode_status *mode_status, bool *use_default) +{ + struct dp_mst_connector *mst_connector; + int hdis, vdis, vref, ar, _hdis, _vdis, _vref, _ar; + bool in_list = false; + + /* + * If the connector exists in the mst connector list and if debug is + * enabled for that connector, use the mst connector settings from the + * list for validation. Otherwise, use non-mst default settings. + */ + mutex_lock(&debug->dp_mst_connector_list.lock); + + if (list_empty(&debug->dp_mst_connector_list.list)) { + mutex_unlock(&debug->dp_mst_connector_list.lock); + *use_default = true; + return; + } + + list_for_each_entry(mst_connector, &debug->dp_mst_connector_list.list, + list) { + if (mst_connector->con_id != dp_panel->connector->base.id) + continue; + + in_list = true; + + if (!mst_connector->debug_en) { + mutex_unlock(&debug->dp_mst_connector_list.lock); + *use_default = false; + *mode_status = MODE_OK; + return; + } + + hdis = mst_connector->hdisplay; + vdis = mst_connector->vdisplay; + vref = mst_connector->vrefresh; + ar = mst_connector->aspect_ratio; + + _hdis = mode->hdisplay; + _vdis = mode->vdisplay; + _vref = mode->vrefresh; + _ar = mode->picture_aspect_ratio; + + if (hdis == _hdis && vdis == _vdis && vref == _vref && + ar == _ar) { + mutex_unlock(&debug->dp_mst_connector_list.lock); + *use_default = false; + *mode_status = MODE_OK; + return; + } + + break; + } + + mutex_unlock(&debug->dp_mst_connector_list.lock); + + if (in_list) { + *use_default = false; + return; + } + + *use_default = true; +} + static enum drm_mode_status dp_display_validate_mode( struct dp_display *dp_display, void *panel, struct drm_display_mode *mode, const struct msm_resource_caps_info *avail_res) { struct dp_display_private *dp; - struct drm_dp_link *link_info; - u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0; struct dp_panel *dp_panel; struct dp_debug *debug; enum drm_mode_status mode_status = MODE_BAD; - bool in_list = false; - struct dp_mst_connector *mst_connector; - int hdis, vdis, vref, ar, _hdis, _vdis, _vref, _ar, rate; struct dp_display_mode dp_mode; - bool dsc_en; - u32 num_lm = 0; int rc = 0; - u32 pclk_khz = 0; + bool use_default = true; if (!dp_display || !mode || !panel || !avail_res || !avail_res->max_mixer_width) { @@ -2101,104 +2267,29 @@ static enum drm_mode_status dp_display_validate_mode( goto end; } - link_info = &dp->panel->link_info; - debug = dp->debug; if (!debug) goto end; dp_display->convert_to_dp_mode(dp_display, panel, mode, &dp_mode); - dsc_en = (dp_mode.timing.comp_info.comp_ratio > 1) ? true : false; - mode_bpp = dsc_en ? - DSC_BPP(dp_mode.timing.comp_info.dsc_info.config) - : dp_mode.timing.bpp; - - mode_rate_khz = mode->clock * mode_bpp; - rate = drm_dp_bw_code_to_link_rate(dp->link->link_params.bw_code); - supported_rate_khz = link_info->num_lanes * rate * 8; - - if (mode_rate_khz > supported_rate_khz) { - DP_MST_DEBUG("pclk:%d, supported_rate:%d\n", - mode->clock, supported_rate_khz); - goto end; - } - - pclk_khz = dp_mode.timing.widebus_en ? - (dp_mode.timing.pixel_clk_khz >> 1) : - (dp_mode.timing.pixel_clk_khz); - - if (pclk_khz > dp_display->max_pclk_khz) { - DP_MST_DEBUG("clk:%d, max:%d\n", pclk_khz, - dp_display->max_pclk_khz); - goto end; - } - - rc = msm_get_mixer_count(dp->priv, mode, avail_res, &num_lm); - if (rc) { - DP_ERR("error getting mixer count. rc:%d\n", rc); - goto end; - } - - if (num_lm > avail_res->num_lm || - (num_lm == 2 && !avail_res->num_3dmux)) { - DP_MST_DEBUG("num_lm:%d, req lm:%d 3dmux:%d\n", num_lm, - avail_res->num_lm, avail_res->num_3dmux); - goto end; - } - - /* - * If the connector exists in the mst connector list and if debug is - * enabled for that connector, use the mst connector settings from the - * list for validation. Otherwise, use non-mst default settings. - */ - mutex_lock(&debug->dp_mst_connector_list.lock); - - if (list_empty(&debug->dp_mst_connector_list.list)) { - mutex_unlock(&debug->dp_mst_connector_list.lock); - goto verify_default; - } - - list_for_each_entry(mst_connector, &debug->dp_mst_connector_list.list, - list) { - if (mst_connector->con_id == dp_panel->connector->base.id) { - in_list = true; - - if (!mst_connector->debug_en) { - mode_status = MODE_OK; - mutex_unlock( - &debug->dp_mst_connector_list.lock); - goto end; - } - - hdis = mst_connector->hdisplay; - vdis = mst_connector->vdisplay; - vref = mst_connector->vrefresh; - ar = mst_connector->aspect_ratio; - - _hdis = mode->hdisplay; - _vdis = mode->vdisplay; - _vref = mode->vrefresh; - _ar = mode->picture_aspect_ratio; - - if (hdis == _hdis && vdis == _vdis && vref == _vref && - ar == _ar) { - mode_status = MODE_OK; - mutex_unlock( - &debug->dp_mst_connector_list.lock); - goto end; - } - - break; - } - } - - mutex_unlock(&debug->dp_mst_connector_list.lock); - - if (in_list) + rc = dp_display_validate_link_clock(dp, mode, dp_mode); + if (rc) + goto end; + + rc = dp_display_validate_pixel_clock(dp_mode, dp_display->max_pclk_khz); + if (rc) + goto end; + + rc = dp_display_validate_mixers(dp->priv, mode, avail_res); + if (rc) + goto end; + + dp_display_validate_mst_connectors(debug, dp_panel, mode, &mode_status, + &use_default); + if (!use_default) goto end; -verify_default: if (debug->debug_en && (mode->hdisplay != debug->hdisplay || mode->vdisplay != debug->vdisplay || mode->vrefresh != debug->vrefresh || From cf7ed76bdd52dc13eef2bc62385c8e0bffe3c47f Mon Sep 17 00:00:00 2001 From: Tatenda Chipeperekwa Date: Tue, 29 Oct 2019 15:06:56 -0700 Subject: [PATCH 02/18] disp: msm: dp: add more state and event based logging Add more state and event based logging in order to enhance state transition and use case based debugging in the driver. Change-Id: I00dee6fcff832c104d3f8b651635a41d9cfc6921 Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_display.c | 107 +++++++++++++++++++++++++++++++++++++------- msm/sde_dbg.h | 14 +++++- 2 files changed, 104 insertions(+), 17 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 7db54515520d..e027060640a6 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -28,16 +28,27 @@ #include "sde_hdcp.h" #include "dp_debug.h" #include "dp_pll.h" +#include "sde_dbg.h" #define DP_MST_DEBUG(fmt, ...) DP_DEBUG(fmt, ##__VA_ARGS__) #define dp_display_state_show(x) { \ DP_ERR("%s: state (0x%x): %s\n", x, dp->state, \ - dp_display_state_name(dp->state)); } + dp_display_state_name(dp->state)); \ + SDE_EVT32_EXTERNAL(dp->state); } + +#define dp_display_state_log(x) { \ + DP_DEBUG("%s: state (0x%x): %s\n", x, dp->state, \ + dp_display_state_name(dp->state)); \ + SDE_EVT32_EXTERNAL(dp->state); } #define dp_display_state_is(x) (dp->state & (x)) -#define dp_display_state_add(x) (dp->state |= (x)) -#define dp_display_state_remove(x) (dp->state &= ~(x)) +#define dp_display_state_add(x) { \ + (dp->state |= (x)); \ + dp_display_state_log("add "#x); } +#define dp_display_state_remove(x) { \ + (dp->state &= ~(x)); \ + dp_display_state_log("remove "#x); } enum dp_display_states { DP_STATE_DISCONNECTED = 0, @@ -721,6 +732,7 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp) int ret = 0; bool hpd = !!dp_display_state_is(DP_STATE_CONNECTED); + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state, hpd); dp->aux->state |= DP_STATE_NOTIFICATION_SENT; if (!dp->mst.mst_active) @@ -743,8 +755,10 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp) DP_WARN("%s timeout\n", hpd ? "connect" : "disconnect"); ret = -EINVAL; } + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, hpd, ret); return ret; skip_wait: + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, hpd, ret); return 0; } @@ -818,8 +832,10 @@ static void dp_display_host_init(struct dp_display_private *dp) bool flip = false; bool reset; - if (dp_display_state_is(DP_STATE_INITIALIZED)) + if (dp_display_state_is(DP_STATE_INITIALIZED)) { + dp_display_state_log("[already initialized]"); return; + } if (dp->hpd->orientation == ORIENTATION_CC2) flip = true; @@ -840,39 +856,54 @@ static void dp_display_host_init(struct dp_display_private *dp) static void dp_display_host_ready(struct dp_display_private *dp) { - if (!dp_display_state_is(DP_STATE_INITIALIZED)) + if (!dp_display_state_is(DP_STATE_INITIALIZED)) { + dp_display_state_show("[not initialized]"); return; + } - if (dp_display_state_is(DP_STATE_READY)) + if (dp_display_state_is(DP_STATE_READY)) { + dp_display_state_log("[already ready]"); return; + } dp->aux->init(dp->aux, dp->parser->aux_cfg); dp->panel->init(dp->panel); dp_display_state_add(DP_STATE_READY); + /* log this as it results from user action of cable connection */ + DP_INFO("[OK]\n"); } static void dp_display_host_unready(struct dp_display_private *dp) { - if (!dp_display_state_is(DP_STATE_INITIALIZED)) + if (!dp_display_state_is(DP_STATE_INITIALIZED)) { + dp_display_state_show("[not initialized]"); return; + } - if (!dp_display_state_is(DP_STATE_READY)) + if (!dp_display_state_is(DP_STATE_READY)) { + dp_display_state_show("[not ready]"); return; + } dp_display_state_remove(DP_STATE_READY); dp->aux->deinit(dp->aux); + /* log this as it results from user action of cable disconnection */ + DP_INFO("[OK]\n"); } static void dp_display_host_deinit(struct dp_display_private *dp) { if (dp->active_stream_cnt) { + SDE_EVT32_EXTERNAL(dp->state, dp->active_stream_cnt); DP_DEBUG("active stream present\n"); return; } - if (!dp_display_state_is(DP_STATE_INITIALIZED)) + if (!dp_display_state_is(DP_STATE_INITIALIZED)) { + dp_display_state_show("[not initialized]"); return; + } dp->ctrl->deinit(dp->ctrl); dp->hpd->host_deinit(dp->hpd, &dp->catalog->hpd); @@ -890,6 +921,7 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) { int rc = -EINVAL; + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); if (dp_display_state_is(DP_STATE_CONNECTED)) { @@ -943,6 +975,7 @@ end: if (!rc && !dp_display_state_is(DP_STATE_ABORTED)) dp_display_send_hpd_notification(dp); + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, rc); return rc; } @@ -1084,6 +1117,8 @@ static void dp_display_clean(struct dp_display_private *dp) struct dp_panel *dp_panel; struct dp_link_hdcp_status *status = &dp->link->hdcp_status; + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); + if (dp_display_is_hdcp_enabled(dp) && status->hdcp_state != HDCP_STATE_INACTIVE) { cancel_delayed_work_sync(&dp->hdcp_cb_work); @@ -1109,12 +1144,14 @@ static void dp_display_clean(struct dp_display_private *dp) dp_display_state_remove(DP_STATE_ENABLED | DP_STATE_CONNECTED); dp->ctrl->off(dp->ctrl); + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state); } static int dp_display_handle_disconnect(struct dp_display_private *dp) { int rc; + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); rc = dp_display_process_hpd_low(dp); if (rc) { /* cancel any pending request */ @@ -1131,6 +1168,7 @@ static int dp_display_handle_disconnect(struct dp_display_private *dp) mutex_unlock(&dp->session_lock); + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state); return rc; } @@ -1168,6 +1206,8 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev) goto end; } + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state, + dp->debug->psm_enabled); dp_display_state_remove(DP_STATE_CONFIGURED); if (dp->debug->psm_enabled && dp_display_state_is(DP_STATE_READY)) @@ -1178,6 +1218,8 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev) if (!dp->debug->sim_mode && !dp->parser->no_aux_switch && !dp->parser->gpio_aux_switch) dp->aux->aux_switch(dp->aux, false, ORIENTATION_NONE); + + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state); end: return rc; } @@ -1226,7 +1268,9 @@ static void dp_display_attention_work(struct work_struct *work) struct dp_display_private *dp = container_of(work, struct dp_display_private, attention_work); + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); + SDE_EVT32_EXTERNAL(dp->state); if (dp->debug->mst_hpd_sim || !dp_display_state_is(DP_STATE_READY)) { mutex_unlock(&dp->session_lock); @@ -1239,8 +1283,10 @@ static void dp_display_attention_work(struct work_struct *work) } mutex_unlock(&dp->session_lock); + SDE_EVT32_EXTERNAL(dp->state, dp->link->sink_request); if (dp->link->sink_request & DS_PORT_STATUS_CHANGED) { + SDE_EVT32_EXTERNAL(dp->state, DS_PORT_STATUS_CHANGED); if (dp_display_is_sink_count_zero(dp)) { dp_display_handle_disconnect(dp); } else { @@ -1252,6 +1298,7 @@ static void dp_display_attention_work(struct work_struct *work) } if (dp->link->sink_request & DP_TEST_LINK_VIDEO_PATTERN) { + SDE_EVT32_EXTERNAL(dp->state, DP_TEST_LINK_VIDEO_PATTERN); dp_display_handle_disconnect(dp); dp->panel->video_test = true; @@ -1267,16 +1314,22 @@ static void dp_display_attention_work(struct work_struct *work) dp_audio_enable(dp, false); mutex_unlock(&dp->session_lock); - if (dp->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) + if (dp->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) { + SDE_EVT32_EXTERNAL(dp->state, + DP_TEST_LINK_PHY_TEST_PATTERN); dp->ctrl->process_phy_test_request(dp->ctrl); + } if (dp->link->sink_request & DP_TEST_LINK_TRAINING) { + SDE_EVT32_EXTERNAL(dp->state, DP_TEST_LINK_TRAINING); dp->link->send_test_response(dp->link); dp->ctrl->link_maintenance(dp->ctrl); } - if (dp->link->sink_request & DP_LINK_STATUS_UPDATED) + if (dp->link->sink_request & DP_LINK_STATUS_UPDATED) { + SDE_EVT32_EXTERNAL(dp->state, DP_LINK_STATUS_UPDATED); dp->ctrl->link_maintenance(dp->ctrl); + } mutex_lock(&dp->session_lock); dp_audio_enable(dp, true); @@ -1292,6 +1345,7 @@ cp_irq: dp->hdcp.ops->cp_irq(dp->hdcp.data); mst_attention: dp_display_mst_attention(dp); + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state); } static int dp_display_usbpd_attention_cb(struct device *dev) @@ -1313,6 +1367,9 @@ static int dp_display_usbpd_attention_cb(struct device *dev) dp->hpd->hpd_irq, dp->hpd->hpd_high, !!dp_display_state_is(DP_STATE_ENABLED), !!dp_display_state_is(DP_STATE_CONNECTED)); + SDE_EVT32_EXTERNAL(dp->state, dp->hpd->hpd_irq, dp->hpd->hpd_high, + !!dp_display_state_is(DP_STATE_ENABLED), + !!dp_display_state_is(DP_STATE_CONNECTED)); if (!dp->hpd->hpd_high) { dp_display_disconnect_sync(dp); @@ -1697,13 +1754,18 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel) dp = container_of(dp_display, struct dp_display_private, dp_display); + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); - if (dp_display_state_is(DP_STATE_ABORTED | DP_STATE_ENABLED)) + if (dp_display_state_is(DP_STATE_ABORTED | DP_STATE_ENABLED)) { + dp_display_state_show("[not initialized]"); goto end; + } - if (!dp_display_is_ready(dp)) + if (!dp_display_is_ready(dp)) { + dp_display_state_show("[not ready]"); goto end; + } /* For supporting DP_PANEL_SRC_INITIATED_POWER_DOWN case */ dp_display_host_init(dp); @@ -1732,6 +1794,7 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel) end: mutex_unlock(&dp->session_lock); + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state); return 0; } @@ -1813,6 +1876,7 @@ static int dp_display_enable(struct dp_display *dp_display, void *panel) dp = container_of(dp_display, struct dp_display_private, dp_display); + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); if (!dp_display_state_is(DP_STATE_READY)) { @@ -1820,8 +1884,10 @@ static int dp_display_enable(struct dp_display *dp_display, void *panel) goto end; } - if (dp_display_state_is(DP_STATE_ABORTED)) + if (dp_display_state_is(DP_STATE_ABORTED)) { + dp_display_state_show("[aborted]"); goto end; + } rc = dp_display_stream_enable(dp, panel); if (rc) @@ -1831,6 +1897,7 @@ static int dp_display_enable(struct dp_display *dp_display, void *panel) dp_display_state_add(DP_STATE_ENABLED); end: mutex_unlock(&dp->session_lock); + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state); return rc; } @@ -1854,6 +1921,7 @@ static int dp_display_post_enable(struct dp_display *dp_display, void *panel) dp = container_of(dp_display, struct dp_display_private, dp_display); dp_panel = panel; + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); if (!dp_display_state_is(DP_STATE_ENABLED)) { @@ -1861,8 +1929,10 @@ static int dp_display_post_enable(struct dp_display *dp_display, void *panel) goto end; } - if (dp_display_state_is(DP_STATE_ABORTED)) + if (dp_display_state_is(DP_STATE_ABORTED)) { + dp_display_state_show("[aborted]"); goto end; + } if (!dp_display_is_ready(dp) || !dp_display_state_is(DP_STATE_READY)) { dp_display_state_show("[not ready]"); @@ -1884,6 +1954,7 @@ end: complete_all(&dp->notification_comp); mutex_unlock(&dp->session_lock); + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state); return 0; } @@ -1912,6 +1983,7 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel) dp = container_of(dp_display, struct dp_display_private, dp_display); + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); status = &dp->link->hdcp_status; @@ -1963,6 +2035,7 @@ clean: end: mutex_unlock(&dp->session_lock); + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state); return 0; } @@ -1982,6 +2055,7 @@ static int dp_display_disable(struct dp_display *dp_display, void *panel) dp_panel = panel; status = &dp->link->hdcp_status; + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); if (!dp_display_state_is(DP_STATE_ENABLED)) { @@ -2008,6 +2082,7 @@ static int dp_display_disable(struct dp_display *dp_display, void *panel) } end: mutex_unlock(&dp->session_lock); + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state); return 0; } @@ -2069,6 +2144,7 @@ static int dp_display_unprepare(struct dp_display *dp_display, void *panel) dp = container_of(dp_display, struct dp_display_private, dp_display); + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); /* @@ -2102,6 +2178,7 @@ static int dp_display_unprepare(struct dp_display *dp_display, void *panel) end: dp_panel->deinit(dp_panel, flags); mutex_unlock(&dp->session_lock); + SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state); return 0; } diff --git a/msm/sde_dbg.h b/msm/sde_dbg.h index 9bec29cecf44..da7fde80b046 100644 --- a/msm/sde_dbg.h +++ b/msm/sde_dbg.h @@ -34,6 +34,7 @@ enum sde_dbg_evtlog_flag { SDE_EVTLOG_CRITICAL = BIT(0), SDE_EVTLOG_IRQ = BIT(1), SDE_EVTLOG_VERBOSE = BIT(2), + SDE_EVTLOG_EXTERNAL = BIT(3), SDE_EVTLOG_ALWAYS = -1 }; @@ -48,7 +49,8 @@ enum sde_dbg_dump_context { SDE_DBG_DUMP_CLK_ENABLED_CTX, }; -#define SDE_EVTLOG_DEFAULT_ENABLE (SDE_EVTLOG_CRITICAL | SDE_EVTLOG_IRQ) +#define SDE_EVTLOG_DEFAULT_ENABLE (SDE_EVTLOG_CRITICAL | SDE_EVTLOG_IRQ | \ + SDE_EVTLOG_EXTERNAL) /* * evtlog will print this number of entries when it is called through @@ -62,7 +64,7 @@ enum sde_dbg_dump_context { * number must be greater than print entry to prevent out of bound evtlog * entry array access. */ -#define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 8) +#define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 32) #define SDE_EVTLOG_MAX_DATA 15 #define SDE_EVTLOG_BUF_MAX 512 #define SDE_EVTLOG_BUF_ALIGN 32 @@ -125,6 +127,14 @@ extern struct sde_dbg_evtlog *sde_dbg_base_evtlog; __LINE__, SDE_EVTLOG_IRQ, ##__VA_ARGS__, \ SDE_EVTLOG_DATA_LIMITER) +/** + * SDE_EVT32_EXTERNAL - Write a list of 32bit values for external display events + * ... - variable arguments + */ +#define SDE_EVT32_EXTERNAL(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \ + __LINE__, SDE_EVTLOG_EXTERNAL, ##__VA_ARGS__, \ + SDE_EVTLOG_DATA_LIMITER) + /** * SDE_DBG_DUMP - trigger dumping of all sde_dbg facilities * @va_args: list of named register dump ranges and regions to dump, as From 3e26909b0a1a110c1562d1fcbd14f083062afe49 Mon Sep 17 00:00:00 2001 From: Tatenda Chipeperekwa Date: Fri, 8 Nov 2019 12:55:44 +0530 Subject: [PATCH 03/18] disp: msm: dp: abort aux before going to dp suspend During resume, DP uses AUX to perform various functionalities like DPCD/EDID read or link training etc. This involves other hardware modules as well like USB and Charger. In a situation like continuous suspend/resume, while DP is processing resume, suspend can trigger resulting in dependent hardware modules to go to sleep. As AUX communication is hardware interrupt based, this can result in unstable system. Abort all functionalities before going to suspend to avoid unnecessary AUX and other functionality failures. Change-Id: Id52d408270232adf7258a7eb064ee969eba4be71 Signed-off-by: Sankeerth Billakanti Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_aux.c | 4 ++-- msm/dp/dp_aux.h | 2 +- msm/dp/dp_ctrl.c | 4 ++-- msm/dp/dp_ctrl.h | 4 ++-- msm/dp/dp_debug.c | 4 ++-- msm/dp/dp_display.c | 40 ++++++++++++++++++++++++++++++++++++---- 6 files changed, 45 insertions(+), 13 deletions(-) diff --git a/msm/dp/dp_aux.c b/msm/dp/dp_aux.c index 907ccdb0b087..7d73abe4f47f 100644 --- a/msm/dp/dp_aux.c +++ b/msm/dp/dp_aux.c @@ -310,7 +310,7 @@ static void dp_aux_reconfig(struct dp_aux *dp_aux) aux->catalog->reset(aux->catalog); } -static void dp_aux_abort_transaction(struct dp_aux *dp_aux) +static void dp_aux_abort_transaction(struct dp_aux *dp_aux, bool abort) { struct dp_aux_private *aux; @@ -321,7 +321,7 @@ static void dp_aux_abort_transaction(struct dp_aux *dp_aux) aux = container_of(dp_aux, struct dp_aux_private, dp_aux); - atomic_set(&aux->aborted, 1); + atomic_set(&aux->aborted, abort); } static void dp_aux_update_offset_and_segment(struct dp_aux_private *aux, diff --git a/msm/dp/dp_aux.h b/msm/dp/dp_aux.h index e7339fc35bc2..850941b1e53d 100644 --- a/msm/dp/dp_aux.h +++ b/msm/dp/dp_aux.h @@ -50,7 +50,7 @@ struct dp_aux { void (*init)(struct dp_aux *aux, struct dp_aux_cfg *aux_cfg); void (*deinit)(struct dp_aux *aux); void (*reconfig)(struct dp_aux *aux); - void (*abort)(struct dp_aux *aux); + void (*abort)(struct dp_aux *aux, bool abort); void (*dpcd_updated)(struct dp_aux *aux); void (*set_sim_mode)(struct dp_aux *aux, bool en, u8 *edid, u8 *dpcd); int (*aux_switch)(struct dp_aux *aux, bool enable, int orientation); diff --git a/msm/dp/dp_ctrl.c b/msm/dp/dp_ctrl.c index be90c50c0e52..1b81192c0278 100644 --- a/msm/dp/dp_ctrl.c +++ b/msm/dp/dp_ctrl.c @@ -102,7 +102,7 @@ static void dp_ctrl_video_ready(struct dp_ctrl_private *ctrl) complete(&ctrl->video_comp); } -static void dp_ctrl_abort(struct dp_ctrl *dp_ctrl) +static void dp_ctrl_abort(struct dp_ctrl *dp_ctrl, bool abort) { struct dp_ctrl_private *ctrl; @@ -113,7 +113,7 @@ static void dp_ctrl_abort(struct dp_ctrl *dp_ctrl) ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); - atomic_set(&ctrl->aborted, 1); + atomic_set(&ctrl->aborted, abort); } static void dp_ctrl_state_ctrl(struct dp_ctrl_private *ctrl, u32 state) diff --git a/msm/dp/dp_ctrl.h b/msm/dp/dp_ctrl.h index ac6af632a342..50b3d48ab9c6 100644 --- a/msm/dp/dp_ctrl.h +++ b/msm/dp/dp_ctrl.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. */ #ifndef _DP_CTRL_H_ @@ -19,7 +19,7 @@ struct dp_ctrl { int (*on)(struct dp_ctrl *dp_ctrl, bool mst_mode, bool fec_en, bool dsc_en, bool shallow); void (*off)(struct dp_ctrl *dp_ctrl); - void (*abort)(struct dp_ctrl *dp_ctrl); + void (*abort)(struct dp_ctrl *dp_ctrl, bool abort); void (*isr)(struct dp_ctrl *dp_ctrl); bool (*handle_sink_request)(struct dp_ctrl *dp_ctrl); void (*process_phy_test_request)(struct dp_ctrl *dp_ctrl); diff --git a/msm/dp/dp_debug.c b/msm/dp/dp_debug.c index 3ddb4d9f6989..54cdb3c008dc 100644 --- a/msm/dp/dp_debug.c +++ b/msm/dp/dp_debug.c @@ -1596,8 +1596,8 @@ static void dp_debug_set_sim_mode(struct dp_debug_private *debug, bool sim) debug->hpd->simulate_connect(debug->hpd, false); debug->hotplug = false; } - debug->aux->abort(debug->aux); - debug->ctrl->abort(debug->ctrl); + debug->aux->abort(debug->aux, true); + debug->ctrl->abort(debug->ctrl, true); debug->aux->set_sim_mode(debug->aux, false, NULL, NULL); debug->dp_debug.sim_mode = false; diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index e027060640a6..ce58c17fe11d 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -1155,8 +1155,8 @@ static int dp_display_handle_disconnect(struct dp_display_private *dp) rc = dp_display_process_hpd_low(dp); if (rc) { /* cancel any pending request */ - dp->ctrl->abort(dp->ctrl); - dp->aux->abort(dp->aux); + dp->ctrl->abort(dp->ctrl, true); + dp->aux->abort(dp->aux, true); } mutex_lock(&dp->session_lock); @@ -1177,8 +1177,8 @@ static void dp_display_disconnect_sync(struct dp_display_private *dp) /* cancel any pending request */ dp_display_state_add(DP_STATE_ABORTED); - dp->ctrl->abort(dp->ctrl); - dp->aux->abort(dp->aux); + dp->ctrl->abort(dp->ctrl, true); + dp->aux->abort(dp->aux, true); /* wait for idle state */ cancel_work_sync(&dp->connect_work); @@ -3104,9 +3104,25 @@ static int dp_pm_prepare(struct device *dev) struct dp_display_private *dp = container_of(g_dp_display, struct dp_display_private, dp_display); + mutex_lock(&dp->session_lock); dp_display_set_mst_state(g_dp_display, PM_SUSPEND); + /* + * There are a few instances where the DP is hotplugged when the device + * is in PM suspend state. After hotplug, it is observed the device + * enters and exits the PM suspend multiple times while aux transactions + * are taking place. This may sometimes cause an unclocked register + * access error. So, abort aux transactions when such a situation + * arises i.e. when DP is connected but display not enabled yet. + */ + if (dp_display_state_is(DP_STATE_CONNECTED) && + !dp_display_state_is(DP_STATE_ENABLED)) { + dp->aux->abort(dp->aux, true); + dp->ctrl->abort(dp->ctrl, true); + } + dp_display_state_add(DP_STATE_SUSPENDED); + mutex_unlock(&dp->session_lock); return 0; } @@ -3116,9 +3132,25 @@ static void dp_pm_complete(struct device *dev) struct dp_display_private *dp = container_of(g_dp_display, struct dp_display_private, dp_display); + mutex_lock(&dp->session_lock); dp_display_set_mst_state(g_dp_display, PM_DEFAULT); + /* + * There are multiple PM suspend entry and exits observed before + * the connect uevent is issued to userspace. The aux transactions are + * aborted during PM suspend entry in dp_pm_prepare to prevent unclocked + * register access. On PM suspend exit, there will be no host_init call + * to reset the abort flags for ctrl and aux incase DP is connected + * but display not enabled. So, resetting abort flags for aux and ctrl. + */ + if (dp_display_state_is(DP_STATE_CONNECTED) && + !dp_display_state_is(DP_STATE_ENABLED)) { + dp->aux->abort(dp->aux, false); + dp->ctrl->abort(dp->ctrl, false); + } + dp_display_state_remove(DP_STATE_SUSPENDED); + mutex_unlock(&dp->session_lock); } static const struct dev_pm_ops dp_pm_ops = { From f7de0942330098c5f23809609f9c012d2202a091 Mon Sep 17 00:00:00 2001 From: Tatenda Chipeperekwa Date: Tue, 5 Nov 2019 12:25:18 -0500 Subject: [PATCH 04/18] disp: msm: dp: Enforce HDCP 2.3 timing requirements The HDCP 2.3 specification added new timing requirements for message read duration. Ensure that these requirements are enforced in the DP display layer. Delay for a specification-allowed amount of time before reading messages to ensure that sinks have enough time to process messages and make their replies available to read. Change-Id: I40b210823a7cbaca6efc12abc4c8e8b98a10e071 Signed-off-by: Fuad Hossain Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_hdcp2p2.c | 34 +++++++++++++++-------- msm/sde_hdcp_2x.c | 68 ++++++++++++++++++++++++++++++++------------- msm/sde_hdcp_2x.h | 29 +++++++++++-------- 3 files changed, 87 insertions(+), 44 deletions(-) diff --git a/msm/dp/dp_hdcp2p2.c b/msm/dp/dp_hdcp2p2.c index b87cfee52657..bc2a25413965 100644 --- a/msm/dp/dp_hdcp2p2.c +++ b/msm/dp/dp_hdcp2p2.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. */ #include @@ -44,7 +44,8 @@ struct dp_hdcp2p2_ctrl { struct hdcp2_buffer response; struct hdcp2_buffer request; uint32_t total_message_length; - uint32_t timeout; + uint32_t transaction_delay; + uint32_t transaction_timeout; struct sde_hdcp_2x_msg_part msg_part[HDCP_MAX_MESSAGE_PARTS]; u8 sink_rx_status; u8 rx_status; @@ -108,7 +109,6 @@ static int dp_hdcp2p2_copy_buf(struct dp_hdcp2p2_ctrl *ctrl, mutex_lock(&ctrl->msg_lock); - ctrl->timeout = data->timeout; num_messages = data->message_data->num_messages; ctrl->total_message_length = 0; /* Total length of all messages */ @@ -132,6 +132,9 @@ static int dp_hdcp2p2_copy_buf(struct dp_hdcp2p2_ctrl *ctrl, ctrl->request.data = data->buf; ctrl->request.length = ctrl->total_message_length; + ctrl->transaction_delay = data->transaction_delay; + ctrl->transaction_timeout = data->transaction_timeout; + mutex_unlock(&ctrl->msg_lock); return 0; @@ -170,7 +173,6 @@ static void dp_hdcp2p2_set_interrupts(struct dp_hdcp2p2_ctrl *ctrl, bool enable) static int dp_hdcp2p2_wakeup(struct hdcp_transport_wakeup_data *data) { struct dp_hdcp2p2_ctrl *ctrl; - u32 const default_timeout_us = 500; if (!data) { DP_ERR("invalid input\n"); @@ -183,11 +185,6 @@ static int dp_hdcp2p2_wakeup(struct hdcp_transport_wakeup_data *data) return -EINVAL; } - if (data->timeout) - ctrl->timeout = (data->timeout) * 2; - else - ctrl->timeout = default_timeout_us; - if (dp_hdcp2p2_copy_buf(ctrl, data)) goto exit; @@ -364,6 +361,8 @@ static int dp_hdcp2p2_aux_read_message(struct dp_hdcp2p2_ctrl *ctrl) int rc = 0, max_size = 16, read_size = 0, bytes_read = 0; int size = ctrl->request.length, offset = ctrl->msg_part->offset; u8 *buf = ctrl->request.data; + s64 diff_ms; + ktime_t start_read, finish_read; if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE || atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTH_FAIL) { @@ -380,6 +379,7 @@ static int dp_hdcp2p2_aux_read_message(struct dp_hdcp2p2_ctrl *ctrl) DP_DEBUG("offset(0x%x), size(%d)\n", offset, size); + start_read = ktime_get(); do { read_size = min(size, max_size); @@ -396,7 +396,14 @@ static int dp_hdcp2p2_aux_read_message(struct dp_hdcp2p2_ctrl *ctrl) offset += read_size; size -= read_size; } while (size > 0); + finish_read = ktime_get(); + diff_ms = ktime_ms_delta(finish_read, start_read); + if (ctrl->transaction_timeout && diff_ms > ctrl->transaction_timeout) { + DP_ERR("HDCP read timeout exceeded (%dms > %dms)\n", diff_ms, + ctrl->transaction_timeout); + rc = -ETIMEDOUT; + } exit: return rc; } @@ -485,7 +492,7 @@ static void dp_hdcp2p2_send_msg(struct dp_hdcp2p2_ctrl *ctrl) rc = dp_hdcp2p2_aux_write_message(ctrl, ctrl->response.data, ctrl->response.length, ctrl->msg_part->offset, - ctrl->timeout); + ctrl->transaction_delay); if (rc) { DP_ERR("Error sending msg to sink %d\n", rc); mutex_unlock(&ctrl->msg_lock); @@ -493,7 +500,7 @@ static void dp_hdcp2p2_send_msg(struct dp_hdcp2p2_ctrl *ctrl) } cdata.cmd = HDCP_2X_CMD_MSG_SEND_SUCCESS; - cdata.timeout = ctrl->timeout; + cdata.timeout = ctrl->transaction_delay; mutex_unlock(&ctrl->msg_lock); exit: @@ -519,7 +526,7 @@ static int dp_hdcp2p2_get_msg_from_sink(struct dp_hdcp2p2_ctrl *ctrl) } cdata.total_message_length = ctrl->total_message_length; - cdata.timeout = ctrl->timeout; + cdata.timeout = ctrl->transaction_delay; exit: if (rc == -ETIMEDOUT) cdata.cmd = HDCP_2X_CMD_MSG_RECV_TIMEOUT; @@ -544,6 +551,9 @@ static void dp_hdcp2p2_recv_msg(struct dp_hdcp2p2_ctrl *ctrl) return; } + if (ctrl->transaction_delay) + msleep(ctrl->transaction_delay); + dp_hdcp2p2_get_msg_from_sink(ctrl); } diff --git a/msm/sde_hdcp_2x.c b/msm/sde_hdcp_2x.c index a3bc7b61854e..28cf474b3a79 100644 --- a/msm/sde_hdcp_2x.c +++ b/msm/sde_hdcp_2x.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "[sde-hdcp-2x] %s: " fmt, __func__ @@ -42,6 +42,9 @@ #define REAUTH_REQ BIT(3) #define LINK_INTEGRITY_FAILURE BIT(4) +/* Temporary define to override wrong TZ value */ +#define AKE_SEND_CERT_MSG_DELAY 100 + struct sde_hdcp_2x_ctrl { DECLARE_KFIFO(cmd_q, enum sde_hdcp_2x_wakeup_cmd, 8); wait_queue_head_t wait_q; @@ -100,49 +103,49 @@ static const struct sde_hdcp_2x_msg_data hdcp_msg_lookup[HDCP2P2_MAX_MESSAGES] = { [AKE_INIT] = { 2, { {"rtx", 0x69000, 8}, {"TxCaps", 0x69008, 3} }, - 0 }, + 0, 0 }, [AKE_SEND_CERT] = { 3, { {"cert-rx", 0x6900B, 522}, {"rrx", 0x69215, 8}, {"RxCaps", 0x6921D, 3} }, - 0 }, + 0, 110 }, [AKE_NO_STORED_KM] = { 1, { {"Ekpub_km", 0x69220, 128} }, - 0 }, + 0, 0 }, [AKE_STORED_KM] = { 2, { {"Ekh_km", 0x692A0, 16}, {"m", 0x692B0, 16} }, - 0 }, + 0, 0 }, [AKE_SEND_H_PRIME] = { 1, { {"H'", 0x692C0, 32} }, - (1 << 1) }, + (1 << 1), 7 }, [AKE_SEND_PAIRING_INFO] = { 1, { {"Ekh_km", 0x692E0, 16} }, - (1 << 2) }, + (1 << 2), 5 }, [LC_INIT] = { 1, { {"rn", 0x692F0, 8} }, - 0 }, + 0, 0 }, [LC_SEND_L_PRIME] = { 1, { {"L'", 0x692F8, 32} }, - 0 }, + 0, 0 }, [SKE_SEND_EKS] = { 2, { {"Edkey_ks", 0x69318, 16}, {"riv", 0x69328, 8} }, - 0 }, + 0, 0 }, [SKE_SEND_TYPE_ID] = { 1, { {"type", 0x69494, 1} }, - 0 }, + 0, 0 }, [REP_SEND_RECV_ID_LIST] = { 4, { {"RxInfo", 0x69330, 2}, {"seq_num_V", 0x69332, 3}, {"V'", 0x69335, 16}, {"ridlist", 0x69345, 155} }, - (1 << 0) }, + (1 << 0), 0 }, [REP_SEND_ACK] = { 1, { {"V", 0x693E0, 16} }, - 0 }, + 0, 0 }, [REP_STREAM_MANAGE] = { 3, { {"seq_num_M", 0x693F0, 3}, {"k", 0x693F3, 2}, {"streamID_Type", 0x693F5, 126} }, - 0 }, + 0, 0 }, [REP_STREAM_READY] = { 1, { {"M'", 0x69473, 32} }, - 0 }, + 0, 7 }, }; static int sde_hdcp_2x_get_next_message(struct sde_hdcp_2x_ctrl *hdcp, @@ -247,6 +250,29 @@ static void sde_hdcp_2x_wait_for_response(struct sde_hdcp_2x_ctrl *hdcp) hdcp->wait_timeout_ms = 0; } +static void sde_hdcp_2x_adjust_transaction_params( + struct sde_hdcp_2x_ctrl *hdcp, + struct hdcp_transport_wakeup_data *data) +{ + switch (hdcp->last_msg) { + case AKE_SEND_CERT: + data->transaction_delay = AKE_SEND_CERT_MSG_DELAY; + break; + case REP_STREAM_READY: + break; + default: + data->transaction_delay = 0; + break; + } + + data->transaction_timeout = + hdcp_msg_lookup[hdcp->last_msg].transaction_timeout; + + pr_debug("%s: transaction delay: %ums, transaction timeout: %ums\n", + sde_hdcp_2x_message_name(hdcp->last_msg), + data->transaction_delay, data->transaction_timeout); +} + static void sde_hdcp_2x_wakeup_client(struct sde_hdcp_2x_ctrl *hdcp, struct hdcp_transport_wakeup_data *data) { @@ -271,6 +297,8 @@ static void sde_hdcp_2x_wakeup_client(struct sde_hdcp_2x_ctrl *hdcp, data->message_data = &hdcp_msg_lookup[hdcp->last_msg]; } + sde_hdcp_2x_adjust_transaction_params(hdcp, data); + rc = hdcp->client_ops->wakeup(data); if (rc) pr_err("error sending %s to client\n", @@ -285,7 +313,7 @@ static inline void sde_hdcp_2x_send_message(struct sde_hdcp_2x_ctrl *hdcp) HDCP_TRANSPORT_CMD_SEND_MESSAGE }; cdata.context = hdcp->client_data; - cdata.timeout = hdcp->app_data.timeout; + cdata.transaction_delay = hdcp->app_data.timeout; cdata.buf_len = hdcp->app_data.response.length; /* ignore the first byte as it contains the message id */ @@ -436,7 +464,7 @@ static void sde_hdcp_2x_initialize_command(struct sde_hdcp_2x_ctrl *hdcp, struct hdcp_transport_wakeup_data *cdata) { cdata->cmd = cmd; - cdata->timeout = hdcp->timeout_left; + cdata->transaction_delay = hdcp->timeout_left; cdata->buf = hdcp->app_data.request.data + 1; } @@ -492,7 +520,7 @@ static void sde_hdcp_2x_msg_sent(struct sde_hdcp_2x_ctrl *hdcp) break; default: cdata.cmd = HDCP_TRANSPORT_CMD_RECV_MESSAGE; - cdata.timeout = hdcp->timeout_left; + cdata.transaction_delay = hdcp->app_data.timeout; cdata.buf = hdcp->app_data.request.data + 1; } @@ -593,7 +621,7 @@ static void sde_hdcp_2x_msg_recvd(struct sde_hdcp_2x_ctrl *hdcp) if (msg[0] == AKE_SEND_H_PRIME && hdcp->no_stored_km) { cdata.cmd = HDCP_TRANSPORT_CMD_RECV_MESSAGE; - cdata.timeout = hdcp->app_data.timeout; + cdata.transaction_delay = hdcp->app_data.timeout; cdata.buf = hdcp->app_data.request.data + 1; goto exit; } @@ -653,7 +681,7 @@ static void sde_hdcp_2x_msg_recvd(struct sde_hdcp_2x_ctrl *hdcp) cdata.cmd = HDCP_TRANSPORT_CMD_SEND_MESSAGE; cdata.buf = hdcp->app_data.response.data + 1; cdata.buf_len = hdcp->app_data.response.length; - cdata.timeout = hdcp->app_data.timeout; + cdata.transaction_delay = hdcp->app_data.timeout; } exit: sde_hdcp_2x_wakeup_client(hdcp, &cdata); diff --git a/msm/sde_hdcp_2x.h b/msm/sde_hdcp_2x.h index 4669564dec13..a8c459c7fa0c 100644 --- a/msm/sde_hdcp_2x.h +++ b/msm/sde_hdcp_2x.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ #ifndef __SDE_HDCP_2X_H__ @@ -114,32 +114,37 @@ struct sde_hdcp_2x_msg_part { /** * struct sde_hdcp_2x_msg_data - HDCP 2.2 message containing one or more parts - * @num_messages: total number of parts in a full message - * @messages: array containing num_messages parts - * @rx_status: value of rx_status register + * @num_messages: total number of parts in a full message + * @messages: array containing num_messages parts + * @rx_status: value of rx_status register + * @transaction_timeout: maximum duration to read/write message from/to sink */ struct sde_hdcp_2x_msg_data { uint32_t num_messages; struct sde_hdcp_2x_msg_part messages[HDCP_MAX_MESSAGE_PARTS]; uint8_t rx_status; + uint32_t transaction_timeout; }; /** * struct hdcp_transport_wakeup_data - data sent to display transport layer - * @cmd: command type - * @context: void pointer to the display transport layer - * @send_msg_buf: buffer containing message to be sent to sink - * @send_msg_len: length of the message to be sent to sink - * @timeout: timeout value for timed transactions - * @abort_mask: mask used to determine whether HDCP link is valid - * @message_data: a pointer to the message description + * @cmd: command type + * @context: void pointer to the display transport layer + * @send_msg_buf: buffer containing message to be sent to sink + * @send_msg_len: length of the message to be sent to sink + * @timeout: timeout value for timed transactions + * @abort_mask: mask used to determine whether HDCP link is valid + * @message_data: a pointer to the message description + * @transaction_delay: amount of time to delay before performing transaction + * @transaction_timeout: maximum duration to read/write message from/to sink */ struct hdcp_transport_wakeup_data { enum hdcp_transport_wakeup_cmd cmd; void *context; unsigned char *buf; u32 buf_len; - u32 timeout; + u32 transaction_delay; + u32 transaction_timeout; u8 abort_mask; const struct sde_hdcp_2x_msg_data *message_data; }; From 732622e52c2d60fb89e1b09d8f4e850a016d2a40 Mon Sep 17 00:00:00 2001 From: Aravind Venkateswaran Date: Tue, 10 Dec 2019 14:07:12 -0800 Subject: [PATCH 05/18] disp: msm: dp: init/deinit host only for cable connection/disconnection DP host initialization and de-initialization need to be done only in response to cable connect/disconnect events. De-initializing host (which includes turning off all regulators), for every HPD low events is unnecessary and can cause unintended issues with DP CTS tests. Change-Id: I95ac71853dc14a46e4175cf0bee4c3eae315fc1e Signed-off-by: Aravind Venkateswaran Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_display.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index ce58c17fe11d..c8dd23d9eb61 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -866,6 +866,25 @@ static void dp_display_host_ready(struct dp_display_private *dp) return; } + /* + * Reset the aborted state for AUX and CTRL modules. This will + * allow these modules to execute normally in response to the + * cable connection event. + * + * One corner case still exists. While the execution flow ensures + * that cable disconnection flushes all pending work items on the DP + * workqueue, and waits for the user module to clean up the DP + * connection session, it is possible that the system delays can + * lead to timeouts in the connect path. As a result, the actual + * connection callback from user modules can come in late and can + * race against a subsequent connection event here which would have + * reset the aborted flags. There is no clear solution for this since + * the connect/disconnect notifications do not currently have any + * sessions IDs. + */ + dp->aux->abort(dp->aux, false); + dp->ctrl->abort(dp->ctrl, false); + dp->aux->init(dp->aux, dp->parser->aux_cfg); dp->panel->init(dp->panel); @@ -934,7 +953,6 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) dp->dp_display.max_pclk_khz = min(dp->parser->max_pclk_khz, dp->debug->max_pclk_khz); - dp_display_host_init(dp); dp_display_host_ready(dp); dp->link->psm_config(dp->link, &dp->panel->link_info, false); @@ -1164,7 +1182,6 @@ static int dp_display_handle_disconnect(struct dp_display_private *dp) dp_display_clean(dp); dp_display_host_unready(dp); - dp_display_host_deinit(dp); mutex_unlock(&dp->session_lock); @@ -1215,6 +1232,10 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev) dp_display_disconnect_sync(dp); + mutex_lock(&dp->session_lock); + dp_display_host_deinit(dp); + mutex_unlock(&dp->session_lock); + if (!dp->debug->sim_mode && !dp->parser->no_aux_switch && !dp->parser->gpio_aux_switch) dp->aux->aux_switch(dp->aux, false, ORIENTATION_NONE); From 5f73d3b6bdc8c07dc67b2212380337ad0fbf94e4 Mon Sep 17 00:00:00 2001 From: Aravind Venkateswaran Date: Tue, 19 Nov 2019 16:47:16 -0800 Subject: [PATCH 06/18] disp: msm: dp: do not abort DP display init if link is aborted It is possible that by the time DP display is initialized by the user mode in response to a hotplug connect event, the DP display has already been disconnected. In such cases, the link aborted flag is set by the driver prior to sending the HPD disconnect event. Current implementation will abort the connection callback when this aborted flag is set. As a result, the DP stream clocks would not be enabled and the DP interface will not be able to generate any vsyncs. This can cause a lot of instability. Allow the connection callbacks from the usermode to complete successfully to avoid these issues. This will also help in subsequent disconnect and connect notifications to be handled gracefully. Change-Id: I9a0d44492a3590e1b6026bc545ee81f7414925cd Signed-off-by: Aravind Venkateswaran Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_display.c | 47 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index c8dd23d9eb61..a0d467e80017 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -1778,8 +1778,22 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel) SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); - if (dp_display_state_is(DP_STATE_ABORTED | DP_STATE_ENABLED)) { - dp_display_state_show("[not initialized]"); + /* + * If the physical connection to the sink is already lost by the time + * we try to set up the connection, we can just skip all the steps + * here safely. + */ + if (dp_display_state_is(DP_STATE_ABORTED)) { + dp_display_state_log("[aborted]"); + goto end; + } + + /* + * If DP_STATE_ENABLED, there is nothing left to do. + * However, this should not happen ideally. So, log this. + */ + if (dp_display_state_is(DP_STATE_ENABLED)) { + dp_display_state_show("[already enabled]"); goto end; } @@ -1900,15 +1914,25 @@ static int dp_display_enable(struct dp_display *dp_display, void *panel) SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); + /* + * If DP_STATE_READY is not set, we should not do any HW + * programming. + */ if (!dp_display_state_is(DP_STATE_READY)) { dp_display_state_show("[host not ready]"); goto end; } - if (dp_display_state_is(DP_STATE_ABORTED)) { - dp_display_state_show("[aborted]"); - goto end; - } + /* + * It is possible that by the time we get call back to establish + * the DP pipeline e2e, the physical DP connection to the sink is + * already lost. In such cases, the DP_STATE_ABORTED would be set. + * However, it is necessary to NOT abort the display setup here so as + * to ensure that the rest of the system is in a stable state prior to + * handling the disconnect notification. + */ + if (dp_display_state_is(DP_STATE_ABORTED)) + dp_display_state_log("[aborted, but continue on]"); rc = dp_display_stream_enable(dp, panel); if (rc) @@ -1945,13 +1969,22 @@ static int dp_display_post_enable(struct dp_display *dp_display, void *panel) SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); + /* + * If DP_STATE_READY is not set, we should not do any HW + * programming. + */ if (!dp_display_state_is(DP_STATE_ENABLED)) { dp_display_state_show("[not enabled]"); goto end; } + /* + * If the physical connection to the sink is already lost by the time + * we try to set up the connection, we can just skip all the steps + * here safely. + */ if (dp_display_state_is(DP_STATE_ABORTED)) { - dp_display_state_show("[aborted]"); + dp_display_state_log("[aborted]"); goto end; } From fac543645073b0ec0ce4cc3249138e3c9f5c153c Mon Sep 17 00:00:00 2001 From: Aravind Venkateswaran Date: Tue, 19 Nov 2019 17:09:21 -0800 Subject: [PATCH 07/18] disp: msm: dp: fix locking during link maintenance Ensure that session lock is held while handling link maintenance to ensure that connect/disconnect handling and link maintenance are serialized. This will avoid un-intended interleaving of session setup/teardown with link maintenance which can result in link training failures. Change-Id: I96bb5b01a80433e26d675a019b8827de9fecb75b Signed-off-by: Aravind Venkateswaran Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_display.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index a0d467e80017..c1668a43bea6 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -1333,7 +1333,6 @@ static void dp_display_attention_work(struct work_struct *work) mutex_lock(&dp->session_lock); dp_audio_enable(dp, false); - mutex_unlock(&dp->session_lock); if (dp->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) { SDE_EVT32_EXTERNAL(dp->state, @@ -1352,7 +1351,6 @@ static void dp_display_attention_work(struct work_struct *work) dp->ctrl->link_maintenance(dp->ctrl); } - mutex_lock(&dp->session_lock); dp_audio_enable(dp, true); mutex_unlock(&dp->session_lock); From d8be39e02ba59b02af441a7530c8e3baf4982430 Mon Sep 17 00:00:00 2001 From: Aravind Venkateswaran Date: Tue, 19 Nov 2019 17:32:10 -0800 Subject: [PATCH 08/18] disp: msm: dp: skip HPD connect notification if IRQ HPD is pending Upon completing the link setup, check to see if any IRQ HPD notification is pending prior to sending the connect notification to the user mode. If an IRQ HPD is pending, address the IRQ first prior to sending the notification. This will ensure that the link setup is stable first before the connection setup is started. This will also help in adhering to the CTS timing requirements for responding to IRQ HPD notifications. The current patchset limits this logic only for SST connections. Change-Id: I3f5e8e9290f915b98ec5e9e6cf9ff14aa9929d96 Signed-off-by: Aravind Venkateswaran Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_display.c | 59 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index c1668a43bea6..34a9e1d57cb5 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -733,6 +733,24 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp) bool hpd = !!dp_display_state_is(DP_STATE_CONNECTED); SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state, hpd); + + /* + * Send the notification only if there is any change. This check is + * necessary since it is possible that the connect_work may or may not + * skip sending the notification in order to respond to a pending + * attention message. Attention work thread will always attempt to + * send the notification after successfully handling the attention + * message. This check here will avoid any unintended duplicate + * notifications. + */ + if (dp_display_state_is(DP_STATE_CONNECT_NOTIFIED) && hpd) { + DP_DEBUG("connection notified already, skip notification\n"); + goto skip_wait; + } else if (dp_display_state_is(DP_STATE_DISCONNECT_NOTIFIED) && !hpd) { + DP_DEBUG("disonnect notified already, skip notification\n"); + goto skip_wait; + } + dp->aux->state |= DP_STATE_NOTIFICATION_SENT; if (!dp->mst.mst_active) @@ -990,9 +1008,30 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) end: mutex_unlock(&dp->session_lock); + /* + * If an IRQ HPD is pending, then do not send a connect notification. + * Once this work returns, the IRQ HPD would be processed and any + * required actions (such as link maintenance) would be done which + * will subsequently send the HPD notification. To keep things simple, + * do this only for SST use-cases. MST use cases require additional + * care in order to handle the side-band communications as well. + * + * One of the main motivations for this is DP LL 1.4 CTS use case + * where it is possible that we could get a test request right after + * a connection, and the strict timing requriements of the test can + * only be met if we do not wait for the e2e connection to be set up. + */ + if (!dp->mst.mst_active && + (work_busy(&dp->attention_work) == WORK_BUSY_PENDING)) { + SDE_EVT32_EXTERNAL(dp->state, 99); + DP_DEBUG("Attention pending, skip HPD notification\n"); + goto skip_notify; + } + if (!rc && !dp_display_state_is(DP_STATE_ABORTED)) dp_display_send_hpd_notification(dp); +skip_notify: SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, rc); return rc; } @@ -1311,6 +1350,10 @@ static void dp_display_attention_work(struct work_struct *work) if (dp_display_is_sink_count_zero(dp)) { dp_display_handle_disconnect(dp); } else { + /* + * connect work should take care of sending + * the HPD notification. + */ if (!dp->mst.mst_active) queue_work(dp->wq, &dp->connect_work); } @@ -1323,6 +1366,10 @@ static void dp_display_attention_work(struct work_struct *work) dp_display_handle_disconnect(dp); dp->panel->video_test = true; + /* + * connect work should take care of sending + * the HPD notification. + */ queue_work(dp->wq, &dp->connect_work); goto mst_attention; @@ -1355,8 +1402,18 @@ static void dp_display_attention_work(struct work_struct *work) mutex_unlock(&dp->session_lock); if (dp->link->sink_request & (DP_TEST_LINK_PHY_TEST_PATTERN | - DP_TEST_LINK_TRAINING)) + DP_TEST_LINK_TRAINING)) { goto mst_attention; + } else { + /* + * It is possible that the connect_work skipped sending + * the HPD notification if the attention message was + * already pending. Send the notification here to + * account for that. This is not needed if this + * attention work was handling a test request + */ + dp_display_send_hpd_notification(dp); + } } cp_irq: From 47cc800aff36808e111b92bf0a170b5513c2c365 Mon Sep 17 00:00:00 2001 From: Aravind Venkateswaran Date: Tue, 19 Nov 2019 17:35:06 -0800 Subject: [PATCH 09/18] disp: msm: dp: delay hotplug connect notification Delay the hotplug connect notification to check for any test requests issued by the sink. This will avoid unwanted connection event callbacks. Change-Id: Id7a04dbbb5ad5b86c3791ac2db5de2c92e98fc46 Signed-off-by: Aravind Venkateswaran Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_display.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 34a9e1d57cb5..a3c9f6a7317f 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -1008,6 +1008,12 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) end: mutex_unlock(&dp->session_lock); + /* + * Delay the HPD connect notification to see if sink generates any + * IRQ HPDs immediately after the HPD high. + */ + usleep_range(10000, 10100); + /* * If an IRQ HPD is pending, then do not send a connect notification. * Once this work returns, the IRQ HPD would be processed and any From 69631c415c51b53d0b28183def2e4cde846c88c9 Mon Sep 17 00:00:00 2001 From: Aravind Venkateswaran Date: Tue, 5 Nov 2019 13:26:48 -0800 Subject: [PATCH 10/18] drm: msm: dp: update logging support Modify the macros to use default kernel logging function if DRM log levels are not explicitly set. This helps in being able to selectively enable function or file level logs. Change-Id: I4f894a2ecf314a3a5fc0bb22a75a0e10dae31e07 Signed-off-by: Aravind Venkateswaran Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_debug.h | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/msm/dp/dp_debug.h b/msm/dp/dp_debug.h index c5d09699a0df..65874f2132bd 100644 --- a/msm/dp/dp_debug.h +++ b/msm/dp/dp_debug.h @@ -13,13 +13,33 @@ #include "dp_display.h" #include "dp_pll.h" -#define DP_WARN(fmt, ...) DRM_WARN("[msm-dp-warn] "fmt, ##__VA_ARGS__) -#define DP_ERR(fmt, ...) DRM_DEV_ERROR(NULL, "[msm-dp-error]" fmt, \ - ##__VA_ARGS__) -#define DP_INFO(fmt, ...) DRM_DEV_INFO(NULL, "[msm-dp-info] "fmt, \ - ##__VA_ARGS__) -#define DP_DEBUG(fmt, ...) DRM_DEV_DEBUG_DP(NULL, "[msm-dp-debug] "fmt, \ - ##__VA_ARGS__) +#define DP_DEBUG(fmt, ...) \ + do { \ + if (unlikely(drm_debug & DRM_UT_KMS)) \ + DRM_DEBUG("[msm-dp-debug][%-4d]"fmt, current->pid, \ + ##__VA_ARGS__); \ + else \ + pr_debug("[drm:%s][msm-dp-debug][%-4d]"fmt, __func__,\ + current->pid, ##__VA_ARGS__); \ + } while (0) + +#define DP_INFO(fmt, ...) \ + do { \ + if (unlikely(drm_debug & DRM_UT_KMS)) \ + DRM_INFO("[msm-dp-info][%-4d]"fmt, current->pid, \ + ##__VA_ARGS__); \ + else \ + pr_info("[drm:%s][msm-dp-info][%-4d]"fmt, __func__, \ + current->pid, ##__VA_ARGS__); \ + } while (0) + +#define DP_WARN(fmt, ...) \ + pr_warn("[drm:%s][msm-dp-warn][%-4d]"fmt, __func__, \ + current->pid, ##__VA_ARGS__) + +#define DP_ERR(fmt, ...) \ + pr_err("[drm:%s][msm-dp-err][%-4d]"fmt, __func__, \ + current->pid, ##__VA_ARGS__) /** * struct dp_debug From 3ce95ac68e741d3656bdd9e4a141a37e988bd981 Mon Sep 17 00:00:00 2001 From: Sankeerth Billakanti Date: Sun, 8 Dec 2019 07:05:36 +0530 Subject: [PATCH 11/18] disp: msm: dp: update min_enc_level only if link is authenticated The min_enc_lvl update is asynchronous to the hdcp2x state machine and can come anytime. In an instance where authentication with the sink is continuously failing, the enforce engine blocks the hdcp22 secure content playback and resets the min enc lvl. This is causing the sde_hdcp_2x_main to perform a send message operation out of sync with the current authentication attempt. This is causing the state machine to go into bad state by changing the next message id, sometimes causing a pagefault error in the sde_hdcp_2x_main while cp_irq is in progress. This change will proceed with the min_enc_lvl change only when the mainlink is hdcp2p2 authenticated. Change-Id: I977ac3a083b9593f8f7e760d4e477d4af424d5c9 Signed-off-by: Sankeerth Billakanti Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_hdcp2p2.c | 5 ----- msm/sde_hdcp_2x.c | 6 ++++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/msm/dp/dp_hdcp2p2.c b/msm/dp/dp_hdcp2p2.c index bc2a25413965..ad1d0a6003a2 100644 --- a/msm/dp/dp_hdcp2p2.c +++ b/msm/dp/dp_hdcp2p2.c @@ -346,11 +346,6 @@ static void dp_hdcp2p2_min_level_change(void *client_ctx, return; } - if (!dp_hdcp2p2_is_valid_state(ctrl)) { - DP_ERR("invalid state\n"); - return; - } - cdata.context = ctrl->lib_ctx; cdata.min_enc_level = min_enc_level; dp_hdcp2p2_wakeup_lib(ctrl, &cdata); diff --git a/msm/sde_hdcp_2x.c b/msm/sde_hdcp_2x.c index 28cf474b3a79..30db5cb70c36 100644 --- a/msm/sde_hdcp_2x.c +++ b/msm/sde_hdcp_2x.c @@ -904,8 +904,10 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data) break; case HDCP_2X_CMD_MIN_ENC_LEVEL: hdcp->min_enc_level = data->min_enc_level; - kfifo_put(&hdcp->cmd_q, data->cmd); - wake_up(&hdcp->wait_q); + if (hdcp->authenticated) { + kfifo_put(&hdcp->cmd_q, data->cmd); + wake_up(&hdcp->wait_q); + } break; default: kfifo_put(&hdcp->cmd_q, data->cmd); From 3630628d0d75ec48d60828745130e98e89cb8683 Mon Sep 17 00:00:00 2001 From: Sankeerth Billakanti Date: Sat, 21 Dec 2019 15:06:55 +0530 Subject: [PATCH 12/18] disp: msm: dp: correct the dp_panel instance used to enable audio When DP_LINK_STATUS_UPDATED irq_hpd occurs, the mst audio instances are turned off, irq_hpd handled and audio instances are turned on again. While turning on, an audio instance different from the ones turned off is being sent, causing ops mismatches eventually leading to a NOC error. These changes will correct the pointer to the audio or the panel instances to match the ones that are turned off. Change-Id: Idd9c013fd64cd6db06bbec34f5083029e4ab522c Signed-off-by: Sankeerth Billakanti Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_display.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index a3c9f6a7317f..1866eeed54ea 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -1166,7 +1166,7 @@ static void dp_audio_enable(struct dp_display_private *dp, bool enable) dp->link->link_params.bw_code; dp_panel->audio->lane_count = dp->link->link_params.lane_count; - dp_panel->audio->on(dp->panel->audio); + dp_panel->audio->on(dp_panel->audio); } else { dp_panel->audio->off(dp_panel->audio); } From be267c80f722dfc2773b164cc4bf10fd67dcd8fb Mon Sep 17 00:00:00 2001 From: Sankeerth Billakanti Date: Mon, 23 Dec 2019 10:11:15 +0530 Subject: [PATCH 13/18] disp: msm: dp: reset panel object for connector when uninstalled When panel object for a dp connector is destroyed, the instance of dp_panel in the connector object is made NULL to prevent further panel object dereferences. Change-Id: I63409ab8c470f14cf9c032e373cb0c0872577504 Signed-off-by: Sankeerth Billakanti Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_panel.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/msm/dp/dp_panel.c b/msm/dp/dp_panel.c index b9ee5cb9a349..6d92a6ae19c8 100644 --- a/msm/dp/dp_panel.c +++ b/msm/dp/dp_panel.c @@ -3069,6 +3069,7 @@ error: void dp_panel_put(struct dp_panel *dp_panel) { struct dp_panel_private *panel; + struct sde_connector *sde_conn; if (!dp_panel) return; @@ -3076,5 +3077,9 @@ void dp_panel_put(struct dp_panel *dp_panel) panel = container_of(dp_panel, struct dp_panel_private, dp_panel); dp_panel_edid_deregister(panel); + sde_conn = to_sde_connector(dp_panel->connector); + if (sde_conn) + sde_conn->drv_panel = NULL; + devm_kfree(panel->dev, panel); } From 33f6b8e11e82e366711043d65d9adfa57fecc94a Mon Sep 17 00:00:00 2001 From: Sankeerth Billakanti Date: Fri, 27 Dec 2019 11:17:46 +0530 Subject: [PATCH 14/18] disp: msm: dp: fix for screen freeze on dp disconnect The DP display driver issues an audio disconnect notification after the video disconnect notification. Sometimes audio driver is waiting for the video commit to complete before turning off the audio engine. In such cases, there is a brief screen freeze observed on the primary. In other cases, when the off call from audio driver gets delayed, a momentary screen freeze is seen. The audio notification happens as part of the display commit call. The order of notification is video and then audio while processing display connect and the same order is followed for disconnect also, which is causing this issue. These changes will modify the order of notification to send audio disconnect first and then video while processing DP cable disconnect. Change-Id: I8bece39c164620b319d971e5a2597d9dc187566e Signed-off-by: Sankeerth Billakanti Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_audio.c | 8 +++++++- msm/dp/dp_display.c | 50 ++++++++++++++++++++++----------------------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/msm/dp/dp_audio.c b/msm/dp/dp_audio.c index 67f3741f9ddf..8284207408ba 100644 --- a/msm/dp/dp_audio.c +++ b/msm/dp/dp_audio.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. */ #include @@ -766,6 +766,12 @@ static int dp_audio_off(struct dp_audio *dp_audio) } audio = container_of(dp_audio, struct dp_audio_private, dp_audio); + + if (!atomic_read(&audio->session_on)) { + DP_DEBUG("audio already off\n"); + return rc; + } + ext = &audio->ext_audio_data; work_pending = cancel_delayed_work_sync(&audio->notify_delayed_work); diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 1866eeed54ea..ca4605f3fbe3 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -246,6 +246,30 @@ static bool dp_display_is_ready(struct dp_display_private *dp) dp->hpd->alt_mode_cfg_done; } +static void dp_audio_enable(struct dp_display_private *dp, bool enable) +{ + struct dp_panel *dp_panel; + int idx; + + for (idx = DP_STREAM_0; idx < DP_STREAM_MAX; idx++) { + if (!dp->active_panels[idx]) + continue; + dp_panel = dp->active_panels[idx]; + + if (dp_panel->audio_supported) { + if (enable) { + dp_panel->audio->bw_code = + dp->link->link_params.bw_code; + dp_panel->audio->lane_count = + dp->link->link_params.lane_count; + dp_panel->audio->on(dp_panel->audio); + } else { + dp_panel->audio->off(dp_panel->audio); + } + } + } +} + static void dp_display_update_hdcp_status(struct dp_display_private *dp, bool reset) { @@ -1065,7 +1089,7 @@ static int dp_display_process_hpd_low(struct dp_display_private *dp) dp_display_state_remove(DP_STATE_CONNECTED); dp->process_hpd_connect = false; - + dp_audio_enable(dp, false); dp_display_process_mst_hpd_low(dp); if ((dp_display_state_is(DP_STATE_CONNECT_NOTIFIED) || @@ -1150,30 +1174,6 @@ static void dp_display_stream_disable(struct dp_display_private *dp, dp->active_stream_cnt--; } -static void dp_audio_enable(struct dp_display_private *dp, bool enable) -{ - struct dp_panel *dp_panel; - int idx; - - for (idx = DP_STREAM_0; idx < DP_STREAM_MAX; idx++) { - if (!dp->active_panels[idx]) - continue; - dp_panel = dp->active_panels[idx]; - - if (dp_panel->audio_supported) { - if (enable) { - dp_panel->audio->bw_code = - dp->link->link_params.bw_code; - dp_panel->audio->lane_count = - dp->link->link_params.lane_count; - dp_panel->audio->on(dp_panel->audio); - } else { - dp_panel->audio->off(dp_panel->audio); - } - } - } -} - static void dp_display_clean(struct dp_display_private *dp) { int idx; From 9ee6c22331bd8a399a8877df7d8de631bc80032c Mon Sep 17 00:00:00 2001 From: Sankeerth Billakanti Date: Fri, 10 Jan 2020 23:05:22 +0530 Subject: [PATCH 15/18] disp: msm: dp: update vswing and pre-emphasis settings for lito Change to update the voltage swing and pre-emphasis settings for lito to support dp phy cts. Change-Id: I97f0b9882291d7d52e10ef4bef6ea6a23780e12a Signed-off-by: Sankeerth Billakanti Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_catalog_v420.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/msm/dp/dp_catalog_v420.c b/msm/dp/dp_catalog_v420.c index 10c6801e0934..cc391d172c24 100644 --- a/msm/dp/dp_catalog_v420.c +++ b/msm/dp/dp_catalog_v420.c @@ -247,7 +247,10 @@ static void dp_catalog_ctrl_update_vx_px_v420(struct dp_catalog_ctrl *ctrl, io_data = catalog->io->dp_ahb; version = dp_read(DP_HW_VERSION); - if (version == 0x10020003) { + /* + * For DP controller versions 1.2.3 and 1.2.4 + */ + if ((version == 0x10020003) || (version == 0x10020004)) { if (high) { value0 = dp_swing_hbr2_hbr3[v_level][p_level]; value1 = dp_pre_emp_hbr2_hbr3[v_level][p_level]; From d7bc0eebfeb520a2d760de79b89a1f3e57a1bdb7 Mon Sep 17 00:00:00 2001 From: Sankeerth Billakanti Date: Tue, 4 Feb 2020 16:40:55 +0530 Subject: [PATCH 16/18] disp: msm: dp: send dp hpd notification from attention_work The hpd notification is skipped in connect_work if there is an attention_work pending. So, the notification has to be sent in attention_work. Some dongles issue multiple attention events with LINK_STATUS_UPDATED while the connect_work is in progress. So, once the link training is successfully completed the LINK_STATUS_UPDATED attention event is considered handled and the attention_work scheduled to execute after the connect_work will return early without handling the hpd_notification cases. This change will ensure the hpd_notification is sent from attention_work in such cases. Change-Id: I5ef6781354b80842d2a6f44360cde25ee6d2b0b7 Signed-off-by: Sankeerth Billakanti Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_display.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index ca4605f3fbe3..e18829044fbe 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -1408,23 +1408,25 @@ static void dp_display_attention_work(struct work_struct *work) mutex_unlock(&dp->session_lock); if (dp->link->sink_request & (DP_TEST_LINK_PHY_TEST_PATTERN | - DP_TEST_LINK_TRAINING)) { + DP_TEST_LINK_TRAINING)) goto mst_attention; - } else { - /* - * It is possible that the connect_work skipped sending - * the HPD notification if the attention message was - * already pending. Send the notification here to - * account for that. This is not needed if this - * attention work was handling a test request - */ - dp_display_send_hpd_notification(dp); - } } cp_irq: if (dp_display_is_hdcp_enabled(dp) && dp->hdcp.ops->cp_irq) dp->hdcp.ops->cp_irq(dp->hdcp.data); + + if (!dp->mst.mst_active) { + /* + * It is possible that the connect_work skipped sending + * the HPD notification if the attention message was + * already pending. Send the notification here to + * account for that. This is not needed if this + * attention work was handling a test request + */ + dp_display_send_hpd_notification(dp); + } + mst_attention: dp_display_mst_attention(dp); SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state); From 54365213d1c3b2278d65ec5200b6b4330aa3eb69 Mon Sep 17 00:00:00 2001 From: Sankeerth Billakanti Date: Mon, 10 Feb 2020 16:26:02 +0530 Subject: [PATCH 17/18] disp: msm: dp: execute host_init on restoring session teardown Ensure host_init is executed when the DP display driver is resuming from a video session teardown. The host_deinit is executed when DP video session teardown is initiated by the userspace while DP cable is connected. If the DP display is disconnected from dongle while the video session is in teardown, the host_init is not executed when the session is restored. All subsequent display connections will fail because the core clocks are not on and aux abort is not reset. This change will ensure host_init is executed whenever the host_deinit is executed during DP video session teardown by userspace. Change-Id: If1ae3eb731a96bd929cd023733c098b513436a1c Signed-off-by: Sankeerth Billakanti Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_display.c | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index e18829044fbe..69e5195a2d51 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -62,6 +62,7 @@ enum dp_display_states { DP_STATE_SUSPENDED = BIT(7), DP_STATE_ABORTED = BIT(8), DP_STATE_HDCP_ABORTED = BIT(9), + DP_STATE_SRC_PWRDN = BIT(10), }; static char *dp_display_state_name(enum dp_display_states state) @@ -111,6 +112,10 @@ static char *dp_display_state_name(enum dp_display_states state) len += scnprintf(buf + len, sizeof(buf) - len, "|%s|", "HDCP_ABORTED"); + if (state & DP_STATE_SRC_PWRDN) + len += scnprintf(buf + len, sizeof(buf) - len, "|%s|", + "SRC_PWRDN"); + if (!strlen(buf)) return "DISCONNECTED"; @@ -995,6 +1000,26 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) dp->dp_display.max_pclk_khz = min(dp->parser->max_pclk_khz, dp->debug->max_pclk_khz); + + /* + * If dp video session is not restored from a previous session teardown + * by userspace, ensure the host_init is executed, in such a scenario, + * so that all the required DP resources are enabled. + * + * Below is one of the sequences of events which describe the above + * scenario: + * a. Source initiated power down resulting in host_deinit. + * b. Sink issues hpd low attention without physical cable disconnect. + * c. Source initiated power up sequence returns early because hpd is + * not high. + * d. Sink issues a hpd high attention event. + */ + if (dp_display_state_is(DP_STATE_SRC_PWRDN) && + dp_display_state_is(DP_STATE_CONFIGURED)) { + dp_display_host_init(dp); + dp_display_state_remove(DP_STATE_SRC_PWRDN); + } + dp_display_host_ready(dp); dp->link->psm_config(dp->link, &dp->panel->link_info, false); @@ -1270,7 +1295,6 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev) SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state, dp->debug->psm_enabled); - dp_display_state_remove(DP_STATE_CONFIGURED); if (dp->debug->psm_enabled && dp_display_state_is(DP_STATE_READY)) dp->link->psm_config(dp->link, &dp->panel->link_info, true); @@ -1279,6 +1303,7 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev) mutex_lock(&dp->session_lock); dp_display_host_deinit(dp); + dp_display_state_remove(DP_STATE_CONFIGURED); mutex_unlock(&dp->session_lock); if (!dp->debug->sim_mode && !dp->parser->no_aux_switch @@ -1841,6 +1866,20 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel) SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); mutex_lock(&dp->session_lock); + /* + * If DP video session is restored by the userspace after display + * disconnect notification from dongle i.e. typeC cable connected to + * source but disconnected at the display side, the DP controller is + * not restored to the desired configured state. So, ensure host_init + * is executed in such a scenario so that all the DP controller + * resources are enabled for the next connection event. + */ + if (dp_display_state_is(DP_STATE_SRC_PWRDN) && + dp_display_state_is(DP_STATE_CONFIGURED)) { + dp_display_host_init(dp); + dp_display_state_remove(DP_STATE_SRC_PWRDN); + } + /* * If the physical connection to the sink is already lost by the time * we try to set up the connection, we can just skip all the steps @@ -1866,7 +1905,6 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel) } /* For supporting DP_PANEL_SRC_INITIATED_POWER_DOWN case */ - dp_display_host_init(dp); dp_display_host_ready(dp); if (dp->debug->psm_enabled) { @@ -2283,6 +2321,7 @@ static int dp_display_unprepare(struct dp_display *dp_display, void *panel) dp->ctrl->off(dp->ctrl); dp_display_host_unready(dp); dp_display_host_deinit(dp); + dp_display_state_add(DP_STATE_SRC_PWRDN); } dp_display_state_remove(DP_STATE_ENABLED); From 0e1496c718646e21147ecf45cdea3a67c97c59a0 Mon Sep 17 00:00:00 2001 From: Rajat Gupta Date: Mon, 10 Feb 2020 14:15:57 +0530 Subject: [PATCH 18/18] disp: msm: hdcp: abort queued tasks while processing PM suspend During suspend, there might be a chance that lib auth work is queued but not yet started. So during pm cycle it might start execution before host_init which can lead to noc error while accessing dp_aux registers. To prevent that set abort flag to abort lib auth work and set interrupts before host_deinit. Change-Id: Ie2c3ac9b0846644d3c2b37f410c341659b030c16 Signed-off-by: Rajat Gupta Signed-off-by: Tatenda Chipeperekwa --- msm/dp/dp_display.c | 18 ++++++++++++++++++ msm/dp/dp_hdcp2p2.c | 12 ++++++++++++ msm/sde_hdcp.h | 3 ++- msm/sde_hdcp_1x.c | 16 +++++++++++++++- 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 69e5195a2d51..63eb244fb0ea 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -513,6 +513,22 @@ static void dp_display_hdcp_process_state(struct dp_display_private *dp) } } +static void dp_display_abort_hdcp(struct dp_display_private *dp, + bool abort) +{ + u8 i = HDCP_VERSION_2P2; + struct dp_hdcp_dev *dev = NULL; + + while (i) { + dev = &dp->hdcp.dev[i]; + i >>= 1; + if (!(dp->hdcp.source_cap & dev->ver)) + continue; + + dev->ops->abort(dev->fd, abort); + } +} + static void dp_display_hdcp_cb_work(struct work_struct *work) { struct dp_display_private *dp; @@ -894,6 +910,7 @@ static void dp_display_host_init(struct dp_display_private *dp) dp->hpd->host_init(dp->hpd, &dp->catalog->hpd); dp->ctrl->init(dp->ctrl, flip, reset); enable_irq(dp->irq); + dp_display_abort_hdcp(dp, false); dp_display_state_add(DP_STATE_INITIALIZED); @@ -971,6 +988,7 @@ static void dp_display_host_deinit(struct dp_display_private *dp) return; } + dp_display_abort_hdcp(dp, true); dp->ctrl->deinit(dp->ctrl); dp->hpd->host_deinit(dp->hpd, &dp->catalog->hpd); dp->power->deinit(dp->power); diff --git a/msm/dp/dp_hdcp2p2.c b/msm/dp/dp_hdcp2p2.c index ad1d0a6003a2..fcbec753ccfc 100644 --- a/msm/dp/dp_hdcp2p2.c +++ b/msm/dp/dp_hdcp2p2.c @@ -31,6 +31,7 @@ struct dp_hdcp2p2_ctrl { DECLARE_KFIFO(cmd_q, enum hdcp_transport_wakeup_cmd, 8); wait_queue_head_t wait_q; atomic_t auth_state; + atomic_t abort; enum dp_hdcp2p2_sink_status sink_status; /* Is sink connected */ struct dp_hdcp2p2_interrupts *intr; struct sde_hdcp_init_data init_data; @@ -151,6 +152,9 @@ static void dp_hdcp2p2_set_interrupts(struct dp_hdcp2p2_ctrl *ctrl, bool enable) void __iomem *base = ctrl->init_data.dp_ahb->base; struct dp_hdcp2p2_interrupts *intr = ctrl->intr; + if (atomic_read(&ctrl->abort)) + return; + while (intr && intr->reg) { struct dp_hdcp2p2_int_set *int_set = intr->int_set; u32 interrupts = 0; @@ -882,6 +886,13 @@ static int dp_hdcp2p2_main(void *data) return 0; } +static void dp_hdcp2p2_abort(void *input, bool abort) +{ + struct dp_hdcp2p2_ctrl *ctrl = input; + + atomic_set(&ctrl->abort, abort); +} + void *sde_dp_hdcp2p2_init(struct sde_hdcp_init_data *init_data) { int rc; @@ -896,6 +907,7 @@ void *sde_dp_hdcp2p2_init(struct sde_hdcp_init_data *init_data) .set_mode = dp_hdcp2p2_register, .on = dp_hdcp2p2_on, .off = dp_hdcp2p2_off, + .abort = dp_hdcp2p2_abort, .cp_irq = dp_hdcp2p2_cp_irq, .register_streams = dp_hdcp2p2_register_streams, .deregister_streams = dp_hdcp2p2_deregister_streams, diff --git a/msm/sde_hdcp.h b/msm/sde_hdcp.h index fd7768b1ff9c..d386eb76682e 100644 --- a/msm/sde_hdcp.h +++ b/msm/sde_hdcp.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2012, 2014-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2012, 2014-2020, The Linux Foundation. All rights reserved. */ #ifndef __SDE_HDCP_H__ @@ -81,6 +81,7 @@ struct sde_hdcp_ops { bool (*feature_supported)(void *input); void (*force_encryption)(void *input, bool enable); bool (*sink_support)(void *input); + void (*abort)(void *input, bool abort); int (*set_mode)(void *input, bool mst_enabled); int (*on)(void *input); void (*off)(void *hdcp_ctrl); diff --git a/msm/sde_hdcp_1x.c b/msm/sde_hdcp_1x.c index 7a74bca2ae6b..e321344ddf23 100644 --- a/msm/sde_hdcp_1x.c +++ b/msm/sde_hdcp_1x.c @@ -207,6 +207,7 @@ struct sde_hdcp_1x { bool reauth; bool ksv_ready; bool force_encryption; + atomic_t abort; enum sde_hdcp_state hdcp_state; struct HDCP_V2V1_MSG_TOPOLOGY current_tp; struct delayed_work hdcp_auth_work; @@ -1040,7 +1041,7 @@ static void sde_hdcp_1x_update_auth_status(struct sde_hdcp_1x *hdcp) static void sde_hdcp_1x_auth_work(struct work_struct *work) { - int rc; + int rc = 0; struct delayed_work *dw = to_delayed_work(work); struct sde_hdcp_1x *hdcp = container_of(dw, struct sde_hdcp_1x, hdcp_auth_work); @@ -1056,6 +1057,9 @@ static void sde_hdcp_1x_auth_work(struct work_struct *work) return; } + if (atomic_read(&hdcp->abort)) + goto end; + hdcp->sink_r0_ready = false; hdcp->reauth = false; hdcp->ksv_ready = false; @@ -1484,6 +1488,15 @@ irq_not_handled: return -EINVAL; } +static void sde_hdcp_1x_abort(void *data, bool abort) +{ + struct sde_hdcp_1x *hdcp = data; + + atomic_set(&hdcp->abort, abort); + cancel_delayed_work_sync(&hdcp->hdcp_auth_work); + flush_workqueue(hdcp->workq); +} + void *sde_hdcp_1x_init(struct sde_hdcp_init_data *init_data) { struct sde_hdcp_1x *hdcp = NULL; @@ -1496,6 +1509,7 @@ void *sde_hdcp_1x_init(struct sde_hdcp_init_data *init_data) .feature_supported = sde_hdcp_1x_feature_supported, .force_encryption = sde_hdcp_1x_force_encryption, .sink_support = sde_hdcp_1x_sink_support, + .abort = sde_hdcp_1x_abort, .off = sde_hdcp_1x_off };