Merge "msm: ext_display: update hpd and notify logic"
This commit is contained in:
commit
9454b9f32d
9 changed files with 598 additions and 463 deletions
|
@ -160,43 +160,46 @@
|
|||
|
||||
static const struct hdcp_msg_data hdcp_msg_lookup[HDCP2P2_MAX_MESSAGES] = {
|
||||
[AKE_INIT_MESSAGE_ID] = { 2,
|
||||
{ {0x69000, 8}, {0x69008, 3} },
|
||||
{ {"rtx", 0x69000, 8}, {"TxCaps", 0x69008, 3} },
|
||||
0 },
|
||||
[AKE_SEND_CERT_MESSAGE_ID] = { 3,
|
||||
{ {0x6900B, 522}, {0x69215, 8}, {0x6921D, 3} },
|
||||
{ {"cert-rx", 0x6900B, 522}, {"rrx", 0x69215, 8},
|
||||
{"RxCaps", 0x6921D, 3} },
|
||||
0 },
|
||||
[AKE_NO_STORED_KM_MESSAGE_ID] = { 1,
|
||||
{ {0x69220, 128} },
|
||||
{ {"Ekpub_km", 0x69220, 128} },
|
||||
0 },
|
||||
[AKE_STORED_KM_MESSAGE_ID] = { 2,
|
||||
{ {0x692A0, 16}, {0x692B0, 16} },
|
||||
{ {"Ekh_km", 0x692A0, 16}, {"m", 0x692B0, 16} },
|
||||
0 },
|
||||
[AKE_SEND_H_PRIME_MESSAGE_ID] = { 1,
|
||||
{ {0x692C0, 32} },
|
||||
{ {"H'", 0x692C0, 32} },
|
||||
(1 << 1) },
|
||||
[AKE_SEND_PAIRING_INFO_MESSAGE_ID] = { 1,
|
||||
{ {0x692E0, 16} },
|
||||
{ {"Ekh_km", 0x692E0, 16} },
|
||||
(1 << 2) },
|
||||
[LC_INIT_MESSAGE_ID] = { 1,
|
||||
{ {0x692F0, 8} },
|
||||
{ {"rn", 0x692F0, 8} },
|
||||
0 },
|
||||
[LC_SEND_L_PRIME_MESSAGE_ID] = { 1,
|
||||
{ {0x692F8, 32} },
|
||||
{ {"L'", 0x692F8, 32} },
|
||||
0 },
|
||||
[SKE_SEND_EKS_MESSAGE_ID] = { 2,
|
||||
{ {0x69318, 16}, {0x69328, 8} },
|
||||
{ {"Edkey_ks", 0x69318, 16}, {"riv", 0x69328, 8} },
|
||||
0 },
|
||||
[REPEATER_AUTH_SEND_RECEIVERID_LIST_MESSAGE_ID] = { 4,
|
||||
{ {0x69330, 2}, {0x69332, 3}, {0x69335, 16}, {0x69345, 155} },
|
||||
{ {"RxInfo", 0x69330, 2}, {"seq_num_V", 0x69332, 3},
|
||||
{"V'", 0x69335, 16}, {"ridlist", 0x69345, 155} },
|
||||
(1 << 0) },
|
||||
[REPEATER_AUTH_SEND_ACK_MESSAGE_ID] = { 1,
|
||||
{ {0x693E0, 16} },
|
||||
{ {"V", 0x693E0, 16} },
|
||||
0 },
|
||||
[REPEATER_AUTH_STREAM_MANAGE_MESSAGE_ID] = { 3,
|
||||
{ {0x693F0, 3}, {0x693F3, 2}, {0x693F5, 126} },
|
||||
{ {"seq_num_M", 0x693F0, 3}, {"k", 0x693F3, 2},
|
||||
{"streamID_Type", 0x693F5, 126} },
|
||||
0 },
|
||||
[REPEATER_AUTH_STREAM_READY_MESSAGE_ID] = { 1,
|
||||
{ {0x69473, 32} },
|
||||
{ {"M'", 0x69473, 32} },
|
||||
0 }
|
||||
};
|
||||
|
||||
|
@ -616,25 +619,48 @@ 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)
|
||||
{
|
||||
int rc = 0;
|
||||
int rc = 0, i;
|
||||
|
||||
if (!handle || !handle->client_ops || !handle->client_ops->wakeup ||
|
||||
!data || (data->cmd == HDMI_HDCP_WKUP_CMD_INVALID))
|
||||
return;
|
||||
|
||||
if (handle && handle->client_ops && handle->client_ops->wakeup &&
|
||||
data && (data->cmd != HDMI_HDCP_WKUP_CMD_INVALID)) {
|
||||
data->abort_mask = REAUTH_REQ | LINK_INTEGRITY_FAILURE;
|
||||
|
||||
if (data->cmd == HDMI_HDCP_WKUP_CMD_SEND_MESSAGE ||
|
||||
data->cmd == HDMI_HDCP_WKUP_CMD_RECV_MESSAGE ||
|
||||
data->cmd == HDMI_HDCP_WKUP_CMD_LINK_POLL) {
|
||||
handle->last_msg =
|
||||
hdcp_lib_get_next_message(handle, data);
|
||||
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)
|
||||
data->message_data =
|
||||
&hdcp_msg_lookup[handle->last_msg];
|
||||
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 {
|
||||
pr_debug("lib->client: %s\n",
|
||||
hdmi_hdcp_cmd_to_str(data->cmd));
|
||||
}
|
||||
|
||||
rc = handle->client_ops->wakeup(data);
|
||||
|
@ -642,10 +668,10 @@ static inline void hdcp_lib_wakeup_client(struct hdcp_lib_handle *handle,
|
|||
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)
|
||||
{
|
||||
char msg_name[50];
|
||||
struct hdmi_hdcp_wakeup_data cdata = {
|
||||
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.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);
|
||||
}
|
||||
|
||||
|
@ -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 (!list_empty(&handle->worker.work_list)) {
|
||||
pr_debug("error: queue not empty\n");
|
||||
rc = -EBUSY;
|
||||
goto exit;
|
||||
}
|
||||
|
@ -1543,9 +1577,8 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data)
|
|||
handle->wakeup_cmd = data->cmd;
|
||||
handle->timeout_left = data->timeout;
|
||||
|
||||
pr_debug("%s, timeout left: %dms, tethered %d\n",
|
||||
hdcp_lib_cmd_to_str(handle->wakeup_cmd),
|
||||
handle->timeout_left, handle->tethered);
|
||||
pr_debug("client->lib: %s\n",
|
||||
hdcp_lib_cmd_to_str(handle->wakeup_cmd));
|
||||
|
||||
rc = hdcp_lib_check_valid_state(handle);
|
||||
if (rc)
|
||||
|
@ -1599,6 +1632,8 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data)
|
|||
break;
|
||||
case HDCP_LIB_WKUP_CMD_MSG_SEND_FAILED:
|
||||
case HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED:
|
||||
case HDCP_LIB_WKUP_CMD_LINK_FAILED:
|
||||
handle->hdcp_state |= HDCP_STATE_ERROR;
|
||||
HDCP_LIB_EXECUTE(clean);
|
||||
break;
|
||||
case HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS:
|
||||
|
@ -1825,7 +1860,7 @@ static void hdcp_lib_clean(struct hdcp_lib_handle *handle)
|
|||
if (!handle) {
|
||||
pr_err("invalid input\n");
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
hdcp_lib_txmtr_deinit(handle);
|
||||
if (!handle->legacy_app)
|
||||
|
@ -1859,6 +1894,7 @@ static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle)
|
|||
struct hdcp_rcvd_msg_rsp *rsp_buf;
|
||||
uint32_t msglen;
|
||||
char *msg = NULL;
|
||||
char msg_name[50];
|
||||
uint32_t message_id_bytes = 0;
|
||||
|
||||
if (!handle || !handle->qseecom_handle ||
|
||||
|
@ -1907,9 +1943,12 @@ static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle)
|
|||
|
||||
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]));
|
||||
|
||||
print_hex_dump(KERN_DEBUG, msg_name,
|
||||
DUMP_PREFIX_NONE, 16, 1, msg, msglen, false);
|
||||
|
||||
/* send the message to QSEECOM */
|
||||
req_buf = (struct hdcp_rcvd_msg_req *)(handle->qseecom_handle->sbuf);
|
||||
req_buf->commandid = HDCP_TXMTR_PROCESS_RECEIVED_MESSAGE;
|
||||
|
@ -1989,13 +2028,8 @@ static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle)
|
|||
handle->hdcp_timeout = rsp_buf->timeout;
|
||||
handle->msglen = rsp_buf->msglen;
|
||||
|
||||
if (!atomic_read(&handle->hdcp_off)) {
|
||||
cdata.cmd = HDMI_HDCP_WKUP_CMD_SEND_MESSAGE;
|
||||
cdata.send_msg_buf = handle->listener_buf;
|
||||
cdata.send_msg_len = handle->msglen;
|
||||
cdata.timeout = handle->hdcp_timeout;
|
||||
}
|
||||
|
||||
if (!atomic_read(&handle->hdcp_off))
|
||||
hdcp_lib_send_message(handle);
|
||||
exit:
|
||||
kzfree(msg);
|
||||
|
||||
|
@ -2026,6 +2060,16 @@ static void hdcp_lib_topology_work(struct kthread_work *work)
|
|||
return;
|
||||
}
|
||||
|
||||
if (atomic_read(&handle->hdcp_off)) {
|
||||
pr_debug("invalid state: hdcp off\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (handle->hdcp_state & HDCP_STATE_ERROR) {
|
||||
pr_debug("invalid state: hdcp error\n");
|
||||
return;
|
||||
}
|
||||
|
||||
reinit_completion(&handle->topo_wait);
|
||||
timeout = wait_for_completion_timeout(&handle->topo_wait, HZ * 3);
|
||||
if (!timeout) {
|
||||
|
|
|
@ -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,10 +1173,16 @@ 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");
|
||||
|
||||
do {
|
||||
if (ret == -EAGAIN) {
|
||||
mdss_dp_mainlink_push_idle(&dp_drv->panel_data);
|
||||
mdss_dp_off_irq(dp_drv);
|
||||
}
|
||||
|
||||
mutex_lock(&dp_drv->train_mutex);
|
||||
|
||||
orientation = usbpd_get_plug_orientation(dp_drv->pd);
|
||||
pr_debug("plug orientation = %d\n", orientation);
|
||||
|
||||
|
@ -1202,9 +1203,13 @@ static int mdss_dp_on_irq(struct mdss_dp_drv_pdata *dp_drv)
|
|||
|
||||
mdss_dp_configure_source_params(dp_drv, &ln_map);
|
||||
|
||||
mdss_dp_train_main_link(dp_drv);
|
||||
|
||||
dp_drv->power_on = true;
|
||||
|
||||
ret = mdss_dp_train_main_link(dp_drv);
|
||||
|
||||
mutex_unlock(&dp_drv->train_mutex);
|
||||
} while (ret == -EAGAIN);
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
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;
|
||||
return mdss_dp_send_cable_notification(dp, enable);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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");
|
||||
return -EINVAL;
|
||||
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,13 +2542,16 @@ 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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
int ret = 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++;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
if (lane >= 1 && lane < max_lane)
|
||||
lane <<= 1; /* increase lane */
|
||||
|
||||
lrate = 270000000; /* 270M */
|
||||
lrate /= 1000; /* avoid using 64 bits */
|
||||
lrate *= rate;
|
||||
lrate /= 10; /* byte, 10 bits --> 8 bits */
|
||||
lrate *= lane;
|
||||
|
||||
pr_debug("new lrate=%u prate=%u rate=%d lane=%d p=%d b=%d\n",
|
||||
lrate, prate, rate, lane, ep->pixel_rate, ep->bpp);
|
||||
|
||||
if (lrate > prate) {
|
||||
ep->link_rate = rate;
|
||||
ep->lane_cnt = lane;
|
||||
pr_debug("new rate=%d %d\n", rate, lane);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* add calculation later */
|
||||
if (!ep)
|
||||
return -EINVAL;
|
||||
|
||||
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 rate=%d\n", ep->link_rate);
|
||||
|
||||
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);
|
||||
|
|
|
@ -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,21 +444,14 @@ 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);
|
||||
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;
|
||||
}
|
||||
sent_bytes += ctrl->msg_part[i].length;
|
||||
}
|
||||
|
||||
cdata.cmd = HDCP_LIB_WKUP_CMD_MSG_SEND_SUCCESS;
|
||||
cdata.timeout = ctrl->timeout;
|
||||
|
@ -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,18 +480,13 @@ 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,
|
||||
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;
|
||||
}
|
||||
bytes_read += ctrl->msg_part[i].length;
|
||||
}
|
||||
|
||||
cdata.recvd_msg_buf = recvd_msg_buf;
|
||||
cdata.recvd_msg_len = ctrl->send_msg_len;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
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 && !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);
|
||||
|
||||
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 {
|
||||
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) {
|
||||
ctrl->cp_irq_done = false;
|
||||
ctrl->sink_rx_status = 0;
|
||||
ctrl->rx_status = 0;
|
||||
}
|
||||
}
|
||||
|
||||
dp_hdcp2p2_get_msg_from_sink(ctrl);
|
||||
}
|
||||
|
||||
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:
|
||||
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)
|
||||
{
|
||||
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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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 (state == EXT_DISPLAY_CABLE_CONNECT) {
|
||||
ext_disp->current_disp = type;
|
||||
|
||||
ret = msm_ext_disp_process_display(ext_disp, state);
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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,36 +617,60 @@ static int msm_ext_disp_get_intf_id(struct platform_device *pdev)
|
|||
goto end;
|
||||
}
|
||||
|
||||
mutex_lock(&ext_disp->lock);
|
||||
ret = ext_disp->current_disp;
|
||||
mutex_unlock(&ext_disp->lock);
|
||||
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_ext_disp_notify(struct platform_device *pdev,
|
||||
enum msm_ext_disp_cable_state new_state)
|
||||
static int msm_ext_disp_update_audio_ops(struct msm_ext_disp *ext_disp,
|
||||
enum msm_ext_disp_cable_state state)
|
||||
{
|
||||
int ret = 0;
|
||||
struct msm_ext_disp_audio_codec_ops *ops = ext_disp->ops;
|
||||
|
||||
if (!ops) {
|
||||
pr_err("Invalid audio ops\n");
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (state == EXT_DISPLAY_CABLE_CONNECT) {
|
||||
ops->audio_info_setup = msm_ext_disp_audio_info_setup;
|
||||
ops->get_audio_edid_blk = msm_ext_disp_get_audio_edid_blk;
|
||||
ops->cable_status = msm_ext_disp_cable_status;
|
||||
ops->get_intf_id = msm_ext_disp_get_intf_id;
|
||||
ops->teardown_done = msm_ext_disp_teardown_done;
|
||||
} else {
|
||||
ops->audio_info_setup = NULL;
|
||||
ops->get_audio_edid_blk = NULL;
|
||||
ops->cable_status = NULL;
|
||||
ops->get_intf_id = NULL;
|
||||
ops->teardown_done = NULL;
|
||||
}
|
||||
end:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_ext_disp_notify(struct platform_device *pdev,
|
||||
enum msm_ext_disp_cable_state state)
|
||||
{
|
||||
int ret = 0;
|
||||
int state = 0;
|
||||
bool switched;
|
||||
struct msm_ext_disp_init_data *data = NULL;
|
||||
struct msm_ext_disp *ext_disp = NULL;
|
||||
|
||||
if (!pdev) {
|
||||
pr_err("Invalid platform device\n");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ext_disp = platform_get_drvdata(pdev);
|
||||
if (!ext_disp) {
|
||||
pr_err("Invalid drvdata\n");
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
mutex_lock(&ext_disp->lock);
|
||||
|
||||
if (state < EXT_DISPLAY_CABLE_DISCONNECT ||
|
||||
state >= EXT_DISPLAY_CABLE_STATE_MAX) {
|
||||
pr_err("Invalid state (%d)\n", state);
|
||||
|
@ -560,56 +678,17 @@ static int msm_ext_disp_notify(struct platform_device *pdev,
|
|||
goto end;
|
||||
}
|
||||
|
||||
state = ext_disp->audio_sdev.state;
|
||||
if (state == new_state)
|
||||
goto end;
|
||||
|
||||
if (ext_disp->ack_enabled &&
|
||||
atomic_read(&ext_disp->ack_pending)) {
|
||||
ret = -EINVAL;
|
||||
pr_err("%s ack pending, not notifying %s\n",
|
||||
state ? "connect" : "disconnect",
|
||||
new_state ? "connect" : "disconnect");
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = msm_ext_disp_get_intf_data(ext_disp, ext_disp->current_disp,
|
||||
&data);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
if (new_state == EXT_DISPLAY_CABLE_CONNECT && ext_disp->ops) {
|
||||
ext_disp->ops->audio_info_setup =
|
||||
msm_ext_disp_audio_info_setup;
|
||||
ext_disp->ops->get_audio_edid_blk =
|
||||
msm_ext_disp_get_audio_edid_blk;
|
||||
ext_disp->ops->cable_status =
|
||||
msm_ext_disp_cable_status;
|
||||
ext_disp->ops->get_intf_id =
|
||||
msm_ext_disp_get_intf_id;
|
||||
ext_disp->ops->teardown_done =
|
||||
msm_ext_disp_teardown_done;
|
||||
}
|
||||
|
||||
switch_set_state(&ext_disp->audio_sdev, (int)new_state);
|
||||
switched = ext_disp->audio_sdev.state != state;
|
||||
|
||||
if (ext_disp->ack_enabled && switched)
|
||||
atomic_set(&ext_disp->ack_pending, 1);
|
||||
|
||||
pr_debug("audio %s %s\n", switched ? "switched to" : "same as",
|
||||
ext_disp->audio_sdev.state ? "HDMI" : "SPKR");
|
||||
pr_debug("%s notifying hpd (%d)\n",
|
||||
msm_ext_disp_name(ext_disp->current_disp), state);
|
||||
|
||||
complete_all(&ext_disp->hpd_comp);
|
||||
end:
|
||||
mutex_unlock(&ext_disp->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack)
|
||||
{
|
||||
u32 ack_hpd;
|
||||
u32 hpd;
|
||||
int ret = 0;
|
||||
struct msm_ext_disp *ext_disp = NULL;
|
||||
|
||||
|
@ -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;
|
||||
|
|
|
@ -26,6 +26,7 @@ enum hdcp_lib_wakeup_cmd {
|
|||
HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED,
|
||||
HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT,
|
||||
HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE,
|
||||
HDCP_LIB_WKUP_CMD_LINK_FAILED,
|
||||
};
|
||||
|
||||
enum hdmi_hdcp_wakeup_cmd {
|
||||
|
@ -47,6 +48,7 @@ struct hdcp_lib_wakeup_data {
|
|||
};
|
||||
|
||||
struct hdcp_msg_part {
|
||||
char *name;
|
||||
uint32_t offset;
|
||||
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";
|
||||
case HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE:
|
||||
return "HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE";
|
||||
case HDCP_LIB_WKUP_CMD_LINK_FAILED:
|
||||
return "HDCP_LIB_WKUP_CMD_LINK_FAILED";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue