diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2017-04-20 16:10:26 -0700 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2017-04-20 16:10:25 -0700 |
commit | e66fc6f5af6fc31695507a38eadb895d5ecc81fc (patch) | |
tree | 71218968001db5f7c929bc1b7fa0e8b250a3e15e | |
parent | ddc8181a4632cd6e53dcffc1bee85196a608dfb4 (diff) | |
parent | 058c973e7e2a0c79643c3034af980f7299672725 (diff) |
Merge "msm: mdss: dp: fix HDCP 1.x state transitions"
-rw-r--r-- | Documentation/devicetree/bindings/fb/mdss-dp.txt | 52 | ||||
-rw-r--r-- | arch/arm/boot/dts/qcom/msm8998-mdss.dtsi | 11 | ||||
-rw-r--r-- | arch/arm/boot/dts/qcom/sdm660-mdss.dtsi | 11 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/mdss_dp.c | 375 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/mdss_dp.h | 116 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/mdss_dp_aux.c | 443 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/mdss_dp_util.c | 72 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/mdss_dp_util.h | 21 | ||||
-rw-r--r-- | drivers/video/fbdev/msm/mdss_hdcp_1x.c | 4 |
9 files changed, 902 insertions, 203 deletions
diff --git a/Documentation/devicetree/bindings/fb/mdss-dp.txt b/Documentation/devicetree/bindings/fb/mdss-dp.txt index aa227c2628da..707e6edb26ea 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dp.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dp.txt @@ -27,7 +27,46 @@ Required properties - qcom,aux-en-gpio: Specifies the aux-channel enable gpio. - qcom,aux-sel-gpio: Specifies the aux-channel select gpio. - qcom,usbplug-cc-gpio: Specifies the usbplug orientation gpio. -- qcom,aux-cfg-settings: An array that specifies the DP AUX configuration settings. +- qcom,aux-cfg0-settings: Specifies the DP AUX configuration 0 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg1-settings: Specifies the DP AUX configuration 1 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg2-settings: Specifies the DP AUX configuration 2 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg3-settings: Specifies the DP AUX configuration 3 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg4-settings: Specifies the DP AUX configuration 4 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg5-settings: Specifies the DP AUX configuration 5 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg6-settings: Specifies the DP AUX configuration 6 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg7-settings: Specifies the DP AUX configuration 7 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg8-settings: Specifies the DP AUX configuration 8 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. +- qcom,aux-cfg9-settings: Specifies the DP AUX configuration 9 settings. The first + entry in this array corresponds to the register offset + within DP AUX, while the remaining entries indicate the + programmable values. Optional properties: - qcom,<type>-supply-entries: A node that lists the elements of the supply used by the @@ -87,7 +126,16 @@ Example: "core_aux_clk", "core_cfg_ahb_clk", "ctrl_link_clk", "ctrl_link_iface_clk", "ctrl_crypto_clk", "ctrl_pixel_clk"; - qcom,aux-cfg-settings = [00 13 00 10 0a 26 0a 03 8b 03]; + qcom,aux-cfg0-settings = [1c 00]; + qcom,aux-cfg1-settings = [20 13 23 1d]; + qcom,aux-cfg2-settings = [24 00]; + qcom,aux-cfg3-settings = [28 00]; + qcom,aux-cfg4-settings = [2c 0a]; + qcom,aux-cfg5-settings = [30 26]; + qcom,aux-cfg6-settings = [34 0a]; + qcom,aux-cfg7-settings = [38 03]; + qcom,aux-cfg8-settings = [3c bb]; + qcom,aux-cfg9-settings = [40 03]; qcom,logical2physical-lane-map = [02 03 01 00]; qcom,phy-register-offset = <0x4>; qcom,max-pclk-frequency-khz = <593470>; diff --git a/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi b/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi index 24186aca22be..2b9e13ea24f2 100644 --- a/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998-mdss.dtsi @@ -502,7 +502,16 @@ qcom,msm_ext_disp = <&msm_ext_disp>; - qcom,aux-cfg-settings = [00 13 00 10 0a 26 0a 03 8b 03]; + qcom,aux-cfg0-settings = [1c 00]; + qcom,aux-cfg1-settings = [20 13 23 1d]; + qcom,aux-cfg2-settings = [24 00]; + qcom,aux-cfg3-settings = [28 00]; + qcom,aux-cfg4-settings = [2c 0a]; + qcom,aux-cfg5-settings = [30 26]; + qcom,aux-cfg6-settings = [34 0a]; + qcom,aux-cfg7-settings = [38 03]; + qcom,aux-cfg8-settings = [3c bb]; + qcom,aux-cfg9-settings = [40 03]; qcom,logical2physical-lane-map = [02 03 01 00]; qcom,core-supply-entries { diff --git a/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi b/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi index b263d2a68792..787c4f1e2fb6 100644 --- a/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660-mdss.dtsi @@ -505,7 +505,16 @@ qcom,msm_ext_disp = <&msm_ext_disp>; - qcom,aux-cfg-settings = [00 13 00 00 0a 28 0a 03 b7 03]; + qcom,aux-cfg0-settings = [20 00]; + qcom,aux-cfg1-settings = [24 13 23 1d]; + qcom,aux-cfg2-settings = [28 00]; + qcom,aux-cfg3-settings = [2c 00]; + qcom,aux-cfg4-settings = [30 0a]; + qcom,aux-cfg5-settings = [34 28]; + qcom,aux-cfg6-settings = [38 0a]; + qcom,aux-cfg7-settings = [3c 03]; + qcom,aux-cfg8-settings = [40 b7]; + qcom,aux-cfg9-settings = [44 03]; qcom,logical2physical-lane-map = [00 01 02 03]; qcom,phy-register-offset = <0x4>; qcom,max-pclk-frequency-khz = <300000>; diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index 82d3e44c7d9f..25ce4a4279da 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -69,6 +69,11 @@ static int mdss_dp_process_phy_test_pattern_request( static int mdss_dp_send_audio_notification( struct mdss_dp_drv_pdata *dp, int val); +static inline void mdss_dp_reset_sink_count(struct mdss_dp_drv_pdata *dp) +{ + memset(&dp->sink_count, 0, sizeof(dp->sink_count)); +} + static inline void mdss_dp_reset_test_data(struct mdss_dp_drv_pdata *dp) { dp->test_data = (const struct dpcd_test_request){ 0 }; @@ -133,22 +138,77 @@ static int mdss_dp_is_clk_prefix(const char *clk_prefix, const char *clk_name) return !strncmp(clk_name, clk_prefix, strlen(clk_prefix)); } +static void mdss_dp_reset_phy_config_indices(struct mdss_dp_drv_pdata *dp) +{ + int i = 0; + + for (i = 0; i < PHY_AUX_CFG_MAX; i++) + dp->aux_cfg[i].current_index = 0; +} + +static void mdss_dp_phy_aux_cfg_reset(struct mdss_dp_drv_pdata *dp) +{ + int i = 0; + + for (i = 0; i < PHY_AUX_CFG_MAX; i++) + dp->aux_cfg[i] = (const struct mdss_dp_phy_cfg){ 0 }; +} + +static int mdss_dp_parse_aux_cfg(struct platform_device *pdev, + struct mdss_dp_drv_pdata *dp) +{ + int len = 0, i = 0, j = 0, config_count = 0; + const char *data; + int const minimum_config_count = 1; + + for (i = 0; i < PHY_AUX_CFG_MAX; i++) { + const char *property = mdss_dp_get_phy_aux_config_property(i); + + data = of_get_property(pdev->dev.of_node, property, &len); + if (!data) { + pr_err("Unable to read %s\n", property); + goto error; + } + + config_count = len - 1; + if ((config_count < minimum_config_count) || + (config_count > MDSS_DP_MAX_PHY_CFG_VALUE_CNT)) { + pr_err("Invalid config count (%d) configs for %s\n", + config_count, property); + goto error; + } + + dp->aux_cfg[i].offset = data[0]; + dp->aux_cfg[i].cfg_cnt = config_count; + pr_debug("%s offset=0x%x, cfg_cnt=%d\n", + property, + dp->aux_cfg[i].offset, + dp->aux_cfg[i].cfg_cnt); + for (j = 1; j < len; j++) { + dp->aux_cfg[i].lut[j - 1] = data[j]; + pr_debug("%s lut[%d]=0x%x\n", + property, + i, + dp->aux_cfg[i].lut[j - 1]); + } + } + + return 0; + +error: + mdss_dp_phy_aux_cfg_reset(dp); + return -EINVAL; +} + static int mdss_dp_parse_prop(struct platform_device *pdev, struct mdss_dp_drv_pdata *dp_drv) { int len = 0, i = 0, rc = 0; const char *data; - data = of_get_property(pdev->dev.of_node, - "qcom,aux-cfg-settings", &len); - if ((!data) || (len != AUX_CFG_LEN)) { - pr_err("%s:%d, Unable to read DP AUX CFG settings", - __func__, __LINE__); - return -EINVAL; - } - - for (i = 0; i < len; i++) - dp_drv->aux_cfg[i] = data[i]; + rc = mdss_dp_parse_aux_cfg(pdev, dp_drv); + if (rc) + return rc; data = of_get_property(pdev->dev.of_node, "qcom,logical2physical-lane-map", &len); @@ -958,6 +1018,12 @@ void mdss_dp_config_ctrl(struct mdss_dp_drv_pdata *dp) mdss_dp_configuration_ctrl(&dp->ctrl_io, data); } +static inline void mdss_dp_ack_state(struct mdss_dp_drv_pdata *dp, int val) +{ + if (dp && dp->ext_audio_data.intf_ops.notify) + dp->ext_audio_data.intf_ops.notify(dp->ext_pdev, val); +} + static int mdss_dp_wait4video_ready(struct mdss_dp_drv_pdata *dp_drv) { int ret = 0; @@ -1230,12 +1296,6 @@ static int dp_init_panel_info(struct mdss_dp_drv_pdata *dp_drv, u32 vic) return 0; } /* dp_init_panel_info */ -static inline void mdss_dp_ack_state(struct mdss_dp_drv_pdata *dp, int val) -{ - if (dp && dp->ext_audio_data.intf_ops.notify) - dp->ext_audio_data.intf_ops.notify(dp->ext_pdev, val); -} - /** * mdss_dp_get_lane_mapping() - returns lane mapping based on given orientation * @orientation: usb plug orientation @@ -1621,15 +1681,19 @@ int mdss_dp_on(struct mdss_panel_data *pdata) dp_drv = container_of(pdata, struct mdss_dp_drv_pdata, panel_data); - if (dp_drv->power_on) { - /* - * Acknowledge the connection event if link training has already - * been done. This will unblock the external display thread and - * allow the driver to progress. For example, in the case of - * video test pattern requests, to send the test response and - * start transmitting the test pattern. - */ - mdss_dp_ack_state(dp_drv, true); + /* + * If the link already active, then nothing needs to be done here. + * However, it is possible that the the power_on flag could be + * set to true but we would still need to initialize the DP host. + * An example of this use-case is when a multiport dongle is connected + * and subsequently the downstream sink is disconnected. This would + * only go through the IRQ HPD path where we tear down the link but + * the power_on flag remains set to true. When the downstream sink + * is subsequently connected again, we need to re-initialize DP + * host + */ + if (dp_drv->power_on && + (dp_drv->new_vic && (dp_drv->new_vic == dp_drv->vic))) { pr_debug("Link already setup, return\n"); return 0; } @@ -1647,6 +1711,23 @@ int mdss_dp_on(struct mdss_panel_data *pdata) return mdss_dp_on_hpd(dp_drv); } +static bool mdss_dp_is_ds_bridge(struct mdss_dp_drv_pdata *dp) +{ + return dp->dpcd.downstream_port.dfp_present; +} + +static bool mdss_dp_is_ds_bridge_sink_count_zero(struct mdss_dp_drv_pdata *dp) +{ + return (mdss_dp_is_ds_bridge(dp) && + (dp->sink_count.count == 0)); +} + +static bool mdss_dp_is_ds_bridge_no_local_edid(struct mdss_dp_drv_pdata *dp) +{ + return (mdss_dp_is_ds_bridge_sink_count_zero(dp) && + !(dp->dpcd.flags & DPCD_PORT_0_EDID_PRESENTED)); +} + static int mdss_dp_off_irq(struct mdss_dp_drv_pdata *dp_drv) { if (!dp_drv->power_on) { @@ -1664,10 +1745,16 @@ static int mdss_dp_off_irq(struct mdss_dp_drv_pdata *dp_drv) /* Make sure DP mainlink and audio engines are disabled */ wmb(); - mdss_dp_ack_state(dp_drv, false); + /* + * If downstream device is a brige which no longer has any + * downstream devices connected to it, then we should reset + * the current panel info + */ + if (mdss_dp_is_ds_bridge_sink_count_zero(dp_drv)) + dp_init_panel_info(dp_drv, HDMI_VFRMT_UNKNOWN); + mutex_unlock(&dp_drv->train_mutex); - complete_all(&dp_drv->irq_comp); pr_debug("end\n"); return 0; @@ -1694,11 +1781,11 @@ static int mdss_dp_off_hpd(struct mdss_dp_drv_pdata *dp_drv) mdss_dp_host_deinit(dp_drv); dp_drv->power_on = false; - dp_drv->sink_info_read = false; dp_init_panel_info(dp_drv, HDMI_VFRMT_UNKNOWN); - mdss_dp_ack_state(dp_drv, false); mdss_dp_reset_test_data(dp_drv); + mdss_dp_reset_sink_count(dp_drv); + dp_drv->prev_sink_count = dp_drv->sink_count; mutex_unlock(&dp_drv->train_mutex); pr_debug("DP off done\n"); @@ -1737,8 +1824,9 @@ static int mdss_dp_send_audio_notification( if (mdss_dp_sink_audio_supp(dp) || dp->audio_test_req) { dp->audio_test_req = false; - pr_debug("sending audio notification\n"); flags |= MSM_EXT_DISP_HPD_AUDIO; + pr_debug("sending audio notification = %d, flags = %d\n", val, + flags); if (dp->ext_audio_data.intf_ops.hpd) ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev, @@ -1763,7 +1851,8 @@ static int mdss_dp_send_video_notification( goto end; } - flags |= MSM_EXT_DISP_HPD_VIDEO; + flags |= MSM_EXT_DISP_HPD_ASYNC_VIDEO; + pr_debug("sending video notification = %d, flags = %d\n", val, flags); if (dp->ext_audio_data.intf_ops.hpd) ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev, @@ -1873,14 +1962,16 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata) mdss_dp_ctrl_reset(&dp_drv->ctrl_io); mdss_dp_phy_reset(&dp_drv->ctrl_io); mdss_dp_aux_reset(&dp_drv->ctrl_io); + mdss_dp_aux_set_limits(&dp_drv->ctrl_io); + mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true); pr_debug("Ctrl_hw_rev =0x%x, phy hw_rev =0x%x\n", mdss_dp_get_ctrl_hw_version(&dp_drv->ctrl_io), mdss_dp_get_phy_hw_version(&dp_drv->phy_io)); - mdss_dp_phy_aux_setup(&dp_drv->phy_io, dp_drv->aux_cfg, - dp_drv->phy_reg_offset); + mdss_dp_reset_phy_config_indices(dp_drv); + mdss_dp_phy_aux_setup(dp_drv); mdss_dp_irq_enable(dp_drv); dp_drv->dp_initialized = true; @@ -1948,10 +2039,12 @@ static int mdss_dp_host_deinit(struct mdss_dp_drv_pdata *dp) static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, enum notification_status status) { - const int irq_comp_timeout = HZ * 2; int ret = 0; + bool notify = false; + bool connect; mutex_lock(&dp->pd_msg_mutex); + pr_debug("beginning notification\n"); if (status == dp->hpd_notification_status) { pr_debug("No change in status %s --> %s\n", mdss_dp_notification_status_to_string(status), @@ -1964,39 +2057,40 @@ static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, case NOTIFY_CONNECT_IRQ_HPD: if (dp->hpd_notification_status != NOTIFY_DISCONNECT_IRQ_HPD) goto invalid_request; - /* Follow the same programming as for NOTIFY_CONNECT */ - mdss_dp_host_init(&dp->panel_data); - mdss_dp_send_video_notification(dp, true); + notify = true; + connect = true; break; case NOTIFY_CONNECT: - if ((dp->hpd_notification_status == NOTIFY_CONNECT_IRQ_HPD) || - (dp->hpd_notification_status == - NOTIFY_DISCONNECT_IRQ_HPD)) + if (dp->hpd_notification_status == NOTIFY_CONNECT_IRQ_HPD) goto invalid_request; - mdss_dp_host_init(&dp->panel_data); - mdss_dp_send_video_notification(dp, true); + notify = true; + connect = true; break; case NOTIFY_DISCONNECT: - mdss_dp_send_audio_notification(dp, false); - mdss_dp_send_video_notification(dp, false); + /* + * Userspace triggers a disconnect event on boot up, this must + * not be processed as there was no previously connected sink + * device. + */ + if (dp->hpd_notification_status == NOTIFY_UNKNOWN) + goto invalid_request; + if (dp->hpd_notification_status == NOTIFY_DISCONNECT_IRQ_HPD) { + /* + * user modules already turned off. Need to explicitly + * turn off DP core here. + */ + mdss_dp_off_hpd(dp); + } else { + notify = true; + connect = false; + } break; case NOTIFY_DISCONNECT_IRQ_HPD: if (dp->hpd_notification_status == NOTIFY_DISCONNECT) goto invalid_request; - mdss_dp_send_audio_notification(dp, false); - mdss_dp_send_video_notification(dp, false); - if (!IS_ERR_VALUE(ret) && ret) { - reinit_completion(&dp->irq_comp); - ret = wait_for_completion_timeout(&dp->irq_comp, - irq_comp_timeout); - if (ret <= 0) { - pr_warn("irq_comp timed out\n"); - ret = -EINVAL; - } else { - ret = 0; - } - } + notify = true; + connect = false; break; default: pr_err("Invalid notification status = %d\n", status); @@ -2004,7 +2098,7 @@ static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, break; } - goto end; + goto notify; invalid_request: pr_err("Invalid request %s --> %s\n", @@ -2013,15 +2107,34 @@ invalid_request: mdss_dp_notification_status_to_string(status)); ret = -EINVAL; -end: +notify: + if (ret || !notify) { + pr_debug("not sending notification\n"); + goto end; + } + + atomic_set(&dp->notification_pending, 1); + if (connect) { + mdss_dp_host_init(&dp->panel_data); + ret = mdss_dp_send_video_notification(dp, true); + } else { + mdss_dp_send_audio_notification(dp, false); + ret = mdss_dp_send_video_notification(dp, false); + } + if (!ret) { pr_debug("Successfully sent notification %s --> %s\n", mdss_dp_notification_status_to_string( dp->hpd_notification_status), mdss_dp_notification_status_to_string(status)); - dp->hpd_notification_status = status; + } else { + pr_err("%s Notification failed\n", + mdss_dp_notification_status_to_string(status)); + atomic_set(&dp->notification_pending, 0); } +end: + dp->hpd_notification_status = status; mutex_unlock(&dp->pd_msg_mutex); return ret; } @@ -2031,9 +2144,6 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp) int ret; u32 max_pclk_khz; - if (dp->sink_info_read) - return 0; - pr_debug("start\n"); ret = mdss_dp_dpcd_cap_read(dp); @@ -2046,8 +2156,25 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp) */ pr_err("dpcd read failed, set failsafe parameters\n"); mdss_dp_set_default_link_parameters(dp); + goto read_edid; + } + + /* + * When connected to a multiport adaptor which does not have a + * local EDID present, do not attempt to read the EDID. + * When connected to a multiport adaptor with no downstream device + * connected to it, do not attempt to read the EDID. It is possible + * that the adaptor may advertise the presence of local EDID, but it + * is not guaranteed to work. + */ + if (mdss_dp_is_ds_bridge_sink_count_zero(dp)) { + if (mdss_dp_is_ds_bridge_no_local_edid(dp)) + pr_debug("No local EDID present on DS branch device\n"); + pr_info("no downstream devices, skip client notification\n"); + goto end; } +read_edid: ret = mdss_dp_edid_read(dp); if (ret) { pr_err("edid read error, setting default resolution\n"); @@ -2058,14 +2185,18 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp) hdmi_edid_set_max_pclk_rate(dp->panel_data.panel_info.edid_data, min(dp->max_pclk_khz, max_pclk_khz)); + if (dp->dpcd_read_required) { + pr_debug("reading DPCD with updated AUX config\n"); + mdss_dp_dpcd_cap_read(dp); + dp->dpcd_read_required = false; + } + ret = hdmi_edid_parser(dp->panel_data.panel_info.edid_data); if (ret) { pr_err("edid parse failed, setting default resolution\n"); goto notify; } - dp->sink_info_read = true; - notify: if (ret) { /* set failsafe parameters */ @@ -2092,7 +2223,6 @@ notify: end: pr_debug("end\n"); return ret; - } static int mdss_dp_check_params(struct mdss_dp_drv_pdata *dp, void *arg) @@ -2814,8 +2944,6 @@ static void mdss_dp_mainlink_push_idle(struct mdss_panel_data *pdata) /* wait until link training is completed */ mutex_lock(&dp_drv->train_mutex); - mdss_dp_aux_set_sink_power_state(dp_drv, SINK_POWER_OFF); - reinit_completion(&dp_drv->idle_comp); mdss_dp_state_ctrl(&dp_drv->ctrl_io, ST_PUSH_IDLE); if (!wait_for_completion_timeout(&dp_drv->idle_comp, @@ -2905,28 +3033,33 @@ static int mdss_dp_event_handler(struct mdss_panel_data *pdata, switch (event) { case MDSS_EVENT_UNBLANK: - mdss_dp_ack_state(dp, true); rc = mdss_dp_on(pdata); break; case MDSS_EVENT_PANEL_ON: mdss_dp_update_hdcp_info(dp); if (dp_is_hdcp_enabled(dp)) { - cancel_delayed_work(&dp->hdcp_cb_work); + cancel_delayed_work_sync(&dp->hdcp_cb_work); dp->hdcp_status = HDCP_STATE_AUTHENTICATING; queue_delayed_work(dp->workq, &dp->hdcp_cb_work, HZ / 2); } break; + case MDSS_EVENT_POST_PANEL_ON: + atomic_set(&dp->notification_pending, 0); + complete_all(&dp->notification_comp); + break; case MDSS_EVENT_PANEL_OFF: rc = mdss_dp_off(pdata); + atomic_set(&dp->notification_pending, 0); + complete_all(&dp->notification_comp); break; case MDSS_EVENT_BLANK: if (dp_is_hdcp_enabled(dp)) { dp->hdcp_status = HDCP_STATE_INACTIVE; - cancel_delayed_work(&dp->hdcp_cb_work); + cancel_delayed_work_sync(&dp->hdcp_cb_work); if (dp->hdcp.ops->off) dp->hdcp.ops->off(dp->hdcp.data); } @@ -3083,6 +3216,7 @@ static int mdss_retrieve_dp_ctrl_resources(struct platform_device *pdev, static void mdss_dp_video_ready(struct mdss_dp_drv_pdata *dp) { pr_debug("dp_video_ready\n"); + mdss_dp_ack_state(dp, true); complete(&dp->video_comp); } @@ -3209,6 +3343,7 @@ irqreturn_t dp_isr(int irq, void *ptr) spin_lock(&dp->lock); isr1 = dp_read(base + DP_INTR_STATUS); isr2 = dp_read(base + DP_INTR_STATUS2); + pr_debug("isr1=0x%08x, isr2=0x%08x\n", isr1, isr2); mask1 = isr1 & dp->mask1; @@ -3425,6 +3560,17 @@ static inline void mdss_dp_link_maintenance(struct mdss_dp_drv_pdata *dp, if (mdss_dp_notify_clients(dp, NOTIFY_DISCONNECT_IRQ_HPD)) return; + if (atomic_read(&dp->notification_pending)) { + int ret; + + pr_debug("waiting for the disconnect to finish\n"); + ret = wait_for_completion_timeout(&dp->notification_comp, HZ); + if (ret <= 0) { + pr_warn("NOTIFY_DISCONNECT_IRQ_HPD timed out\n"); + return; + } + } + mdss_dp_on_irq(dp, lt_needed); } @@ -3576,7 +3722,7 @@ static int mdss_dp_process_audio_pattern_request(struct mdss_dp_drv_pdata *dp) return -EINVAL; if (dp_is_hdcp_enabled(dp) && dp->hdcp.ops->off) { - cancel_delayed_work(&dp->hdcp_cb_work); + cancel_delayed_work_sync(&dp->hdcp_cb_work); dp->hdcp.ops->off(dp->hdcp.data); } @@ -3622,10 +3768,46 @@ static int mdss_dp_process_audio_pattern_request(struct mdss_dp_drv_pdata *dp) static int mdss_dp_process_downstream_port_status_change( struct mdss_dp_drv_pdata *dp) { - if (!mdss_dp_is_downstream_port_status_changed(dp)) + bool ds_status_changed = false; + + if (mdss_dp_is_downstream_port_status_changed(dp)) { + pr_debug("downstream port status changed\n"); + ds_status_changed = true; + } + + /* + * Ideally sink should update the downstream port status changed + * whenever it updates the downstream sink count. However, it is + * possible that only the sink count is updated without setting + * the downstream port status changed bit. + */ + if (dp->sink_count.count != dp->prev_sink_count.count) { + pr_debug("downstream sink count changed from %d --> %d\n", + dp->prev_sink_count.count, dp->sink_count.count); + ds_status_changed = true; + } + + if (!ds_status_changed) return -EINVAL; - return mdss_dp_edid_read(dp); + mdss_dp_notify_clients(dp, NOTIFY_DISCONNECT_IRQ_HPD); + if (atomic_read(&dp->notification_pending)) { + int ret; + + pr_debug("waiting for the disconnect to finish\n"); + ret = wait_for_completion_timeout(&dp->notification_comp, HZ); + if (ret <= 0) { + pr_warn("NOTIFY_DISCONNECT_IRQ_HPD timed out\n"); + return -ETIMEDOUT; + } + } + + if (mdss_dp_is_ds_bridge_sink_count_zero(dp)) { + pr_debug("sink count is zero, nothing to do\n"); + return 0; + } + + return mdss_dp_process_hpd_high(dp); } static bool mdss_dp_video_pattern_test_lt_needed(struct mdss_dp_drv_pdata *dp) @@ -3723,19 +3905,19 @@ static int mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) mdss_dp_aux_parse_sink_status_field(dp); - ret = mdss_dp_process_link_training_request(dp); + ret = mdss_dp_process_downstream_port_status_change(dp); if (!ret) goto exit; - ret = mdss_dp_process_phy_test_pattern_request(dp); + ret = mdss_dp_process_link_training_request(dp); if (!ret) goto exit; - ret = mdss_dp_process_link_status_update(dp); + ret = mdss_dp_process_phy_test_pattern_request(dp); if (!ret) goto exit; - ret = mdss_dp_process_downstream_port_status_change(dp); + ret = mdss_dp_process_link_status_update(dp); if (!ret) goto exit; @@ -3748,7 +3930,6 @@ static int mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) goto exit; pr_debug("done\n"); - exit: dp->hpd_irq_on = false; return ret; @@ -3842,11 +4023,21 @@ static void mdss_dp_process_attention(struct mdss_dp_drv_pdata *dp_drv) if (!dp_drv->alt_mode.dp_status.hpd_high) { pr_debug("Attention: HPD low\n"); + if (!dp_drv->power_on) { + pr_debug("HPD already low\n"); + return; + } + if (dp_is_hdcp_enabled(dp_drv) && dp_drv->hdcp.ops->off) { - cancel_delayed_work(&dp_drv->hdcp_cb_work); + cancel_delayed_work_sync(&dp_drv->hdcp_cb_work); dp_drv->hdcp.ops->off(dp_drv->hdcp.data); } + /* + * Reset the sink count before nofifying clients since HPD Low + * indicates that the downstream device has been disconnected. + */ + mdss_dp_reset_sink_count(dp_drv); mdss_dp_notify_clients(dp_drv, NOTIFY_DISCONNECT); pr_debug("Attention: Notified clients\n"); @@ -3874,6 +4065,11 @@ static void mdss_dp_process_attention(struct mdss_dp_drv_pdata *dp_drv) pr_debug("Attention: HPD high\n"); + if (dp_drv->power_on) { + pr_debug("HPD high processed already\n"); + return; + } + dp_drv->alt_mode.current_state |= DP_STATUS_DONE; if (dp_drv->alt_mode.current_state & DP_CONFIGURE_DONE) { @@ -3895,6 +4091,7 @@ static void mdss_dp_handle_attention(struct mdss_dp_drv_pdata *dp) pr_debug("processing item %d in the list\n", ++i); + reinit_completion(&dp->notification_comp); mutex_lock(&dp->attention_lock); node = list_first_entry(&dp->attention_head, struct mdss_dp_attention_node, list); @@ -3909,6 +4106,21 @@ static void mdss_dp_handle_attention(struct mdss_dp_drv_pdata *dp) mdss_dp_usbpd_ext_dp_status(&dp->alt_mode.dp_status); mdss_dp_process_attention(dp); + if (atomic_read(&dp->notification_pending)) { + pr_debug("waiting for the attention event to finish\n"); + /* + * This wait is intentionally implemented without a + * timeout since this is happens only in possible error + * conditions e.g. if the display framework does not + * power off/on the DisplayPort device in time. Other + * events might already be queued from the sink at this + * point and they cannot be processed until the power + * off/on is complete otherwise we might have problems + * with interleaving of these events e.g. un-clocked + * register access. + */ + wait_for_completion(&dp->notification_comp); + } pr_debug("done processing item %d in the list\n", i); }; @@ -4080,8 +4292,9 @@ static int mdss_dp_probe(struct platform_device *pdev) dp_drv->inited = true; dp_drv->hpd_irq_on = false; + atomic_set(&dp_drv->notification_pending, 0); mdss_dp_reset_test_data(dp_drv); - init_completion(&dp_drv->irq_comp); + init_completion(&dp_drv->notification_comp); dp_drv->suspend_vic = HDMI_VFRMT_UNKNOWN; pr_debug("done\n"); diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index 4decb26ea073..e9f5a04e2d19 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -77,7 +77,7 @@ #define EDP_INTR_I2C_NACK BIT(18) #define EDP_INTR_I2C_DEFER BIT(21) #define EDP_INTR_PLL_UNLOCKED BIT(24) -#define EDP_INTR_AUX_ERROR BIT(27) +#define EDP_INTR_PHY_AUX_ERR BIT(27) #define EDP_INTR_STATUS1 \ @@ -85,7 +85,7 @@ EDP_INTR_WRONG_ADDR | EDP_INTR_TIMEOUT | \ EDP_INTR_NACK_DEFER | EDP_INTR_WRONG_DATA_CNT | \ EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER | \ - EDP_INTR_PLL_UNLOCKED | EDP_INTR_AUX_ERROR) + EDP_INTR_PLL_UNLOCKED | EDP_INTR_PHY_AUX_ERR) #define EDP_INTR_MASK1 (EDP_INTR_STATUS1 << 2) @@ -110,6 +110,8 @@ struct edp_buf { int len; /* dara length */ char trans_num; /* transaction number */ char i2c; /* 1 == i2c cmd, 0 == native cmd */ + bool no_send_addr; + bool no_send_stop; }; /* USBPD-TypeC specific Macros */ @@ -186,6 +188,7 @@ struct dp_alt_mode { #define DPCD_MAX_DOWNSPREAD_0_5 BIT(2) #define DPCD_NO_AUX_HANDSHAKE BIT(3) #define DPCD_PORT_0_EDID_PRESENTED BIT(4) +#define DPCD_PORT_1_EDID_PRESENTED BIT(5) /* event */ #define EV_EDP_AUX_SETUP BIT(0) @@ -239,6 +242,8 @@ struct downstream_port_config { bool oui_support; }; +#define DP_MAX_DS_PORT_COUNT 2 + struct dpcd_cap { char major; char minor; @@ -249,7 +254,7 @@ struct dpcd_cap { char enhanced_frame; u32 max_link_rate; /* 162, 270 and 540 Mb, divided by 10 */ u32 flags; - u32 rx_port0_buf_size; + u32 rx_port_buf_size[DP_MAX_DS_PORT_COUNT]; u32 training_read_interval;/* us */ struct downstream_port_config downstream_port; }; @@ -426,6 +431,102 @@ struct mdss_dp_crc_data { u32 b_cb; }; +#define MDSS_DP_MAX_PHY_CFG_VALUE_CNT 3 +struct mdss_dp_phy_cfg { + u32 cfg_cnt; + u32 current_index; + u32 offset; + u32 lut[MDSS_DP_MAX_PHY_CFG_VALUE_CNT]; +}; + +/* PHY AUX config registers */ +enum dp_phy_aux_config_type { + PHY_AUX_CFG0, + PHY_AUX_CFG1, + PHY_AUX_CFG2, + PHY_AUX_CFG3, + PHY_AUX_CFG4, + PHY_AUX_CFG5, + PHY_AUX_CFG6, + PHY_AUX_CFG7, + PHY_AUX_CFG8, + PHY_AUX_CFG9, + PHY_AUX_CFG_MAX, +}; + +static inline const char *mdss_dp_get_phy_aux_config_property(u32 cfg_type) +{ + switch (cfg_type) { + case PHY_AUX_CFG0: + return "qcom,aux-cfg0-settings"; + case PHY_AUX_CFG1: + return "qcom,aux-cfg1-settings"; + case PHY_AUX_CFG2: + return "qcom,aux-cfg2-settings"; + case PHY_AUX_CFG3: + return "qcom,aux-cfg3-settings"; + case PHY_AUX_CFG4: + return "qcom,aux-cfg4-settings"; + case PHY_AUX_CFG5: + return "qcom,aux-cfg5-settings"; + case PHY_AUX_CFG6: + return "qcom,aux-cfg6-settings"; + case PHY_AUX_CFG7: + return "qcom,aux-cfg7-settings"; + case PHY_AUX_CFG8: + return "qcom,aux-cfg8-settings"; + case PHY_AUX_CFG9: + return "qcom,aux-cfg9-settings"; + default: + return "unknown"; + } +} + +static inline char *mdss_dp_phy_aux_config_type_to_string(u32 cfg_type) +{ + switch (cfg_type) { + case PHY_AUX_CFG0: + return DP_ENUM_STR(PHY_AUX_CFG0); + case PHY_AUX_CFG1: + return DP_ENUM_STR(PHY_AUX_CFG1); + case PHY_AUX_CFG2: + return DP_ENUM_STR(PHY_AUX_CFG2); + case PHY_AUX_CFG3: + return DP_ENUM_STR(PHY_AUX_CFG3); + case PHY_AUX_CFG4: + return DP_ENUM_STR(PHY_AUX_CFG4); + case PHY_AUX_CFG5: + return DP_ENUM_STR(PHY_AUX_CFG5); + case PHY_AUX_CFG6: + return DP_ENUM_STR(PHY_AUX_CFG6); + case PHY_AUX_CFG7: + return DP_ENUM_STR(PHY_AUX_CFG7); + case PHY_AUX_CFG8: + return DP_ENUM_STR(PHY_AUX_CFG8); + case PHY_AUX_CFG9: + return DP_ENUM_STR(PHY_AUX_CFG9); + default: + return "unknown"; + } +} + +enum dp_aux_transaction { + DP_AUX_WRITE, + DP_AUX_READ +}; + +static inline char *mdss_dp_aux_transaction_to_string(u32 transaction) +{ + switch (transaction) { + case DP_AUX_WRITE: + return DP_ENUM_STR(DP_AUX_WRITE); + case DP_AUX_READ: + return DP_ENUM_STR(DP_AUX_READ); + default: + return "unknown"; + } +} + struct mdss_dp_drv_pdata { /* device driver */ int (*on) (struct mdss_panel_data *pdata); @@ -449,11 +550,11 @@ struct mdss_dp_drv_pdata { bool core_clks_on; bool link_clks_on; bool power_on; - bool sink_info_read; u32 suspend_vic; bool hpd; bool psm_enabled; bool audio_test_req; + bool dpcd_read_required; /* dp specific */ unsigned char *base; @@ -513,7 +614,7 @@ struct mdss_dp_drv_pdata { struct completion aux_comp; struct completion idle_comp; struct completion video_comp; - struct completion irq_comp; + struct completion notification_comp; struct mutex aux_mutex; struct mutex train_mutex; struct mutex pd_msg_mutex; @@ -540,13 +641,14 @@ struct mdss_dp_drv_pdata { struct dp_statistic dp_stat; bool hpd_irq_on; u32 hpd_notification_status; + atomic_t notification_pending; struct mdss_dp_event_data dp_event; struct task_struct *ev_thread; /* dt settings */ char l_map[4]; - u32 aux_cfg[AUX_CFG_LEN]; + struct mdss_dp_phy_cfg aux_cfg[PHY_AUX_CFG_MAX]; struct workqueue_struct *workq; struct delayed_work hdcp_cb_work; @@ -562,6 +664,7 @@ struct mdss_dp_drv_pdata { struct dpcd_test_request test_data; struct dpcd_sink_count sink_count; + struct dpcd_sink_count prev_sink_count; struct list_head attention_head; }; @@ -688,6 +791,7 @@ enum dp_aux_error { EDP_AUX_ERR_NACK = -3, EDP_AUX_ERR_DEFER = -4, EDP_AUX_ERR_NACK_DEFER = -5, + EDP_AUX_ERR_PHY = -6, }; static inline char *mdss_dp_get_aux_error(u32 aux_error) diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 8566b1d6985a..37209c161366 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -73,6 +73,21 @@ static int dp_buf_trailing(struct edp_buf *eb) return (int)(eb->end - eb->data); } +static void mdss_dp_aux_clear_hw_interrupts(void __iomem *phy_base) +{ + u32 data; + + data = dp_read(phy_base + DP_PHY_AUX_INTERRUPT_STATUS); + pr_debug("PHY_AUX_INTERRUPT_STATUS=0x%08x\n", data); + + dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0x1f); + dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0x9f); + dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0); + + /* Ensure that all interrupts are cleared and acked */ + wmb(); +} + /* * edp aux dp_buf_add_cmd: * NO native and i2c command mix allowed @@ -123,35 +138,46 @@ static int dp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd) return cmd->len - 1; } -static int dp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base) +static int dp_cmd_fifo_tx(struct mdss_dp_drv_pdata *dp) { u32 data; - char *dp; + char *datap; int len, cnt; + struct edp_buf *tp = &dp->txp; + void __iomem *base = dp->base; + len = tp->len; /* total byte to cmd fifo */ if (len == 0) return 0; cnt = 0; - dp = tp->start; + datap = tp->start; while (cnt < len) { - data = *dp; /* data byte */ + data = *datap; /* data byte */ data <<= 8; data &= 0x00ff00; /* index = 0, write */ if (cnt == 0) data |= BIT(31); /* INDEX_WRITE */ dp_write(base + DP_AUX_DATA, data); cnt++; - dp++; + datap++; } + /* clear the current tx request before queuing a new one */ + dp_write(base + DP_AUX_TRANS_CTRL, 0); + + /* clear any previous PHY AUX interrupts */ + mdss_dp_aux_clear_hw_interrupts(dp->phy_io.base); + data = (tp->trans_num - 1); if (tp->i2c) { data |= BIT(8); /* I2C */ - data |= BIT(10); /* NO SEND ADDR */ - data |= BIT(11); /* NO SEND STOP */ + if (tp->no_send_addr) + data |= BIT(10); /* NO SEND ADDR */ + if (tp->no_send_stop) + data |= BIT(11); /* NO SEND STOP */ } data |= BIT(9); /* GO */ @@ -164,7 +190,7 @@ static int dp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base) { u32 data; char *dp; - int i; + int i, actual_i; data = 0; /* index = 0 */ data |= BIT(31); /* INDEX_WRITE */ @@ -177,7 +203,12 @@ static int dp_cmd_fifo_rx(struct edp_buf *rp, int len, unsigned char *base) data = dp_read(base + DP_AUX_DATA); for (i = 0; i < len; i++) { data = dp_read(base + DP_AUX_DATA); - *dp++ = (char)((data >> 8) & 0xff); + *dp++ = (char)((data >> 8) & 0xFF); + + actual_i = (data >> 16) & 0xFF; + if (i != actual_i) + pr_warn("Index mismatch: expected %d, found %d\n", + i, actual_i); } rp->len = len; @@ -214,9 +245,16 @@ static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep, reinit_completion(&ep->aux_comp); - len = dp_cmd_fifo_tx(&ep->txp, ep->base); + tp->no_send_addr = true; + tp->no_send_stop = true; + len = dp_cmd_fifo_tx(ep); - wait_for_completion_timeout(&ep->aux_comp, HZ/4); + if (!wait_for_completion_timeout(&ep->aux_comp, HZ/4)) { + pr_err("aux write timeout\n"); + ep->aux_error_num = EDP_AUX_ERR_TOUT; + /* Reset the AUX controller state machine */ + mdss_dp_aux_reset(&ep->ctrl_io); + } if (ep->aux_error_num == EDP_AUX_ERR_NONE) ret = len; @@ -228,13 +266,6 @@ static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep, return ret; } -int dp_aux_write(void *ep, struct edp_cmd *cmd) -{ - int rc = dp_aux_write_cmds(ep, cmd); - - return rc < 0 ? -EINVAL : 0; -} - static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, struct edp_cmd *cmds) { @@ -242,6 +273,7 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, struct edp_buf *tp; struct edp_buf *rp; int len, ret; + u32 data; mutex_lock(&ep->aux_mutex); ep->aux_cmd_busy = 1; @@ -270,10 +302,23 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, reinit_completion(&ep->aux_comp); - dp_cmd_fifo_tx(tp, ep->base); + tp->no_send_addr = true; + tp->no_send_stop = false; + dp_cmd_fifo_tx(ep); - wait_for_completion_timeout(&ep->aux_comp, HZ/4); + if (!wait_for_completion_timeout(&ep->aux_comp, HZ/4)) { + pr_err("aux read timeout\n"); + ep->aux_error_num = EDP_AUX_ERR_TOUT; + /* Reset the AUX controller state machine */ + mdss_dp_aux_reset(&ep->ctrl_io); + ret = ep->aux_error_num; + goto end; + } + /* clear the current rx request before queuing a new one */ + data = dp_read(ep->base + DP_AUX_TRANS_CTRL); + data &= (~BIT(9)); + dp_write(ep->base + DP_AUX_TRANS_CTRL, data); if (ep->aux_error_num == EDP_AUX_ERR_NONE) { ret = dp_cmd_fifo_rx(rp, len, ep->base); @@ -284,58 +329,128 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, ret = ep->aux_error_num; } +end: ep->aux_cmd_busy = 0; mutex_unlock(&ep->aux_mutex); return ret; } -int dp_aux_read(void *ep, struct edp_cmd *cmds) -{ - int rc = dp_aux_read_cmds(ep, cmds); - - return rc < 0 ? -EINVAL : 0; -} - void dp_aux_native_handler(struct mdss_dp_drv_pdata *ep, u32 isr) { - if (isr & EDP_INTR_AUX_I2C_DONE) + pr_debug("isr=0x%08x\n", isr); + if (isr & EDP_INTR_AUX_I2C_DONE) { ep->aux_error_num = EDP_AUX_ERR_NONE; - else if (isr & EDP_INTR_WRONG_ADDR) + } else if (isr & EDP_INTR_WRONG_ADDR) { ep->aux_error_num = EDP_AUX_ERR_ADDR; - else if (isr & EDP_INTR_TIMEOUT) + } else if (isr & EDP_INTR_TIMEOUT) { ep->aux_error_num = EDP_AUX_ERR_TOUT; - if (isr & EDP_INTR_NACK_DEFER) + } else if (isr & EDP_INTR_NACK_DEFER) { ep->aux_error_num = EDP_AUX_ERR_NACK; + } else if (isr & EDP_INTR_PHY_AUX_ERR) { + ep->aux_error_num = EDP_AUX_ERR_PHY; + mdss_dp_aux_clear_hw_interrupts(ep->phy_io.base); + } else { + ep->aux_error_num = EDP_AUX_ERR_NONE; + } complete(&ep->aux_comp); } void dp_aux_i2c_handler(struct mdss_dp_drv_pdata *ep, u32 isr) { + pr_debug("isr=0x%08x\n", isr); if (isr & EDP_INTR_AUX_I2C_DONE) { if (isr & (EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER)) ep->aux_error_num = EDP_AUX_ERR_NACK; else ep->aux_error_num = EDP_AUX_ERR_NONE; } else { - if (isr & EDP_INTR_WRONG_ADDR) + if (isr & EDP_INTR_WRONG_ADDR) { ep->aux_error_num = EDP_AUX_ERR_ADDR; - else if (isr & EDP_INTR_TIMEOUT) + } else if (isr & EDP_INTR_TIMEOUT) { ep->aux_error_num = EDP_AUX_ERR_TOUT; - if (isr & EDP_INTR_NACK_DEFER) + } else if (isr & EDP_INTR_NACK_DEFER) { ep->aux_error_num = EDP_AUX_ERR_NACK_DEFER; - if (isr & EDP_INTR_I2C_NACK) + } else if (isr & EDP_INTR_I2C_NACK) { ep->aux_error_num = EDP_AUX_ERR_NACK; - if (isr & EDP_INTR_I2C_DEFER) + } else if (isr & EDP_INTR_I2C_DEFER) { ep->aux_error_num = EDP_AUX_ERR_DEFER; + } else if (isr & EDP_INTR_PHY_AUX_ERR) { + ep->aux_error_num = EDP_AUX_ERR_PHY; + mdss_dp_aux_clear_hw_interrupts(ep->phy_io.base); + } else { + ep->aux_error_num = EDP_AUX_ERR_NONE; + } } complete(&ep->aux_comp); } -static int dp_aux_write_buf(struct mdss_dp_drv_pdata *ep, u32 addr, - char *buf, int len, int i2c) +static int dp_aux_rw_cmds_retry(struct mdss_dp_drv_pdata *dp, + struct edp_cmd *cmd, enum dp_aux_transaction transaction) +{ + int const retry_count = 5; + int adjust_count = 0; + int i; + u32 aux_cfg1_config_count; + int ret; + + aux_cfg1_config_count = mdss_dp_phy_aux_get_config_cnt(dp, + PHY_AUX_CFG1); +retry: + i = 0; + ret = 0; + do { + struct edp_cmd cmd1 = *cmd; + + dp->aux_error_num = EDP_AUX_ERR_NONE; + pr_debug("Trying %s, iteration count: %d\n", + mdss_dp_aux_transaction_to_string(transaction), + i + 1); + if (transaction == DP_AUX_READ) + ret = dp_aux_read_cmds(dp, &cmd1); + else if (transaction == DP_AUX_WRITE) + ret = dp_aux_write_cmds(dp, &cmd1); + + i++; + } while ((i < retry_count) && (ret < 0)); + + if (ret >= 0) /* rw success */ + goto end; + + if (adjust_count >= aux_cfg1_config_count) { + pr_err("PHY_AUX_CONFIG1 calibration failed\n"); + goto end; + } + + /* Adjust AUX configuration and retry */ + pr_debug("AUX failure (%d), adjust AUX settings\n", ret); + mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1); + adjust_count++; + goto retry; + +end: + return ret; +} + +/** + * dp_aux_write_buf_retry() - send a AUX write command + * @dp: display port driver data + * @addr: AUX address (in hex) to write the command to + * @buf: the buffer containing the actual payload + * @len: the length of the buffer @buf + * @i2c: indicates if it is an i2c-over-aux transaction + * @retry: specifies if retries should be attempted upon failures + * + * Send an AUX write command with the specified payload over the AUX + * channel. This function can send both native AUX command or an + * i2c-over-AUX command. In addition, if specified, it can also retry + * when failures are detected. The retry logic would adjust AUX PHY + * parameters on the fly. + */ +static int dp_aux_write_buf_retry(struct mdss_dp_drv_pdata *dp, u32 addr, + char *buf, int len, int i2c, bool retry) { struct edp_cmd cmd; @@ -346,11 +461,42 @@ static int dp_aux_write_buf(struct mdss_dp_drv_pdata *ep, u32 addr, cmd.len = len & 0x0ff; cmd.next = 0; - return dp_aux_write_cmds(ep, &cmd); + if (retry) + return dp_aux_rw_cmds_retry(dp, &cmd, DP_AUX_WRITE); + else + return dp_aux_write_cmds(dp, &cmd); } -static int dp_aux_read_buf(struct mdss_dp_drv_pdata *ep, u32 addr, - int len, int i2c) +static int dp_aux_write_buf(struct mdss_dp_drv_pdata *dp, u32 addr, + char *buf, int len, int i2c) +{ + return dp_aux_write_buf_retry(dp, addr, buf, len, i2c, true); +} + +int dp_aux_write(void *dp, struct edp_cmd *cmd) +{ + int rc = dp_aux_write_cmds(dp, cmd); + + return rc < 0 ? -EINVAL : 0; +} + +/** + * dp_aux_read_buf_retry() - send a AUX read command + * @dp: display port driver data + * @addr: AUX address (in hex) to write the command to + * @buf: the buffer containing the actual payload + * @len: the length of the buffer @buf + * @i2c: indicates if it is an i2c-over-aux transaction + * @retry: specifies if retries should be attempted upon failures + * + * Send an AUX write command with the specified payload over the AUX + * channel. This function can send both native AUX command or an + * i2c-over-AUX command. In addition, if specified, it can also retry + * when failures are detected. The retry logic would adjust AUX PHY + * parameters on the fly. + */ +static int dp_aux_read_buf_retry(struct mdss_dp_drv_pdata *dp, u32 addr, + int len, int i2c, bool retry) { struct edp_cmd cmd = {0}; @@ -361,7 +507,23 @@ static int dp_aux_read_buf(struct mdss_dp_drv_pdata *ep, u32 addr, cmd.len = len & 0x0ff; cmd.next = 0; - return dp_aux_read_cmds(ep, &cmd); + if (retry) + return dp_aux_rw_cmds_retry(dp, &cmd, DP_AUX_READ); + else + return dp_aux_read_cmds(dp, &cmd); +} + +static int dp_aux_read_buf(struct mdss_dp_drv_pdata *dp, u32 addr, + int len, int i2c) +{ + return dp_aux_read_buf_retry(dp, addr, len, i2c, true); +} + +int dp_aux_read(void *dp, struct edp_cmd *cmds) +{ + int rc = dp_aux_read_cmds(dp, cmds); + + return rc < 0 ? -EINVAL : 0; } /* @@ -733,16 +895,68 @@ static void dp_aux_send_checksum(struct mdss_dp_drv_pdata *dp, u32 checksum) dp_aux_write_buf(dp, 0x260, data, 1, 0); } +int mdss_dp_aux_read_edid(struct mdss_dp_drv_pdata *dp, + u8 *buf, int size, int blk_num) +{ + int max_size_bytes = 16; + int rc, read_size; + int ret = 0; + u8 offset_lut[] = {0x0, 0x80}; + u8 offset; + + if (dp->test_data.test_requested == TEST_EDID_READ) + max_size_bytes = 128; + + /* + * Calculate the offset of the desired EDID block to be read. + * For even blocks, offset starts at 0x0 + * For odd blocks, offset starts at 0x80 + */ + if (blk_num % 2) + offset = offset_lut[1]; + else + offset = offset_lut[0]; + + do { + struct edp_cmd cmd = {0}; + + read_size = min(size, max_size_bytes); + cmd.read = 1; + cmd.addr = EDID_START_ADDRESS; + cmd.len = read_size; + cmd.out_buf = buf; + cmd.i2c = 1; + + /* Write the offset first prior to reading the data */ + pr_debug("offset=0x%x, size=%d\n", offset, size); + dp_aux_write_buf_retry(dp, EDID_START_ADDRESS, &offset, 1, 1, + false); + rc = dp_aux_read(dp, &cmd); + if (rc < 0) { + pr_err("aux read failed\n"); + return rc; + } + + print_hex_dump(KERN_DEBUG, "DP:EDID: ", DUMP_PREFIX_NONE, 16, 1, + buf, read_size, false); + buf += read_size; + offset += read_size; + size -= read_size; + ret += read_size; + } while (size > 0); + + return ret; +} + int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp) { - struct edp_buf *rp = &dp->rxp; int rlen, ret = 0; int edid_blk = 0, blk_num = 0, retries = 10; bool edid_parsing_done = false; - const u8 cea_tag = 0x02, start_ext_blk = 0x1; u32 const segment_addr = 0x30; u32 checksum = 0; - char segment = 0x1; + bool phy_aux_update_requested = false; + bool ext_block_parsing_done = false; ret = dp_aux_chan_ready(dp); if (ret) { @@ -750,72 +964,91 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp) return ret; } + memset(dp->edid_buf, 0, dp->edid_buf_size); + /** * Parse the test request vector to see whether there is a * TEST_EDID_READ test request. */ dp_sink_parse_test_request(dp); - do { - rlen = dp_aux_read_buf(dp, EDID_START_ADDRESS + - (blk_num * EDID_BLOCK_SIZE), - EDID_BLOCK_SIZE, 1); + while (retries) { + u8 segment; + u8 edid_buf[EDID_BLOCK_SIZE] = {0}; + + /* + * Write the segment first. + * Segment = 0, for blocks 0 and 1 + * Segment = 1, for blocks 2 and 3 + * Segment = 2, for blocks 3 and 4 + * and so on ... + */ + segment = blk_num >> 1; + dp_aux_write_buf_retry(dp, segment_addr, &segment, 1, 1, false); + + rlen = mdss_dp_aux_read_edid(dp, edid_buf, EDID_BLOCK_SIZE, + blk_num); if (rlen != EDID_BLOCK_SIZE) { - pr_err("Read failed. rlen=%d\n", rlen); + pr_err("Read failed. rlen=%s\n", + mdss_dp_get_aux_error(rlen)); + mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1); + phy_aux_update_requested = true; + retries--; continue; } - pr_debug("blk_num=%d, rlen=%d\n", blk_num, rlen); - - if (dp_edid_is_valid_header(rp->data)) { - ret = dp_edid_buf_error(rp->data, rp->len); + print_hex_dump(KERN_DEBUG, "DP:EDID: ", DUMP_PREFIX_NONE, 16, 1, + edid_buf, EDID_BLOCK_SIZE, false); + if (dp_edid_is_valid_header(edid_buf)) { + ret = dp_edid_buf_error(edid_buf, rlen); if (ret) { pr_err("corrupt edid block detected\n"); + mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1); + phy_aux_update_requested = true; + retries--; continue; } if (edid_parsing_done) { + pr_debug("block 0 parsed already\n"); blk_num++; + retries--; continue; } - dp_extract_edid_manufacturer(&dp->edid, rp->data); - dp_extract_edid_product(&dp->edid, rp->data); - dp_extract_edid_version(&dp->edid, rp->data); - dp_extract_edid_ext_block_cnt(&dp->edid, rp->data); - dp_extract_edid_video_support(&dp->edid, rp->data); - dp_extract_edid_feature(&dp->edid, rp->data); + dp_extract_edid_manufacturer(&dp->edid, edid_buf); + dp_extract_edid_product(&dp->edid, edid_buf); + dp_extract_edid_version(&dp->edid, edid_buf); + dp_extract_edid_ext_block_cnt(&dp->edid, edid_buf); + dp_extract_edid_video_support(&dp->edid, edid_buf); + dp_extract_edid_feature(&dp->edid, edid_buf); dp_extract_edid_detailed_timing_description(&dp->edid, - rp->data); + edid_buf); edid_parsing_done = true; + } else if (!edid_parsing_done) { + pr_debug("Invalid edid block 0 header\n"); + /* Retry block 0 with adjusted phy aux settings */ + mdss_dp_phy_aux_update_config(dp, PHY_AUX_CFG1); + phy_aux_update_requested = true; + retries--; + continue; } else { edid_blk++; blk_num++; - - /* fix dongle byte shift issue */ - if (edid_blk == 1 && rp->data[0] != cea_tag) { - u8 tmp[EDID_BLOCK_SIZE - 1]; - - memcpy(tmp, rp->data, EDID_BLOCK_SIZE - 1); - rp->data[0] = cea_tag; - memcpy(rp->data + 1, tmp, EDID_BLOCK_SIZE - 1); - } } memcpy(dp->edid_buf + (edid_blk * EDID_BLOCK_SIZE), - rp->data, EDID_BLOCK_SIZE); + edid_buf, EDID_BLOCK_SIZE); - checksum = rp->data[rp->len - 1]; + checksum = edid_buf[rlen - 1]; /* break if no more extension blocks present */ - if (edid_blk == dp->edid.ext_block_cnt) + if (edid_blk >= dp->edid.ext_block_cnt) { + ext_block_parsing_done = true; break; - - /* write segment number to read block 3 onwards */ - if (edid_blk == start_ext_blk) - dp_aux_write_buf(dp, segment_addr, &segment, 1, 1); - } while (retries--); + } + } if (dp->test_data.test_requested == TEST_EDID_READ) { pr_debug("sending checksum %d\n", checksum); @@ -823,6 +1056,18 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp) dp->test_data = (const struct dpcd_test_request){ 0 }; } + /* + * Trigger the reading of DPCD if there was a change in the AUX + * configuration caused by a failure while reading the EDID. + * This is required to ensure the integrity and validity + * of the sink capabilities read that will subsequently be used + * to establish the mainlink. + */ + if (edid_parsing_done && ext_block_parsing_done + && phy_aux_update_requested) { + dp->dpcd_read_required = true; + } + return ret; } @@ -834,6 +1079,10 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) struct dpcd_cap *cap; struct edp_buf *rp; int rlen; + int i; + + cap = &ep->dpcd; + memset(cap, 0, sizeof(*cap)); rlen = dp_aux_read_buf(ep, 0, len, 0); if (rlen <= 0) { @@ -848,11 +1097,8 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) } rp = &ep->rxp; - cap = &ep->dpcd; bp = rp->data; - memset(cap, 0, sizeof(*cap)); - data = *bp++; /* byte 0 */ cap->major = (data >> 4) & 0x0f; cap->minor = data & 0x0f; @@ -909,6 +1155,11 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) data = *bp++; /* Byte 7: DOWN_STREAM_PORT_COUNT */ cap->downstream_port.dfp_count = data & 0x7; + if (cap->downstream_port.dfp_count > DP_MAX_DS_PORT_COUNT) { + pr_debug("DS port count %d greater that max (%d) supported\n", + cap->downstream_port.dfp_count, DP_MAX_DS_PORT_COUNT); + cap->downstream_port.dfp_count = DP_MAX_DS_PORT_COUNT; + } cap->downstream_port.msa_timing_par_ignored = data & BIT(6); cap->downstream_port.oui_support = data & BIT(7); pr_debug("dfp_count = %d, msa_timing_par_ignored = %d\n", @@ -916,17 +1167,23 @@ int mdss_dp_dpcd_cap_read(struct mdss_dp_drv_pdata *ep) cap->downstream_port.msa_timing_par_ignored); pr_debug("oui_support = %d\n", cap->downstream_port.oui_support); - data = *bp++; /* byte 8 */ - if (data & BIT(1)) { - cap->flags |= DPCD_PORT_0_EDID_PRESENTED; - pr_debug("edid presented\n"); - } - - data = *bp++; /* byte 9 */ - cap->rx_port0_buf_size = (data + 1) * 32; - pr_debug("lane_buf_size=%d\n", cap->rx_port0_buf_size); + for (i = 0; i < DP_MAX_DS_PORT_COUNT; i++) { + data = *bp++; /* byte 8 + i*2 */ + pr_debug("parsing capabilities for DS port %d\n", i); + if (data & BIT(1)) { + if (i == 0) + cap->flags |= DPCD_PORT_0_EDID_PRESENTED; + else + cap->flags |= DPCD_PORT_1_EDID_PRESENTED; + pr_debug("local edid present\n"); + } else { + pr_debug("local edid absent\n"); + } - bp += 2; /* skip 10, 11 port1 capability */ + data = *bp++; /* byte 9 + i*2 */ + cap->rx_port_buf_size[i] = (data + 1) * 32; + pr_debug("lane_buf_size=%d\n", cap->rx_port_buf_size[i]); + } data = *bp++; /* byte 12 */ cap->i2c_speed_ctrl = data; @@ -1258,6 +1515,8 @@ static void dp_sink_parse_sink_count(struct mdss_dp_drv_pdata *ep) int const param_len = 0x1; int const sink_count_addr = 0x200; + ep->prev_sink_count = ep->sink_count; + rlen = dp_aux_read_buf(ep, sink_count_addr, param_len, 0); if (rlen < param_len) { pr_err("failed to read sink count\n"); @@ -2363,8 +2622,8 @@ clear: void mdss_dp_aux_parse_sink_status_field(struct mdss_dp_drv_pdata *ep) { dp_sink_parse_sink_count(ep); - dp_sink_parse_test_request(ep); mdss_dp_aux_link_status_read(ep, 6); + dp_sink_parse_test_request(ep); } int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *ep) diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c index ea492f54054c..0d9cf7b72b4d 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.c +++ b/drivers/video/fbdev/msm/mdss_dp_util.c @@ -858,6 +858,48 @@ void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io, u8 link_rate, pr_debug("dp_tu=0x%x\n", dp_tu); } +void mdss_dp_aux_set_limits(struct dss_io_data *ctrl_io) +{ + u32 const max_aux_timeout_count = 0xFFFFF; + u32 const max_aux_limits = 0xFFFFFFFF; + + pr_debug("timeout=0x%x, limits=0x%x\n", + max_aux_timeout_count, max_aux_limits); + + writel_relaxed(max_aux_timeout_count, + ctrl_io->base + DP_AUX_TIMEOUT_COUNT); + writel_relaxed(max_aux_limits, ctrl_io->base + DP_AUX_LIMITS); +} + +void mdss_dp_phy_aux_update_config(struct mdss_dp_drv_pdata *dp, + enum dp_phy_aux_config_type config_type) +{ + u32 new_index; + struct dss_io_data *phy_io = &dp->phy_io; + struct mdss_dp_phy_cfg *cfg = mdss_dp_phy_aux_get_config(dp, + config_type); + + if (!cfg) { + pr_err("invalid config type %s", + mdss_dp_phy_aux_config_type_to_string(config_type)); + return; + } + + new_index = (cfg->current_index + 1) % cfg->cfg_cnt; + + pr_debug("Updating %s from 0x%08x to 0x%08x\n", + mdss_dp_phy_aux_config_type_to_string(config_type), + cfg->lut[cfg->current_index], cfg->lut[new_index]); + writel_relaxed(cfg->lut[new_index], phy_io->base + cfg->offset); + cfg->current_index = new_index; + + /* Make sure the new HW configuration takes effect */ + wmb(); + + /* Reset the AUX controller before any subsequent transactions */ + mdss_dp_aux_reset(&dp->ctrl_io); +} + void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, char *l_map) { u8 bits_per_lane = 2; @@ -870,26 +912,24 @@ void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io, char *l_map) ctrl_io->base + DP_LOGICAL2PHYSCIAL_LANE_MAPPING); } -void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io, u32 *aux_cfg, - u32 phy_reg_offset) +void mdss_dp_phy_aux_setup(struct mdss_dp_drv_pdata *dp) { - void __iomem *adjusted_phy_io_base = phy_io->base + phy_reg_offset; + int i; + void __iomem *adjusted_phy_io_base = dp->phy_io.base + + dp->phy_reg_offset; writel_relaxed(0x3d, adjusted_phy_io_base + DP_PHY_PD_CTL); - /* DP AUX CFG register programming */ - writel_relaxed(aux_cfg[0], adjusted_phy_io_base + DP_PHY_AUX_CFG0); - writel_relaxed(aux_cfg[1], adjusted_phy_io_base + DP_PHY_AUX_CFG1); - writel_relaxed(aux_cfg[2], adjusted_phy_io_base + DP_PHY_AUX_CFG2); - writel_relaxed(aux_cfg[3], adjusted_phy_io_base + DP_PHY_AUX_CFG3); - writel_relaxed(aux_cfg[4], adjusted_phy_io_base + DP_PHY_AUX_CFG4); - writel_relaxed(aux_cfg[5], adjusted_phy_io_base + DP_PHY_AUX_CFG5); - writel_relaxed(aux_cfg[6], adjusted_phy_io_base + DP_PHY_AUX_CFG6); - writel_relaxed(aux_cfg[7], adjusted_phy_io_base + DP_PHY_AUX_CFG7); - writel_relaxed(aux_cfg[8], adjusted_phy_io_base + DP_PHY_AUX_CFG8); - writel_relaxed(aux_cfg[9], adjusted_phy_io_base + DP_PHY_AUX_CFG9); - - writel_relaxed(0x1f, adjusted_phy_io_base + DP_PHY_AUX_INTERRUPT_MASK); + for (i = 0; i < PHY_AUX_CFG_MAX; i++) { + struct mdss_dp_phy_cfg *cfg = mdss_dp_phy_aux_get_config(dp, i); + + pr_debug("%s: offset=0x%08x, value=0x%08x\n", + mdss_dp_phy_aux_config_type_to_string(i), cfg->offset, + cfg->lut[cfg->current_index]); + writel_relaxed(cfg->lut[cfg->current_index], + dp->phy_io.base + cfg->offset); + }; + writel_relaxed(0x1e, adjusted_phy_io_base + DP_PHY_AUX_INTERRUPT_MASK); } int mdss_dp_irq_setup(struct mdss_dp_drv_pdata *dp_drv) diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h index 8f19e7cdf3cf..4c93e48e97dc 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.h +++ b/drivers/video/fbdev/msm/mdss_dp_util.h @@ -35,6 +35,8 @@ #define DP_AUX_CTRL (0x00000230) #define DP_AUX_DATA (0x00000234) #define DP_AUX_TRANS_CTRL (0x00000238) +#define DP_AUX_TIMEOUT_COUNT (0x0000023C) +#define DP_AUX_LIMITS (0x00000240) #define DP_AUX_STATUS (0x00000244) #define DP_DPCD_CP_IRQ (0x201) @@ -163,6 +165,7 @@ #define DP_PHY_AUX_CFG9 (0x00000040) #define DP_PHY_AUX_INTERRUPT_MASK (0x00000044) #define DP_PHY_AUX_INTERRUPT_CLEAR (0x00000048) +#define DP_PHY_AUX_INTERRUPT_STATUS (0x000000B8) #define DP_PHY_SPARE0 0x00A8 @@ -271,6 +274,19 @@ static const struct dp_vc_tu_mapping_table tu_table[] = { 0x21, 0x000c, false, 0x00, 0x00, 0x00, 0x27}, }; +static inline struct mdss_dp_phy_cfg *mdss_dp_phy_aux_get_config( + struct mdss_dp_drv_pdata *dp, enum dp_phy_aux_config_type cfg_type) +{ + return &dp->aux_cfg[cfg_type]; +} + +static inline u32 mdss_dp_phy_aux_get_config_cnt( + struct mdss_dp_drv_pdata *dp, enum dp_phy_aux_config_type cfg_type) +{ + return dp->aux_cfg[cfg_type].cfg_cnt; +} + +void mdss_dp_aux_set_limits(struct dss_io_data *ctrl_io); int dp_aux_read(void *ep, struct edp_cmd *cmds); int dp_aux_write(void *ep, struct edp_cmd *cmd); void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data); @@ -285,8 +301,9 @@ void mdss_dp_assert_phy_reset(struct dss_io_data *ctrl_io, bool assert); void mdss_dp_setup_tr_unit(struct dss_io_data *ctrl_io, u8 link_rate, u8 ln_cnt, u32 res, struct mdss_panel_info *pinfo); void mdss_dp_config_misc(struct mdss_dp_drv_pdata *dp, u32 bd, u32 cc); -void mdss_dp_phy_aux_setup(struct dss_io_data *phy_io, u32 *aux_cfg, - u32 phy_reg_offset); +void mdss_dp_phy_aux_setup(struct mdss_dp_drv_pdata *dp); +void mdss_dp_phy_aux_update_config(struct mdss_dp_drv_pdata *dp, + enum dp_phy_aux_config_type config_type); void mdss_dp_hpd_configure(struct dss_io_data *ctrl_io, bool enable); void mdss_dp_aux_ctrl(struct dss_io_data *ctrl_io, bool enable); void mdss_dp_mainlink_ctrl(struct dss_io_data *ctrl_io, bool enable); diff --git a/drivers/video/fbdev/msm/mdss_hdcp_1x.c b/drivers/video/fbdev/msm/mdss_hdcp_1x.c index 834726e84bda..2dc9c8f96c5b 100644 --- a/drivers/video/fbdev/msm/mdss_hdcp_1x.c +++ b/drivers/video/fbdev/msm/mdss_hdcp_1x.c @@ -1381,7 +1381,8 @@ int hdcp_1x_authenticate(void *input) flush_delayed_work(&hdcp->hdcp_auth_work); - if (!hdcp_1x_state(HDCP_STATE_INACTIVE)) { + if (!hdcp_1x_state(HDCP_STATE_INACTIVE) && + !hdcp_1x_state(HDCP_STATE_AUTH_FAIL)) { pr_err("invalid state\n"); return -EINVAL; } @@ -1443,7 +1444,6 @@ int hdcp_1x_reauthenticate(void *input) DSS_REG_W(io, reg_set->reset, reg & ~reg_set->reset_bit); - hdcp->hdcp_state = HDCP_STATE_INACTIVE; hdcp_1x_authenticate(hdcp); return ret; |