summaryrefslogtreecommitdiff
path: root/drivers/video/fbdev
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2016-11-04 22:22:03 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2016-11-04 22:22:02 -0700
commit9454b9f32d6e14d841a6989eddee0413041eaf92 (patch)
tree9e4b0c072e8091c89b38925da31d98b2b3ace261 /drivers/video/fbdev
parent68afff6f342e10c5bd0f7553e88718ca6a63fe47 (diff)
parent0659e58553f180f8c44cbceec45a3132ed73dcef (diff)
Merge "msm: ext_display: update hpd and notify logic"
Diffstat (limited to 'drivers/video/fbdev')
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.c308
-rw-r--r--drivers/video/fbdev/msm/mdss_dp.h18
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_aux.c118
-rw-r--r--drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c158
-rw-r--r--drivers/video/fbdev/msm/mdss_hdcp.h1
-rw-r--r--drivers/video/fbdev/msm/mdss_hdcp_1x.c32
-rw-r--r--drivers/video/fbdev/msm/msm_ext_display.c284
7 files changed, 503 insertions, 416 deletions
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c
index fa2e47e06503..75e42ca8cd88 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)
{
@@ -902,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 */
@@ -926,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;
@@ -958,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__);
@@ -1040,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);
}
/**
@@ -1156,19 +1143,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 +1173,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,12 +1278,19 @@ 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;
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:
@@ -1311,6 +1323,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)
{
@@ -1390,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");
@@ -1413,52 +1432,30 @@ 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);
-}
-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;
+end:
+ return ret;
}
-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)
{
- if (enable) {
- 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);
- }
-
- pr_debug("notify state %s done\n",
- enable ? "ENABLE" : "DISABLE");
+ 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;
@@ -1614,22 +1611,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;
@@ -1652,8 +1645,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)
@@ -1691,19 +1696,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;
@@ -1882,6 +1887,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)
{
@@ -1913,8 +1925,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;
@@ -2191,6 +2205,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;
}
@@ -2205,6 +2224,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;
}
@@ -2307,14 +2327,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);
-
- 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;
+ dp->hpd_irq_clients_notified = true;
+
+ 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;
+ }
}
}
@@ -2344,19 +2370,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 +2397,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 +2417,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);
}
/**
@@ -2393,21 +2449,31 @@ static void mdss_dp_process_link_training_request(struct mdss_dp_drv_pdata *dp)
* (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)
{
- pr_debug("enter: HPD IRQ High\n");
+ int ret = 0;
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;
- mdss_dp_reset_test_data(dp);
+ ret = mdss_dp_process_downstream_port_status_change(dp);
+ if (!ret)
+ goto exit;
pr_debug("done\n");
+exit:
+ mdss_dp_reset_test_data(dp);
+
+ return ret;
}
/**
@@ -2417,11 +2483,15 @@ static void mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp)
* 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 -EINVAL;
+
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);
@@ -2430,6 +2500,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,
@@ -2471,14 +2542,17 @@ 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) {
- mdss_dp_process_hpd_irq_high(dp_drv);
- break;
- }
+ pr_debug("Attention: hpd_irq high\n");
- if (dp_drv->hpd_irq_toggled
- && !dp_drv->alt_mode.dp_status.hpd_irq) {
- mdss_dp_process_hpd_irq_low(dp_drv);
- break;
+ 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);
+
+ 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) {
@@ -2500,9 +2574,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;
@@ -2677,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");
@@ -2718,13 +2787,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 4ba2d20d4261..e801eceeef1b 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 {
@@ -437,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;
@@ -463,13 +475,14 @@ 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;
/* 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;
@@ -480,6 +493,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_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c
index 91066662e793..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)
@@ -846,11 +853,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;
@@ -861,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;
@@ -1431,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++");
@@ -1458,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 */
}
@@ -1480,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++");
@@ -1505,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;
}
@@ -1518,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;
-
- rate = ep->link_rate;
- lane = ep->lane_cnt;
- max_lane = ep->dpcd.max_lane_count;
-
- prate = ep->pixel_rate;
- prate /= 1000; /* avoid using 64 biits */
- prate *= ep->bpp;
- prate /= 8; /* byte */
-
- if (rate > DP_LINK_RATE_162 && rate <= DP_LINK_RATE_MAX) {
- rate -= 4; /* reduce rate */
- changed++;
- }
+ int ret = 0;
- if (changed) {
- if (lane >= 1 && lane < max_lane)
- lane <<= 1; /* increase lane */
+ if (!ep)
+ return -EINVAL;
- lrate = 270000000; /* 270M */
- lrate /= 1000; /* avoid using 64 bits */
- lrate *= rate;
- lrate /= 10; /* byte, 10 bits --> 8 bits */
- lrate *= lane;
+ 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;
+ };
- 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);
+ pr_debug("new rate=%d\n", ep->link_rate);
- 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)
@@ -1595,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);
@@ -1605,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;
}
}
@@ -1618,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/mdss_dp_hdcp2p2.c b/drivers/video/fbdev/msm/mdss_dp_hdcp2p2.c
index 3891806b09bb..79cd94cfbe88 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;
}
@@ -452,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");
@@ -474,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;
@@ -505,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;
@@ -518,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;
@@ -550,7 +507,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 +515,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");
-
- ctrl->sink_rx_status = 0;
- rc = -ENOLINK;
- cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
-
- goto exit;
- }
+ if (ctrl->rx_status) {
+ if (!ctrl->cp_irq_done) {
+ pr_debug("waiting for CP_IRQ\n");
+ ctrl->polling = true;
+ return;
+ }
- 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 +581,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 +631,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 +683,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 +741,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/drivers/video/fbdev/msm/mdss_hdcp_1x.c b/drivers/video/fbdev/msm/mdss_hdcp_1x.c
index 1e502cf750a6..a8182c2f0e76 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}, \
@@ -1295,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);
@@ -1383,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);
@@ -1431,7 +1438,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 +1469,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 +1494,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__);
@@ -1501,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
@@ -1527,12 +1540,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 */
diff --git a/drivers/video/fbdev/msm/msm_ext_display.c b/drivers/video/fbdev/msm/msm_ext_display.c
index f6c6548bdaa5..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)
{
@@ -313,14 +316,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 +333,77 @@ 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_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,
@@ -337,7 +411,6 @@ static int msm_ext_disp_hpd(struct platform_device *pdev,
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) {
@@ -379,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;
}
- msm_ext_disp_send_cable_notification(ext_disp, state);
-
pr_debug("Hpd (%d) for display (%s)\n", state,
msm_ext_disp_name(type));
@@ -427,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;
@@ -480,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:
@@ -495,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) {
@@ -502,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)
@@ -523,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_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 new_state)
+ 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;
- }
-
- mutex_lock(&ext_disp->lock);
-
- if (state < EXT_DISPLAY_CABLE_DISCONNECT ||
- 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)) {
+ if (state < EXT_DISPLAY_CABLE_DISCONNECT ||
+ state >= EXT_DISPLAY_CABLE_STATE_MAX) {
+ pr_err("Invalid state (%d)\n", state);
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;
@@ -624,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;
@@ -640,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;
}
@@ -850,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;