From 821b280d6a83e16825039c097829f663b2d7f577 Mon Sep 17 00:00:00 2001 From: Tatenda Chipeperekwa Date: Tue, 25 Oct 2016 15:48:05 -0700 Subject: [PATCH 1/7] msm: mdss: dp: process downstream port status change Add support to process the downstream port status change if it is detected during HPD IRQ. Change-Id: I967f13216859be4c147e03c7ea318958300b2886 Signed-off-by: Tatenda Chipeperekwa --- drivers/video/fbdev/msm/mdss_dp.c | 64 ++++++++++++++++++++++++--- drivers/video/fbdev/msm/mdss_dp.h | 14 ++++++ drivers/video/fbdev/msm/mdss_dp_aux.c | 29 +++++++++++- 3 files changed, 99 insertions(+), 8 deletions(-) diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index fa2e47e06503..dea326a2fc76 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -1311,6 +1311,12 @@ static inline bool mdss_dp_is_link_status_updated(struct mdss_dp_drv_pdata *dp) return dp->link_status.link_status_updated; } +static inline bool mdss_dp_is_downstream_port_status_changed( + struct mdss_dp_drv_pdata *dp) +{ + return dp->link_status.downstream_port_status_changed; +} + static inline bool mdss_dp_is_link_training_requested( struct mdss_dp_drv_pdata *dp) { @@ -2308,6 +2314,7 @@ static int mdss_dp_hpd_irq_notify_clients(struct mdss_dp_drv_pdata *dp) if (dp->hpd_irq_toggled) { mdss_dp_notify_clients(dp, false); + dp->hpd_irq_clients_notified = true; reinit_completion(&dp->irq_comp); ret = wait_for_completion_timeout(&dp->irq_comp, @@ -2344,19 +2351,24 @@ static inline void mdss_dp_link_retraining(struct mdss_dp_drv_pdata *dp) * This function will check for changes in the link status, e.g. clock * recovery done on all lanes, and trigger link training if there is a * failure/error on the link. + * + * The function will return 0 if the a link status update has been processed, + * otherwise it will return -EINVAL. */ -static void mdss_dp_process_link_status_update(struct mdss_dp_drv_pdata *dp) +static int mdss_dp_process_link_status_update(struct mdss_dp_drv_pdata *dp) { if (!mdss_dp_is_link_status_updated(dp) || (mdss_dp_aux_channel_eq_done(dp) && mdss_dp_aux_clock_recovery_done(dp))) - return; + return -EINVAL; pr_info("channel_eq_done = %d, clock_recovery_done = %d\n", mdss_dp_aux_channel_eq_done(dp), mdss_dp_aux_clock_recovery_done(dp)); mdss_dp_link_retraining(dp); + + return 0; } /** @@ -2366,11 +2378,14 @@ static void mdss_dp_process_link_status_update(struct mdss_dp_drv_pdata *dp) * This function will handle new link training requests that are initiated by * the sink. In particular, it will update the requested lane count and link * link rate, and then trigger the link retraining procedure. + * + * The function will return 0 if a link training request has been processed, + * otherwise it will return -EINVAL. */ -static void mdss_dp_process_link_training_request(struct mdss_dp_drv_pdata *dp) +static int mdss_dp_process_link_training_request(struct mdss_dp_drv_pdata *dp) { if (!mdss_dp_is_link_training_requested(dp)) - return; + return -EINVAL; mdss_dp_send_test_response(dp); @@ -2383,6 +2398,28 @@ static void mdss_dp_process_link_training_request(struct mdss_dp_drv_pdata *dp) dp->link_rate = dp->test_data.test_link_rate; mdss_dp_link_retraining(dp); + + return 0; +} + +/** + * mdss_dp_process_downstream_port_status_change() - process port status changes + * @dp: Display Port Driver data + * + * This function will handle downstream port updates that are initiated by + * the sink. If the downstream port status has changed, the EDID is read via + * AUX. + * + * The function will return 0 if a downstream port update has been + * processed, otherwise it will return -EINVAL. + */ +static int mdss_dp_process_downstream_port_status_change( + struct mdss_dp_drv_pdata *dp) +{ + if (!mdss_dp_is_downstream_port_status_changed(dp)) + return -EINVAL; + + return mdss_dp_edid_read(dp); } /** @@ -2395,16 +2432,27 @@ static void mdss_dp_process_link_training_request(struct mdss_dp_drv_pdata *dp) */ static void mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) { + int ret = 0; + pr_debug("enter: HPD IRQ High\n"); dp->hpd_irq_on = true; mdss_dp_aux_parse_sink_status_field(dp); - mdss_dp_process_link_training_request(dp); + ret = mdss_dp_process_link_training_request(dp); + if (!ret) + goto exit; - mdss_dp_process_link_status_update(dp); + ret = mdss_dp_process_link_status_update(dp); + if (!ret) + goto exit; + ret = mdss_dp_process_downstream_port_status_change(dp); + if (!ret) + goto exit; + +exit: mdss_dp_reset_test_data(dp); pr_debug("done\n"); @@ -2419,9 +2467,13 @@ static void mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) */ static void mdss_dp_process_hpd_irq_low(struct mdss_dp_drv_pdata *dp) { + if (!dp->hpd_irq_clients_notified) + return; + pr_debug("enter: HPD IRQ low\n"); dp->hpd_irq_on = false; + dp->hpd_irq_clients_notified = false; mdss_dp_update_cable_status(dp, false); mdss_dp_mainlink_push_idle(&dp->panel_data); diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index 4ba2d20d4261..0d4e8da90da5 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -228,6 +228,18 @@ struct dp_alt_mode { #define DP_LINK_RATE_MULTIPLIER 27000000 #define DP_MAX_PIXEL_CLK_KHZ 675000 +struct downstream_port_config { + /* Byte 02205h */ + bool dfp_present; + u32 dfp_type; + bool format_conversion; + bool detailed_cap_info_available; + /* Byte 02207h */ + u32 dfp_count; + bool msa_timing_par_ignored; + bool oui_support; +}; + struct dpcd_cap { char major; char minor; @@ -240,6 +252,7 @@ struct dpcd_cap { u32 flags; u32 rx_port0_buf_size; u32 training_read_interval;/* us */ + struct downstream_port_config downstream_port; }; struct dpcd_link_status { @@ -466,6 +479,7 @@ struct mdss_dp_drv_pdata { bool wait_for_audio_comp; bool hpd_irq_on; bool hpd_irq_toggled; + bool hpd_irq_clients_notified; /* event */ struct workqueue_struct *workq; diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 91066662e793..25f726a4c05f 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -846,11 +846,36 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep, if (--rlen <= 0) return; - bp += 3; /* skip 5, 6 and 7 */ - rlen -= 3; + data = *bp++; /* Byte 5: DOWN_STREAM_PORT_PRESENT */ + cap->downstream_port.dfp_present = data & BIT(0); + cap->downstream_port.dfp_type = data & 0x6; + cap->downstream_port.format_conversion = data & BIT(3); + cap->downstream_port.detailed_cap_info_available = data & BIT(4); + pr_debug("dfp_present = %d, dfp_type = %d\n", + cap->downstream_port.dfp_present, + cap->downstream_port.dfp_type); + pr_debug("format_conversion = %d, detailed_cap_info_available = %d\n", + cap->downstream_port.format_conversion, + cap->downstream_port.detailed_cap_info_available); + if (--rlen <= 0) + return; + + bp += 1; /* Skip Byte 6 */ + rlen -= 1; if (rlen <= 0) return; + data = *bp++; /* Byte 7: DOWN_STREAM_PORT_COUNT */ + cap->downstream_port.dfp_count = data & 0x7; + 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", + cap->downstream_port.dfp_count, + cap->downstream_port.msa_timing_par_ignored); + pr_debug("oui_support = %d\n", cap->downstream_port.oui_support); + if (--rlen <= 0) + return; + data = *bp++; /* byte 8 */ if (data & BIT(1)) { cap->flags |= DPCD_PORT_0_EDID_PRESENTED; From 01cb3026c647fecaf22449ed25548c590cd5c1c8 Mon Sep 17 00:00:00 2001 From: Ajay Singh Parmar Date: Thu, 20 Oct 2016 17:13:19 -0700 Subject: [PATCH 2/7] msm: mdss: hdcp2p2: fix deadlock during re-authentication Fix the dead lock happening during re-authentication where threads were waiting for each other during clearing the last session and starting a new one. Change-Id: Ife18adde8349acb92b22e228d0bbc18edbf2c90e Signed-off-by: Ajay Singh Parmar --- drivers/misc/hdcp.c | 89 ++++++++++++----- drivers/video/fbdev/msm/mdss_dp.c | 63 +++++++----- drivers/video/fbdev/msm/mdss_dp.h | 2 + drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c | 114 ++++++---------------- drivers/video/fbdev/msm/mdss_hdcp.h | 1 + include/linux/hdcp_qseecom.h | 3 + 6 files changed, 141 insertions(+), 131 deletions(-) diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c index 76add503b6b8..8b7c16e9175d 100644 --- a/drivers/misc/hdcp.c +++ b/drivers/misc/hdcp.c @@ -616,32 +616,53 @@ static int hdcp_lib_get_next_message(struct hdcp_lib_handle *handle, } } -static inline void hdcp_lib_wakeup_client(struct hdcp_lib_handle *handle, - struct hdmi_hdcp_wakeup_data *data) +static void hdcp_lib_wakeup_client(struct hdcp_lib_handle *handle, + struct hdmi_hdcp_wakeup_data *data) { - int rc = 0; + int rc = 0, i; - if (handle && handle->client_ops && handle->client_ops->wakeup && - data && (data->cmd != HDMI_HDCP_WKUP_CMD_INVALID)) { - data->abort_mask = REAUTH_REQ | LINK_INTEGRITY_FAILURE; + if (!handle || !handle->client_ops || !handle->client_ops->wakeup || + !data || (data->cmd == HDMI_HDCP_WKUP_CMD_INVALID)) + return; - if (data->cmd == HDMI_HDCP_WKUP_CMD_SEND_MESSAGE || - data->cmd == HDMI_HDCP_WKUP_CMD_RECV_MESSAGE || - data->cmd == HDMI_HDCP_WKUP_CMD_LINK_POLL) { - handle->last_msg = - hdcp_lib_get_next_message(handle, data); + data->abort_mask = REAUTH_REQ | LINK_INTEGRITY_FAILURE; - if (handle->last_msg > INVALID_MESSAGE_ID && - handle->last_msg < HDCP2P2_MAX_MESSAGES) - data->message_data = - &hdcp_msg_lookup[handle->last_msg]; + if (data->cmd == HDMI_HDCP_WKUP_CMD_SEND_MESSAGE || + data->cmd == HDMI_HDCP_WKUP_CMD_RECV_MESSAGE || + data->cmd == HDMI_HDCP_WKUP_CMD_LINK_POLL) { + handle->last_msg = hdcp_lib_get_next_message(handle, data); + + pr_debug("lib->client: %s (%s)\n", + hdmi_hdcp_cmd_to_str(data->cmd), + hdcp_lib_message_name(handle->last_msg)); + + if (handle->last_msg > INVALID_MESSAGE_ID && + handle->last_msg < HDCP2P2_MAX_MESSAGES) { + u32 msg_num, rx_status; + const struct hdcp_msg_part *msg; + + data->message_data = &hdcp_msg_lookup[handle->last_msg]; + + msg_num = data->message_data->num_messages; + msg = data->message_data->messages; + rx_status = data->message_data->rx_status; + + pr_debug("rxstatus 0x%x\n", rx_status); + pr_debug("%6s | %4s\n", "offset", "len"); + + for (i = 0; i < msg_num; i++) + pr_debug("%6x | %4d\n", + msg[i].offset, msg[i].length); } - - rc = handle->client_ops->wakeup(data); - if (rc) - pr_err("error sending %s to client\n", - hdmi_hdcp_cmd_to_str(data->cmd)); + } else { + pr_debug("lib->client: %s\n", + hdmi_hdcp_cmd_to_str(data->cmd)); } + + rc = handle->client_ops->wakeup(data); + if (rc) + pr_err("error sending %s to client\n", + hdmi_hdcp_cmd_to_str(data->cmd)); } static inline void hdcp_lib_send_message(struct hdcp_lib_handle *handle) @@ -1486,6 +1507,7 @@ static int hdcp_lib_check_valid_state(struct hdcp_lib_handle *handle) if (handle->wakeup_cmd == HDCP_LIB_WKUP_CMD_START) { if (!list_empty(&handle->worker.work_list)) { + pr_debug("error: queue not empty\n"); rc = -EBUSY; goto exit; } @@ -1543,9 +1565,8 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data) handle->wakeup_cmd = data->cmd; handle->timeout_left = data->timeout; - pr_debug("%s, timeout left: %dms, tethered %d\n", - hdcp_lib_cmd_to_str(handle->wakeup_cmd), - handle->timeout_left, handle->tethered); + pr_debug("client->lib: %s\n", + hdcp_lib_cmd_to_str(handle->wakeup_cmd)); rc = hdcp_lib_check_valid_state(handle); if (rc) @@ -1599,6 +1620,8 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data) break; case HDCP_LIB_WKUP_CMD_MSG_SEND_FAILED: case HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED: + case HDCP_LIB_WKUP_CMD_LINK_FAILED: + handle->hdcp_state |= HDCP_STATE_ERROR; HDCP_LIB_EXECUTE(clean); break; case HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS: @@ -1825,7 +1848,7 @@ static void hdcp_lib_clean(struct hdcp_lib_handle *handle) if (!handle) { pr_err("invalid input\n"); return; - }; + } hdcp_lib_txmtr_deinit(handle); if (!handle->legacy_app) @@ -1859,6 +1882,7 @@ static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle) struct hdcp_rcvd_msg_rsp *rsp_buf; uint32_t msglen; char *msg = NULL; + char msg_name[50]; uint32_t message_id_bytes = 0; if (!handle || !handle->qseecom_handle || @@ -1907,8 +1931,11 @@ static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle) mutex_unlock(&handle->msg_lock); - pr_debug("msg received: %s from sink\n", - hdcp_lib_message_name((int)msg[0])); + snprintf(msg_name, sizeof(msg_name), "%s: ", + hdcp_lib_message_name((int)msg[0])); + + print_hex_dump(KERN_DEBUG, msg_name, + DUMP_PREFIX_NONE, 16, 1, msg, msglen, false); /* send the message to QSEECOM */ req_buf = (struct hdcp_rcvd_msg_req *)(handle->qseecom_handle->sbuf); @@ -2026,6 +2053,16 @@ static void hdcp_lib_topology_work(struct kthread_work *work) return; } + if (atomic_read(&handle->hdcp_off)) { + pr_debug("invalid state: hdcp off\n"); + return; + } + + if (handle->hdcp_state & HDCP_STATE_ERROR) { + pr_debug("invalid state: hdcp error\n"); + return; + } + reinit_completion(&handle->topo_wait); timeout = wait_for_completion_timeout(&handle->topo_wait, HZ * 3); if (!timeout) { diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index dea326a2fc76..dbaf3247e8bb 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -1620,22 +1620,18 @@ end: return rc; } -static void mdss_dp_hdcp_cb(void *ptr, enum hdcp_states status) +static void mdss_dp_hdcp_cb_work(struct work_struct *work) { - struct mdss_dp_drv_pdata *dp = ptr; + struct mdss_dp_drv_pdata *dp; + struct delayed_work *dw = to_delayed_work(work); struct hdcp_ops *ops; int rc = 0; - if (!dp) { - pr_debug("invalid input\n"); - return; - } + dp = container_of(dw, struct mdss_dp_drv_pdata, hdcp_cb_work); ops = dp->hdcp.ops; - mutex_lock(&dp->train_mutex); - - switch (status) { + switch (dp->hdcp_status) { case HDCP_STATE_AUTHENTICATED: pr_debug("hdcp authenticated\n"); dp->hdcp.auth_state = true; @@ -1658,8 +1654,20 @@ static void mdss_dp_hdcp_cb(void *ptr, enum hdcp_states status) default: break; } +} - mutex_unlock(&dp->train_mutex); +static void mdss_dp_hdcp_cb(void *ptr, enum hdcp_states status) +{ + struct mdss_dp_drv_pdata *dp = ptr; + + if (!dp) { + pr_err("invalid input\n"); + return; + } + + dp->hdcp_status = status; + + queue_delayed_work(dp->workq, &dp->hdcp_cb_work, HZ/4); } static int mdss_dp_hdcp_init(struct mdss_panel_data *pdata) @@ -1888,6 +1896,13 @@ static void mdss_dp_update_hdcp_info(struct mdss_dp_drv_pdata *dp) dp->hdcp.ops = ops; } +static inline bool dp_is_hdcp_enabled(struct mdss_dp_drv_pdata *dp_drv) +{ + return dp_drv->hdcp.feature_enabled && + (dp_drv->hdcp.hdcp1_present || dp_drv->hdcp.hdcp2_present) && + dp_drv->hdcp.ops; +} + static int mdss_dp_event_handler(struct mdss_panel_data *pdata, int event, void *arg) { @@ -1919,8 +1934,10 @@ static int mdss_dp_event_handler(struct mdss_panel_data *pdata, rc = mdss_dp_off(pdata); break; case MDSS_EVENT_BLANK: - if (dp->hdcp.ops && dp->hdcp.ops->off) + if (dp_is_hdcp_enabled(dp) && dp->hdcp.ops->off) { + flush_delayed_work(&dp->hdcp_cb_work); dp->hdcp.ops->off(dp->hdcp.data); + } mdss_dp_mainlink_push_idle(pdata); break; @@ -2197,6 +2214,11 @@ irqreturn_t dp_isr(int irq, void *ptr) dp_aux_native_handler(dp, isr1); } + if (dp->hdcp.ops && dp->hdcp.ops->isr) { + if (dp->hdcp.ops->isr(dp->hdcp.data)) + pr_err("dp_hdcp_isr failed\n"); + } + return IRQ_HANDLED; } @@ -2211,6 +2233,7 @@ static int mdss_dp_event_setup(struct mdss_dp_drv_pdata *dp) } INIT_WORK(&dp->work, mdss_dp_event_work); + INIT_DELAYED_WORK(&dp->hdcp_cb_work, mdss_dp_hdcp_cb_work); return 0; } @@ -2434,8 +2457,6 @@ static void mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) { int ret = 0; - pr_debug("enter: HPD IRQ High\n"); - dp->hpd_irq_on = true; mdss_dp_aux_parse_sink_status_field(dp); @@ -2523,6 +2544,12 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, dp_drv->alt_mode.dp_status.hpd_irq; if (dp_drv->alt_mode.dp_status.hpd_irq) { + pr_debug("Attention: hpd_irq high\n"); + + if (dp_drv->power_on && dp_drv->hdcp.ops && + dp_drv->hdcp.ops->cp_irq) + dp_drv->hdcp.ops->cp_irq(dp_drv->hdcp.data); + mdss_dp_process_hpd_irq_high(dp_drv); break; } @@ -2552,9 +2579,6 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, else dp_send_events(dp_drv, EV_USBPD_DP_CONFIGURE); - if (dp_drv->alt_mode.dp_status.hpd_irq && dp_drv->power_on && - dp_drv->hdcp.ops && dp_drv->hdcp.ops->isr) - dp_drv->hdcp.ops->isr(dp_drv->hdcp.data); break; case DP_VDM_STATUS: dp_drv->alt_mode.dp_status.response = *vdos; @@ -2770,13 +2794,6 @@ void *mdss_dp_get_hdcp_data(struct device *dev) return dp_drv->hdcp.data; } -static inline bool dp_is_hdcp_enabled(struct mdss_dp_drv_pdata *dp_drv) -{ - return dp_drv->hdcp.feature_enabled && - (dp_drv->hdcp.hdcp1_present || dp_drv->hdcp.hdcp2_present) && - dp_drv->hdcp.ops; -} - static inline bool dp_is_stream_shareable(struct mdss_dp_drv_pdata *dp_drv) { bool ret = 0; diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index 0d4e8da90da5..cfacbfa1d413 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -484,6 +484,7 @@ struct mdss_dp_drv_pdata { /* event */ struct workqueue_struct *workq; struct work_struct work; + struct delayed_work hdcp_cb_work; u32 current_event; spinlock_t event_lock; spinlock_t lock; @@ -494,6 +495,7 @@ struct mdss_dp_drv_pdata { u32 vic; u32 new_vic; int fb_node; + int hdcp_status; struct dpcd_test_request test_data; struct dpcd_sink_count sink_count; diff --git a/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c index 3891806b09bb..a5146437978b 100644 --- a/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c +++ b/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c @@ -53,7 +53,6 @@ struct dp_hdcp2p2_ctrl { struct kthread_work send_msg; struct kthread_work recv_msg; struct kthread_work link; - struct kthread_work poll; char *msg_buf; uint32_t send_msg_len; /* length of all parameters in msg */ uint32_t timeout; @@ -67,26 +66,6 @@ struct dp_hdcp2p2_ctrl { bool polling; }; -static inline char *dp_hdcp_cmd_to_str(uint32_t cmd) -{ - switch (cmd) { - case HDMI_HDCP_WKUP_CMD_SEND_MESSAGE: - return "HDMI_HDCP_WKUP_CMD_SEND_MESSAGE"; - case HDMI_HDCP_WKUP_CMD_RECV_MESSAGE: - return "HDMI_HDCP_WKUP_CMD_RECV_MESSAGE"; - case HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS: - return "HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS"; - case HDMI_HDCP_WKUP_CMD_STATUS_FAILED: - return "DP_HDCP_WKUP_CMD_STATUS_FAIL"; - case HDMI_HDCP_WKUP_CMD_LINK_POLL: - return "HDMI_HDCP_WKUP_CMD_LINK_POLL"; - case HDMI_HDCP_WKUP_CMD_AUTHENTICATE: - return "HDMI_HDCP_WKUP_CMD_AUTHENTICATE"; - default: - return "???"; - } -} - static inline bool dp_hdcp2p2_is_valid_state(struct dp_hdcp2p2_ctrl *ctrl) { if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_AUTHENTICATE) @@ -193,7 +172,7 @@ static int dp_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data) queue_kthread_work(&ctrl->worker, &ctrl->status); break; case HDMI_HDCP_WKUP_CMD_LINK_POLL: - queue_kthread_work(&ctrl->worker, &ctrl->poll); + ctrl->polling = true; break; case HDMI_HDCP_WKUP_CMD_AUTHENTICATE: queue_kthread_work(&ctrl->worker, &ctrl->auth); @@ -203,10 +182,11 @@ static int dp_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data) } exit: mutex_unlock(&ctrl->wakeup_mutex); + return 0; } -static inline int dp_hdcp2p2_wakeup_lib(struct dp_hdcp2p2_ctrl *ctrl, +static inline void dp_hdcp2p2_wakeup_lib(struct dp_hdcp2p2_ctrl *ctrl, struct hdcp_lib_wakeup_data *data) { int rc = 0; @@ -218,8 +198,6 @@ static inline int dp_hdcp2p2_wakeup_lib(struct dp_hdcp2p2_ctrl *ctrl, pr_err("error sending %s to lib\n", hdcp_lib_cmd_to_str(data->cmd)); } - - return rc; } static void dp_hdcp2p2_reset(struct dp_hdcp2p2_ctrl *ctrl) @@ -339,8 +317,6 @@ static void dp_hdcp2p2_auth_failed(struct dp_hdcp2p2_ctrl *ctrl) return; } - atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL); - /* notify DP about HDCP failure */ ctrl->init_data.notify_status(ctrl->init_data.cb_data, HDCP_STATE_AUTH_FAIL); @@ -349,8 +325,7 @@ static void dp_hdcp2p2_auth_failed(struct dp_hdcp2p2_ctrl *ctrl) static int dp_hdcp2p2_aux_read_message(struct dp_hdcp2p2_ctrl *ctrl, u8 *buf, int size, int offset, u32 timeout) { - int rc, max_size = 16, read_size, len = size; - u8 *buf_start = buf; + int rc, max_size = 16, read_size; if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { pr_err("hdcp is off\n"); @@ -378,8 +353,6 @@ static int dp_hdcp2p2_aux_read_message(struct dp_hdcp2p2_ctrl *ctrl, size -= read_size; } while (size > 0); - print_hex_dump(KERN_DEBUG, "hdcp2p2: ", DUMP_PREFIX_NONE, - 16, 1, buf_start, len, false); return rc; } @@ -550,7 +523,6 @@ exit: static void dp_hdcp2p2_recv_msg_work(struct kthread_work *work) { - int rc = 0; struct hdcp_lib_wakeup_data cdata = { HDCP_LIB_WKUP_CMD_INVALID }; struct dp_hdcp2p2_ctrl *ctrl = container_of(work, struct dp_hdcp2p2_ctrl, recv_msg); @@ -559,44 +531,24 @@ static void dp_hdcp2p2_recv_msg_work(struct kthread_work *work) if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { pr_err("hdcp is off\n"); - goto exit; + return; } - if (ctrl->sink_rx_status & ctrl->abort_mask) { - pr_err("reauth or Link fail triggered by sink\n"); + if (ctrl->rx_status) { + if (!ctrl->cp_irq_done) { + pr_debug("waiting for CP_IRQ\n"); + ctrl->polling = true; + return; + } - ctrl->sink_rx_status = 0; - rc = -ENOLINK; - cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; - - goto exit; - } - - if (ctrl->rx_status && !ctrl->sink_rx_status) { - pr_debug("Recv msg for RxStatus, but no CP_IRQ yet\n"); - ctrl->polling = true; - goto exit; + if (ctrl->rx_status & ctrl->sink_rx_status) { + ctrl->cp_irq_done = false; + ctrl->sink_rx_status = 0; + ctrl->rx_status = 0; + } } dp_hdcp2p2_get_msg_from_sink(ctrl); - - return; -exit: - if (rc) - dp_hdcp2p2_wakeup_lib(ctrl, &cdata); -} - -static void dp_hdcp2p2_poll_work(struct kthread_work *work) -{ - struct dp_hdcp2p2_ctrl *ctrl = container_of(work, - struct dp_hdcp2p2_ctrl, poll); - - if (ctrl->cp_irq_done) { - ctrl->cp_irq_done = false; - dp_hdcp2p2_get_msg_from_sink(ctrl); - } else { - ctrl->polling = true; - } } static void dp_hdcp2p2_auth_status_work(struct kthread_work *work) @@ -645,44 +597,45 @@ static void dp_hdcp2p2_link_work(struct kthread_work *work) if (rc) { pr_err("failed to read rx status\n"); - cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; + cdata.cmd = HDCP_LIB_WKUP_CMD_LINK_FAILED; + atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL); goto exit; } if (ctrl->sink_rx_status & ctrl->abort_mask) { - pr_err("reauth or Link fail triggered by sink\n"); + if (ctrl->sink_rx_status & BIT(3)) + pr_err("reauth_req set by sink\n"); + + if (ctrl->sink_rx_status & BIT(4)) + pr_err("link failure reported by sink\n"); ctrl->sink_rx_status = 0; ctrl->rx_status = 0; rc = -ENOLINK; - cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; + + cdata.cmd = HDCP_LIB_WKUP_CMD_LINK_FAILED; + atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL); goto exit; } - /* if polling, get message from sink else let polling start */ if (ctrl->polling && (ctrl->sink_rx_status & ctrl->rx_status)) { ctrl->sink_rx_status = 0; ctrl->rx_status = 0; - rc = dp_hdcp2p2_get_msg_from_sink(ctrl); + dp_hdcp2p2_get_msg_from_sink(ctrl); ctrl->polling = false; } else { ctrl->cp_irq_done = true; } exit: - dp_hdcp2p2_wakeup_lib(ctrl, &cdata); - - if (rc) { - dp_hdcp2p2_auth_failed(ctrl); - return; - } + if (rc) + dp_hdcp2p2_wakeup_lib(ctrl, &cdata); } static void dp_hdcp2p2_auth_work(struct kthread_work *work) { - int rc = 0; struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID}; struct dp_hdcp2p2_ctrl *ctrl = container_of(work, struct dp_hdcp2p2_ctrl, auth); @@ -694,12 +647,10 @@ static void dp_hdcp2p2_auth_work(struct kthread_work *work) else cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; - rc = dp_hdcp2p2_wakeup_lib(ctrl, &cdata); - if (rc) - dp_hdcp2p2_auth_failed(ctrl); + dp_hdcp2p2_wakeup_lib(ctrl, &cdata); } -static int dp_hdcp2p2_isr(void *input) +static int dp_hdcp2p2_cp_irq(void *input) { struct dp_hdcp2p2_ctrl *ctrl = input; @@ -748,7 +699,7 @@ void *dp_hdcp2p2_init(struct hdcp_init_data *init_data) .authenticate = dp_hdcp2p2_authenticate, .feature_supported = dp_hdcp2p2_feature_supported, .off = dp_hdcp2p2_off, - .isr = dp_hdcp2p2_isr + .cp_irq = dp_hdcp2p2_cp_irq, }; static struct hdcp_client_ops client_ops = { @@ -806,7 +757,6 @@ void *dp_hdcp2p2_init(struct hdcp_init_data *init_data) init_kthread_work(&ctrl->recv_msg, dp_hdcp2p2_recv_msg_work); init_kthread_work(&ctrl->status, dp_hdcp2p2_auth_status_work); init_kthread_work(&ctrl->link, dp_hdcp2p2_link_work); - init_kthread_work(&ctrl->poll, dp_hdcp2p2_poll_work); ctrl->thread = kthread_run(kthread_worker_fn, &ctrl->worker, "dp_hdcp2p2"); diff --git a/drivers/video/fbdev/msm/mdss_hdcp.h b/drivers/video/fbdev/msm/mdss_hdcp.h index d373d22384e8..6e347a867366 100644 --- a/drivers/video/fbdev/msm/mdss_hdcp.h +++ b/drivers/video/fbdev/msm/mdss_hdcp.h @@ -55,6 +55,7 @@ struct hdcp_init_data { struct hdcp_ops { int (*isr)(void *ptr); + int (*cp_irq)(void *ptr); int (*reauthenticate)(void *input); int (*authenticate)(void *hdcp_ctrl); bool (*feature_supported)(void *input); diff --git a/include/linux/hdcp_qseecom.h b/include/linux/hdcp_qseecom.h index f66264bc935a..6ad79378737e 100644 --- a/include/linux/hdcp_qseecom.h +++ b/include/linux/hdcp_qseecom.h @@ -26,6 +26,7 @@ enum hdcp_lib_wakeup_cmd { HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED, HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT, HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE, + HDCP_LIB_WKUP_CMD_LINK_FAILED, }; enum hdmi_hdcp_wakeup_cmd { @@ -106,6 +107,8 @@ static inline char *hdcp_lib_cmd_to_str(uint32_t cmd) return "HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT"; case HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE: return "HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE"; + case HDCP_LIB_WKUP_CMD_LINK_FAILED: + return "HDCP_LIB_WKUP_CMD_LINK_FAILED"; default: return "???"; } From dc10995a1b0c5ee71ae0221d72018eeac72bf30a Mon Sep 17 00:00:00 2001 From: Ajay Singh Parmar Date: Sun, 23 Oct 2016 23:49:40 -0700 Subject: [PATCH 3/7] msm: mdss: hdcp2p2: optimize aux message read and write hdcp 2.2 message has multiple parts with different addresses. Currently, each address is read/written as a separate aux transactions. As, for a particular message, all parts are read/written contiguously, make single aux transaction instead of multiple transactions to avoid unnecessary aux delays. Change-Id: I284bc56aa94eef127c2bdd0f80aab7b0cf080342 Signed-off-by: Ajay Singh Parmar --- drivers/misc/hdcp.c | 53 +++++++++++++---------- drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c | 44 ++++++------------- include/linux/hdcp_qseecom.h | 1 + 3 files changed, 45 insertions(+), 53 deletions(-) diff --git a/drivers/misc/hdcp.c b/drivers/misc/hdcp.c index 8b7c16e9175d..69ec7127102c 100644 --- a/drivers/misc/hdcp.c +++ b/drivers/misc/hdcp.c @@ -160,43 +160,46 @@ static const struct hdcp_msg_data hdcp_msg_lookup[HDCP2P2_MAX_MESSAGES] = { [AKE_INIT_MESSAGE_ID] = { 2, - { {0x69000, 8}, {0x69008, 3} }, + { {"rtx", 0x69000, 8}, {"TxCaps", 0x69008, 3} }, 0 }, [AKE_SEND_CERT_MESSAGE_ID] = { 3, - { {0x6900B, 522}, {0x69215, 8}, {0x6921D, 3} }, + { {"cert-rx", 0x6900B, 522}, {"rrx", 0x69215, 8}, + {"RxCaps", 0x6921D, 3} }, 0 }, [AKE_NO_STORED_KM_MESSAGE_ID] = { 1, - { {0x69220, 128} }, + { {"Ekpub_km", 0x69220, 128} }, 0 }, [AKE_STORED_KM_MESSAGE_ID] = { 2, - { {0x692A0, 16}, {0x692B0, 16} }, + { {"Ekh_km", 0x692A0, 16}, {"m", 0x692B0, 16} }, 0 }, [AKE_SEND_H_PRIME_MESSAGE_ID] = { 1, - { {0x692C0, 32} }, + { {"H'", 0x692C0, 32} }, (1 << 1) }, [AKE_SEND_PAIRING_INFO_MESSAGE_ID] = { 1, - { {0x692E0, 16} }, + { {"Ekh_km", 0x692E0, 16} }, (1 << 2) }, [LC_INIT_MESSAGE_ID] = { 1, - { {0x692F0, 8} }, + { {"rn", 0x692F0, 8} }, 0 }, [LC_SEND_L_PRIME_MESSAGE_ID] = { 1, - { {0x692F8, 32} }, + { {"L'", 0x692F8, 32} }, 0 }, [SKE_SEND_EKS_MESSAGE_ID] = { 2, - { {0x69318, 16}, {0x69328, 8} }, + { {"Edkey_ks", 0x69318, 16}, {"riv", 0x69328, 8} }, 0 }, [REPEATER_AUTH_SEND_RECEIVERID_LIST_MESSAGE_ID] = { 4, - { {0x69330, 2}, {0x69332, 3}, {0x69335, 16}, {0x69345, 155} }, + { {"RxInfo", 0x69330, 2}, {"seq_num_V", 0x69332, 3}, + {"V'", 0x69335, 16}, {"ridlist", 0x69345, 155} }, (1 << 0) }, [REPEATER_AUTH_SEND_ACK_MESSAGE_ID] = { 1, - { {0x693E0, 16} }, + { {"V", 0x693E0, 16} }, 0 }, [REPEATER_AUTH_STREAM_MANAGE_MESSAGE_ID] = { 3, - { {0x693F0, 3}, {0x693F3, 2}, {0x693F5, 126} }, + { {"seq_num_M", 0x693F0, 3}, {"k", 0x693F3, 2}, + {"streamID_Type", 0x693F5, 126} }, 0 }, [REPEATER_AUTH_STREAM_READY_MESSAGE_ID] = { 1, - { {0x69473, 32} }, + { {"M'", 0x69473, 32} }, 0 } }; @@ -648,11 +651,12 @@ static void hdcp_lib_wakeup_client(struct hdcp_lib_handle *handle, rx_status = data->message_data->rx_status; pr_debug("rxstatus 0x%x\n", rx_status); - pr_debug("%6s | %4s\n", "offset", "len"); + pr_debug("%10s | %6s | %4s\n", "name", "offset", "len"); for (i = 0; i < msg_num; i++) - pr_debug("%6x | %4d\n", - msg[i].offset, msg[i].length); + pr_debug("%10s | %6x | %4d\n", + msg[i].name, msg[i].offset, + msg[i].length); } } else { pr_debug("lib->client: %s\n", @@ -667,6 +671,7 @@ static void hdcp_lib_wakeup_client(struct hdcp_lib_handle *handle, static inline void hdcp_lib_send_message(struct hdcp_lib_handle *handle) { + char msg_name[50]; struct hdmi_hdcp_wakeup_data cdata = { HDMI_HDCP_WKUP_CMD_SEND_MESSAGE }; @@ -676,6 +681,13 @@ static inline void hdcp_lib_send_message(struct hdcp_lib_handle *handle) cdata.send_msg_len = handle->msglen; cdata.timeout = handle->hdcp_timeout; + snprintf(msg_name, sizeof(msg_name), "%s: ", + hdcp_lib_message_name((int)cdata.send_msg_buf[0])); + + print_hex_dump(KERN_DEBUG, msg_name, + DUMP_PREFIX_NONE, 16, 1, cdata.send_msg_buf, + cdata.send_msg_len, false); + hdcp_lib_wakeup_client(handle, &cdata); } @@ -2016,13 +2028,8 @@ static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle) handle->hdcp_timeout = rsp_buf->timeout; handle->msglen = rsp_buf->msglen; - if (!atomic_read(&handle->hdcp_off)) { - cdata.cmd = HDMI_HDCP_WKUP_CMD_SEND_MESSAGE; - cdata.send_msg_buf = handle->listener_buf; - cdata.send_msg_len = handle->msglen; - cdata.timeout = handle->hdcp_timeout; - } - + if (!atomic_read(&handle->hdcp_off)) + hdcp_lib_send_message(handle); exit: kzfree(msg); diff --git a/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c index a5146437978b..79cd94cfbe88 100644 --- a/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c +++ b/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c @@ -425,12 +425,9 @@ end: static void dp_hdcp2p2_send_msg_work(struct kthread_work *work) { int rc = 0; - int i; - int sent_bytes = 0; struct dp_hdcp2p2_ctrl *ctrl = container_of(work, struct dp_hdcp2p2_ctrl, send_msg); struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID}; - char *buf = NULL; if (!ctrl) { pr_err("invalid input\n"); @@ -447,20 +444,13 @@ static void dp_hdcp2p2_send_msg_work(struct kthread_work *work) mutex_lock(&ctrl->msg_lock); - /* Loop through number of parameters in the messages. */ - for (i = 0; i < ctrl->num_messages; i++) { - buf = ctrl->msg_buf + sent_bytes; - - /* Forward the message to the sink */ - rc = dp_hdcp2p2_aux_write_message(ctrl, buf, - (size_t)ctrl->msg_part[i].length, - ctrl->msg_part[i].offset, ctrl->timeout); - if (rc) { - pr_err("Error sending msg to sink %d\n", rc); - mutex_unlock(&ctrl->msg_lock); - goto exit; - } - sent_bytes += ctrl->msg_part[i].length; + rc = dp_hdcp2p2_aux_write_message(ctrl, ctrl->msg_buf, + ctrl->send_msg_len, ctrl->msg_part->offset, + ctrl->timeout); + if (rc) { + pr_err("Error sending msg to sink %d\n", rc); + mutex_unlock(&ctrl->msg_lock); + goto exit; } cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_SEND_SUCCESS; @@ -478,10 +468,9 @@ exit: static int dp_hdcp2p2_get_msg_from_sink(struct dp_hdcp2p2_ctrl *ctrl) { - int i, rc = 0; + int rc = 0; char *recvd_msg_buf = NULL; struct hdcp_lib_wakeup_data cdata = { HDCP_LIB_WKUP_CMD_INVALID }; - int bytes_read = 0; cdata.context = ctrl->lib_ctx; @@ -491,17 +480,12 @@ static int dp_hdcp2p2_get_msg_from_sink(struct dp_hdcp2p2_ctrl *ctrl) goto exit; } - for (i = 0; i < ctrl->num_messages; i++) { - rc = dp_hdcp2p2_aux_read_message( - ctrl, recvd_msg_buf + bytes_read, - ctrl->msg_part[i].length, - ctrl->msg_part[i].offset, - ctrl->timeout); - if (rc) { - pr_err("error reading message %d\n", rc); - goto exit; - } - bytes_read += ctrl->msg_part[i].length; + rc = dp_hdcp2p2_aux_read_message(ctrl, recvd_msg_buf, + ctrl->send_msg_len, ctrl->msg_part->offset, + ctrl->timeout); + if (rc) { + pr_err("error reading message %d\n", rc); + goto exit; } cdata.recvd_msg_buf = recvd_msg_buf; diff --git a/include/linux/hdcp_qseecom.h b/include/linux/hdcp_qseecom.h index 6ad79378737e..68f2dd993170 100644 --- a/include/linux/hdcp_qseecom.h +++ b/include/linux/hdcp_qseecom.h @@ -48,6 +48,7 @@ struct hdcp_lib_wakeup_data { }; struct hdcp_msg_part { + char *name; uint32_t offset; uint32_t length; }; From dec2c9c98d4315290da22136f63e7ac561aefa74 Mon Sep 17 00:00:00 2001 From: Ajay Singh Parmar Date: Wed, 26 Oct 2016 15:59:21 -0700 Subject: [PATCH 4/7] msm: mdss: dp: add support for link retraining at lower link rate Add support to restart link training at a lower link rate if the training has failed at the current rate in Clock Recovery phase or has reached the maximum number of retries in the Channel Equalization phase. Change-Id: Ic7ac0b7ac19d19577d4d1223c8638f17ad9d78af Signed-off-by: Ajay Singh Parmar --- drivers/video/fbdev/msm/mdss_dp.c | 109 +++++++++++++++------- drivers/video/fbdev/msm/mdss_dp_aux.c | 89 ++++++++---------- drivers/video/fbdev/msm/msm_ext_display.c | 8 +- 3 files changed, 120 insertions(+), 86 deletions(-) diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index dbaf3247e8bb..391d4c24eb7f 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -57,6 +57,10 @@ static u32 supported_modes[] = { HDMI_VFRMT_4096x2160p60_256_135, HDMI_EVFRMT_4096x2160p24_16_9 }; +static int mdss_dp_off_irq(struct mdss_dp_drv_pdata *dp_drv); +static void mdss_dp_mainlink_push_idle(struct mdss_panel_data *pdata); +static inline void mdss_dp_link_retraining(struct mdss_dp_drv_pdata *dp); + static void mdss_dp_put_dt_clk_data(struct device *dev, struct dss_module_power *module_power) { @@ -1156,19 +1160,27 @@ static void mdss_dp_configure_source_params(struct mdss_dp_drv_pdata *dp, * * Initiates training of the DP main link and checks the state of the main * link after the training is complete. + * + * Return: error code. -EINVAL if any invalid data or -EAGAIN if retraining + * is required. */ -static void mdss_dp_train_main_link(struct mdss_dp_drv_pdata *dp) +static int mdss_dp_train_main_link(struct mdss_dp_drv_pdata *dp) { + int ret = 0; int ready = 0; pr_debug("enter\n"); + ret = mdss_dp_link_train(dp); + if (ret) + goto end; - mdss_dp_link_train(dp); mdss_dp_wait4train(dp); ready = mdss_dp_mainlink_ready(dp, BIT(0)); pr_debug("main link %s\n", ready ? "READY" : "NOT READY"); +end: + return ret; } static int mdss_dp_on_irq(struct mdss_dp_drv_pdata *dp_drv) @@ -1178,33 +1190,43 @@ static int mdss_dp_on_irq(struct mdss_dp_drv_pdata *dp_drv) struct lane_mapping ln_map; /* wait until link training is completed */ - mutex_lock(&dp_drv->train_mutex); - pr_debug("enter\n"); - orientation = usbpd_get_plug_orientation(dp_drv->pd); - pr_debug("plug orientation = %d\n", orientation); + do { + if (ret == -EAGAIN) { + mdss_dp_mainlink_push_idle(&dp_drv->panel_data); + mdss_dp_off_irq(dp_drv); + } - ret = mdss_dp_get_lane_mapping(dp_drv, orientation, &ln_map); - if (ret) - goto exit; + mutex_lock(&dp_drv->train_mutex); - mdss_dp_phy_share_lane_config(&dp_drv->phy_io, - orientation, dp_drv->dpcd.max_lane_count); + orientation = usbpd_get_plug_orientation(dp_drv->pd); + pr_debug("plug orientation = %d\n", orientation); - ret = mdss_dp_enable_mainlink_clocks(dp_drv); - if (ret) - goto exit; + ret = mdss_dp_get_lane_mapping(dp_drv, orientation, &ln_map); + if (ret) + goto exit; - mdss_dp_mainlink_reset(&dp_drv->ctrl_io); + mdss_dp_phy_share_lane_config(&dp_drv->phy_io, + orientation, dp_drv->dpcd.max_lane_count); - reinit_completion(&dp_drv->idle_comp); + ret = mdss_dp_enable_mainlink_clocks(dp_drv); + if (ret) + goto exit; - mdss_dp_configure_source_params(dp_drv, &ln_map); + mdss_dp_mainlink_reset(&dp_drv->ctrl_io); - mdss_dp_train_main_link(dp_drv); + reinit_completion(&dp_drv->idle_comp); + + mdss_dp_configure_source_params(dp_drv, &ln_map); + + dp_drv->power_on = true; + + ret = mdss_dp_train_main_link(dp_drv); + + mutex_unlock(&dp_drv->train_mutex); + } while (ret == -EAGAIN); - dp_drv->power_on = true; pr_debug("end\n"); exit: @@ -1273,7 +1295,14 @@ int mdss_dp_on_hpd(struct mdss_dp_drv_pdata *dp_drv) mdss_dp_configure_source_params(dp_drv, &ln_map); link_training: - mdss_dp_train_main_link(dp_drv); + dp_drv->power_on = true; + + if (-EAGAIN == mdss_dp_train_main_link(dp_drv)) { + mutex_unlock(&dp_drv->train_mutex); + + mdss_dp_link_retraining(dp_drv); + return 0; + } dp_drv->cont_splash = 0; @@ -1419,18 +1448,23 @@ int mdss_dp_off(struct mdss_panel_data *pdata) return mdss_dp_off_hpd(dp); } -static void mdss_dp_send_cable_notification( +static int mdss_dp_send_cable_notification( struct mdss_dp_drv_pdata *dp, int val) { + int ret = 0; if (!dp) { DEV_ERR("%s: invalid input\n", __func__); - return; + ret = -EINVAL; + goto end; } if (dp && dp->ext_audio_data.intf_ops.hpd) - dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev, + ret = dp->ext_audio_data.intf_ops.hpd(dp->ext_pdev, dp->ext_audio_data.type, val); + +end: + return ret; } static void mdss_dp_audio_codec_wait(struct mdss_dp_drv_pdata *dp) @@ -1450,18 +1484,22 @@ static void mdss_dp_audio_codec_wait(struct mdss_dp_drv_pdata *dp) dp->wait_for_audio_comp = false; } -static void mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, bool enable) +static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, bool enable) { + int notified = 0; + if (enable) { - mdss_dp_send_cable_notification(dp, enable); + notified = mdss_dp_send_cable_notification(dp, enable); } else { mdss_dp_set_audio_switch_node(dp, enable); mdss_dp_audio_codec_wait(dp); - mdss_dp_send_cable_notification(dp, enable); + notified = mdss_dp_send_cable_notification(dp, enable); } pr_debug("notify state %s done\n", enable ? "ENABLE" : "DISABLE"); + + return notified; } @@ -2336,15 +2374,20 @@ static int mdss_dp_hpd_irq_notify_clients(struct mdss_dp_drv_pdata *dp) int ret = 0; if (dp->hpd_irq_toggled) { - mdss_dp_notify_clients(dp, false); dp->hpd_irq_clients_notified = true; - 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"); - return -EINVAL; + ret = mdss_dp_notify_clients(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; + } } } diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 25f726a4c05f..9014e3a02d21 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -801,6 +801,8 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep, cap = &ep->dpcd; bp = rp->data; + memset(cap, 0, sizeof(*cap)); + data = *bp++; /* byte 0 */ cap->major = (data >> 4) & 0x0f; cap->minor = data & 0x0f; @@ -819,8 +821,13 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep, if (data & BIT(7)) cap->enhanced_frame++; - if (data & 0x40) + if (data & 0x40) { cap->flags |= DPCD_TPS3; + pr_debug("pattern 3 supported\n"); + } else { + pr_debug("pattern 3 not supported\n"); + } + data &= 0x0f; cap->max_lane_count = data; if (--rlen <= 0) @@ -886,7 +893,7 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep, data = *bp++; /* byte 9 */ cap->rx_port0_buf_size = (data + 1) * 32; - pr_debug("lane_buf_size=%d", cap->rx_port0_buf_size); + pr_debug("lane_buf_size=%d\n", cap->rx_port0_buf_size); if (--rlen <= 0) return; @@ -1456,6 +1463,7 @@ static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep) int tries, old_v_level; int ret = 0; int usleep_time; + int const maximum_retries = 5; pr_debug("Entered++"); @@ -1483,7 +1491,7 @@ static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep) if (old_v_level == ep->v_level) { tries++; - if (tries >= 5) { + if (tries >= maximum_retries) { ret = -1; break; /* quit */ } @@ -1505,6 +1513,7 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep) int ret = 0; int usleep_time; char pattern; + int const maximum_retries = 5; pr_debug("Entered++"); @@ -1530,7 +1539,7 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep) } tries++; - if (tries > 4) { + if (tries > maximum_retries) { ret = -1; break; } @@ -1543,47 +1552,27 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep) static int dp_link_rate_down_shift(struct mdss_dp_drv_pdata *ep) { - u32 prate, lrate; - int rate, lane, max_lane; - int changed = 0; + int ret = 0; - rate = ep->link_rate; - lane = ep->lane_cnt; - max_lane = ep->dpcd.max_lane_count; + if (!ep) + return -EINVAL; - prate = ep->pixel_rate; - prate /= 1000; /* avoid using 64 biits */ - prate *= ep->bpp; - prate /= 8; /* byte */ + switch (ep->link_rate) { + case DP_LINK_RATE_540: + ep->link_rate = DP_LINK_RATE_270; + break; + case DP_LINK_RATE_270: + ep->link_rate = DP_LINK_RATE_162; + break; + case DP_LINK_RATE_162: + default: + ret = -EINVAL; + break; + }; - if (rate > DP_LINK_RATE_162 && rate <= DP_LINK_RATE_MAX) { - rate -= 4; /* reduce rate */ - changed++; - } + pr_debug("new rate=%d\n", ep->link_rate); - if (changed) { - if (lane >= 1 && lane < max_lane) - lane <<= 1; /* increase lane */ - - lrate = 270000000; /* 270M */ - lrate /= 1000; /* avoid using 64 bits */ - lrate *= rate; - lrate /= 10; /* byte, 10 bits --> 8 bits */ - lrate *= lane; - - pr_debug("new lrate=%u prate=%u rate=%d lane=%d p=%d b=%d\n", - lrate, prate, rate, lane, ep->pixel_rate, ep->bpp); - - if (lrate > prate) { - ep->link_rate = rate; - ep->lane_cnt = lane; - pr_debug("new rate=%d %d\n", rate, lane); - return 0; - } - } - - /* add calculation later */ - return -EINVAL; + return ret; } int mdss_dp_aux_set_sink_power_state(struct mdss_dp_drv_pdata *ep, char state) @@ -1620,7 +1609,6 @@ int mdss_dp_link_train(struct mdss_dp_drv_pdata *dp) mdss_dp_aux_set_sink_power_state(dp, SINK_POWER_ON); -train_start: dp->v_level = 0; /* start from default level */ dp->p_level = 0; mdss_dp_config_ctrl(dp); @@ -1630,11 +1618,12 @@ train_start: ret = dp_start_link_train_1(dp); if (ret < 0) { - if (dp_link_rate_down_shift(dp) == 0) { - goto train_start; + if (!dp_link_rate_down_shift(dp)) { + pr_debug("retry with lower rate\n"); + return -EAGAIN; } else { pr_err("Training 1 failed\n"); - ret = -1; + ret = -EINVAL; goto clear; } } @@ -1643,21 +1632,21 @@ train_start: ret = dp_start_link_train_2(dp); if (ret < 0) { - if (dp_link_rate_down_shift(dp) == 0) { - goto train_start; + if (!dp_link_rate_down_shift(dp)) { + pr_debug("retry with lower rate\n"); + return -EAGAIN; } else { pr_err("Training 2 failed\n"); - ret = -1; + ret = -EINVAL; goto clear; } } pr_debug("Training 2 completed successfully\n"); - clear: dp_clear_training_pattern(dp); - if (ret != -1) { + if (ret != -EINVAL) { mdss_dp_setup_tr_unit(&dp->ctrl_io, dp->link_rate, dp->lane_cnt, dp->vic); mdss_dp_state_ctrl(&dp->ctrl_io, ST_SEND_VIDEO); diff --git a/drivers/video/fbdev/msm/msm_ext_display.c b/drivers/video/fbdev/msm/msm_ext_display.c index f6c6548bdaa5..36a5530cb761 100644 --- a/drivers/video/fbdev/msm/msm_ext_display.c +++ b/drivers/video/fbdev/msm/msm_ext_display.c @@ -313,14 +313,14 @@ static void msm_ext_disp_remove_intf_data(struct msm_ext_disp *ext_disp, } } -static void msm_ext_disp_send_cable_notification(struct msm_ext_disp *ext_disp, +static int msm_ext_disp_send_cable_notification(struct msm_ext_disp *ext_disp, enum msm_ext_disp_cable_state new_state) { int state = EXT_DISPLAY_CABLE_STATE_MAX; if (!ext_disp) { pr_err("Invalid params\n"); - return; + return -EINVAL; } state = ext_disp->hdmi_sdev.state; @@ -330,6 +330,8 @@ static void msm_ext_disp_send_cable_notification(struct msm_ext_disp *ext_disp, ext_disp->hdmi_sdev.state == state ? "is same" : "switched to", ext_disp->hdmi_sdev.state); + + return ext_disp->hdmi_sdev.state == state ? 0 : 1; } static int msm_ext_disp_hpd(struct platform_device *pdev, @@ -398,7 +400,7 @@ static int msm_ext_disp_hpd(struct platform_device *pdev, ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX; } - msm_ext_disp_send_cable_notification(ext_disp, state); + ret = msm_ext_disp_send_cable_notification(ext_disp, state); pr_debug("Hpd (%d) for display (%s)\n", state, msm_ext_disp_name(type)); From 7963ee24855344e5ad6b1b3b2830b000c2a8fbb6 Mon Sep 17 00:00:00 2001 From: Ajay Singh Parmar Date: Thu, 27 Oct 2016 11:39:22 -0700 Subject: [PATCH 5/7] msm: mdss: hdcp1x: fix hdcp 1x registration and reset Keep the correct hdcp1x data after hdcp1x registration to avoid issues with invalid data access. Also, increase the re-authenticate timeout to give sink and source reasonable time to reset the hdcp engines. Fix the reset bit before hdcp off and re-authentication. Change-Id: Ie1d1540a87e96a33d3e5521cf933399a60d467ab Signed-off-by: Ajay Singh Parmar --- drivers/video/fbdev/msm/mdss_dp.c | 8 ++++---- drivers/video/fbdev/msm/mdss_hdcp_1x.c | 24 ++++++++++++++++-------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index 391d4c24eb7f..22d3d45ccea3 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -1743,19 +1743,19 @@ static int mdss_dp_hdcp_init(struct mdss_panel_data *pdata) hdcp_init_data.sec_access = true; hdcp_init_data.client_id = HDCP_CLIENT_DP; - dp_drv->hdcp.data = hdcp_1x_init(&hdcp_init_data); - if (IS_ERR_OR_NULL(dp_drv->hdcp.data)) { + dp_drv->hdcp.hdcp1 = hdcp_1x_init(&hdcp_init_data); + if (IS_ERR_OR_NULL(dp_drv->hdcp.hdcp1)) { pr_err("Error hdcp init\n"); rc = -EINVAL; goto error; } - dp_drv->panel_data.panel_info.hdcp_1x_data = dp_drv->hdcp.data; + dp_drv->panel_data.panel_info.hdcp_1x_data = dp_drv->hdcp.hdcp1; pr_debug("HDCP 1.3 initialized\n"); dp_drv->hdcp.hdcp2 = dp_hdcp2p2_init(&hdcp_init_data); - if (!IS_ERR_OR_NULL(dp_drv->hdcp.data)) + if (!IS_ERR_OR_NULL(dp_drv->hdcp.hdcp2)) pr_debug("HDCP 2.2 initialized\n"); dp_drv->hdcp.feature_enabled = true; diff --git a/drivers/video/fbdev/msm/mdss_hdcp_1x.c b/drivers/video/fbdev/msm/mdss_hdcp_1x.c index 1e502cf750a6..1e0ad986afb1 100644 --- a/drivers/video/fbdev/msm/mdss_hdcp_1x.c +++ b/drivers/video/fbdev/msm/mdss_hdcp_1x.c @@ -153,6 +153,7 @@ struct hdcp_reg_set { u32 sec_data12; u32 reset; + u32 reset_bit; }; #define HDCP_REG_SET_CLIENT_HDMI \ @@ -175,7 +176,7 @@ struct hdcp_reg_set { HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA10, \ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA11, \ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA12, \ - HDMI_HDCP_RESET} + HDMI_HDCP_RESET, BIT(0)} #define HDCP_REG_SET_CLIENT_DP \ {DP_HDCP_STATUS, 16, 14, 13, DP_HDCP_CTRL, \ @@ -193,7 +194,8 @@ struct hdcp_reg_set { HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA9, \ HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA10, \ HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA11, \ - HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA12, 0} + HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA12, \ + DP_SW_RESET, BIT(1)} #define HDCP_HDMI_SINK_ADDR_MAP \ {{"bcaps", 0x40, 1}, {"bksv", 0x00, 5}, {"r0'", 0x08, 2}, \ @@ -1431,7 +1433,7 @@ int hdcp_1x_reauthenticate(void *input) struct hdcp_reg_set *reg_set; struct hdcp_int_set *isr; u32 hdmi_hw_version; - u32 ret = 0; + u32 ret = 0, reg; if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) { DEV_ERR("%s: invalid input\n", __func__); @@ -1462,15 +1464,17 @@ int hdcp_1x_reauthenticate(void *input) /* Disable HDCP interrupts */ DSS_REG_W(io, isr->int_reg, DSS_REG_R(io, isr->int_reg) & ~HDCP_INT_EN); - if (reg_set->reset) - DSS_REG_W(io, reg_set->reset, BIT(0)); + reg = DSS_REG_R(io, reg_set->reset); + DSS_REG_W(io, reg_set->reset, reg | reg_set->reset_bit); /* Disable encryption and disable the HDCP block */ DSS_REG_W(io, reg_set->ctrl, 0); + DSS_REG_W(io, reg_set->reset, reg & ~reg_set->reset_bit); + if (!hdcp_1x_load_keys(input)) queue_delayed_work(hdcp_ctrl->init_data.workq, - &hdcp_ctrl->hdcp_auth_work, HZ/2); + &hdcp_ctrl->hdcp_auth_work, HZ); else queue_work(hdcp_ctrl->init_data.workq, &hdcp_ctrl->hdcp_int_work); @@ -1485,6 +1489,7 @@ void hdcp_1x_off(void *input) struct hdcp_reg_set *reg_set; struct hdcp_int_set *isr; int rc = 0; + u32 reg; if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) { DEV_ERR("%s: invalid input\n", __func__); @@ -1527,12 +1532,15 @@ void hdcp_1x_off(void *input) DEV_DBG("%s: %s: Deleted hdcp int work\n", __func__, HDCP_STATE_NAME); - if (reg_set->reset) - DSS_REG_W(io, reg_set->reset, BIT(0)); + + reg = DSS_REG_R(io, reg_set->reset); + DSS_REG_W(io, reg_set->reset, reg | reg_set->reset_bit); /* Disable encryption and disable the HDCP block */ DSS_REG_W(io, reg_set->ctrl, 0); + DSS_REG_W(io, reg_set->reset, reg & ~reg_set->reset_bit); + DEV_DBG("%s: %s: HDCP: Off\n", __func__, HDCP_STATE_NAME); } /* hdcp_1x_off */ From d4a840722fd7559d65ffbd8be6c1924aec9b5568 Mon Sep 17 00:00:00 2001 From: Ajay Singh Parmar Date: Thu, 27 Oct 2016 22:44:06 -0700 Subject: [PATCH 6/7] msm: mdss: hdcp1x: handle hpd for hdcp use-cases On receiving attention command from displayport with hpd high, proceed with hdcp authentication. Also, once the authentication is successful, enable the encryption. Change-Id: I970545bbf99ba89804016844f5b294a7e762e6e5 Signed-off-by: Ajay Singh Parmar --- drivers/video/fbdev/msm/mdss_dp.c | 23 +++++++++++------------ drivers/video/fbdev/msm/mdss_hdcp_1x.c | 8 ++++++++ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index 22d3d45ccea3..87cb089efddd 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -2496,7 +2496,7 @@ static int mdss_dp_process_downstream_port_status_change( * (including cases when there are back to back HPD IRQ HIGH) indicating * the start of a new link training request or sink status update. */ -static void mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) +static int mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) { int ret = 0; @@ -2516,10 +2516,11 @@ static void mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) if (!ret) goto exit; + pr_debug("done\n"); exit: mdss_dp_reset_test_data(dp); - pr_debug("done\n"); + return ret; } /** @@ -2529,10 +2530,10 @@ exit: * This function will handle the HPD IRQ state transitions from HIGH to LOW, * indicating the end of a test request. */ -static void mdss_dp_process_hpd_irq_low(struct mdss_dp_drv_pdata *dp) +static int mdss_dp_process_hpd_irq_low(struct mdss_dp_drv_pdata *dp) { if (!dp->hpd_irq_clients_notified) - return; + return -EINVAL; pr_debug("enter: HPD IRQ low\n"); @@ -2546,6 +2547,7 @@ static void mdss_dp_process_hpd_irq_low(struct mdss_dp_drv_pdata *dp) mdss_dp_reset_test_data(dp); pr_debug("done\n"); + return 0; } static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, @@ -2593,14 +2595,11 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, dp_drv->hdcp.ops->cp_irq) dp_drv->hdcp.ops->cp_irq(dp_drv->hdcp.data); - mdss_dp_process_hpd_irq_high(dp_drv); - break; - } - - if (dp_drv->hpd_irq_toggled - && !dp_drv->alt_mode.dp_status.hpd_irq) { - mdss_dp_process_hpd_irq_low(dp_drv); - break; + if (!mdss_dp_process_hpd_irq_high(dp_drv)) + break; + } else if (dp_drv->hpd_irq_toggled) { + if (!mdss_dp_process_hpd_irq_low(dp_drv)) + break; } if (!dp_drv->alt_mode.dp_status.hpd_high) { diff --git a/drivers/video/fbdev/msm/mdss_hdcp_1x.c b/drivers/video/fbdev/msm/mdss_hdcp_1x.c index 1e0ad986afb1..a8182c2f0e76 100644 --- a/drivers/video/fbdev/msm/mdss_hdcp_1x.c +++ b/drivers/video/fbdev/msm/mdss_hdcp_1x.c @@ -1297,6 +1297,9 @@ static void hdcp_1x_int_work(struct work_struct *work) return; } + if (hdcp_ctrl->hdcp_state == HDCP_STATE_AUTHENTICATED) + hdcp1_set_enc(false); + mutex_lock(hdcp_ctrl->init_data.mutex); hdcp_ctrl->hdcp_state = HDCP_STATE_AUTH_FAIL; mutex_unlock(hdcp_ctrl->init_data.mutex); @@ -1385,6 +1388,8 @@ error: hdcp_ctrl->init_data.cb_data, hdcp_ctrl->hdcp_state); } + + hdcp1_set_enc(true); } else { DEV_DBG("%s: %s: HDCP state changed during authentication\n", __func__, HDCP_STATE_NAME); @@ -1506,6 +1511,9 @@ void hdcp_1x_off(void *input) return; } + if (hdcp_ctrl->hdcp_state == HDCP_STATE_AUTHENTICATED) + hdcp1_set_enc(false); + /* * Disable HDCP interrupts. * Also, need to set the state to inactive here so that any ongoing From 0659e58553f180f8c44cbceec45a3132ed73dcef Mon Sep 17 00:00:00 2001 From: Ajay Singh Parmar Date: Fri, 28 Oct 2016 01:06:41 -0700 Subject: [PATCH 7/7] msm: ext_display: update hpd and notify logic Use "hpd" method to notify external display module about cable status change and "notify" as an acknowledgment to power on or off. This makes hpd method as a blocking call completed by notify call. Change-Id: I5ef7cf5c95d46a695f20a51214a2afabd6feb4b6 Signed-off-by: Ajay Singh Parmar --- drivers/video/fbdev/msm/mdss_dp.c | 59 +---- drivers/video/fbdev/msm/mdss_dp.h | 2 - drivers/video/fbdev/msm/msm_ext_display.c | 280 +++++++++++++--------- 3 files changed, 167 insertions(+), 174 deletions(-) diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index 87cb089efddd..75e42ca8cd88 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -906,8 +906,6 @@ static int dp_audio_info_setup(struct platform_device *pdev, mdss_dp_set_safe_to_exit_level(&dp_ctrl->ctrl_io, dp_ctrl->lane_cnt); mdss_dp_audio_enable(&dp_ctrl->ctrl_io, true); - dp_ctrl->wait_for_audio_comp = true; - return rc; } /* dp_audio_info_setup */ @@ -930,17 +928,6 @@ static int dp_get_audio_edid_blk(struct platform_device *pdev, return rc; } /* dp_get_audio_edid_blk */ -static void dp_audio_codec_teardown_done(struct platform_device *pdev) -{ - struct mdss_dp_drv_pdata *dp = platform_get_drvdata(pdev); - - if (!dp) - pr_err("invalid input\n"); - - pr_debug("audio codec teardown done\n"); - complete_all(&dp->audio_comp); -} - static int mdss_dp_init_ext_disp(struct mdss_dp_drv_pdata *dp) { int ret = 0; @@ -962,8 +949,6 @@ static int mdss_dp_init_ext_disp(struct mdss_dp_drv_pdata *dp) dp_get_audio_edid_blk; dp->ext_audio_data.codec_ops.cable_status = dp_get_cable_status; - dp->ext_audio_data.codec_ops.teardown_done = - dp_audio_codec_teardown_done; if (!dp->pdev->dev.of_node) { pr_err("%s cannot find dp dev.of_node\n", __func__); @@ -1044,12 +1029,10 @@ 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_set_audio_switch_node( - struct mdss_dp_drv_pdata *dp, int val) +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); + dp->ext_audio_data.intf_ops.notify(dp->ext_pdev, val); } /** @@ -1307,7 +1290,7 @@ link_training: dp_drv->cont_splash = 0; dp_drv->power_on = true; - mdss_dp_set_audio_switch_node(dp_drv, true); + mdss_dp_ack_state(dp_drv, true); pr_debug("End-\n"); exit: @@ -1425,6 +1408,7 @@ static int mdss_dp_off_hpd(struct mdss_dp_drv_pdata *dp_drv) dp_drv->dp_initialized = false; dp_drv->power_on = false; + mdss_dp_ack_state(dp_drv, false); mutex_unlock(&dp_drv->train_mutex); pr_debug("DP off done\n"); @@ -1467,42 +1451,11 @@ end: return ret; } -static void mdss_dp_audio_codec_wait(struct mdss_dp_drv_pdata *dp) -{ - const int audio_completion_timeout_ms = HZ * 3; - int ret = 0; - - if (!dp->wait_for_audio_comp) - return; - - reinit_completion(&dp->audio_comp); - ret = wait_for_completion_timeout(&dp->audio_comp, - audio_completion_timeout_ms); - if (ret <= 0) - pr_warn("audio codec teardown timed out\n"); - - dp->wait_for_audio_comp = false; -} - static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, bool enable) { - int notified = 0; - - if (enable) { - notified = mdss_dp_send_cable_notification(dp, enable); - } else { - mdss_dp_set_audio_switch_node(dp, enable); - mdss_dp_audio_codec_wait(dp); - notified = mdss_dp_send_cable_notification(dp, enable); - } - - pr_debug("notify state %s done\n", - enable ? "ENABLE" : "DISABLE"); - - return notified; + return mdss_dp_send_cable_notification(dp, enable); } - static int mdss_dp_edid_init(struct mdss_panel_data *pdata) { struct mdss_dp_drv_pdata *dp_drv = NULL; @@ -2795,10 +2748,8 @@ static int mdss_dp_probe(struct platform_device *pdev) mdss_dp_device_register(dp_drv); dp_drv->inited = true; - dp_drv->wait_for_audio_comp = false; dp_drv->hpd_irq_on = false; mdss_dp_reset_test_data(dp_drv); - init_completion(&dp_drv->audio_comp); init_completion(&dp_drv->irq_comp); pr_debug("done\n"); diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index cfacbfa1d413..e801eceeef1b 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -450,7 +450,6 @@ struct mdss_dp_drv_pdata { struct completion train_comp; struct completion idle_comp; struct completion video_comp; - struct completion audio_comp; struct completion irq_comp; struct mutex aux_mutex; struct mutex train_mutex; @@ -476,7 +475,6 @@ struct mdss_dp_drv_pdata { char delay_start; u32 bpp; struct dp_statistic dp_stat; - bool wait_for_audio_comp; bool hpd_irq_on; bool hpd_irq_toggled; bool hpd_irq_clients_notified; diff --git a/drivers/video/fbdev/msm/msm_ext_display.c b/drivers/video/fbdev/msm/msm_ext_display.c index 36a5530cb761..ca01ee6345d2 100644 --- a/drivers/video/fbdev/msm/msm_ext_display.c +++ b/drivers/video/fbdev/msm/msm_ext_display.c @@ -38,15 +38,18 @@ struct msm_ext_disp { struct switch_dev hdmi_sdev; struct switch_dev audio_sdev; bool ack_enabled; - atomic_t ack_pending; + bool audio_session_on; struct list_head display_list; struct mutex lock; + struct completion hpd_comp; }; static int msm_ext_disp_get_intf_data(struct msm_ext_disp *ext_disp, enum msm_ext_disp_type type, struct msm_ext_disp_init_data **data); static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack); +static int msm_ext_disp_update_audio_ops(struct msm_ext_disp *ext_disp, + enum msm_ext_disp_cable_state state); static int msm_ext_disp_switch_dev_register(struct msm_ext_disp *ext_disp) { @@ -334,12 +337,80 @@ static int msm_ext_disp_send_cable_notification(struct msm_ext_disp *ext_disp, return ext_disp->hdmi_sdev.state == state ? 0 : 1; } +static int msm_ext_disp_send_audio_notification(struct msm_ext_disp *ext_disp, + enum msm_ext_disp_cable_state new_state) +{ + int state = EXT_DISPLAY_CABLE_STATE_MAX; + + if (!ext_disp) { + pr_err("Invalid params\n"); + return -EINVAL; + } + + state = ext_disp->audio_sdev.state; + switch_set_state(&ext_disp->audio_sdev, !!new_state); + + pr_debug("Audio state %s %d\n", + ext_disp->audio_sdev.state == state ? + "is same" : "switched to", + ext_disp->audio_sdev.state); + + return ext_disp->audio_sdev.state == state ? 0 : 1; +} + +static int msm_ext_disp_process_display(struct msm_ext_disp *ext_disp, + enum msm_ext_disp_cable_state state) +{ + int ret = msm_ext_disp_send_cable_notification(ext_disp, state); + + /* positive ret value means audio node was switched */ + if (IS_ERR_VALUE(ret) || !ret) { + pr_debug("not waiting for display\n"); + goto end; + } + + reinit_completion(&ext_disp->hpd_comp); + ret = wait_for_completion_timeout(&ext_disp->hpd_comp, HZ * 2); + if (!ret) { + pr_err("display timeout\n"); + ret = -EINVAL; + goto end; + } + + ret = 0; +end: + return ret; +} + +static int msm_ext_disp_process_audio(struct msm_ext_disp *ext_disp, + enum msm_ext_disp_cable_state state) +{ + int ret = msm_ext_disp_send_audio_notification(ext_disp, state); + + /* positive ret value means audio node was switched */ + if (IS_ERR_VALUE(ret) || !ret || !ext_disp->ack_enabled) { + pr_debug("not waiting for audio\n"); + goto end; + } + + reinit_completion(&ext_disp->hpd_comp); + ret = wait_for_completion_timeout(&ext_disp->hpd_comp, HZ * 2); + if (!ret) { + pr_err("audio timeout\n"); + ret = -EINVAL; + goto end; + } + + ret = 0; +end: + return ret; +} + static int msm_ext_disp_hpd(struct platform_device *pdev, enum msm_ext_disp_type type, enum msm_ext_disp_cable_state state) { int ret = 0; - struct msm_ext_disp_init_data *data = NULL; struct msm_ext_disp *ext_disp = NULL; if (!pdev) { @@ -381,27 +452,28 @@ static int msm_ext_disp_hpd(struct platform_device *pdev, goto end; } - ret = msm_ext_disp_get_intf_data(ext_disp, type, &data); - if (ret) - goto end; - if (state == EXT_DISPLAY_CABLE_CONNECT) { - ext_disp->current_disp = data->type; - } else if ((state == EXT_DISPLAY_CABLE_DISCONNECT) && - !ext_disp->ack_enabled) { - if (ext_disp->ops) { - ext_disp->ops->audio_info_setup = NULL; - ext_disp->ops->get_audio_edid_blk = NULL; - ext_disp->ops->cable_status = NULL; - ext_disp->ops->get_intf_id = NULL; - ext_disp->ops->teardown_done = NULL; - } + ext_disp->current_disp = type; + + ret = msm_ext_disp_process_display(ext_disp, state); + if (ret) + goto end; + + msm_ext_disp_update_audio_ops(ext_disp, state); + if (ret) + goto end; + + ret = msm_ext_disp_process_audio(ext_disp, state); + if (ret) + goto end; + } else { + msm_ext_disp_process_audio(ext_disp, state); + msm_ext_disp_update_audio_ops(ext_disp, state); + msm_ext_disp_process_display(ext_disp, state); ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX; } - ret = msm_ext_disp_send_cable_notification(ext_disp, state); - pr_debug("Hpd (%d) for display (%s)\n", state, msm_ext_disp_name(type)); @@ -429,23 +501,18 @@ static int msm_ext_disp_get_intf_data_helper(struct platform_device *pdev, goto end; } - mutex_lock(&ext_disp->lock); - if (ext_disp->current_disp == EXT_DISPLAY_TYPE_MAX) { ret = -EINVAL; pr_err("No display connected\n"); - goto error; + goto end; } ret = msm_ext_disp_get_intf_data(ext_disp, ext_disp->current_disp, data); - if (ret) - goto error; -error: - mutex_unlock(&ext_disp->lock); end: return ret; } + static int msm_ext_disp_cable_status(struct platform_device *pdev, u32 vote) { int ret = 0; @@ -482,11 +549,21 @@ static int msm_ext_disp_audio_info_setup(struct platform_device *pdev, { int ret = 0; struct msm_ext_disp_init_data *data = NULL; + struct msm_ext_disp *ext_disp = NULL; ret = msm_ext_disp_get_intf_data_helper(pdev, &data); if (ret || !data) goto end; + ext_disp = platform_get_drvdata(pdev); + if (!ext_disp) { + pr_err("No drvdata found\n"); + ret = -EINVAL; + goto end; + } + + ext_disp->audio_session_on = true; + ret = data->codec_ops.audio_info_setup(data->pdev, params); end: @@ -497,6 +574,7 @@ static void msm_ext_disp_teardown_done(struct platform_device *pdev) { int ret = 0; struct msm_ext_disp_init_data *data = NULL; + struct msm_ext_disp *ext_disp = NULL; ret = msm_ext_disp_get_intf_data_helper(pdev, &data); if (ret || !data) { @@ -504,7 +582,21 @@ static void msm_ext_disp_teardown_done(struct platform_device *pdev) return; } - data->codec_ops.teardown_done(data->pdev); + ext_disp = platform_get_drvdata(pdev); + if (!ext_disp) { + pr_err("No drvdata found\n"); + return; + } + + if (data->codec_ops.teardown_done) + data->codec_ops.teardown_done(data->pdev); + + ext_disp->audio_session_on = false; + + pr_debug("%s tearing down audio\n", + msm_ext_disp_name(ext_disp->current_disp)); + + complete_all(&ext_disp->hpd_comp); } static int msm_ext_disp_get_intf_id(struct platform_device *pdev) @@ -525,93 +617,78 @@ static int msm_ext_disp_get_intf_id(struct platform_device *pdev) goto end; } - mutex_lock(&ext_disp->lock); ret = ext_disp->current_disp; - mutex_unlock(&ext_disp->lock); end: return ret; } -static int msm_ext_disp_notify(struct platform_device *pdev, - enum msm_ext_disp_cable_state new_state) +static int msm_ext_disp_update_audio_ops(struct msm_ext_disp *ext_disp, + enum msm_ext_disp_cable_state state) +{ + int ret = 0; + struct msm_ext_disp_audio_codec_ops *ops = ext_disp->ops; + + if (!ops) { + pr_err("Invalid audio ops\n"); + ret = -EINVAL; + goto end; + } + + if (state == EXT_DISPLAY_CABLE_CONNECT) { + ops->audio_info_setup = msm_ext_disp_audio_info_setup; + ops->get_audio_edid_blk = msm_ext_disp_get_audio_edid_blk; + ops->cable_status = msm_ext_disp_cable_status; + ops->get_intf_id = msm_ext_disp_get_intf_id; + ops->teardown_done = msm_ext_disp_teardown_done; + } else { + ops->audio_info_setup = NULL; + ops->get_audio_edid_blk = NULL; + ops->cable_status = NULL; + ops->get_intf_id = NULL; + ops->teardown_done = NULL; + } +end: + return ret; +} + +static int msm_ext_disp_notify(struct platform_device *pdev, + enum msm_ext_disp_cable_state state) { int ret = 0; - int state = 0; - bool switched; - struct msm_ext_disp_init_data *data = NULL; struct msm_ext_disp *ext_disp = NULL; if (!pdev) { pr_err("Invalid platform device\n"); - return -EINVAL; + ret = -EINVAL; + goto end; } ext_disp = platform_get_drvdata(pdev); if (!ext_disp) { pr_err("Invalid drvdata\n"); - return -EINVAL; + ret = -EINVAL; + goto end; } - mutex_lock(&ext_disp->lock); - if (state < EXT_DISPLAY_CABLE_DISCONNECT || - state >= EXT_DISPLAY_CABLE_STATE_MAX) { + state >= EXT_DISPLAY_CABLE_STATE_MAX) { pr_err("Invalid state (%d)\n", state); ret = -EINVAL; goto end; } - state = ext_disp->audio_sdev.state; - if (state == new_state) - goto end; - - if (ext_disp->ack_enabled && - atomic_read(&ext_disp->ack_pending)) { - ret = -EINVAL; - pr_err("%s ack pending, not notifying %s\n", - state ? "connect" : "disconnect", - new_state ? "connect" : "disconnect"); - goto end; - } - - ret = msm_ext_disp_get_intf_data(ext_disp, ext_disp->current_disp, - &data); - if (ret) - goto end; - - if (new_state == EXT_DISPLAY_CABLE_CONNECT && ext_disp->ops) { - ext_disp->ops->audio_info_setup = - msm_ext_disp_audio_info_setup; - ext_disp->ops->get_audio_edid_blk = - msm_ext_disp_get_audio_edid_blk; - ext_disp->ops->cable_status = - msm_ext_disp_cable_status; - ext_disp->ops->get_intf_id = - msm_ext_disp_get_intf_id; - ext_disp->ops->teardown_done = - msm_ext_disp_teardown_done; - } - - switch_set_state(&ext_disp->audio_sdev, (int)new_state); - switched = ext_disp->audio_sdev.state != state; - - if (ext_disp->ack_enabled && switched) - atomic_set(&ext_disp->ack_pending, 1); - - pr_debug("audio %s %s\n", switched ? "switched to" : "same as", - ext_disp->audio_sdev.state ? "HDMI" : "SPKR"); + pr_debug("%s notifying hpd (%d)\n", + msm_ext_disp_name(ext_disp->current_disp), state); + complete_all(&ext_disp->hpd_comp); end: - mutex_unlock(&ext_disp->lock); - return ret; } static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack) { u32 ack_hpd; - u32 hpd; int ret = 0; struct msm_ext_disp *ext_disp = NULL; @@ -626,10 +703,6 @@ static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack) return -EINVAL; } - mutex_lock(&ext_disp->lock); - - hpd = ext_disp->current_disp != EXT_DISPLAY_TYPE_MAX; - if (ack & AUDIO_ACK_SET_ENABLE) { ext_disp->ack_enabled = ack & AUDIO_ACK_ENABLE ? true : false; @@ -642,44 +715,14 @@ static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack) if (!ext_disp->ack_enabled) goto end; - atomic_set(&ext_disp->ack_pending, 0); - ack_hpd = ack & AUDIO_ACK_CONNECT; - pr_debug("acknowledging %s\n", - ack_hpd ? "connect" : "disconnect"); - - /** - * If the ack feature is enabled and we receive an ack for - * disconnect then we reset the current display state to - * empty. - */ - if (!ack_hpd) { - if (ext_disp->ops) { - ext_disp->ops->audio_info_setup = NULL; - ext_disp->ops->get_audio_edid_blk = NULL; - ext_disp->ops->cable_status = NULL; - ext_disp->ops->get_intf_id = NULL; - ext_disp->ops->teardown_done = NULL; - } - - ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX; - } - - if (ack_hpd != hpd) { - pr_err("unbalanced audio state, ack %d, hpd %d\n", - ack_hpd, hpd); - - mutex_unlock(&ext_disp->lock); - - ret = msm_ext_disp_notify(pdev, hpd); - - return ret; - } + pr_debug("%s acknowledging audio (%d)\n", + msm_ext_disp_name(ext_disp->current_disp), ack_hpd); + if (!ext_disp->audio_session_on) + complete_all(&ext_disp->hpd_comp); end: - mutex_unlock(&ext_disp->lock); - return ret; } @@ -852,6 +895,7 @@ static int msm_ext_disp_probe(struct platform_device *pdev) mutex_init(&ext_disp->lock); INIT_LIST_HEAD(&ext_disp->display_list); + init_completion(&ext_disp->hpd_comp); ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX; return ret;