Merge "msm: ext_display: update hpd and notify logic"

This commit is contained in:
Linux Build Service Account 2016-11-04 22:22:03 -07:00 committed by Gerrit - the friendly Code Review server
commit 9454b9f32d
9 changed files with 598 additions and 463 deletions

View file

@ -160,43 +160,46 @@
static const struct hdcp_msg_data hdcp_msg_lookup[HDCP2P2_MAX_MESSAGES] = { static const struct hdcp_msg_data hdcp_msg_lookup[HDCP2P2_MAX_MESSAGES] = {
[AKE_INIT_MESSAGE_ID] = { 2, [AKE_INIT_MESSAGE_ID] = { 2,
{ {0x69000, 8}, {0x69008, 3} }, { {"rtx", 0x69000, 8}, {"TxCaps", 0x69008, 3} },
0 }, 0 },
[AKE_SEND_CERT_MESSAGE_ID] = { 3, [AKE_SEND_CERT_MESSAGE_ID] = { 3,
{ {0x6900B, 522}, {0x69215, 8}, {0x6921D, 3} }, { {"cert-rx", 0x6900B, 522}, {"rrx", 0x69215, 8},
{"RxCaps", 0x6921D, 3} },
0 }, 0 },
[AKE_NO_STORED_KM_MESSAGE_ID] = { 1, [AKE_NO_STORED_KM_MESSAGE_ID] = { 1,
{ {0x69220, 128} }, { {"Ekpub_km", 0x69220, 128} },
0 }, 0 },
[AKE_STORED_KM_MESSAGE_ID] = { 2, [AKE_STORED_KM_MESSAGE_ID] = { 2,
{ {0x692A0, 16}, {0x692B0, 16} }, { {"Ekh_km", 0x692A0, 16}, {"m", 0x692B0, 16} },
0 }, 0 },
[AKE_SEND_H_PRIME_MESSAGE_ID] = { 1, [AKE_SEND_H_PRIME_MESSAGE_ID] = { 1,
{ {0x692C0, 32} }, { {"H'", 0x692C0, 32} },
(1 << 1) }, (1 << 1) },
[AKE_SEND_PAIRING_INFO_MESSAGE_ID] = { 1, [AKE_SEND_PAIRING_INFO_MESSAGE_ID] = { 1,
{ {0x692E0, 16} }, { {"Ekh_km", 0x692E0, 16} },
(1 << 2) }, (1 << 2) },
[LC_INIT_MESSAGE_ID] = { 1, [LC_INIT_MESSAGE_ID] = { 1,
{ {0x692F0, 8} }, { {"rn", 0x692F0, 8} },
0 }, 0 },
[LC_SEND_L_PRIME_MESSAGE_ID] = { 1, [LC_SEND_L_PRIME_MESSAGE_ID] = { 1,
{ {0x692F8, 32} }, { {"L'", 0x692F8, 32} },
0 }, 0 },
[SKE_SEND_EKS_MESSAGE_ID] = { 2, [SKE_SEND_EKS_MESSAGE_ID] = { 2,
{ {0x69318, 16}, {0x69328, 8} }, { {"Edkey_ks", 0x69318, 16}, {"riv", 0x69328, 8} },
0 }, 0 },
[REPEATER_AUTH_SEND_RECEIVERID_LIST_MESSAGE_ID] = { 4, [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) }, (1 << 0) },
[REPEATER_AUTH_SEND_ACK_MESSAGE_ID] = { 1, [REPEATER_AUTH_SEND_ACK_MESSAGE_ID] = { 1,
{ {0x693E0, 16} }, { {"V", 0x693E0, 16} },
0 }, 0 },
[REPEATER_AUTH_STREAM_MANAGE_MESSAGE_ID] = { 3, [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 }, 0 },
[REPEATER_AUTH_STREAM_READY_MESSAGE_ID] = { 1, [REPEATER_AUTH_STREAM_READY_MESSAGE_ID] = { 1,
{ {0x69473, 32} }, { {"M'", 0x69473, 32} },
0 } 0 }
}; };
@ -616,36 +619,59 @@ static int hdcp_lib_get_next_message(struct hdcp_lib_handle *handle,
} }
} }
static inline void hdcp_lib_wakeup_client(struct hdcp_lib_handle *handle, static void hdcp_lib_wakeup_client(struct hdcp_lib_handle *handle,
struct hdmi_hdcp_wakeup_data *data) struct hdmi_hdcp_wakeup_data *data)
{ {
int rc = 0; int rc = 0, i;
if (handle && handle->client_ops && handle->client_ops->wakeup && if (!handle || !handle->client_ops || !handle->client_ops->wakeup ||
data && (data->cmd != HDMI_HDCP_WKUP_CMD_INVALID)) { !data || (data->cmd == HDMI_HDCP_WKUP_CMD_INVALID))
data->abort_mask = REAUTH_REQ | LINK_INTEGRITY_FAILURE; return;
if (data->cmd == HDMI_HDCP_WKUP_CMD_SEND_MESSAGE || data->abort_mask = REAUTH_REQ | LINK_INTEGRITY_FAILURE;
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);
if (handle->last_msg > INVALID_MESSAGE_ID && if (data->cmd == HDMI_HDCP_WKUP_CMD_SEND_MESSAGE ||
handle->last_msg < HDCP2P2_MAX_MESSAGES) data->cmd == HDMI_HDCP_WKUP_CMD_RECV_MESSAGE ||
data->message_data = data->cmd == HDMI_HDCP_WKUP_CMD_LINK_POLL) {
&hdcp_msg_lookup[handle->last_msg]; 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("%10s | %6s | %4s\n", "name", "offset", "len");
for (i = 0; i < msg_num; i++)
pr_debug("%10s | %6x | %4d\n",
msg[i].name, msg[i].offset,
msg[i].length);
} }
} else {
rc = handle->client_ops->wakeup(data); pr_debug("lib->client: %s\n",
if (rc) hdmi_hdcp_cmd_to_str(data->cmd));
pr_err("error sending %s to client\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) static inline void hdcp_lib_send_message(struct hdcp_lib_handle *handle)
{ {
char msg_name[50];
struct hdmi_hdcp_wakeup_data cdata = { struct hdmi_hdcp_wakeup_data cdata = {
HDMI_HDCP_WKUP_CMD_SEND_MESSAGE HDMI_HDCP_WKUP_CMD_SEND_MESSAGE
}; };
@ -655,6 +681,13 @@ static inline void hdcp_lib_send_message(struct hdcp_lib_handle *handle)
cdata.send_msg_len = handle->msglen; cdata.send_msg_len = handle->msglen;
cdata.timeout = handle->hdcp_timeout; 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); hdcp_lib_wakeup_client(handle, &cdata);
} }
@ -1486,6 +1519,7 @@ static int hdcp_lib_check_valid_state(struct hdcp_lib_handle *handle)
if (handle->wakeup_cmd == HDCP_LIB_WKUP_CMD_START) { if (handle->wakeup_cmd == HDCP_LIB_WKUP_CMD_START) {
if (!list_empty(&handle->worker.work_list)) { if (!list_empty(&handle->worker.work_list)) {
pr_debug("error: queue not empty\n");
rc = -EBUSY; rc = -EBUSY;
goto exit; goto exit;
} }
@ -1543,9 +1577,8 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data)
handle->wakeup_cmd = data->cmd; handle->wakeup_cmd = data->cmd;
handle->timeout_left = data->timeout; handle->timeout_left = data->timeout;
pr_debug("%s, timeout left: %dms, tethered %d\n", pr_debug("client->lib: %s\n",
hdcp_lib_cmd_to_str(handle->wakeup_cmd), hdcp_lib_cmd_to_str(handle->wakeup_cmd));
handle->timeout_left, handle->tethered);
rc = hdcp_lib_check_valid_state(handle); rc = hdcp_lib_check_valid_state(handle);
if (rc) if (rc)
@ -1599,6 +1632,8 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data)
break; break;
case HDCP_LIB_WKUP_CMD_MSG_SEND_FAILED: case HDCP_LIB_WKUP_CMD_MSG_SEND_FAILED:
case HDCP_LIB_WKUP_CMD_MSG_RECV_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); HDCP_LIB_EXECUTE(clean);
break; break;
case HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS: case HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS:
@ -1825,7 +1860,7 @@ static void hdcp_lib_clean(struct hdcp_lib_handle *handle)
if (!handle) { if (!handle) {
pr_err("invalid input\n"); pr_err("invalid input\n");
return; return;
}; }
hdcp_lib_txmtr_deinit(handle); hdcp_lib_txmtr_deinit(handle);
if (!handle->legacy_app) if (!handle->legacy_app)
@ -1859,6 +1894,7 @@ static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle)
struct hdcp_rcvd_msg_rsp *rsp_buf; struct hdcp_rcvd_msg_rsp *rsp_buf;
uint32_t msglen; uint32_t msglen;
char *msg = NULL; char *msg = NULL;
char msg_name[50];
uint32_t message_id_bytes = 0; uint32_t message_id_bytes = 0;
if (!handle || !handle->qseecom_handle || if (!handle || !handle->qseecom_handle ||
@ -1907,8 +1943,11 @@ static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle)
mutex_unlock(&handle->msg_lock); mutex_unlock(&handle->msg_lock);
pr_debug("msg received: %s from sink\n", snprintf(msg_name, sizeof(msg_name), "%s: ",
hdcp_lib_message_name((int)msg[0])); 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 */ /* send the message to QSEECOM */
req_buf = (struct hdcp_rcvd_msg_req *)(handle->qseecom_handle->sbuf); req_buf = (struct hdcp_rcvd_msg_req *)(handle->qseecom_handle->sbuf);
@ -1989,13 +2028,8 @@ static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle)
handle->hdcp_timeout = rsp_buf->timeout; handle->hdcp_timeout = rsp_buf->timeout;
handle->msglen = rsp_buf->msglen; handle->msglen = rsp_buf->msglen;
if (!atomic_read(&handle->hdcp_off)) { if (!atomic_read(&handle->hdcp_off))
cdata.cmd = HDMI_HDCP_WKUP_CMD_SEND_MESSAGE; hdcp_lib_send_message(handle);
cdata.send_msg_buf = handle->listener_buf;
cdata.send_msg_len = handle->msglen;
cdata.timeout = handle->hdcp_timeout;
}
exit: exit:
kzfree(msg); kzfree(msg);
@ -2026,6 +2060,16 @@ static void hdcp_lib_topology_work(struct kthread_work *work)
return; 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); reinit_completion(&handle->topo_wait);
timeout = wait_for_completion_timeout(&handle->topo_wait, HZ * 3); timeout = wait_for_completion_timeout(&handle->topo_wait, HZ * 3);
if (!timeout) { if (!timeout) {

View file

@ -57,6 +57,10 @@ static u32 supported_modes[] = {
HDMI_VFRMT_4096x2160p60_256_135, HDMI_EVFRMT_4096x2160p24_16_9 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, static void mdss_dp_put_dt_clk_data(struct device *dev,
struct dss_module_power *module_power) 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_set_safe_to_exit_level(&dp_ctrl->ctrl_io, dp_ctrl->lane_cnt);
mdss_dp_audio_enable(&dp_ctrl->ctrl_io, true); mdss_dp_audio_enable(&dp_ctrl->ctrl_io, true);
dp_ctrl->wait_for_audio_comp = true;
return rc; return rc;
} /* dp_audio_info_setup */ } /* dp_audio_info_setup */
@ -926,17 +928,6 @@ static int dp_get_audio_edid_blk(struct platform_device *pdev,
return rc; return rc;
} /* dp_get_audio_edid_blk */ } /* 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) static int mdss_dp_init_ext_disp(struct mdss_dp_drv_pdata *dp)
{ {
int ret = 0; 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_get_audio_edid_blk;
dp->ext_audio_data.codec_ops.cable_status = dp->ext_audio_data.codec_ops.cable_status =
dp_get_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) { if (!dp->pdev->dev.of_node) {
pr_err("%s cannot find dp dev.of_node\n", __func__); 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; return 0;
} /* dp_init_panel_info */ } /* dp_init_panel_info */
static inline void mdss_dp_set_audio_switch_node( static inline void mdss_dp_ack_state(struct mdss_dp_drv_pdata *dp, int val)
struct mdss_dp_drv_pdata *dp, int val)
{ {
if (dp && dp->ext_audio_data.intf_ops.notify) if (dp && dp->ext_audio_data.intf_ops.notify)
dp->ext_audio_data.intf_ops.notify(dp->ext_pdev, dp->ext_audio_data.intf_ops.notify(dp->ext_pdev, val);
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 * Initiates training of the DP main link and checks the state of the main
* link after the training is complete. * 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; int ready = 0;
pr_debug("enter\n"); pr_debug("enter\n");
ret = mdss_dp_link_train(dp);
if (ret)
goto end;
mdss_dp_link_train(dp);
mdss_dp_wait4train(dp); mdss_dp_wait4train(dp);
ready = mdss_dp_mainlink_ready(dp, BIT(0)); ready = mdss_dp_mainlink_ready(dp, BIT(0));
pr_debug("main link %s\n", ready ? "READY" : "NOT READY"); 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) 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; struct lane_mapping ln_map;
/* wait until link training is completed */ /* wait until link training is completed */
mutex_lock(&dp_drv->train_mutex);
pr_debug("enter\n"); pr_debug("enter\n");
orientation = usbpd_get_plug_orientation(dp_drv->pd); do {
pr_debug("plug orientation = %d\n", orientation); 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); mutex_lock(&dp_drv->train_mutex);
if (ret)
goto exit;
mdss_dp_phy_share_lane_config(&dp_drv->phy_io, orientation = usbpd_get_plug_orientation(dp_drv->pd);
orientation, dp_drv->dpcd.max_lane_count); pr_debug("plug orientation = %d\n", orientation);
ret = mdss_dp_enable_mainlink_clocks(dp_drv); ret = mdss_dp_get_lane_mapping(dp_drv, orientation, &ln_map);
if (ret) if (ret)
goto exit; 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"); pr_debug("end\n");
exit: 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); mdss_dp_configure_source_params(dp_drv, &ln_map);
link_training: 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->cont_splash = 0;
dp_drv->power_on = true; 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"); pr_debug("End-\n");
exit: 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; 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( static inline bool mdss_dp_is_link_training_requested(
struct mdss_dp_drv_pdata *dp) 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->dp_initialized = false;
dp_drv->power_on = false; dp_drv->power_on = false;
mdss_dp_ack_state(dp_drv, false);
mutex_unlock(&dp_drv->train_mutex); mutex_unlock(&dp_drv->train_mutex);
pr_debug("DP off done\n"); 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); 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) struct mdss_dp_drv_pdata *dp, int val)
{ {
int ret = 0;
if (!dp) { if (!dp) {
DEV_ERR("%s: invalid input\n", __func__); DEV_ERR("%s: invalid input\n", __func__);
return; ret = -EINVAL;
goto end;
} }
if (dp && dp->ext_audio_data.intf_ops.hpd) 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); dp->ext_audio_data.type, val);
end:
return ret;
} }
static void mdss_dp_audio_codec_wait(struct mdss_dp_drv_pdata *dp) static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, bool enable)
{ {
const int audio_completion_timeout_ms = HZ * 3; return mdss_dp_send_cable_notification(dp, enable);
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 void 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");
}
static int mdss_dp_edid_init(struct mdss_panel_data *pdata) static int mdss_dp_edid_init(struct mdss_panel_data *pdata)
{ {
struct mdss_dp_drv_pdata *dp_drv = NULL; struct mdss_dp_drv_pdata *dp_drv = NULL;
@ -1614,22 +1611,18 @@ end:
return rc; 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; struct hdcp_ops *ops;
int rc = 0; int rc = 0;
if (!dp) { dp = container_of(dw, struct mdss_dp_drv_pdata, hdcp_cb_work);
pr_debug("invalid input\n");
return;
}
ops = dp->hdcp.ops; ops = dp->hdcp.ops;
mutex_lock(&dp->train_mutex); switch (dp->hdcp_status) {
switch (status) {
case HDCP_STATE_AUTHENTICATED: case HDCP_STATE_AUTHENTICATED:
pr_debug("hdcp authenticated\n"); pr_debug("hdcp authenticated\n");
dp->hdcp.auth_state = true; dp->hdcp.auth_state = true;
@ -1652,8 +1645,20 @@ static void mdss_dp_hdcp_cb(void *ptr, enum hdcp_states status)
default: default:
break; 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) 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.sec_access = true;
hdcp_init_data.client_id = HDCP_CLIENT_DP; hdcp_init_data.client_id = HDCP_CLIENT_DP;
dp_drv->hdcp.data = hdcp_1x_init(&hdcp_init_data); dp_drv->hdcp.hdcp1 = hdcp_1x_init(&hdcp_init_data);
if (IS_ERR_OR_NULL(dp_drv->hdcp.data)) { if (IS_ERR_OR_NULL(dp_drv->hdcp.hdcp1)) {
pr_err("Error hdcp init\n"); pr_err("Error hdcp init\n");
rc = -EINVAL; rc = -EINVAL;
goto error; 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"); pr_debug("HDCP 1.3 initialized\n");
dp_drv->hdcp.hdcp2 = dp_hdcp2p2_init(&hdcp_init_data); 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"); pr_debug("HDCP 2.2 initialized\n");
dp_drv->hdcp.feature_enabled = true; 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; 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, static int mdss_dp_event_handler(struct mdss_panel_data *pdata,
int event, void *arg) int event, void *arg)
{ {
@ -1913,8 +1925,10 @@ static int mdss_dp_event_handler(struct mdss_panel_data *pdata,
rc = mdss_dp_off(pdata); rc = mdss_dp_off(pdata);
break; break;
case MDSS_EVENT_BLANK: 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); dp->hdcp.ops->off(dp->hdcp.data);
}
mdss_dp_mainlink_push_idle(pdata); mdss_dp_mainlink_push_idle(pdata);
break; break;
@ -2191,6 +2205,11 @@ irqreturn_t dp_isr(int irq, void *ptr)
dp_aux_native_handler(dp, isr1); 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; 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_WORK(&dp->work, mdss_dp_event_work);
INIT_DELAYED_WORK(&dp->hdcp_cb_work, mdss_dp_hdcp_cb_work);
return 0; return 0;
} }
@ -2307,14 +2327,20 @@ static int mdss_dp_hpd_irq_notify_clients(struct mdss_dp_drv_pdata *dp)
int ret = 0; int ret = 0;
if (dp->hpd_irq_toggled) { if (dp->hpd_irq_toggled) {
mdss_dp_notify_clients(dp, false); dp->hpd_irq_clients_notified = true;
reinit_completion(&dp->irq_comp); ret = mdss_dp_notify_clients(dp, false);
ret = wait_for_completion_timeout(&dp->irq_comp,
irq_comp_timeout); if (!IS_ERR_VALUE(ret) && ret) {
if (ret <= 0) { reinit_completion(&dp->irq_comp);
pr_warn("irq_comp timed out\n"); ret = wait_for_completion_timeout(&dp->irq_comp,
return -EINVAL; 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 * 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 * recovery done on all lanes, and trigger link training if there is a
* failure/error on the link. * 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) || if (!mdss_dp_is_link_status_updated(dp) ||
(mdss_dp_aux_channel_eq_done(dp) && (mdss_dp_aux_channel_eq_done(dp) &&
mdss_dp_aux_clock_recovery_done(dp))) mdss_dp_aux_clock_recovery_done(dp)))
return; return -EINVAL;
pr_info("channel_eq_done = %d, clock_recovery_done = %d\n", pr_info("channel_eq_done = %d, clock_recovery_done = %d\n",
mdss_dp_aux_channel_eq_done(dp), mdss_dp_aux_channel_eq_done(dp),
mdss_dp_aux_clock_recovery_done(dp)); mdss_dp_aux_clock_recovery_done(dp));
mdss_dp_link_retraining(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 * 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 * the sink. In particular, it will update the requested lane count and link
* link rate, and then trigger the link retraining procedure. * 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)) if (!mdss_dp_is_link_training_requested(dp))
return; return -EINVAL;
mdss_dp_send_test_response(dp); 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; dp->link_rate = dp->test_data.test_link_rate;
mdss_dp_link_retraining(dp); 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 * (including cases when there are back to back HPD IRQ HIGH) indicating
* the start of a new link training request or sink status update. * 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; dp->hpd_irq_on = true;
mdss_dp_aux_parse_sink_status_field(dp); 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"); 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, * This function will handle the HPD IRQ state transitions from HIGH to LOW,
* indicating the end of a test request. * 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"); pr_debug("enter: HPD IRQ low\n");
dp->hpd_irq_on = false; dp->hpd_irq_on = false;
dp->hpd_irq_clients_notified = false;
mdss_dp_update_cable_status(dp, false); mdss_dp_update_cable_status(dp, false);
mdss_dp_mainlink_push_idle(&dp->panel_data); 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); mdss_dp_reset_test_data(dp);
pr_debug("done\n"); pr_debug("done\n");
return 0;
} }
static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd, 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; dp_drv->alt_mode.dp_status.hpd_irq;
if (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); pr_debug("Attention: hpd_irq high\n");
break;
}
if (dp_drv->hpd_irq_toggled if (dp_drv->power_on && dp_drv->hdcp.ops &&
&& !dp_drv->alt_mode.dp_status.hpd_irq) { dp_drv->hdcp.ops->cp_irq)
mdss_dp_process_hpd_irq_low(dp_drv); dp_drv->hdcp.ops->cp_irq(dp_drv->hdcp.data);
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) { 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 else
dp_send_events(dp_drv, EV_USBPD_DP_CONFIGURE); 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; break;
case DP_VDM_STATUS: case DP_VDM_STATUS:
dp_drv->alt_mode.dp_status.response = *vdos; 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); mdss_dp_device_register(dp_drv);
dp_drv->inited = true; dp_drv->inited = true;
dp_drv->wait_for_audio_comp = false;
dp_drv->hpd_irq_on = false; dp_drv->hpd_irq_on = false;
mdss_dp_reset_test_data(dp_drv); mdss_dp_reset_test_data(dp_drv);
init_completion(&dp_drv->audio_comp);
init_completion(&dp_drv->irq_comp); init_completion(&dp_drv->irq_comp);
pr_debug("done\n"); pr_debug("done\n");
@ -2718,13 +2787,6 @@ void *mdss_dp_get_hdcp_data(struct device *dev)
return dp_drv->hdcp.data; 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) static inline bool dp_is_stream_shareable(struct mdss_dp_drv_pdata *dp_drv)
{ {
bool ret = 0; bool ret = 0;

View file

@ -228,6 +228,18 @@ struct dp_alt_mode {
#define DP_LINK_RATE_MULTIPLIER 27000000 #define DP_LINK_RATE_MULTIPLIER 27000000
#define DP_MAX_PIXEL_CLK_KHZ 675000 #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 { struct dpcd_cap {
char major; char major;
char minor; char minor;
@ -240,6 +252,7 @@ struct dpcd_cap {
u32 flags; u32 flags;
u32 rx_port0_buf_size; u32 rx_port0_buf_size;
u32 training_read_interval;/* us */ u32 training_read_interval;/* us */
struct downstream_port_config downstream_port;
}; };
struct dpcd_link_status { struct dpcd_link_status {
@ -437,7 +450,6 @@ struct mdss_dp_drv_pdata {
struct completion train_comp; struct completion train_comp;
struct completion idle_comp; struct completion idle_comp;
struct completion video_comp; struct completion video_comp;
struct completion audio_comp;
struct completion irq_comp; struct completion irq_comp;
struct mutex aux_mutex; struct mutex aux_mutex;
struct mutex train_mutex; struct mutex train_mutex;
@ -463,13 +475,14 @@ struct mdss_dp_drv_pdata {
char delay_start; char delay_start;
u32 bpp; u32 bpp;
struct dp_statistic dp_stat; struct dp_statistic dp_stat;
bool wait_for_audio_comp;
bool hpd_irq_on; bool hpd_irq_on;
bool hpd_irq_toggled; bool hpd_irq_toggled;
bool hpd_irq_clients_notified;
/* event */ /* event */
struct workqueue_struct *workq; struct workqueue_struct *workq;
struct work_struct work; struct work_struct work;
struct delayed_work hdcp_cb_work;
u32 current_event; u32 current_event;
spinlock_t event_lock; spinlock_t event_lock;
spinlock_t lock; spinlock_t lock;
@ -480,6 +493,7 @@ struct mdss_dp_drv_pdata {
u32 vic; u32 vic;
u32 new_vic; u32 new_vic;
int fb_node; int fb_node;
int hdcp_status;
struct dpcd_test_request test_data; struct dpcd_test_request test_data;
struct dpcd_sink_count sink_count; struct dpcd_sink_count sink_count;

View file

@ -801,6 +801,8 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
cap = &ep->dpcd; cap = &ep->dpcd;
bp = rp->data; bp = rp->data;
memset(cap, 0, sizeof(*cap));
data = *bp++; /* byte 0 */ data = *bp++; /* byte 0 */
cap->major = (data >> 4) & 0x0f; cap->major = (data >> 4) & 0x0f;
cap->minor = data & 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)) if (data & BIT(7))
cap->enhanced_frame++; cap->enhanced_frame++;
if (data & 0x40) if (data & 0x40) {
cap->flags |= DPCD_TPS3; cap->flags |= DPCD_TPS3;
pr_debug("pattern 3 supported\n");
} else {
pr_debug("pattern 3 not supported\n");
}
data &= 0x0f; data &= 0x0f;
cap->max_lane_count = data; cap->max_lane_count = data;
if (--rlen <= 0) if (--rlen <= 0)
@ -846,11 +853,36 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep,
if (--rlen <= 0) if (--rlen <= 0)
return; return;
bp += 3; /* skip 5, 6 and 7 */ data = *bp++; /* Byte 5: DOWN_STREAM_PORT_PRESENT */
rlen -= 3; 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) if (rlen <= 0)
return; 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 */ data = *bp++; /* byte 8 */
if (data & BIT(1)) { if (data & BIT(1)) {
cap->flags |= DPCD_PORT_0_EDID_PRESENTED; 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 */ data = *bp++; /* byte 9 */
cap->rx_port0_buf_size = (data + 1) * 32; 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) if (--rlen <= 0)
return; return;
@ -1431,6 +1463,7 @@ static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep)
int tries, old_v_level; int tries, old_v_level;
int ret = 0; int ret = 0;
int usleep_time; int usleep_time;
int const maximum_retries = 5;
pr_debug("Entered++"); 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) { if (old_v_level == ep->v_level) {
tries++; tries++;
if (tries >= 5) { if (tries >= maximum_retries) {
ret = -1; ret = -1;
break; /* quit */ break; /* quit */
} }
@ -1480,6 +1513,7 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep)
int ret = 0; int ret = 0;
int usleep_time; int usleep_time;
char pattern; char pattern;
int const maximum_retries = 5;
pr_debug("Entered++"); pr_debug("Entered++");
@ -1505,7 +1539,7 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep)
} }
tries++; tries++;
if (tries > 4) { if (tries > maximum_retries) {
ret = -1; ret = -1;
break; 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) static int dp_link_rate_down_shift(struct mdss_dp_drv_pdata *ep)
{ {
u32 prate, lrate; int ret = 0;
int rate, lane, max_lane;
int changed = 0;
rate = ep->link_rate; if (!ep)
lane = ep->lane_cnt; return -EINVAL;
max_lane = ep->dpcd.max_lane_count;
prate = ep->pixel_rate; switch (ep->link_rate) {
prate /= 1000; /* avoid using 64 biits */ case DP_LINK_RATE_540:
prate *= ep->bpp; ep->link_rate = DP_LINK_RATE_270;
prate /= 8; /* byte */ 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) { pr_debug("new rate=%d\n", ep->link_rate);
rate -= 4; /* reduce rate */
changed++;
}
if (changed) { return ret;
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;
} }
int mdss_dp_aux_set_sink_power_state(struct mdss_dp_drv_pdata *ep, char state) 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); mdss_dp_aux_set_sink_power_state(dp, SINK_POWER_ON);
train_start:
dp->v_level = 0; /* start from default level */ dp->v_level = 0; /* start from default level */
dp->p_level = 0; dp->p_level = 0;
mdss_dp_config_ctrl(dp); mdss_dp_config_ctrl(dp);
@ -1605,11 +1618,12 @@ train_start:
ret = dp_start_link_train_1(dp); ret = dp_start_link_train_1(dp);
if (ret < 0) { if (ret < 0) {
if (dp_link_rate_down_shift(dp) == 0) { if (!dp_link_rate_down_shift(dp)) {
goto train_start; pr_debug("retry with lower rate\n");
return -EAGAIN;
} else { } else {
pr_err("Training 1 failed\n"); pr_err("Training 1 failed\n");
ret = -1; ret = -EINVAL;
goto clear; goto clear;
} }
} }
@ -1618,21 +1632,21 @@ train_start:
ret = dp_start_link_train_2(dp); ret = dp_start_link_train_2(dp);
if (ret < 0) { if (ret < 0) {
if (dp_link_rate_down_shift(dp) == 0) { if (!dp_link_rate_down_shift(dp)) {
goto train_start; pr_debug("retry with lower rate\n");
return -EAGAIN;
} else { } else {
pr_err("Training 2 failed\n"); pr_err("Training 2 failed\n");
ret = -1; ret = -EINVAL;
goto clear; goto clear;
} }
} }
pr_debug("Training 2 completed successfully\n"); pr_debug("Training 2 completed successfully\n");
clear: clear:
dp_clear_training_pattern(dp); dp_clear_training_pattern(dp);
if (ret != -1) { if (ret != -EINVAL) {
mdss_dp_setup_tr_unit(&dp->ctrl_io, dp->link_rate, mdss_dp_setup_tr_unit(&dp->ctrl_io, dp->link_rate,
dp->lane_cnt, dp->vic); dp->lane_cnt, dp->vic);
mdss_dp_state_ctrl(&dp->ctrl_io, ST_SEND_VIDEO); mdss_dp_state_ctrl(&dp->ctrl_io, ST_SEND_VIDEO);

View file

@ -53,7 +53,6 @@ struct dp_hdcp2p2_ctrl {
struct kthread_work send_msg; struct kthread_work send_msg;
struct kthread_work recv_msg; struct kthread_work recv_msg;
struct kthread_work link; struct kthread_work link;
struct kthread_work poll;
char *msg_buf; char *msg_buf;
uint32_t send_msg_len; /* length of all parameters in msg */ uint32_t send_msg_len; /* length of all parameters in msg */
uint32_t timeout; uint32_t timeout;
@ -67,26 +66,6 @@ struct dp_hdcp2p2_ctrl {
bool polling; 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) static inline bool dp_hdcp2p2_is_valid_state(struct dp_hdcp2p2_ctrl *ctrl)
{ {
if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_AUTHENTICATE) 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); queue_kthread_work(&ctrl->worker, &ctrl->status);
break; break;
case HDMI_HDCP_WKUP_CMD_LINK_POLL: case HDMI_HDCP_WKUP_CMD_LINK_POLL:
queue_kthread_work(&ctrl->worker, &ctrl->poll); ctrl->polling = true;
break; break;
case HDMI_HDCP_WKUP_CMD_AUTHENTICATE: case HDMI_HDCP_WKUP_CMD_AUTHENTICATE:
queue_kthread_work(&ctrl->worker, &ctrl->auth); queue_kthread_work(&ctrl->worker, &ctrl->auth);
@ -203,10 +182,11 @@ static int dp_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data)
} }
exit: exit:
mutex_unlock(&ctrl->wakeup_mutex); mutex_unlock(&ctrl->wakeup_mutex);
return 0; 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) struct hdcp_lib_wakeup_data *data)
{ {
int rc = 0; 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", pr_err("error sending %s to lib\n",
hdcp_lib_cmd_to_str(data->cmd)); hdcp_lib_cmd_to_str(data->cmd));
} }
return rc;
} }
static void dp_hdcp2p2_reset(struct dp_hdcp2p2_ctrl *ctrl) 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; return;
} }
atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL);
/* notify DP about HDCP failure */ /* notify DP about HDCP failure */
ctrl->init_data.notify_status(ctrl->init_data.cb_data, ctrl->init_data.notify_status(ctrl->init_data.cb_data,
HDCP_STATE_AUTH_FAIL); 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, static int dp_hdcp2p2_aux_read_message(struct dp_hdcp2p2_ctrl *ctrl,
u8 *buf, int size, int offset, u32 timeout) u8 *buf, int size, int offset, u32 timeout)
{ {
int rc, max_size = 16, read_size, len = size; int rc, max_size = 16, read_size;
u8 *buf_start = buf;
if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) { if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
pr_err("hdcp is off\n"); 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; size -= read_size;
} while (size > 0); } while (size > 0);
print_hex_dump(KERN_DEBUG, "hdcp2p2: ", DUMP_PREFIX_NONE,
16, 1, buf_start, len, false);
return rc; return rc;
} }
@ -452,12 +425,9 @@ end:
static void dp_hdcp2p2_send_msg_work(struct kthread_work *work) static void dp_hdcp2p2_send_msg_work(struct kthread_work *work)
{ {
int rc = 0; int rc = 0;
int i;
int sent_bytes = 0;
struct dp_hdcp2p2_ctrl *ctrl = container_of(work, struct dp_hdcp2p2_ctrl *ctrl = container_of(work,
struct dp_hdcp2p2_ctrl, send_msg); struct dp_hdcp2p2_ctrl, send_msg);
struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID}; struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
char *buf = NULL;
if (!ctrl) { if (!ctrl) {
pr_err("invalid input\n"); 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); mutex_lock(&ctrl->msg_lock);
/* Loop through number of parameters in the messages. */ rc = dp_hdcp2p2_aux_write_message(ctrl, ctrl->msg_buf,
for (i = 0; i < ctrl->num_messages; i++) { ctrl->send_msg_len, ctrl->msg_part->offset,
buf = ctrl->msg_buf + sent_bytes; ctrl->timeout);
if (rc) {
/* Forward the message to the sink */ pr_err("Error sending msg to sink %d\n", rc);
rc = dp_hdcp2p2_aux_write_message(ctrl, buf, mutex_unlock(&ctrl->msg_lock);
(size_t)ctrl->msg_part[i].length, goto exit;
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;
} }
cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_SEND_SUCCESS; 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) 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; char *recvd_msg_buf = NULL;
struct hdcp_lib_wakeup_data cdata = { HDCP_LIB_WKUP_CMD_INVALID }; struct hdcp_lib_wakeup_data cdata = { HDCP_LIB_WKUP_CMD_INVALID };
int bytes_read = 0;
cdata.context = ctrl->lib_ctx; cdata.context = ctrl->lib_ctx;
@ -518,17 +480,12 @@ static int dp_hdcp2p2_get_msg_from_sink(struct dp_hdcp2p2_ctrl *ctrl)
goto exit; goto exit;
} }
for (i = 0; i < ctrl->num_messages; i++) { rc = dp_hdcp2p2_aux_read_message(ctrl, recvd_msg_buf,
rc = dp_hdcp2p2_aux_read_message( ctrl->send_msg_len, ctrl->msg_part->offset,
ctrl, recvd_msg_buf + bytes_read, ctrl->timeout);
ctrl->msg_part[i].length, if (rc) {
ctrl->msg_part[i].offset, pr_err("error reading message %d\n", rc);
ctrl->timeout); goto exit;
if (rc) {
pr_err("error reading message %d\n", rc);
goto exit;
}
bytes_read += ctrl->msg_part[i].length;
} }
cdata.recvd_msg_buf = recvd_msg_buf; cdata.recvd_msg_buf = recvd_msg_buf;
@ -550,7 +507,6 @@ exit:
static void dp_hdcp2p2_recv_msg_work(struct kthread_work *work) 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 hdcp_lib_wakeup_data cdata = { HDCP_LIB_WKUP_CMD_INVALID };
struct dp_hdcp2p2_ctrl *ctrl = container_of(work, struct dp_hdcp2p2_ctrl *ctrl = container_of(work,
struct dp_hdcp2p2_ctrl, recv_msg); 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) { if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
pr_err("hdcp is off\n"); pr_err("hdcp is off\n");
goto exit; return;
} }
if (ctrl->sink_rx_status & ctrl->abort_mask) { if (ctrl->rx_status) {
pr_err("reauth or Link fail triggered by sink\n"); if (!ctrl->cp_irq_done) {
pr_debug("waiting for CP_IRQ\n");
ctrl->polling = true;
return;
}
ctrl->sink_rx_status = 0; if (ctrl->rx_status & ctrl->sink_rx_status) {
rc = -ENOLINK; ctrl->cp_irq_done = false;
cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; ctrl->sink_rx_status = 0;
ctrl->rx_status = 0;
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;
} }
dp_hdcp2p2_get_msg_from_sink(ctrl); 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) 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) { if (rc) {
pr_err("failed to read rx status\n"); 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; goto exit;
} }
if (ctrl->sink_rx_status & ctrl->abort_mask) { 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->sink_rx_status = 0;
ctrl->rx_status = 0; ctrl->rx_status = 0;
rc = -ENOLINK; 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; goto exit;
} }
/* if polling, get message from sink else let polling start */
if (ctrl->polling && (ctrl->sink_rx_status & ctrl->rx_status)) { if (ctrl->polling && (ctrl->sink_rx_status & ctrl->rx_status)) {
ctrl->sink_rx_status = 0; ctrl->sink_rx_status = 0;
ctrl->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; ctrl->polling = false;
} else { } else {
ctrl->cp_irq_done = true; ctrl->cp_irq_done = true;
} }
exit: exit:
dp_hdcp2p2_wakeup_lib(ctrl, &cdata); if (rc)
dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
if (rc) {
dp_hdcp2p2_auth_failed(ctrl);
return;
}
} }
static void dp_hdcp2p2_auth_work(struct kthread_work *work) 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 hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
struct dp_hdcp2p2_ctrl *ctrl = container_of(work, struct dp_hdcp2p2_ctrl *ctrl = container_of(work,
struct dp_hdcp2p2_ctrl, auth); struct dp_hdcp2p2_ctrl, auth);
@ -694,12 +631,10 @@ static void dp_hdcp2p2_auth_work(struct kthread_work *work)
else else
cdata.cmd = HDCP_LIB_WKUP_CMD_STOP; cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
rc = dp_hdcp2p2_wakeup_lib(ctrl, &cdata); dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
if (rc)
dp_hdcp2p2_auth_failed(ctrl);
} }
static int dp_hdcp2p2_isr(void *input) static int dp_hdcp2p2_cp_irq(void *input)
{ {
struct dp_hdcp2p2_ctrl *ctrl = 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, .authenticate = dp_hdcp2p2_authenticate,
.feature_supported = dp_hdcp2p2_feature_supported, .feature_supported = dp_hdcp2p2_feature_supported,
.off = dp_hdcp2p2_off, .off = dp_hdcp2p2_off,
.isr = dp_hdcp2p2_isr .cp_irq = dp_hdcp2p2_cp_irq,
}; };
static struct hdcp_client_ops client_ops = { 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->recv_msg, dp_hdcp2p2_recv_msg_work);
init_kthread_work(&ctrl->status, dp_hdcp2p2_auth_status_work); init_kthread_work(&ctrl->status, dp_hdcp2p2_auth_status_work);
init_kthread_work(&ctrl->link, dp_hdcp2p2_link_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->thread = kthread_run(kthread_worker_fn,
&ctrl->worker, "dp_hdcp2p2"); &ctrl->worker, "dp_hdcp2p2");

