summaryrefslogtreecommitdiff
path: root/drivers/video/fbdev
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2016-12-30 06:27:57 -0800
committerGerrit - the friendly Code Review server <code-review@localhost>2016-12-30 06:27:56 -0800
commit3f317425d697a93e2fc6b2c1da00adcb55fbfccf (patch)
tree4ea3042a845436884f1cd7a93d8bf0070785fc66 /drivers/video/fbdev
parent36d9d0f8f42892e1fb9dec988e4a4a03ebfd6410 (diff)
parent1c8ee2be72f6a8cb9a2ce20deb52ac1fb4577237 (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.c271
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.h4
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c95
-rw-r--r--drivers/video/fbdev/msm/mdss_hdcp_1x.c55
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)