aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video
diff options
context:
space:
mode:
authorTomi Valkeinen <tomi.valkeinen@ti.com>2012-01-17 11:09:57 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-03-12 10:32:59 -0700
commit21189f03d3ec3a74d9949907c828410d7a9a86d5 (patch)
treeaaba0ce5e80357481b822ccc18fc26d598efb7ed /drivers/video
parent8d1b18d569d59c8aa95964e2e5e4c5d544928258 (diff)
downloadkernel_samsung_smdk4412-21189f03d3ec3a74d9949907c828410d7a9a86d5.zip
kernel_samsung_smdk4412-21189f03d3ec3a74d9949907c828410d7a9a86d5.tar.gz
kernel_samsung_smdk4412-21189f03d3ec3a74d9949907c828410d7a9a86d5.tar.bz2
OMAPDSS: HDMI: PHY burnout fix
commit c49d005b6cc8491fad5b24f82805be2d6bcbd3dd upstream. A hardware bug in the OMAP4 HDMI PHY causes physical damage to the board if the HDMI PHY is kept powered on when the cable is not connected. This patch solves the problem by adding hot-plug-detection into the HDMI IP driver. This is not a real HPD support in the sense that nobody else than the IP driver gets to know about the HPD events, but is only meant to fix the HW bug. The strategy is simple: If the display device is turned off by the user, the PHY power is set to OFF. When the display device is turned on by the user, the PHY power is set either to LDOON or TXON, depending on whether the HDMI cable is connected. The reason to avoid PHY OFF when the display device is on, but the cable is disconnected, is that when the PHY is turned OFF, the HDMI IP is not "ticking" and thus the DISPC does not receive pixel clock from the HDMI IP. This would, for example, prevent any VSYNCs from happening, and would thus affect the users of omapdss. By using LDOON when the cable is disconnected we'll avoid the HW bug, but keep the HDMI working as usual from the user's point of view. Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/video')
-rw-r--r--drivers/video/omap2/dss/hdmi.c71
1 files changed, 67 insertions, 4 deletions
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index f3369cf..fadd6a0 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -29,6 +29,7 @@
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/string.h>
+#include <linux/gpio.h>
#include <video/omapdss.h>
#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
@@ -54,6 +55,9 @@ static struct {
u8 edid_set;
bool custom_set;
struct hdmi_config cfg;
+
+ int hpd_gpio;
+ bool phy_tx_enabled;
} hdmi;
/*
@@ -278,6 +282,47 @@ static int hdmi_pll_reset(void)
return 0;
}
+static int hdmi_check_hpd_state(void)
+{
+ unsigned long flags;
+ bool hpd;
+ int r;
+ /* this should be in ti_hdmi_4xxx_ip private data */
+ static DEFINE_SPINLOCK(phy_tx_lock);
+
+ spin_lock_irqsave(&phy_tx_lock, flags);
+
+ hpd = gpio_get_value(hdmi.hpd_gpio);
+
+ if (hpd == hdmi.phy_tx_enabled) {
+ spin_unlock_irqrestore(&phy_tx_lock, flags);
+ return 0;
+ }
+
+ if (hpd)
+ r = hdmi_set_phy_pwr(HDMI_PHYPWRCMD_TXON);
+ else
+ r = hdmi_set_phy_pwr(HDMI_PHYPWRCMD_LDOON);
+
+ if (r) {
+ DSSERR("Failed to %s PHY TX power\n",
+ hpd ? "enable" : "disable");
+ goto err;
+ }
+
+ hdmi.phy_tx_enabled = hpd;
+err:
+ spin_unlock_irqrestore(&phy_tx_lock, flags);
+ return r;
+}
+
+static irqreturn_t hpd_irq_handler(int irq, void *data)
+{
+ hdmi_check_hpd_state();
+
+ return IRQ_HANDLED;
+}
+
static int hdmi_phy_init(void)
{
u16 r = 0;
@@ -286,10 +331,6 @@ static int hdmi_phy_init(void)
if (r)
return r;
- r = hdmi_set_phy_pwr(HDMI_PHYPWRCMD_TXON);
- if (r)
- return r;
-
/*
* Read address 0 in order to get the SCP reset done completed
* Dummy access performed to make sure reset is done
@@ -311,6 +352,23 @@ static int hdmi_phy_init(void)
/* Write to phy address 3 to change the polarity control */
REG_FLD_MOD(HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27);
+ r = request_threaded_irq(gpio_to_irq(hdmi.hpd_gpio),
+ NULL, hpd_irq_handler,
+ IRQF_DISABLED | IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING, "hpd", NULL);
+ if (r) {
+ DSSERR("HPD IRQ request failed\n");
+ hdmi_set_phy_pwr(HDMI_PHYPWRCMD_OFF);
+ return r;
+ }
+
+ r = hdmi_check_hpd_state();
+ if (r) {
+ free_irq(gpio_to_irq(hdmi.hpd_gpio), NULL);
+ hdmi_set_phy_pwr(HDMI_PHYPWRCMD_OFF);
+ return r;
+ }
+
return 0;
}
@@ -361,7 +419,9 @@ static int hdmi_pll_program(struct hdmi_pll_info *fmt)
static void hdmi_phy_off(void)
{
+ free_irq(gpio_to_irq(hdmi.hpd_gpio), NULL);
hdmi_set_phy_pwr(HDMI_PHYPWRCMD_OFF);
+ hdmi.phy_tx_enabled = false;
}
static int hdmi_core_ddc_edid(u8 *pedid, int ext)
@@ -1236,12 +1296,15 @@ void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev)
int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev)
{
+ struct omap_dss_hdmi_data *priv = dssdev->data;
int r = 0;
DSSDBG("ENTER hdmi_display_enable\n");
mutex_lock(&hdmi.lock);
+ hdmi.hpd_gpio = priv->hpd_gpio;
+
r = omap_dss_start_device(dssdev);
if (r) {
DSSERR("failed to start device\n");