View file

@ -55,6 +55,7 @@ struct hdcp_init_data {
struct hdcp_ops { struct hdcp_ops {
int (*isr)(void *ptr); int (*isr)(void *ptr);
int (*cp_irq)(void *ptr);
int (*reauthenticate)(void *input); int (*reauthenticate)(void *input);
int (*authenticate)(void *hdcp_ctrl); int (*authenticate)(void *hdcp_ctrl);
bool (*feature_supported)(void *input); bool (*feature_supported)(void *input);

View file

@ -153,6 +153,7 @@ struct hdcp_reg_set {
u32 sec_data12; u32 sec_data12;
u32 reset; u32 reset;
u32 reset_bit;
}; };
#define HDCP_REG_SET_CLIENT_HDMI \ #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_DATA10, \
HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA11, \ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA11, \
HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA12, \ HDCP_SEC_TZ_HV_HLOS_HDCP_RCVPORT_DATA12, \
HDMI_HDCP_RESET} HDMI_HDCP_RESET, BIT(0)}
#define HDCP_REG_SET_CLIENT_DP \ #define HDCP_REG_SET_CLIENT_DP \
{DP_HDCP_STATUS, 16, 14, 13, DP_HDCP_CTRL, \ {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_DATA9, \
HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA10, \ 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_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 \ #define HDCP_HDMI_SINK_ADDR_MAP \
{{"bcaps", 0x40, 1}, {"bksv", 0x00, 5}, {"r0'", 0x08, 2}, \ {{"bcaps", 0x40, 1}, {"bksv", 0x00, 5}, {"r0'", 0x08, 2}, \
@ -1295,6 +1297,9 @@ static void hdcp_1x_int_work(struct work_struct *work)
return; return;
} }
if (hdcp_ctrl->hdcp_state == HDCP_STATE_AUTHENTICATED)
hdcp1_set_enc(false);
mutex_lock(hdcp_ctrl->init_data.mutex); mutex_lock(hdcp_ctrl->init_data.mutex);
hdcp_ctrl->hdcp_state = HDCP_STATE_AUTH_FAIL; hdcp_ctrl->hdcp_state = HDCP_STATE_AUTH_FAIL;
mutex_unlock(hdcp_ctrl->init_data.mutex); mutex_unlock(hdcp_ctrl->init_data.mutex);
@ -1383,6 +1388,8 @@ error:
hdcp_ctrl->init_data.cb_data, hdcp_ctrl->init_data.cb_data,
hdcp_ctrl->hdcp_state); hdcp_ctrl->hdcp_state);
} }
hdcp1_set_enc(true);
} else { } else {
DEV_DBG("%s: %s: HDCP state changed during authentication\n", DEV_DBG("%s: %s: HDCP state changed during authentication\n",
__func__, HDCP_STATE_NAME); __func__, HDCP_STATE_NAME);
@ -1431,7 +1438,7 @@ int hdcp_1x_reauthenticate(void *input)
struct hdcp_reg_set *reg_set; struct hdcp_reg_set *reg_set;
struct hdcp_int_set *isr; struct hdcp_int_set *isr;
u32 hdmi_hw_version; u32 hdmi_hw_version;
u32 ret = 0; u32 ret = 0, reg;
if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) { if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) {
DEV_ERR("%s: invalid input\n", __func__); DEV_ERR("%s: invalid input\n", __func__);
@ -1462,15 +1469,17 @@ int hdcp_1x_reauthenticate(void *input)
/* Disable HDCP interrupts */ /* Disable HDCP interrupts */
DSS_REG_W(io, isr->int_reg, DSS_REG_R(io, isr->int_reg) & ~HDCP_INT_EN); DSS_REG_W(io, isr->int_reg, DSS_REG_R(io, isr->int_reg) & ~HDCP_INT_EN);
if (reg_set->reset) reg = DSS_REG_R(io, reg_set->reset);
DSS_REG_W(io, reg_set->reset, BIT(0)); DSS_REG_W(io, reg_set->reset, reg | reg_set->reset_bit);
/* Disable encryption and disable the HDCP block */ /* Disable encryption and disable the HDCP block */
DSS_REG_W(io, reg_set->ctrl, 0); 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)) if (!hdcp_1x_load_keys(input))
queue_delayed_work(hdcp_ctrl->init_data.workq, queue_delayed_work(hdcp_ctrl->init_data.workq,
&hdcp_ctrl->hdcp_auth_work, HZ/2); &hdcp_ctrl->hdcp_auth_work, HZ);
else else
queue_work(hdcp_ctrl->init_data.workq, queue_work(hdcp_ctrl->init_data.workq,
&hdcp_ctrl->hdcp_int_work); &hdcp_ctrl->hdcp_int_work);
@ -1485,6 +1494,7 @@ void hdcp_1x_off(void *input)
struct hdcp_reg_set *reg_set; struct hdcp_reg_set *reg_set;
struct hdcp_int_set *isr; struct hdcp_int_set *isr;
int rc = 0; int rc = 0;
u32 reg;
if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) { if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) {
DEV_ERR("%s: invalid input\n", __func__); DEV_ERR("%s: invalid input\n", __func__);
@ -1501,6 +1511,9 @@ void hdcp_1x_off(void *input)
return; return;
} }
if (hdcp_ctrl->hdcp_state == HDCP_STATE_AUTHENTICATED)
hdcp1_set_enc(false);
/* /*
* Disable HDCP interrupts. * Disable HDCP interrupts.
* Also, need to set the state to inactive here so that any ongoing * 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__, DEV_DBG("%s: %s: Deleted hdcp int work\n", __func__,
HDCP_STATE_NAME); 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 */ /* Disable encryption and disable the HDCP block */
DSS_REG_W(io, reg_set->ctrl, 0); 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); DEV_DBG("%s: %s: HDCP: Off\n", __func__, HDCP_STATE_NAME);
} /* hdcp_1x_off */ } /* hdcp_1x_off */

