Skip to content

Commit

Permalink
OMAPDSS: HDMI: PHY burnout fix
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Tomi Valkeinen committed Jan 26, 2012
1 parent aa74274 commit c49d005
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 4 deletions.
5 changes: 5 additions & 0 deletions arch/arm/mach-omap2/board-4430sdp.c
Original file line number Diff line number Diff line change
Expand Up @@ -732,13 +732,18 @@ static void sdp4430_lcd_init(void)
pr_err("%s: Could not get lcd2_reset_gpio\n", __func__);
}

static struct omap_dss_hdmi_data sdp4430_hdmi_data = {
.hpd_gpio = HDMI_GPIO_HPD,
};

static struct omap_dss_device sdp4430_hdmi_device = {
.name = "hdmi",
.driver_name = "hdmi_panel",
.type = OMAP_DISPLAY_TYPE_HDMI,
.platform_enable = sdp4430_panel_enable_hdmi,
.platform_disable = sdp4430_panel_disable_hdmi,
.channel = OMAP_DSS_CHANNEL_DIGIT,
.data = &sdp4430_hdmi_data,
};

static struct picodlp_panel_data sdp4430_picodlp_pdata = {
Expand Down
5 changes: 5 additions & 0 deletions arch/arm/mach-omap2/board-omap4panda.c
Original file line number Diff line number Diff line change
Expand Up @@ -502,13 +502,18 @@ static void omap4_panda_panel_disable_hdmi(struct omap_dss_device *dssdev)
gpio_free_array(panda_hdmi_gpios, ARRAY_SIZE(panda_hdmi_gpios));
}

static struct omap_dss_hdmi_data omap4_panda_hdmi_data = {
.hpd_gpio = HDMI_GPIO_HPD,
};

static struct omap_dss_device omap4_panda_hdmi_device = {
.name = "hdmi",
.driver_name = "hdmi_panel",
.type = OMAP_DISPLAY_TYPE_HDMI,
.platform_enable = omap4_panda_panel_enable_hdmi,
.platform_disable = omap4_panda_panel_disable_hdmi,
.channel = OMAP_DSS_CHANNEL_DIGIT,
.data = &omap4_panda_hdmi_data,
};

static struct omap_dss_device *omap4_panda_dss_devices[] = {
Expand Down
3 changes: 3 additions & 0 deletions drivers/video/omap2/dss/hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@ bool omapdss_hdmi_detect(void)

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");
Expand All @@ -509,6 +510,8 @@ int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev)
goto err0;
}

hdmi.ip_data.hpd_gpio = priv->hpd_gpio;

r = omap_dss_start_device(dssdev);
if (r) {
DSSERR("failed to start device\n");
Expand Down
4 changes: 4 additions & 0 deletions drivers/video/omap2/dss/ti_hdmi.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ struct hdmi_ip_data {
const struct ti_hdmi_ip_ops *ops;
struct hdmi_config cfg;
struct hdmi_pll_info pll_data;

/* ti_hdmi_4xxx_ip private data. These should be in a separate struct */
int hpd_gpio;
bool phy_tx_enabled;
};
int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data);
void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data);
Expand Down
68 changes: 64 additions & 4 deletions drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/seq_file.h>
#include <linux/gpio.h>

#include "ti_hdmi_4xxx_ip.h"
#include "dss.h"
Expand Down Expand Up @@ -223,6 +224,49 @@ void ti_hdmi_4xxx_pll_disable(struct hdmi_ip_data *ip_data)
hdmi_set_pll_pwr(ip_data, HDMI_PLLPWRCMD_ALLOFF);
}

static int hdmi_check_hpd_state(struct hdmi_ip_data *ip_data)
{
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(ip_data->hpd_gpio);

if (hpd == ip_data->phy_tx_enabled) {
spin_unlock_irqrestore(&phy_tx_lock, flags);
return 0;
}

if (hpd)
r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON);
else
r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);

if (r) {
DSSERR("Failed to %s PHY TX power\n",
hpd ? "enable" : "disable");
goto err;
}

ip_data->phy_tx_enabled = hpd;
err:
spin_unlock_irqrestore(&phy_tx_lock, flags);
return r;
}

static irqreturn_t hpd_irq_handler(int irq, void *data)
{
struct hdmi_ip_data *ip_data = data;

hdmi_check_hpd_state(ip_data);

return IRQ_HANDLED;
}

int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data)
{
u16 r = 0;
Expand All @@ -232,10 +276,6 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data)
if (r)
return r;

r = hdmi_set_phy_pwr(ip_data, 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
Expand All @@ -257,12 +297,32 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data)
/* Write to phy address 3 to change the polarity control */
REG_FLD_MOD(phy_base, HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27);

r = request_threaded_irq(gpio_to_irq(ip_data->hpd_gpio),
NULL, hpd_irq_handler,
IRQF_DISABLED | IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING, "hpd", ip_data);
if (r) {
DSSERR("HPD IRQ request failed\n");
hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
return r;
}

r = hdmi_check_hpd_state(ip_data);
if (r) {
free_irq(gpio_to_irq(ip_data->hpd_gpio), ip_data);
hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
return r;
}

return 0;
}

void ti_hdmi_4xxx_phy_disable(struct hdmi_ip_data *ip_data)
{
free_irq(gpio_to_irq(ip_data->hpd_gpio), ip_data);

hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
ip_data->phy_tx_enabled = false;
}

static int hdmi_core_ddc_init(struct hdmi_ip_data *ip_data)
Expand Down
5 changes: 5 additions & 0 deletions include/video/omapdss.h
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,11 @@ struct omap_dss_device {
int (*get_backlight)(struct omap_dss_device *dssdev);
};

struct omap_dss_hdmi_data
{
int hpd_gpio;
};

struct omap_dss_driver {
struct device_driver driver;

Expand Down

0 comments on commit c49d005

Please sign in to comment.