Loading drivers/video/msm/mdss/mdss_hdmi_tx.c +79 −68 Original line number Diff line number Diff line Loading @@ -730,9 +730,13 @@ static ssize_t hdmi_tx_sysfs_wta_hpd(struct device *dev, rc = hdmi_tx_sysfs_enable_hpd(hdmi_ctrl, false); mutex_lock(&hdmi_ctrl->power_mutex); if (hdmi_ctrl->panel_power_on && hdmi_ctrl->hpd_state) { mutex_unlock(&hdmi_ctrl->power_mutex); hdmi_tx_set_audio_switch_node(hdmi_ctrl, 0); hdmi_tx_wait_for_audio_engine(hdmi_ctrl); } else { mutex_unlock(&hdmi_ctrl->power_mutex); } hdmi_tx_send_cable_notification(hdmi_ctrl, 0); Loading Loading @@ -1413,17 +1417,6 @@ static void hdmi_tx_hpd_int_work(struct work_struct *work) hdmi_ctrl->hpd_state ? "CONNECT" : "DISCONNECT"); if (hdmi_ctrl->hpd_state) { /* * If a down stream device or bridge chip is attached to hdmi * Tx core output, it is likely that it might be powering the * hpd module ON/OFF on cable connect/disconnect as it would * have its own mechanism of detecting cable. Flush power off * work is needed in case there is any race condidtion between * power off and on during fast cable plug in/out. */ if (hdmi_ctrl->ds_registered) flush_work(&hdmi_ctrl->power_off_work); if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, true)) { DEV_ERR("%s: Failed to enable ddc power\n", __func__); return; Loading @@ -1446,8 +1439,8 @@ static void hdmi_tx_hpd_int_work(struct work_struct *work) hdmi_tx_send_cable_notification(hdmi_ctrl, false); } if (!completion_done(&hdmi_ctrl->hpd_done)) complete_all(&hdmi_ctrl->hpd_done); if (!completion_done(&hdmi_ctrl->hpd_int_done)) complete_all(&hdmi_ctrl->hpd_int_done); } /* hdmi_tx_hpd_int_work */ static int hdmi_tx_check_capability(struct hdmi_tx_ctrl *hdmi_ctrl) Loading Loading @@ -2552,7 +2545,9 @@ static int hdmi_tx_audio_info_setup(struct platform_device *pdev, return -ENODEV; } mutex_lock(&hdmi_ctrl->power_mutex); if (!hdmi_tx_is_dvi_mode(hdmi_ctrl) && hdmi_ctrl->panel_power_on) { mutex_unlock(&hdmi_ctrl->power_mutex); /* Map given sample rate to Enum */ if (sample_rate == 32000) sample_rate = AUDIO_SAMPLE_RATE_32KHZ; Loading Loading @@ -2580,6 +2575,7 @@ static int hdmi_tx_audio_info_setup(struct platform_device *pdev, DEV_ERR("%s: hdmi_tx_audio_iframe_setup failed.rc=%d\n", __func__, rc); } else { mutex_unlock(&hdmi_ctrl->power_mutex); rc = -EPERM; } Loading Loading @@ -2848,21 +2844,27 @@ static void hdmi_tx_hpd_polarity_setup(struct hdmi_tx_ctrl *hdmi_ctrl, } } /* hdmi_tx_hpd_polarity_setup */ static void hdmi_tx_power_off_work(struct work_struct *work) static int hdmi_tx_power_off(struct mdss_panel_data *panel_data) { struct hdmi_tx_ctrl *hdmi_ctrl = NULL; struct dss_io_data *io = NULL; struct hdmi_tx_ctrl *hdmi_ctrl = hdmi_tx_get_drvdata_from_panel_data(panel_data); hdmi_ctrl = container_of(work, struct hdmi_tx_ctrl, power_off_work); if (!hdmi_ctrl) { DEV_DBG("%s: invalid input\n", __func__); return; mutex_lock(&hdmi_ctrl->power_mutex); if (!hdmi_ctrl || (!panel_data->panel_info.cont_splash_enabled && !hdmi_ctrl->panel_power_on)) { mutex_unlock(&hdmi_ctrl->power_mutex); DEV_ERR("%s: invalid input\n", __func__); return -EINVAL; } else { mutex_unlock(&hdmi_ctrl->power_mutex); } io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO]; if (!io->base) { DEV_ERR("%s: Core io is not initialized\n", __func__); return; return -EINVAL; } if (!hdmi_tx_is_dvi_mode(hdmi_ctrl)) Loading @@ -2874,41 +2876,20 @@ static void hdmi_tx_power_off_work(struct work_struct *work) hdmi_tx_core_off(hdmi_ctrl); mutex_lock(&hdmi_ctrl->power_mutex); hdmi_ctrl->panel_power_on = false; mutex_unlock(&hdmi_ctrl->power_mutex); if (hdmi_ctrl->hpd_off_pending) { hdmi_tx_hpd_off(hdmi_ctrl); hdmi_ctrl->hpd_off_pending = false; } mutex_lock(&hdmi_ctrl->mutex); hdmi_ctrl->panel_power_on = false; mutex_unlock(&hdmi_ctrl->mutex); DEV_INFO("%s: HDMI Core: OFF\n", __func__); if (hdmi_ctrl->hdmi_tx_hpd_done) hdmi_ctrl->hdmi_tx_hpd_done( hdmi_ctrl->downstream_data); } /* hdmi_tx_power_off_work */ static int hdmi_tx_power_off(struct mdss_panel_data *panel_data) { struct hdmi_tx_ctrl *hdmi_ctrl = hdmi_tx_get_drvdata_from_panel_data(panel_data); if (!hdmi_ctrl || (!panel_data->panel_info.cont_splash_enabled && !hdmi_ctrl->panel_power_on)) { DEV_ERR("%s: invalid input\n", __func__); return -EINVAL; } /* * Queue work item to handle power down sequence. * This is needed since we need to wait for the audio engine * to shutdown first before we shutdown the HDMI core. */ DEV_DBG("%s: Queuing work to power off HDMI core\n", __func__); queue_work(hdmi_ctrl->workq, &hdmi_ctrl->power_off_work); DEV_INFO("%s: HDMI Core: OFF\n", __func__); return 0; } /* hdmi_tx_power_off */ Loading Loading @@ -2941,9 +2922,6 @@ static int hdmi_tx_power_on(struct mdss_panel_data *panel_data) panel_info = &panel_data->panel_info; hdmi_ctrl->hdcp_feature_on = hdcp_feature_on; /* If a power down is already underway, wait for it to finish */ flush_work(&hdmi_ctrl->power_off_work); res_changed = hdmi_tx_set_video_fmt(hdmi_ctrl, panel_info); DEV_DBG("%s: %dx%d%s\n", __func__, Loading @@ -2955,7 +2933,9 @@ static int hdmi_tx_power_on(struct mdss_panel_data *panel_data) panel_data->panel_info.cont_splash_enabled = false; if (res_changed == RESOLUTION_UNCHANGED) { mutex_lock(&hdmi_ctrl->power_mutex); hdmi_ctrl->panel_power_on = true; mutex_unlock(&hdmi_ctrl->power_mutex); hdmi_cec_config( hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC]); Loading @@ -2973,9 +2953,9 @@ static int hdmi_tx_power_on(struct mdss_panel_data *panel_data) return rc; } mutex_lock(&hdmi_ctrl->mutex); mutex_lock(&hdmi_ctrl->power_mutex); hdmi_ctrl->panel_power_on = true; mutex_unlock(&hdmi_ctrl->mutex); mutex_unlock(&hdmi_ctrl->power_mutex); hdmi_cec_config(hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC]); Loading Loading @@ -3027,6 +3007,7 @@ static void hdmi_tx_hpd_off(struct hdmi_tx_ctrl *hdmi_ctrl) } /* finish the ongoing hpd work if any */ if (!hdmi_ctrl->panel_suspend) flush_work(&hdmi_ctrl->hpd_int_work); /* Turn off HPD interrupts */ Loading @@ -3053,6 +3034,10 @@ static void hdmi_tx_hpd_off(struct hdmi_tx_ctrl *hdmi_ctrl) spin_unlock_irqrestore(&hdmi_ctrl->hpd_state_lock, flags); hdmi_ctrl->hpd_initialized = false; if (!completion_done(&hdmi_ctrl->hpd_off_done)) complete_all(&hdmi_ctrl->hpd_off_done); DEV_DBG("%s: HPD is now OFF\n", __func__); } /* hdmi_tx_hpd_off */ Loading Loading @@ -3125,16 +3110,29 @@ static int hdmi_tx_sysfs_enable_hpd(struct hdmi_tx_ctrl *hdmi_ctrl, int on) DEV_INFO("%s: %d\n", __func__, on); if (on) { if (hdmi_ctrl->hpd_off_pending) { u32 timeout; INIT_COMPLETION(hdmi_ctrl->hpd_off_done); timeout = wait_for_completion_timeout( &hdmi_ctrl->hpd_off_done, HZ); if (!timeout) DEV_ERR("%s: hpd off still pending\n", __func__); } rc = hdmi_tx_hpd_on(hdmi_ctrl); } else { /* If power down is already underway, wait for it to finish */ flush_work(&hdmi_ctrl->power_off_work); if (!hdmi_ctrl->panel_power_on) mutex_lock(&hdmi_ctrl->power_mutex); if (!hdmi_ctrl->panel_power_on && !hdmi_ctrl->hpd_off_pending) { mutex_unlock(&hdmi_ctrl->power_mutex); hdmi_tx_hpd_off(hdmi_ctrl); else } else { mutex_unlock(&hdmi_ctrl->power_mutex); hdmi_ctrl->hpd_off_pending = true; } } return rc; } /* hdmi_tx_sysfs_enable_hpd */ Loading Loading @@ -3206,6 +3204,14 @@ static irqreturn_t hdmi_tx_isr(int irq, void *data) * new hpd interrupt. */ DSS_REG_W(io, HDMI_HPD_INT_CTRL, BIT(0)); /* * If suspend has already triggered, don't start the hpd work * to avoid a possible deadlock during suspend where hpd off * waits for hpd interrupt to finish. Suspend thread will * eventually reset the HPD module. */ if (!hdmi_ctrl->panel_suspend) queue_work(hdmi_ctrl->workq, &hdmi_ctrl->hpd_int_work); } Loading Loading @@ -3250,6 +3256,7 @@ static void hdmi_tx_dev_deinit(struct hdmi_tx_ctrl *hdmi_ctrl) if (hdmi_ctrl->workq) destroy_workqueue(hdmi_ctrl->workq); mutex_destroy(&hdmi_ctrl->lut_lock); mutex_destroy(&hdmi_ctrl->power_mutex); mutex_destroy(&hdmi_ctrl->cable_notify_mutex); mutex_destroy(&hdmi_ctrl->mutex); Loading Loading @@ -3280,6 +3287,7 @@ static int hdmi_tx_dev_init(struct hdmi_tx_ctrl *hdmi_ctrl) mutex_init(&hdmi_ctrl->mutex); mutex_init(&hdmi_ctrl->lut_lock); mutex_init(&hdmi_ctrl->cable_notify_mutex); mutex_init(&hdmi_ctrl->power_mutex); INIT_LIST_HEAD(&hdmi_ctrl->cable_notify_handlers); Loading @@ -3299,11 +3307,11 @@ static int hdmi_tx_dev_init(struct hdmi_tx_ctrl *hdmi_ctrl) hdmi_ctrl->hpd_state = false; hdmi_ctrl->hpd_initialized = false; hdmi_ctrl->hpd_off_pending = false; init_completion(&hdmi_ctrl->hpd_done); init_completion(&hdmi_ctrl->hpd_int_done); init_completion(&hdmi_ctrl->hpd_off_done); INIT_WORK(&hdmi_ctrl->hpd_int_work, hdmi_tx_hpd_int_work); INIT_WORK(&hdmi_ctrl->cable_notify_work, hdmi_tx_cable_notify_work); INIT_WORK(&hdmi_ctrl->power_off_work, hdmi_tx_power_off_work); spin_lock_init(&hdmi_ctrl->hpd_state_lock); Loading Loading @@ -3398,7 +3406,7 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data, } if (hdmi_ctrl->pdata.primary) { INIT_COMPLETION(hdmi_ctrl->hpd_done); INIT_COMPLETION(hdmi_ctrl->hpd_int_done); rc = hdmi_tx_sysfs_enable_hpd(hdmi_ctrl, true); if (rc) { DEV_ERR("%s: hpd_enable failed. rc=%d\n", Loading Loading @@ -3432,12 +3440,8 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data, break; case MDSS_EVENT_RESUME: /* If a suspend is already underway, wait for it to finish */ if (hdmi_ctrl->panel_suspend && hdmi_ctrl->panel_power_on) flush_work(&hdmi_ctrl->power_off_work); if (hdmi_ctrl->hpd_feature_on) { INIT_COMPLETION(hdmi_ctrl->hpd_done); INIT_COMPLETION(hdmi_ctrl->hpd_int_done); rc = hdmi_tx_hpd_on(hdmi_ctrl); if (rc) Loading @@ -3452,7 +3456,7 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data, hdmi_ctrl->panel_suspend = false; timeout = wait_for_completion_timeout( &hdmi_ctrl->hpd_done, HZ/10); &hdmi_ctrl->hpd_int_done, HZ/10); if (!timeout && !hdmi_ctrl->hpd_state) { DEV_INFO("%s: cable removed during suspend\n", __func__); Loading Loading @@ -3482,12 +3486,16 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data, break; case MDSS_EVENT_SUSPEND: if (!hdmi_ctrl->panel_power_on) { mutex_lock(&hdmi_ctrl->power_mutex); if (!hdmi_ctrl->panel_power_on && !hdmi_ctrl->hpd_off_pending) { mutex_unlock(&hdmi_ctrl->power_mutex); if (hdmi_ctrl->hpd_feature_on) hdmi_tx_hpd_off(hdmi_ctrl); hdmi_ctrl->panel_suspend = false; } else { mutex_unlock(&hdmi_ctrl->power_mutex); hdmi_ctrl->hpd_off_pending = true; hdmi_ctrl->panel_suspend = true; } Loading @@ -3508,13 +3516,16 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data, break; case MDSS_EVENT_PANEL_OFF: mutex_lock(&hdmi_ctrl->power_mutex); if (hdmi_ctrl->panel_power_on) { mutex_unlock(&hdmi_ctrl->power_mutex); hdmi_tx_config_avmute(hdmi_ctrl, 1); rc = hdmi_tx_power_off(panel_data); if (rc) DEV_ERR("%s: hdmi_tx_power_off failed.rc=%d\n", __func__, rc); } else { mutex_unlock(&hdmi_ctrl->power_mutex); DEV_DBG("%s: hdmi is already powered off\n", __func__); } Loading drivers/video/msm/mdss/mdss_hdmi_tx.h +4 −3 Original line number Diff line number Diff line /* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. /* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -71,6 +71,7 @@ struct hdmi_tx_ctrl { struct mutex mutex; struct mutex lut_lock; struct mutex power_mutex; struct mutex cable_notify_mutex; struct list_head cable_notify_handlers; struct kobject *kobj; Loading @@ -94,10 +95,10 @@ struct hdmi_tx_ctrl { u8 mhl_hpd_on; struct hdmi_util_ds_data ds_data; struct completion hpd_done; struct completion hpd_int_done; struct completion hpd_off_done; struct work_struct hpd_int_work; struct work_struct power_off_work; struct work_struct cable_notify_work; bool hdcp_feature_on; Loading Loading
drivers/video/msm/mdss/mdss_hdmi_tx.c +79 −68 Original line number Diff line number Diff line Loading @@ -730,9 +730,13 @@ static ssize_t hdmi_tx_sysfs_wta_hpd(struct device *dev, rc = hdmi_tx_sysfs_enable_hpd(hdmi_ctrl, false); mutex_lock(&hdmi_ctrl->power_mutex); if (hdmi_ctrl->panel_power_on && hdmi_ctrl->hpd_state) { mutex_unlock(&hdmi_ctrl->power_mutex); hdmi_tx_set_audio_switch_node(hdmi_ctrl, 0); hdmi_tx_wait_for_audio_engine(hdmi_ctrl); } else { mutex_unlock(&hdmi_ctrl->power_mutex); } hdmi_tx_send_cable_notification(hdmi_ctrl, 0); Loading Loading @@ -1413,17 +1417,6 @@ static void hdmi_tx_hpd_int_work(struct work_struct *work) hdmi_ctrl->hpd_state ? "CONNECT" : "DISCONNECT"); if (hdmi_ctrl->hpd_state) { /* * If a down stream device or bridge chip is attached to hdmi * Tx core output, it is likely that it might be powering the * hpd module ON/OFF on cable connect/disconnect as it would * have its own mechanism of detecting cable. Flush power off * work is needed in case there is any race condidtion between * power off and on during fast cable plug in/out. */ if (hdmi_ctrl->ds_registered) flush_work(&hdmi_ctrl->power_off_work); if (hdmi_tx_enable_power(hdmi_ctrl, HDMI_TX_DDC_PM, true)) { DEV_ERR("%s: Failed to enable ddc power\n", __func__); return; Loading @@ -1446,8 +1439,8 @@ static void hdmi_tx_hpd_int_work(struct work_struct *work) hdmi_tx_send_cable_notification(hdmi_ctrl, false); } if (!completion_done(&hdmi_ctrl->hpd_done)) complete_all(&hdmi_ctrl->hpd_done); if (!completion_done(&hdmi_ctrl->hpd_int_done)) complete_all(&hdmi_ctrl->hpd_int_done); } /* hdmi_tx_hpd_int_work */ static int hdmi_tx_check_capability(struct hdmi_tx_ctrl *hdmi_ctrl) Loading Loading @@ -2552,7 +2545,9 @@ static int hdmi_tx_audio_info_setup(struct platform_device *pdev, return -ENODEV; } mutex_lock(&hdmi_ctrl->power_mutex); if (!hdmi_tx_is_dvi_mode(hdmi_ctrl) && hdmi_ctrl->panel_power_on) { mutex_unlock(&hdmi_ctrl->power_mutex); /* Map given sample rate to Enum */ if (sample_rate == 32000) sample_rate = AUDIO_SAMPLE_RATE_32KHZ; Loading Loading @@ -2580,6 +2575,7 @@ static int hdmi_tx_audio_info_setup(struct platform_device *pdev, DEV_ERR("%s: hdmi_tx_audio_iframe_setup failed.rc=%d\n", __func__, rc); } else { mutex_unlock(&hdmi_ctrl->power_mutex); rc = -EPERM; } Loading Loading @@ -2848,21 +2844,27 @@ static void hdmi_tx_hpd_polarity_setup(struct hdmi_tx_ctrl *hdmi_ctrl, } } /* hdmi_tx_hpd_polarity_setup */ static void hdmi_tx_power_off_work(struct work_struct *work) static int hdmi_tx_power_off(struct mdss_panel_data *panel_data) { struct hdmi_tx_ctrl *hdmi_ctrl = NULL; struct dss_io_data *io = NULL; struct hdmi_tx_ctrl *hdmi_ctrl = hdmi_tx_get_drvdata_from_panel_data(panel_data); hdmi_ctrl = container_of(work, struct hdmi_tx_ctrl, power_off_work); if (!hdmi_ctrl) { DEV_DBG("%s: invalid input\n", __func__); return; mutex_lock(&hdmi_ctrl->power_mutex); if (!hdmi_ctrl || (!panel_data->panel_info.cont_splash_enabled && !hdmi_ctrl->panel_power_on)) { mutex_unlock(&hdmi_ctrl->power_mutex); DEV_ERR("%s: invalid input\n", __func__); return -EINVAL; } else { mutex_unlock(&hdmi_ctrl->power_mutex); } io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO]; if (!io->base) { DEV_ERR("%s: Core io is not initialized\n", __func__); return; return -EINVAL; } if (!hdmi_tx_is_dvi_mode(hdmi_ctrl)) Loading @@ -2874,41 +2876,20 @@ static void hdmi_tx_power_off_work(struct work_struct *work) hdmi_tx_core_off(hdmi_ctrl); mutex_lock(&hdmi_ctrl->power_mutex); hdmi_ctrl->panel_power_on = false; mutex_unlock(&hdmi_ctrl->power_mutex); if (hdmi_ctrl->hpd_off_pending) { hdmi_tx_hpd_off(hdmi_ctrl); hdmi_ctrl->hpd_off_pending = false; } mutex_lock(&hdmi_ctrl->mutex); hdmi_ctrl->panel_power_on = false; mutex_unlock(&hdmi_ctrl->mutex); DEV_INFO("%s: HDMI Core: OFF\n", __func__); if (hdmi_ctrl->hdmi_tx_hpd_done) hdmi_ctrl->hdmi_tx_hpd_done( hdmi_ctrl->downstream_data); } /* hdmi_tx_power_off_work */ static int hdmi_tx_power_off(struct mdss_panel_data *panel_data) { struct hdmi_tx_ctrl *hdmi_ctrl = hdmi_tx_get_drvdata_from_panel_data(panel_data); if (!hdmi_ctrl || (!panel_data->panel_info.cont_splash_enabled && !hdmi_ctrl->panel_power_on)) { DEV_ERR("%s: invalid input\n", __func__); return -EINVAL; } /* * Queue work item to handle power down sequence. * This is needed since we need to wait for the audio engine * to shutdown first before we shutdown the HDMI core. */ DEV_DBG("%s: Queuing work to power off HDMI core\n", __func__); queue_work(hdmi_ctrl->workq, &hdmi_ctrl->power_off_work); DEV_INFO("%s: HDMI Core: OFF\n", __func__); return 0; } /* hdmi_tx_power_off */ Loading Loading @@ -2941,9 +2922,6 @@ static int hdmi_tx_power_on(struct mdss_panel_data *panel_data) panel_info = &panel_data->panel_info; hdmi_ctrl->hdcp_feature_on = hdcp_feature_on; /* If a power down is already underway, wait for it to finish */ flush_work(&hdmi_ctrl->power_off_work); res_changed = hdmi_tx_set_video_fmt(hdmi_ctrl, panel_info); DEV_DBG("%s: %dx%d%s\n", __func__, Loading @@ -2955,7 +2933,9 @@ static int hdmi_tx_power_on(struct mdss_panel_data *panel_data) panel_data->panel_info.cont_splash_enabled = false; if (res_changed == RESOLUTION_UNCHANGED) { mutex_lock(&hdmi_ctrl->power_mutex); hdmi_ctrl->panel_power_on = true; mutex_unlock(&hdmi_ctrl->power_mutex); hdmi_cec_config( hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC]); Loading @@ -2973,9 +2953,9 @@ static int hdmi_tx_power_on(struct mdss_panel_data *panel_data) return rc; } mutex_lock(&hdmi_ctrl->mutex); mutex_lock(&hdmi_ctrl->power_mutex); hdmi_ctrl->panel_power_on = true; mutex_unlock(&hdmi_ctrl->mutex); mutex_unlock(&hdmi_ctrl->power_mutex); hdmi_cec_config(hdmi_ctrl->feature_data[HDMI_TX_FEAT_CEC]); Loading Loading @@ -3027,6 +3007,7 @@ static void hdmi_tx_hpd_off(struct hdmi_tx_ctrl *hdmi_ctrl) } /* finish the ongoing hpd work if any */ if (!hdmi_ctrl->panel_suspend) flush_work(&hdmi_ctrl->hpd_int_work); /* Turn off HPD interrupts */ Loading @@ -3053,6 +3034,10 @@ static void hdmi_tx_hpd_off(struct hdmi_tx_ctrl *hdmi_ctrl) spin_unlock_irqrestore(&hdmi_ctrl->hpd_state_lock, flags); hdmi_ctrl->hpd_initialized = false; if (!completion_done(&hdmi_ctrl->hpd_off_done)) complete_all(&hdmi_ctrl->hpd_off_done); DEV_DBG("%s: HPD is now OFF\n", __func__); } /* hdmi_tx_hpd_off */ Loading Loading @@ -3125,16 +3110,29 @@ static int hdmi_tx_sysfs_enable_hpd(struct hdmi_tx_ctrl *hdmi_ctrl, int on) DEV_INFO("%s: %d\n", __func__, on); if (on) { if (hdmi_ctrl->hpd_off_pending) { u32 timeout; INIT_COMPLETION(hdmi_ctrl->hpd_off_done); timeout = wait_for_completion_timeout( &hdmi_ctrl->hpd_off_done, HZ); if (!timeout) DEV_ERR("%s: hpd off still pending\n", __func__); } rc = hdmi_tx_hpd_on(hdmi_ctrl); } else { /* If power down is already underway, wait for it to finish */ flush_work(&hdmi_ctrl->power_off_work); if (!hdmi_ctrl->panel_power_on) mutex_lock(&hdmi_ctrl->power_mutex); if (!hdmi_ctrl->panel_power_on && !hdmi_ctrl->hpd_off_pending) { mutex_unlock(&hdmi_ctrl->power_mutex); hdmi_tx_hpd_off(hdmi_ctrl); else } else { mutex_unlock(&hdmi_ctrl->power_mutex); hdmi_ctrl->hpd_off_pending = true; } } return rc; } /* hdmi_tx_sysfs_enable_hpd */ Loading Loading @@ -3206,6 +3204,14 @@ static irqreturn_t hdmi_tx_isr(int irq, void *data) * new hpd interrupt. */ DSS_REG_W(io, HDMI_HPD_INT_CTRL, BIT(0)); /* * If suspend has already triggered, don't start the hpd work * to avoid a possible deadlock during suspend where hpd off * waits for hpd interrupt to finish. Suspend thread will * eventually reset the HPD module. */ if (!hdmi_ctrl->panel_suspend) queue_work(hdmi_ctrl->workq, &hdmi_ctrl->hpd_int_work); } Loading Loading @@ -3250,6 +3256,7 @@ static void hdmi_tx_dev_deinit(struct hdmi_tx_ctrl *hdmi_ctrl) if (hdmi_ctrl->workq) destroy_workqueue(hdmi_ctrl->workq); mutex_destroy(&hdmi_ctrl->lut_lock); mutex_destroy(&hdmi_ctrl->power_mutex); mutex_destroy(&hdmi_ctrl->cable_notify_mutex); mutex_destroy(&hdmi_ctrl->mutex); Loading Loading @@ -3280,6 +3287,7 @@ static int hdmi_tx_dev_init(struct hdmi_tx_ctrl *hdmi_ctrl) mutex_init(&hdmi_ctrl->mutex); mutex_init(&hdmi_ctrl->lut_lock); mutex_init(&hdmi_ctrl->cable_notify_mutex); mutex_init(&hdmi_ctrl->power_mutex); INIT_LIST_HEAD(&hdmi_ctrl->cable_notify_handlers); Loading @@ -3299,11 +3307,11 @@ static int hdmi_tx_dev_init(struct hdmi_tx_ctrl *hdmi_ctrl) hdmi_ctrl->hpd_state = false; hdmi_ctrl->hpd_initialized = false; hdmi_ctrl->hpd_off_pending = false; init_completion(&hdmi_ctrl->hpd_done); init_completion(&hdmi_ctrl->hpd_int_done); init_completion(&hdmi_ctrl->hpd_off_done); INIT_WORK(&hdmi_ctrl->hpd_int_work, hdmi_tx_hpd_int_work); INIT_WORK(&hdmi_ctrl->cable_notify_work, hdmi_tx_cable_notify_work); INIT_WORK(&hdmi_ctrl->power_off_work, hdmi_tx_power_off_work); spin_lock_init(&hdmi_ctrl->hpd_state_lock); Loading Loading @@ -3398,7 +3406,7 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data, } if (hdmi_ctrl->pdata.primary) { INIT_COMPLETION(hdmi_ctrl->hpd_done); INIT_COMPLETION(hdmi_ctrl->hpd_int_done); rc = hdmi_tx_sysfs_enable_hpd(hdmi_ctrl, true); if (rc) { DEV_ERR("%s: hpd_enable failed. rc=%d\n", Loading Loading @@ -3432,12 +3440,8 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data, break; case MDSS_EVENT_RESUME: /* If a suspend is already underway, wait for it to finish */ if (hdmi_ctrl->panel_suspend && hdmi_ctrl->panel_power_on) flush_work(&hdmi_ctrl->power_off_work); if (hdmi_ctrl->hpd_feature_on) { INIT_COMPLETION(hdmi_ctrl->hpd_done); INIT_COMPLETION(hdmi_ctrl->hpd_int_done); rc = hdmi_tx_hpd_on(hdmi_ctrl); if (rc) Loading @@ -3452,7 +3456,7 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data, hdmi_ctrl->panel_suspend = false; timeout = wait_for_completion_timeout( &hdmi_ctrl->hpd_done, HZ/10); &hdmi_ctrl->hpd_int_done, HZ/10); if (!timeout && !hdmi_ctrl->hpd_state) { DEV_INFO("%s: cable removed during suspend\n", __func__); Loading Loading @@ -3482,12 +3486,16 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data, break; case MDSS_EVENT_SUSPEND: if (!hdmi_ctrl->panel_power_on) { mutex_lock(&hdmi_ctrl->power_mutex); if (!hdmi_ctrl->panel_power_on && !hdmi_ctrl->hpd_off_pending) { mutex_unlock(&hdmi_ctrl->power_mutex); if (hdmi_ctrl->hpd_feature_on) hdmi_tx_hpd_off(hdmi_ctrl); hdmi_ctrl->panel_suspend = false; } else { mutex_unlock(&hdmi_ctrl->power_mutex); hdmi_ctrl->hpd_off_pending = true; hdmi_ctrl->panel_suspend = true; } Loading @@ -3508,13 +3516,16 @@ static int hdmi_tx_panel_event_handler(struct mdss_panel_data *panel_data, break; case MDSS_EVENT_PANEL_OFF: mutex_lock(&hdmi_ctrl->power_mutex); if (hdmi_ctrl->panel_power_on) { mutex_unlock(&hdmi_ctrl->power_mutex); hdmi_tx_config_avmute(hdmi_ctrl, 1); rc = hdmi_tx_power_off(panel_data); if (rc) DEV_ERR("%s: hdmi_tx_power_off failed.rc=%d\n", __func__, rc); } else { mutex_unlock(&hdmi_ctrl->power_mutex); DEV_DBG("%s: hdmi is already powered off\n", __func__); } Loading
drivers/video/msm/mdss/mdss_hdmi_tx.h +4 −3 Original line number Diff line number Diff line /* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. /* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -71,6 +71,7 @@ struct hdmi_tx_ctrl { struct mutex mutex; struct mutex lut_lock; struct mutex power_mutex; struct mutex cable_notify_mutex; struct list_head cable_notify_handlers; struct kobject *kobj; Loading @@ -94,10 +95,10 @@ struct hdmi_tx_ctrl { u8 mhl_hpd_on; struct hdmi_util_ds_data ds_data; struct completion hpd_done; struct completion hpd_int_done; struct completion hpd_off_done; struct work_struct hpd_int_work; struct work_struct power_off_work; struct work_struct cable_notify_work; bool hdcp_feature_on; Loading