View file

@ -38,15 +38,18 @@ struct msm_ext_disp {
struct switch_dev hdmi_sdev; struct switch_dev hdmi_sdev;
struct switch_dev audio_sdev; struct switch_dev audio_sdev;
bool ack_enabled; bool ack_enabled;
atomic_t ack_pending; bool audio_session_on;
struct list_head display_list; struct list_head display_list;
struct mutex lock; struct mutex lock;
struct completion hpd_comp;
}; };
static int msm_ext_disp_get_intf_data(struct msm_ext_disp *ext_disp, static int msm_ext_disp_get_intf_data(struct msm_ext_disp *ext_disp,
enum msm_ext_disp_type type, enum msm_ext_disp_type type,
struct msm_ext_disp_init_data **data); 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_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) 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) enum msm_ext_disp_cable_state new_state)
{ {
int state = EXT_DISPLAY_CABLE_STATE_MAX; int state = EXT_DISPLAY_CABLE_STATE_MAX;
if (!ext_disp) { if (!ext_disp) {
pr_err("Invalid params\n"); pr_err("Invalid params\n");
return; return -EINVAL;
} }
state = ext_disp->hdmi_sdev.state; 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 ? ext_disp->hdmi_sdev.state == state ?
"is same" : "switched to", "is same" : "switched to",
ext_disp->hdmi_sdev.state); 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, 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) enum msm_ext_disp_cable_state state)
{ {
int ret = 0; int ret = 0;
struct msm_ext_disp_init_data *data = NULL;
struct msm_ext_disp *ext_disp = NULL; struct msm_ext_disp *ext_disp = NULL;
if (!pdev) { if (!pdev) {
@ -379,27 +452,28 @@ static int msm_ext_disp_hpd(struct platform_device *pdev,
goto end; goto end;
} }
ret = msm_ext_disp_get_intf_data(ext_disp, type, &data);
if (ret)
goto end;
if (state == EXT_DISPLAY_CABLE_CONNECT) { if (state == EXT_DISPLAY_CABLE_CONNECT) {
ext_disp->current_disp = data->type; ext_disp->current_disp = type;
} else if ((state == EXT_DISPLAY_CABLE_DISCONNECT) &&
!ext_disp->ack_enabled) { ret = msm_ext_disp_process_display(ext_disp, state);
if (ext_disp->ops) { if (ret)
ext_disp->ops->audio_info_setup = NULL; goto end;
ext_disp->ops->get_audio_edid_blk = NULL;
ext_disp->ops->cable_status = NULL; msm_ext_disp_update_audio_ops(ext_disp, state);
ext_disp->ops->get_intf_id = NULL; if (ret)
ext_disp->ops->teardown_done = NULL; 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; 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, pr_debug("Hpd (%d) for display (%s)\n", state,
msm_ext_disp_name(type)); msm_ext_disp_name(type));
@ -427,23 +501,18 @@ static int msm_ext_disp_get_intf_data_helper(struct platform_device *pdev,
goto end; goto end;
} }
mutex_lock(&ext_disp->lock);
if (ext_disp->current_disp == EXT_DISPLAY_TYPE_MAX) { if (ext_disp->current_disp == EXT_DISPLAY_TYPE_MAX) {
ret = -EINVAL; ret = -EINVAL;
pr_err("No display connected\n"); pr_err("No display connected\n");
goto error; goto end;
} }
ret = msm_ext_disp_get_intf_data(ext_disp, ext_disp->current_disp, ret = msm_ext_disp_get_intf_data(ext_disp, ext_disp->current_disp,
data); data);
if (ret)
goto error;
error:
mutex_unlock(&ext_disp->lock);
end: end:
return ret; return ret;
} }
static int msm_ext_disp_cable_status(struct platform_device *pdev, u32 vote) static int msm_ext_disp_cable_status(struct platform_device *pdev, u32 vote)
{ {
int ret = 0; int ret = 0;
@ -480,11 +549,21 @@ static int msm_ext_disp_audio_info_setup(struct platform_device *pdev,
{ {
int ret = 0; int ret = 0;
struct msm_ext_disp_init_data *data = NULL; 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); ret = msm_ext_disp_get_intf_data_helper(pdev, &data);
if (ret || !data) if (ret || !data)
goto end; 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); ret = data->codec_ops.audio_info_setup(data->pdev, params);
end: end:
@ -495,6 +574,7 @@ static void msm_ext_disp_teardown_done(struct platform_device *pdev)
{ {
int ret = 0; int ret = 0;
struct msm_ext_disp_init_data *data = NULL; 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); ret = msm_ext_disp_get_intf_data_helper(pdev, &data);
if (ret || !data) { if (ret || !data) {
@ -502,7 +582,21 @@ static void msm_ext_disp_teardown_done(struct platform_device *pdev)
return; 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) 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; goto end;
} }
mutex_lock(&ext_disp->lock);
ret = ext_disp->current_disp; ret = ext_disp->current_disp;
mutex_unlock(&ext_disp->lock);
end: end:
return ret; return ret;
} }
static int msm_ext_disp_notify(struct platform_device *pdev, static int msm_ext_disp_update_audio_ops(struct msm_ext_disp *ext_disp,
enum msm_ext_disp_cable_state new_state) 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 ret = 0;
int state = 0;
bool switched;
struct msm_ext_disp_init_data *data = NULL;
struct msm_ext_disp *ext_disp = NULL; struct msm_ext_disp *ext_disp = NULL;
if (!pdev) { if (!pdev) {
pr_err("Invalid platform device\n"); pr_err("Invalid platform device\n");
return -EINVAL; ret = -EINVAL;
goto end;
} }
ext_disp = platform_get_drvdata(pdev); ext_disp = platform_get_drvdata(pdev);
if (!ext_disp) { if (!ext_disp) {
pr_err("Invalid drvdata\n"); pr_err("Invalid drvdata\n");
return -EINVAL; ret = -EINVAL;
goto end;
} }
mutex_lock(&ext_disp->lock);
if (state < EXT_DISPLAY_CABLE_DISCONNECT || 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); pr_err("Invalid state (%d)\n", state);
ret = -EINVAL; ret = -EINVAL;
goto end; goto end;
} }
state = ext_disp->audio_sdev.state; pr_debug("%s notifying hpd (%d)\n",
if (state == new_state) msm_ext_disp_name(ext_disp->current_disp), 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");
complete_all(&ext_disp->hpd_comp);
end: end:
mutex_unlock(&ext_disp->lock);
return ret; return ret;
} }
static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack) static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack)
{ {
u32 ack_hpd; u32 ack_hpd;
u32 hpd;
int ret = 0; int ret = 0;
struct msm_ext_disp *ext_disp = NULL; 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; return -EINVAL;
} }
mutex_lock(&ext_disp->lock);
hpd = ext_disp->current_disp != EXT_DISPLAY_TYPE_MAX;
if (ack & AUDIO_ACK_SET_ENABLE) { if (ack & AUDIO_ACK_SET_ENABLE) {
ext_disp->ack_enabled = ack & AUDIO_ACK_ENABLE ? ext_disp->ack_enabled = ack & AUDIO_ACK_ENABLE ?
true : false; true : false;
@ -640,44 +715,14 @@ static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack)
if (!ext_disp->ack_enabled) if (!ext_disp->ack_enabled)
goto end; goto end;
atomic_set(&ext_disp->ack_pending, 0);
ack_hpd = ack & AUDIO_ACK_CONNECT; ack_hpd = ack & AUDIO_ACK_CONNECT;
pr_debug("acknowledging %s\n", pr_debug("%s acknowledging audio (%d)\n",
ack_hpd ? "connect" : "disconnect"); msm_ext_disp_name(ext_disp->current_disp), ack_hpd);
/**
* 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;
}
if (!ext_disp->audio_session_on)
complete_all(&ext_disp->hpd_comp);
end: end:
mutex_unlock(&ext_disp->lock);
return ret; return ret;
} }
@ -850,6 +895,7 @@ static int msm_ext_disp_probe(struct platform_device *pdev)
mutex_init(&ext_disp->lock); mutex_init(&ext_disp->lock);
INIT_LIST_HEAD(&ext_disp->display_list); INIT_LIST_HEAD(&ext_disp->display_list);
init_completion(&ext_disp->hpd_comp);
ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX; ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX;
return ret; return ret;

View file

@ -26,6 +26,7 @@ enum hdcp_lib_wakeup_cmd {
HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED, HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED,
HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT, HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT,
HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE, HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE,
HDCP_LIB_WKUP_CMD_LINK_FAILED,
}; };
enum hdmi_hdcp_wakeup_cmd { enum hdmi_hdcp_wakeup_cmd {
@ -47,6 +48,7 @@ struct hdcp_lib_wakeup_data {
}; };
struct hdcp_msg_part { struct hdcp_msg_part {
char *name;
uint32_t offset; uint32_t offset;
uint32_t length; uint32_t length;
}; };
@ -106,6 +108,8 @@ static inline char *hdcp_lib_cmd_to_str(uint32_t cmd)
return "HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT"; return "HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT";
case HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE: case HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE:
return "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: default:
return "???"; return "???";
} }