diff --git a/arch/arm/mach-msm/include/mach/msm_hdmi_audio_codec.h b/arch/arm/mach-msm/include/mach/msm_hdmi_audio_codec.h index ff3da1151047..397304408723 100644 --- a/arch/arm/mach-msm/include/mach/msm_hdmi_audio_codec.h +++ b/arch/arm/mach-msm/include/mach/msm_hdmi_audio_codec.h @@ -30,6 +30,7 @@ struct msm_hdmi_audio_codec_ops { bool down_mix); int (*get_audio_edid_blk) (struct platform_device *pdev, struct msm_hdmi_audio_edid_blk *blk); + int (*hdmi_cable_status) (struct platform_device *pdev, u32 vote); }; int msm_hdmi_register_audio_codec(struct platform_device *pdev, diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index b4a2c2f55081..d4b75d0d0f2d 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -373,6 +373,7 @@ static inline u32 hdmi_tx_is_dvi_mode(struct hdmi_tx_ctrl *hdmi_ctrl) static void hdmi_tx_wait_for_audio_engine(struct hdmi_tx_ctrl *hdmi_ctrl) { u32 status = 0; + u32 wait_for_vote = 50; struct dss_io_data *io = NULL; if (!hdmi_ctrl) { @@ -386,6 +387,18 @@ static void hdmi_tx_wait_for_audio_engine(struct hdmi_tx_ctrl *hdmi_ctrl) return; } + /* + * wait for 5 sec max for audio engine to acknowledge if hdmi tx core + * can be safely turned off. Sleep for a reasonable time to make sure + * vote_hdmi_core_on variable is updated properly by audio. + */ + while (hdmi_ctrl->vote_hdmi_core_on && --wait_for_vote) + msleep(100); + + + if (!wait_for_vote) + DEV_ERR("%s: HDMI core still voted for power on\n", __func__); + if (readl_poll_timeout(io->base + HDMI_AUDIO_PKT_CTRL, status, (status & BIT(0)) == 0, AUDIO_POLL_SLEEP_US, AUDIO_POLL_TIMEOUT_US)) @@ -2240,6 +2253,29 @@ int msm_hdmi_register_mhl(struct platform_device *pdev, return 0; } +static int hdmi_tx_get_cable_status(struct platform_device *pdev, u32 vote) +{ + struct hdmi_tx_ctrl *hdmi_ctrl = platform_get_drvdata(pdev); + unsigned long flags; + u32 hpd; + + if (!hdmi_ctrl) { + DEV_ERR("%s: invalid input\n", __func__); + return -ENODEV; + } + + spin_lock_irqsave(&hdmi_ctrl->hpd_state_lock, flags); + hpd = hdmi_ctrl->hpd_state; + spin_unlock_irqrestore(&hdmi_ctrl->hpd_state_lock, flags); + + hdmi_ctrl->vote_hdmi_core_on = false; + + if (vote && hpd) + hdmi_ctrl->vote_hdmi_core_on = true; + + return hpd; +} + int msm_hdmi_register_audio_codec(struct platform_device *pdev, struct msm_hdmi_audio_codec_ops *ops) { @@ -2252,6 +2288,7 @@ int msm_hdmi_register_audio_codec(struct platform_device *pdev, ops->audio_info_setup = hdmi_tx_audio_info_setup; ops->get_audio_edid_blk = hdmi_tx_get_audio_edid_blk; + ops->hdmi_cable_status = hdmi_tx_get_cable_status; return 0; } /* hdmi_tx_audio_register */ @@ -2565,6 +2602,7 @@ static void hdmi_tx_hpd_off(struct hdmi_tx_ctrl *hdmi_ctrl) { int rc = 0; struct dss_io_data *io = NULL; + unsigned long flags; if (!hdmi_ctrl) { DEV_ERR("%s: invalid input\n", __func__); @@ -2604,7 +2642,10 @@ static void hdmi_tx_hpd_off(struct hdmi_tx_ctrl *hdmi_ctrl) DEV_INFO("%s: Failed to disable hpd power. Error=%d\n", __func__, rc); + spin_lock_irqsave(&hdmi_ctrl->hpd_state_lock, flags); hdmi_ctrl->hpd_state = false; + spin_unlock_irqrestore(&hdmi_ctrl->hpd_state_lock, flags); + hdmi_ctrl->hpd_initialized = false; } /* hdmi_tx_hpd_off */ @@ -2730,6 +2771,7 @@ static irqreturn_t hdmi_tx_isr(int irq, void *data) { struct dss_io_data *io = NULL; struct hdmi_tx_ctrl *hdmi_ctrl = (struct hdmi_tx_ctrl *)data; + unsigned long flags; if (!hdmi_ctrl) { DEV_WARN("%s: invalid input data, ISR ignored\n", __func__); @@ -2744,8 +2786,10 @@ static irqreturn_t hdmi_tx_isr(int irq, void *data) } if (DSS_REG_R(io, HDMI_HPD_INT_STATUS) & BIT(0)) { + spin_lock_irqsave(&hdmi_ctrl->hpd_state_lock, flags); hdmi_ctrl->hpd_state = (DSS_REG_R(io, HDMI_HPD_INT_STATUS) & BIT(1)) >> 1; + spin_unlock_irqrestore(&hdmi_ctrl->hpd_state_lock, flags); /* * Ack the current hpd interrupt and stop listening to @@ -2844,6 +2888,8 @@ static int hdmi_tx_dev_init(struct hdmi_tx_ctrl *hdmi_ctrl) INIT_WORK(&hdmi_ctrl->power_off_work, hdmi_tx_power_off_work); + spin_lock_init(&hdmi_ctrl->hpd_state_lock); + hdmi_ctrl->audio_data.sample_rate = AUDIO_SAMPLE_RATE_48KHZ; hdmi_ctrl->audio_data.channel_num = MSM_HDMI_AUDIO_CHANNEL_2; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.h b/drivers/video/fbdev/msm/mdss_hdmi_tx.h index c4d03269edf2..66071e94fce1 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.h @@ -58,6 +58,7 @@ struct hdmi_tx_ctrl { struct switch_dev sdev; struct switch_dev audio_sdev; struct workqueue_struct *workq; + spinlock_t hpd_state_lock; uint32_t video_resolution; @@ -68,6 +69,7 @@ struct hdmi_tx_ctrl { u32 hpd_off_pending; u32 hpd_feature_on; u32 hpd_initialized; + u32 vote_hdmi_core_on; u8 timing_gen_on; u32 mhl_max_pclk; u8 mhl_hpd_on;