diff options
| author | Linux Build Service Account <lnxbuild@localhost> | 2016-12-30 06:27:57 -0800 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2016-12-30 06:27:56 -0800 |
| commit | 3f317425d697a93e2fc6b2c1da00adcb55fbfccf (patch) | |
| tree | 4ea3042a845436884f1cd7a93d8bf0070785fc66 /drivers/video/fbdev | |
| parent | 36d9d0f8f42892e1fb9dec988e4a4a03ebfd6410 (diff) | |
| parent | 1c8ee2be72f6a8cb9a2ce20deb52ac1fb4577237 (diff) | |
Merge "msm: mdss: dp: add support for downstream device power management"
Diffstat (limited to 'drivers/video/fbdev')
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dp.c | 271 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dp.h | 4 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dp_aux.c | 95 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_hdcp_1x.c | 55 |
4 files changed, 365 insertions, 60 deletions
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index 7076b36bb6b6..cd7471b1cd8e 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -68,6 +68,7 @@ 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_handle_attention(struct mdss_dp_drv_pdata *dp_drv); +static void dp_send_events(struct mdss_dp_drv_pdata *dp, u32 events); static void mdss_dp_put_dt_clk_data(struct device *dev, struct dss_module_power *module_power) @@ -1234,6 +1235,15 @@ static int mdss_dp_on_irq(struct mdss_dp_drv_pdata *dp_drv) dp_drv->power_on = true; + if (dp_drv->psm_enabled) { + ret = mdss_dp_aux_send_psm_request(dp_drv, false); + if (ret) { + pr_err("Failed to exit low power mode, rc=%d\n", + ret); + goto exit; + } + } + ret = mdss_dp_train_main_link(dp_drv); mutex_unlock(&dp_drv->train_mutex); @@ -1301,6 +1311,15 @@ int mdss_dp_on_hpd(struct mdss_dp_drv_pdata *dp_drv) mdss_dp_configure_source_params(dp_drv, &ln_map); + if (dp_drv->psm_enabled) { + ret = mdss_dp_aux_send_psm_request(dp_drv, false); + if (ret) { + pr_err("Failed to exit low power mode, rc=%d\n", ret); + goto exit; + } + } + + link_training: dp_drv->power_on = true; @@ -1387,6 +1406,7 @@ static int mdss_dp_off_irq(struct mdss_dp_drv_pdata *dp_drv) wmb(); mdss_dp_disable_mainlink_clocks(dp_drv); dp_drv->power_on = false; + dp_drv->sink_info_read = false; mutex_unlock(&dp_drv->train_mutex); complete_all(&dp_drv->irq_comp); @@ -1434,6 +1454,8 @@ static int mdss_dp_off_hpd(struct mdss_dp_drv_pdata *dp_drv) dp_drv->dp_initialized = false; dp_drv->power_on = false; + dp_drv->sink_info_read = false; + mdss_dp_ack_state(dp_drv, false); mutex_unlock(&dp_drv->train_mutex); pr_debug("DP off done\n"); @@ -1545,7 +1567,7 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata) panel_data); if (dp_drv->dp_initialized) { - pr_err("%s: host init done already\n", __func__); + pr_debug("%s: host init done already\n", __func__); return 0; } @@ -1588,37 +1610,47 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata) mdss_dp_phy_aux_setup(&dp_drv->phy_io); mdss_dp_irq_enable(dp_drv); - pr_debug("irq enabled\n"); - mdss_dp_dpcd_cap_read(dp_drv); + dp_drv->dp_initialized = true; + + return 0; - ret = mdss_dp_edid_read(dp_drv); +clk_error: + mdss_dp_regulator_ctrl(dp_drv, false); + mdss_dp_config_gpios(dp_drv, false); +vreg_error: + return ret; +} + +static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp) +{ + int ret; + + if (dp->sink_info_read) + return 0; + + mdss_dp_dpcd_cap_read(dp); + + ret = mdss_dp_edid_read(dp); if (ret) { - pr_info("edid read error, setting default resolution\n"); - mdss_dp_set_default_resolution(dp_drv); - goto edid_error; - } + pr_debug("edid read error, setting default resolution\n"); - pr_debug("edid_read success. buf_size=%d\n", - dp_drv->edid_buf_size); + mdss_dp_set_default_resolution(dp); + goto end; + } - ret = hdmi_edid_parser(dp_drv->panel_data.panel_info.edid_data); + ret = hdmi_edid_parser(dp->panel_data.panel_info.edid_data); if (ret) { - DEV_ERR("%s: edid parse failed\n", __func__); - goto edid_error; + pr_err("edid parse failed\n"); + goto end; } -edid_error: - mdss_dp_update_cable_status(dp_drv, true); - mdss_dp_notify_clients(dp_drv, true); - dp_drv->dp_initialized = true; + dp->sink_info_read = true; +end: + mdss_dp_update_cable_status(dp, true); + mdss_dp_notify_clients(dp, true); return ret; -clk_error: - mdss_dp_regulator_ctrl(dp_drv, false); - mdss_dp_config_gpios(dp_drv, false); -vreg_error: - return ret; } static int mdss_dp_check_params(struct mdss_dp_drv_pdata *dp, void *arg) @@ -1855,13 +1887,191 @@ static ssize_t mdss_dp_sysfs_rda_s3d_mode(struct device *dev, return ret; } +static bool mdss_dp_is_test_ongoing(struct mdss_dp_drv_pdata *dp) +{ + return dp->hpd_irq_clients_notified; +} + +/** + * mdss_dp_psm_config() - Downstream device uPacket RX Power Management + * @dp: Display Port Driver data + * + * Perform required steps to configure the uPacket RX of a downstream + * connected device in a power-save mode. + */ +static int mdss_dp_psm_config(struct mdss_dp_drv_pdata *dp, bool enable) +{ + int ret = 0; + + if (!dp) { + pr_err("invalid data\n"); + return -EINVAL; + } + + if (dp->psm_enabled == enable) { + pr_debug("No change in psm requested\n"); + goto end; + } + + pr_debug("Power save mode %s requested\n", enable ? "entry" : "exit"); + + if (enable) { + ret = mdss_dp_aux_send_psm_request(dp, true); + if (ret) + goto end; + + /* + * If this configuration is requested as part of an + * automated test, then HPD notification has already been + * sent out. Just disable the main-link and turn off DP Tx. + * + * Otherwise, trigger a complete shutdown of the pipeline. + */ + if (mdss_dp_is_test_ongoing(dp)) { + mdss_dp_mainlink_push_idle(&dp->panel_data); + mdss_dp_off_irq(dp); + } else { + mdss_dp_notify_clients(dp, false); + } + } else { + /* + * If this configuration is requested as part of an + * automated test, then just perform a link retraining. + * + * Otherwise, re-initialize the host and setup the complete + * pipeline from scratch by sending a connection notification + * to user modules. + */ + if (mdss_dp_is_test_ongoing(dp)) { + mdss_dp_link_retraining(dp); + } else { + mdss_dp_host_init(&dp->panel_data); + mdss_dp_notify_clients(dp, true); + } + } + +end: + pr_debug("Power save mode %s %s\n", + dp->psm_enabled ? "entry" : "exit", + ret ? "failed" : "successful"); + + return ret; +} + +static ssize_t mdss_dp_wta_psm(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int psm; + int rc; + ssize_t ret = strnlen(buf, PAGE_SIZE); + struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev); + + if (!dp) { + pr_err("invalid data\n"); + ret = -EINVAL; + goto end; + } + + rc = kstrtoint(buf, 10, &psm); + if (rc) { + pr_err("kstrtoint failed. ret=%d\n", (int)ret); + goto end; + } + + rc = mdss_dp_psm_config(dp, psm ? true : false); + if (rc) { + pr_err("failed to config Power Save Mode\n"); + goto end; + } + +end: + return ret; +} + +static ssize_t mdss_dp_rda_psm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev); + + if (!dp) { + pr_err("invalid input\n"); + return -EINVAL; + } + + ret = snprintf(buf, PAGE_SIZE, "%d\n", dp->psm_enabled ? 1 : 0); + pr_debug("psm: %s\n", dp->psm_enabled ? "enabled" : "disabled"); + + return ret; +} + +static ssize_t mdss_dp_wta_hpd(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int hpd; + ssize_t ret = strnlen(buf, PAGE_SIZE); + struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev); + + if (!dp) { + pr_err("invalid data\n"); + ret = -EINVAL; + goto end; + } + + ret = kstrtoint(buf, 10, &hpd); + if (ret) { + pr_err("kstrtoint failed. ret=%d\n", (int)ret); + goto end; + } + + dp->hpd = !!hpd; + pr_debug("hpd=%d\n", dp->hpd); + + if (dp->hpd && dp->cable_connected) { + if (dp->alt_mode.current_state & DP_CONFIGURE_DONE) { + mdss_dp_host_init(&dp->panel_data); + mdss_dp_process_hpd_high(dp); + } else { + dp_send_events(dp, EV_USBPD_DISCOVER_MODES); + } + } else if (!dp->hpd && dp->power_on) { + mdss_dp_notify_clients(dp, false); + } +end: + return ret; +} + +static ssize_t mdss_dp_rda_hpd(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct mdss_dp_drv_pdata *dp = mdss_dp_get_drvdata(dev); + + if (!dp) { + pr_err("invalid input\n"); + return -EINVAL; + } + + ret = snprintf(buf, PAGE_SIZE, "%d\n", dp->hpd); + pr_debug("hpd: %d\n", dp->hpd); + + return ret; +} + static DEVICE_ATTR(connected, S_IRUGO, mdss_dp_rda_connected, NULL); static DEVICE_ATTR(s3d_mode, S_IRUGO | S_IWUSR, mdss_dp_sysfs_rda_s3d_mode, mdss_dp_sysfs_wta_s3d_mode); +static DEVICE_ATTR(hpd, S_IRUGO | S_IWUSR, mdss_dp_rda_hpd, + mdss_dp_wta_hpd); +static DEVICE_ATTR(psm, S_IRUGO | S_IWUSR, mdss_dp_rda_psm, + mdss_dp_wta_psm); + static struct attribute *mdss_dp_fs_attrs[] = { &dev_attr_connected.attr, &dev_attr_s3d_mode.attr, + &dev_attr_hpd.attr, + &dev_attr_psm.attr, NULL, }; @@ -2328,8 +2538,9 @@ static void usbpd_connect_callback(struct usbpd_svid_handler *hdlr) } mdss_dp_update_cable_status(dp_drv, true); - dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES); - pr_debug("discover_mode event sent\n"); + + if (dp_drv->hpd) + dp_send_events(dp_drv, EV_USBPD_DISCOVER_MODES); } static void usbpd_disconnect_callback(struct usbpd_svid_handler *hdlr) @@ -2703,8 +2914,10 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, dp_drv->alt_mode.current_state |= DP_CONFIGURE_DONE; pr_debug("Configure: config USBPD to DP done\n"); + mdss_dp_host_init(&dp_drv->panel_data); + if (dp_drv->alt_mode.dp_status.hpd_high) - mdss_dp_host_init(&dp_drv->panel_data); + mdss_dp_process_hpd_high(dp_drv); break; default: pr_err("unknown cmd: %d\n", cmd); @@ -2752,12 +2965,12 @@ static void mdss_dp_process_attention(struct mdss_dp_drv_pdata *dp_drv) dp_drv->alt_mode.current_state |= DP_STATUS_DONE; - if (dp_drv->alt_mode.current_state & DP_CONFIGURE_DONE) + if (dp_drv->alt_mode.current_state & DP_CONFIGURE_DONE) { mdss_dp_host_init(&dp_drv->panel_data); - else + mdss_dp_process_hpd_high(dp_drv); + } else { dp_send_events(dp_drv, EV_USBPD_DP_CONFIGURE); - - pr_debug("exit\n"); + } } static void mdss_dp_handle_attention(struct mdss_dp_drv_pdata *dp) diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index 399ca61e0f46..5ab26b466765 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -399,6 +399,9 @@ struct mdss_dp_drv_pdata { bool core_clks_on; bool link_clks_on; bool power_on; + bool sink_info_read; + bool hpd; + bool psm_enabled; /* dp specific */ unsigned char *base; @@ -681,6 +684,7 @@ void mdss_dp_lane_power_ctrl(struct mdss_dp_drv_pdata *ep, int up); void mdss_dp_config_ctrl(struct mdss_dp_drv_pdata *ep); char mdss_dp_gen_link_clk(struct mdss_panel_info *pinfo, char lane_cnt); int mdss_dp_aux_set_sink_power_state(struct mdss_dp_drv_pdata *ep, char state); +int mdss_dp_aux_send_psm_request(struct mdss_dp_drv_pdata *dp, bool enable); void mdss_dp_aux_send_test_response(struct mdss_dp_drv_pdata *ep); void *mdss_dp_get_hdcp_data(struct device *dev); int mdss_dp_hdcp2p2_init(struct mdss_dp_drv_pdata *dp_drv); diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index b8be110f04f0..fb88fb8fc335 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -148,8 +148,11 @@ static int dp_cmd_fifo_tx(struct edp_buf *tp, unsigned char *base) } data = (tp->trans_num - 1); - if (tp->i2c) + if (tp->i2c) { data |= BIT(8); /* I2C */ + data |= BIT(10); /* NO SEND ADDR */ + data |= BIT(11); /* NO SEND STOP */ + } data |= BIT(9); /* GO */ dp_write(base + DP_AUX_TRANS_CTRL, data); @@ -213,7 +216,7 @@ static int dp_aux_write_cmds(struct mdss_dp_drv_pdata *ep, len = dp_cmd_fifo_tx(&ep->txp, ep->base); - wait_for_completion(&ep->aux_comp); + wait_for_completion_timeout(&ep->aux_comp, HZ/4); if (ep->aux_error_num == EDP_AUX_ERR_NONE) ret = len; @@ -269,7 +272,7 @@ static int dp_aux_read_cmds(struct mdss_dp_drv_pdata *ep, dp_cmd_fifo_tx(tp, ep->base); - wait_for_completion(&ep->aux_comp); + wait_for_completion_timeout(&ep->aux_comp, HZ/4); if (ep->aux_error_num == EDP_AUX_ERR_NONE) { ret = dp_cmd_fifo_rx(rp, len, ep->base); @@ -731,8 +734,10 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp) int rlen, ret = 0; int edid_blk = 0, blk_num = 0, retries = 10; bool edid_parsing_done = false; - const u8 cea_tag = 0x02; + const u8 cea_tag = 0x02, start_ext_blk = 0x1; + u32 const segment_addr = 0x30; u32 checksum = 0; + char segment = 0x1; ret = dp_aux_chan_ready(dp); if (ret) { @@ -761,7 +766,7 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp) ret = dp_edid_buf_error(rp->data, rp->len); if (ret) { pr_err("corrupt edid block detected\n"); - goto end; + continue; } if (edid_parsing_done) { @@ -779,7 +784,6 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp) rp->data); edid_parsing_done = true; - checksum = rp->data[rp->len - 1]; } else { edid_blk++; blk_num++; @@ -797,11 +801,17 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp) memcpy(dp->edid_buf + (edid_blk * EDID_BLOCK_SIZE), rp->data, EDID_BLOCK_SIZE); + checksum = rp->data[rp->len - 1]; + + /* break if no more extension blocks present */ if (edid_blk == dp->edid.ext_block_cnt) - goto end; + 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--); -end: if (dp->test_data.test_requested == TEST_EDID_READ) { pr_debug("sending checksum %d\n", checksum); dp_aux_send_checksum(dp, checksum); @@ -1016,6 +1026,51 @@ int mdss_dp_aux_link_status_read(struct mdss_dp_drv_pdata *ep, int len) return len; } +/* + * mdss_dp_aux_send_psm_request() - sends a power save mode messge to sink + * @dp: Display Port Driver data + */ +int mdss_dp_aux_send_psm_request(struct mdss_dp_drv_pdata *dp, bool enable) +{ + u8 psm_request[4]; + int rc = 0; + + psm_request[0] = enable ? 2 : 1; + + pr_debug("sending psm %s request\n", enable ? "entry" : "exit"); + if (enable) { + dp_aux_write_buf(dp, 0x600, psm_request, 1, 0); + } else { + ktime_t timeout = ktime_add_ms(ktime_get(), 20); + + /* + * It could take up to 1ms (20 ms of embedded sinks) till + * the sink is ready to reply to this AUX transaction. It is + * expected that the source keep retrying periodically during + * this time. + */ + for (;;) { + rc = dp_aux_write_buf(dp, 0x600, psm_request, 1, 0); + if ((rc >= 0) || + (ktime_compare(ktime_get(), timeout) > 0)) + break; + usleep_range(100, 120); + } + + /* + * if the aux transmission succeeded, then the function would + * return the number of bytes transmitted. + */ + if (rc > 0) + rc = 0; + } + + if (!rc) + dp->psm_enabled = enable; + + return rc; +} + /** * mdss_dp_aux_send_test_response() - sends a test response to the sink * @dp: Display Port Driver data @@ -1499,18 +1554,18 @@ static void dp_host_train_set(struct mdss_dp_drv_pdata *ep, int train) } char vm_pre_emphasis[4][4] = { - {0x00, 0x09, 0x11, 0x0C}, /* pe0, 0 db */ - {0x00, 0x0A, 0x10, 0xFF}, /* pe1, 3.5 db */ - {0x00, 0x0C, 0xFF, 0xFF}, /* pe2, 6.0 db */ - {0x00, 0xFF, 0xFF, 0xFF} /* pe3, 9.5 db */ + {0x00, 0x0B, 0x12, 0xFF}, /* pe0, 0 db */ + {0x00, 0x0A, 0x12, 0xFF}, /* pe1, 3.5 db */ + {0x00, 0x0C, 0xFF, 0xFF}, /* pe2, 6.0 db */ + {0xFF, 0xFF, 0xFF, 0xFF} /* pe3, 9.5 db */ }; /* voltage swing, 0.2v and 1.0v are not support */ char vm_voltage_swing[4][4] = { - {0x07, 0x0f, 0x12, 0x1E}, /* sw0, 0.4v */ + {0x07, 0x0F, 0x14, 0xFF}, /* sw0, 0.4v */ {0x11, 0x1D, 0x1F, 0xFF}, /* sw1, 0.6 v */ {0x18, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8 v */ - {0x1E, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */ + {0xFF, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */ }; static void dp_aux_set_voltage_and_pre_emphasis_lvl( @@ -1524,6 +1579,14 @@ static void dp_aux_set_voltage_and_pre_emphasis_lvl( value0 = vm_voltage_swing[(int)(dp->v_level)][(int)(dp->p_level)]; value1 = vm_pre_emphasis[(int)(dp->v_level)][(int)(dp->p_level)]; + /* program default setting first */ + dp_write(dp->phy_io.base + QSERDES_TX0_OFFSET + TXn_TX_DRV_LVL, 0x2A); + dp_write(dp->phy_io.base + QSERDES_TX1_OFFSET + TXn_TX_DRV_LVL, 0x2A); + dp_write(dp->phy_io.base + QSERDES_TX0_OFFSET + TXn_TX_EMP_POST1_LVL, + 0x20); + dp_write(dp->phy_io.base + QSERDES_TX1_OFFSET + TXn_TX_EMP_POST1_LVL, + 0x20); + /* Enable MUX to use Cursor values from these registers */ value0 |= BIT(5); value1 |= BIT(5); @@ -1590,10 +1653,10 @@ static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep) pr_debug("Entered++"); - dp_host_train_set(ep, 0x01); /* train_1 */ dp_cap_lane_rate_set(ep); dp_train_pattern_set_write(ep, 0x21); /* train_1 */ dp_aux_set_voltage_and_pre_emphasis_lvl(ep); + dp_host_train_set(ep, 0x01); /* train_1 */ tries = 0; old_v_level = ep->v_level; @@ -1648,7 +1711,6 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep) dp_train_pattern_set_write(ep, pattern | 0x20);/* train_2 */ do { - dp_aux_set_voltage_and_pre_emphasis_lvl(ep); dp_host_train_set(ep, pattern); usleep_time = ep->dpcd.training_read_interval; @@ -1668,6 +1730,7 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep) } dp_sink_train_set_adjust(ep); + dp_aux_set_voltage_and_pre_emphasis_lvl(ep); } while (1); return ret; diff --git a/drivers/video/fbdev/msm/mdss_hdcp_1x.c b/drivers/video/fbdev/msm/mdss_hdcp_1x.c index 212f3be96bf2..08307fe8eb16 100644 --- a/drivers/video/fbdev/msm/mdss_hdcp_1x.c +++ b/drivers/video/fbdev/msm/mdss_hdcp_1x.c @@ -195,7 +195,7 @@ struct hdcp_reg_set { #define HDCP_DP_SINK_ADDR_MAP \ {{"bcaps", 0x68028, 1}, {"bksv", 0x68000, 5}, {"r0'", 0x68005, 2}, \ - {"binfo", 0x6802A, 2}, {"cp_irq_status", 0x68029, 2}, \ + {"binfo", 0x6802A, 2}, {"cp_irq_status", 0x68029, 1}, \ {"ksv-fifo", 0x6802C, 0}, {"v_h0", 0x68014, 4}, {"v_h1", 0x68018, 4}, \ {"v_h2", 0x6801C, 4}, {"v_h3", 0x68020, 4}, {"v_h4", 0x68024, 4}, \ {"an", 0x6800C, 8}, {"aksv", 0x68007, 5}, {"ainfo", 0x6803B, 1} } @@ -1763,6 +1763,30 @@ static void hdcp_1x_update_client_reg_set(struct hdcp_1x *hdcp) } } +static bool hdcp_1x_is_cp_irq_raised(struct hdcp_1x *hdcp) +{ + int ret; + u8 buf = 0; + struct hdcp_sink_addr sink = {"irq", 0x201, 1}; + + ret = hdcp_1x_read(hdcp, &sink, &buf, false); + if (IS_ERR_VALUE(ret)) + pr_err("error reading irq_vector\n"); + + return buf & BIT(2) ? true : false; +} + +static void hdcp_1x_clear_cp_irq(struct hdcp_1x *hdcp) +{ + int ret; + u8 buf = BIT(2); + struct hdcp_sink_addr sink = {"irq", 0x201, 1}; + + ret = hdcp_1x_write(hdcp, &sink, &buf); + if (IS_ERR_VALUE(ret)) + pr_err("error clearing irq_vector\n"); +} + static int hdcp_1x_cp_irq(void *input) { struct hdcp_1x *hdcp = (struct hdcp_1x *)input; @@ -1771,14 +1795,19 @@ static int hdcp_1x_cp_irq(void *input) if (!hdcp) { pr_err("invalid input\n"); - goto end; + goto irq_not_handled; + } + + if (!hdcp_1x_is_cp_irq_raised(hdcp)) { + pr_debug("cp_irq not raised\n"); + goto irq_not_handled; } ret = hdcp_1x_read(hdcp, &hdcp->sink_addr.cp_irq_status, &buf, false); if (IS_ERR_VALUE(ret)) { pr_err("error reading cp_irq_status\n"); - return ret; + goto irq_not_handled; } if ((buf & BIT(2)) || (buf & BIT(3))) { @@ -1793,27 +1822,23 @@ static int hdcp_1x_cp_irq(void *input) complete_all(&hdcp->sink_r0_available); hdcp_1x_update_auth_status(hdcp); - - goto end; - } - - if (buf & BIT(1)) { + } else if (buf & BIT(1)) { pr_debug("R0' AVAILABLE\n"); hdcp->sink_r0_ready = true; complete_all(&hdcp->sink_r0_available); - goto end; - } - - if ((buf & BIT(0))) { + } else if ((buf & BIT(0))) { pr_debug("KSVs READY\n"); hdcp->ksv_ready = true; - goto end; + } else { + pr_debug("spurious interrupt\n"); } - return -EINVAL; -end: + hdcp_1x_clear_cp_irq(hdcp); return 0; + +irq_not_handled: + return -EINVAL; } void *hdcp_1x_init(struct hdcp_init_data *init_data) |
