msm: ext_display: update hpd and notify logic

Use "hpd" method to notify external display module about
cable status change and "notify" as an acknowledgment to
power on or off. This makes hpd method as a blocking
call completed by notify call.

Change-Id: I5ef7cf5c95d46a695f20a51214a2afabd6feb4b6
Signed-off-by: Ajay Singh Parmar <aparmar@codeaurora.org>
This commit is contained in:
Ajay Singh Parmar 2016-10-28 01:06:41 -07:00
parent d4a840722f
commit 0659e58553
3 changed files with 167 additions and 174 deletions

View file

@ -906,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 */
@ -930,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;
@ -962,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__);
@ -1044,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);
}
/**
@ -1307,7 +1290,7 @@ link_training:
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:
@ -1425,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");
@ -1467,42 +1451,11 @@ end:
return ret;
}
static void mdss_dp_audio_codec_wait(struct mdss_dp_drv_pdata *dp)
{
const int audio_completion_timeout_ms = HZ * 3;
int ret = 0;
if (!dp->wait_for_audio_comp)
return;
reinit_completion(&dp->audio_comp);
ret = wait_for_completion_timeout(&dp->audio_comp,
audio_completion_timeout_ms);
if (ret <= 0)
pr_warn("audio codec teardown timed out\n");
dp->wait_for_audio_comp = false;
}
static int mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, bool enable)
{
int notified = 0;
if (enable) {
notified = mdss_dp_send_cable_notification(dp, enable);
} else {
mdss_dp_set_audio_switch_node(dp, enable);
mdss_dp_audio_codec_wait(dp);
notified = mdss_dp_send_cable_notification(dp, enable);
}
pr_debug("notify state %s done\n",
enable ? "ENABLE" : "DISABLE");
return notified;
return mdss_dp_send_cable_notification(dp, enable);
}
static int mdss_dp_edid_init(struct mdss_panel_data *pdata)
{
struct mdss_dp_drv_pdata *dp_drv = NULL;
@ -2795,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");

View file

@ -450,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;
@ -476,7 +475,6 @@ 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;

View file

@ -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)
{
@ -334,12 +337,80 @@ static int msm_ext_disp_send_cable_notification(struct msm_ext_disp *ext_disp,
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,
enum msm_ext_disp_type type,
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) {
@ -381,27 +452,28 @@ static int msm_ext_disp_hpd(struct platform_device *pdev,
goto end;
}
ret = msm_ext_disp_get_intf_data(ext_disp, type, &data);
if (ret)
goto end;
if (state == EXT_DISPLAY_CABLE_CONNECT) {
ext_disp->current_disp = data->type;
} else if ((state == EXT_DISPLAY_CABLE_DISCONNECT) &&
!ext_disp->ack_enabled) {
if (ext_disp->ops) {
ext_disp->ops->audio_info_setup = NULL;
ext_disp->ops->get_audio_edid_blk = NULL;
ext_disp->ops->cable_status = NULL;
ext_disp->ops->get_intf_id = NULL;
ext_disp->ops->teardown_done = NULL;
}
ext_disp->current_disp = type;
ret = msm_ext_disp_process_display(ext_disp, state);
if (ret)
goto end;
msm_ext_disp_update_audio_ops(ext_disp, state);
if (ret)
goto end;
ret = msm_ext_disp_process_audio(ext_disp, state);
if (ret)
goto end;
} else {
msm_ext_disp_process_audio(ext_disp, state);
msm_ext_disp_update_audio_ops(ext_disp, state);
msm_ext_disp_process_display(ext_disp, state);
ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX;
}
ret = msm_ext_disp_send_cable_notification(ext_disp, state);
pr_debug("Hpd (%d) for display (%s)\n", state,
msm_ext_disp_name(type));
@ -429,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;
@ -482,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:
@ -497,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) {
@ -504,7 +582,21 @@ static void msm_ext_disp_teardown_done(struct platform_device *pdev)
return;
}
data->codec_ops.teardown_done(data->pdev);
ext_disp = platform_get_drvdata(pdev);
if (!ext_disp) {
pr_err("No drvdata found\n");
return;
}
if (data->codec_ops.teardown_done)
data->codec_ops.teardown_done(data->pdev);
ext_disp->audio_session_on = false;
pr_debug("%s tearing down audio\n",
msm_ext_disp_name(ext_disp->current_disp));
complete_all(&ext_disp->hpd_comp);
}
static int msm_ext_disp_get_intf_id(struct platform_device *pdev)
@ -525,93 +617,78 @@ static int msm_ext_disp_get_intf_id(struct platform_device *pdev)
goto end;
}
mutex_lock(&ext_disp->lock);
ret = ext_disp->current_disp;
mutex_unlock(&ext_disp->lock);
end:
return ret;
}
static int msm_ext_disp_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) {
state >= EXT_DISPLAY_CABLE_STATE_MAX) {
pr_err("Invalid state (%d)\n", state);
ret = -EINVAL;
goto end;
}
state = ext_disp->audio_sdev.state;
if (state == new_state)
goto end;
if (ext_disp->ack_enabled &&
atomic_read(&ext_disp->ack_pending)) {
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;
@ -626,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;
@ -642,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;
}
@ -852,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;