aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomi Valkeinen <tomi.valkeinen@ti.com>2012-01-17 04:09:57 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-03-12 13:32:59 -0400
commit21189f03d3ec3a74d9949907c828410d7a9a86d5 (patch)
treeaaba0ce5e80357481b822ccc18fc26d598efb7ed
parent8d1b18d569d59c8aa95964e2e5e4c5d544928258 (diff)
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>
-rw-r--r--arch/arm/mach-omap2/board-4430sdp.c5
-rw-r--r--arch/arm/mach-omap2/board-omap4panda.c5
-rw-r--r--drivers/video/omap2/dss/hdmi.c71
-rw-r--r--include/video/omapdss.h5
4 files changed, 82 insertions, 4 deletions
diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c
index 0ee578a2251..14a5971d0d4 100644
--- a/arch/arm/mach-omap2/board-4430sdp.c
+++ b/arch/arm/mach-omap2/board-4430sdp.c
@@ -610,6 +610,10 @@ static void sdp4430_panel_disable_hdmi(struct omap_dss_device *dssdev)
610 gpio_free_array(sdp4430_hdmi_gpios, ARRAY_SIZE(sdp4430_hdmi_gpios)); 610 gpio_free_array(sdp4430_hdmi_gpios, ARRAY_SIZE(sdp4430_hdmi_gpios));
611} 611}
612 612
613static struct omap_dss_hdmi_data sdp4430_hdmi_data = {
614 .hpd_gpio = HDMI_GPIO_HPD,
615};
616
613static struct omap_dss_device sdp4430_hdmi_device = { 617static struct omap_dss_device sdp4430_hdmi_device = {
614 .name = "hdmi", 618 .name = "hdmi",
615 .driver_name = "hdmi_panel", 619 .driver_name = "hdmi_panel",
@@ -617,6 +621,7 @@ static struct omap_dss_device sdp4430_hdmi_device = {
617 .platform_enable = sdp4430_panel_enable_hdmi, 621 .platform_enable = sdp4430_panel_enable_hdmi,
618 .platform_disable = sdp4430_panel_disable_hdmi, 622 .platform_disable = sdp4430_panel_disable_hdmi,
619 .channel = OMAP_DSS_CHANNEL_DIGIT, 623 .channel = OMAP_DSS_CHANNEL_DIGIT,
624 .data = &sdp4430_hdmi_data,
620}; 625};
621 626
622static struct omap_dss_device *sdp4430_dss_devices[] = { 627static struct omap_dss_device *sdp4430_dss_devices[] = {
diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c
index 2b3f7e0f5d3..107dfc377a8 100644
--- a/arch/arm/mach-omap2/board-omap4panda.c
+++ b/arch/arm/mach-omap2/board-omap4panda.c
@@ -646,6 +646,10 @@ static void omap4_panda_panel_disable_hdmi(struct omap_dss_device *dssdev)
646 gpio_free_array(panda_hdmi_gpios, ARRAY_SIZE(panda_hdmi_gpios)); 646 gpio_free_array(panda_hdmi_gpios, ARRAY_SIZE(panda_hdmi_gpios));
647} 647}
648 648
649static struct omap_dss_hdmi_data omap4_panda_hdmi_data = {
650 .hpd_gpio = HDMI_GPIO_HPD,
651};
652
649static struct omap_dss_device omap4_panda_hdmi_device = { 653static struct omap_dss_device omap4_panda_hdmi_device = {
650 .name = "hdmi", 654 .name = "hdmi",
651 .driver_name = "hdmi_panel", 655 .driver_name = "hdmi_panel",
@@ -653,6 +657,7 @@ static struct omap_dss_device omap4_panda_hdmi_device = {
653 .platform_enable = omap4_panda_panel_enable_hdmi, 657 .platform_enable = omap4_panda_panel_enable_hdmi,
654 .platform_disable = omap4_panda_panel_disable_hdmi, 658 .platform_disable = omap4_panda_panel_disable_hdmi,
655 .channel = OMAP_DSS_CHANNEL_DIGIT, 659 .channel = OMAP_DSS_CHANNEL_DIGIT,
660 .data = &omap4_panda_hdmi_data,
656}; 661};
657 662
658static struct omap_dss_device *omap4_panda_dss_devices[] = { 663static struct omap_dss_device *omap4_panda_dss_devices[] = {
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index f3369cf3337..fadd6a0836c 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -29,6 +29,7 @@
29#include <linux/mutex.h> 29#include <linux/mutex.h>
30#include <linux/delay.h> 30#include <linux/delay.h>
31#include <linux/string.h> 31#include <linux/string.h>
32#include <linux/gpio.h>
32#include <video/omapdss.h> 33#include <video/omapdss.h>
33#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \ 34#if defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) || \
34 defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE) 35 defined(CONFIG_SND_OMAP_SOC_OMAP4_HDMI_MODULE)
@@ -54,6 +55,9 @@ static struct {
54 u8 edid_set; 55 u8 edid_set;
55 bool custom_set; 56 bool custom_set;
56 struct hdmi_config cfg; 57 struct hdmi_config cfg;
58
59 int hpd_gpio;
60 bool phy_tx_enabled;
57} hdmi; 61} hdmi;
58 62
59/* 63/*
@@ -278,6 +282,47 @@ static int hdmi_pll_reset(void)
278 return 0; 282 return 0;
279} 283}
280 284
285static int hdmi_check_hpd_state(void)
286{
287 unsigned long flags;
288 bool hpd;
289 int r;
290 /* this should be in ti_hdmi_4xxx_ip private data */
291 static DEFINE_SPINLOCK(phy_tx_lock);
292
293 spin_lock_irqsave(&phy_tx_lock, flags);
294
295 hpd = gpio_get_value(hdmi.hpd_gpio);
296
297 if (hpd == hdmi.phy_tx_enabled) {
298 spin_unlock_irqrestore(&phy_tx_lock, flags);
299 return 0;
300 }
301
302 if (hpd)
303 r = hdmi_set_phy_pwr(HDMI_PHYPWRCMD_TXON);
304 else
305 r = hdmi_set_phy_pwr(HDMI_PHYPWRCMD_LDOON);
306
307 if (r) {
308 DSSERR("Failed to %s PHY TX power\n",
309 hpd ? "enable" : "disable");
310 goto err;
311 }
312
313 hdmi.phy_tx_enabled = hpd;
314err:
315 spin_unlock_irqrestore(&phy_tx_lock, flags);
316 return r;
317}
318
319static irqreturn_t hpd_irq_handler(int irq, void *data)
320{
321 hdmi_check_hpd_state();
322
323 return IRQ_HANDLED;
324}
325
281static int hdmi_phy_init(void) 326static int hdmi_phy_init(void)
282{ 327{
283 u16 r = 0; 328 u16 r = 0;
@@ -286,10 +331,6 @@ static int hdmi_phy_init(void)
286 if (r) 331 if (r)
287 return r; 332 return r;
288 333
289 r = hdmi_set_phy_pwr(HDMI_PHYPWRCMD_TXON);
290 if (r)
291 return r;
292
293 /* 334 /*
294 * Read address 0 in order to get the SCP reset done completed 335 * Read address 0 in order to get the SCP reset done completed
295 * Dummy access performed to make sure reset is done 336 * Dummy access performed to make sure reset is done
@@ -311,6 +352,23 @@ static int hdmi_phy_init(void)
311 /* Write to phy address 3 to change the polarity control */ 352 /* Write to phy address 3 to change the polarity control */
312 REG_FLD_MOD(HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27); 353 REG_FLD_MOD(HDMI_TXPHY_PAD_CFG_CTRL, 0x1, 27, 27);
313 354
355 r = request_threaded_irq(gpio_to_irq(hdmi.hpd_gpio),
356 NULL, hpd_irq_handler,
357 IRQF_DISABLED | IRQF_TRIGGER_RISING |
358 IRQF_TRIGGER_FALLING, "hpd", NULL);
359 if (r) {
360 DSSERR("HPD IRQ request failed\n");
361 hdmi_set_phy_pwr(HDMI_PHYPWRCMD_OFF);
362 return r;
363 }
364
365 r = hdmi_check_hpd_state();
366 if (r) {
367 free_irq(gpio_to_irq(hdmi.hpd_gpio), NULL);
368 hdmi_set_phy_pwr(HDMI_PHYPWRCMD_OFF);
369 return r;
370 }
371
314 return 0; 372 return 0;
315} 373}
316 374
@@ -361,7 +419,9 @@ static int hdmi_pll_program(struct hdmi_pll_info *fmt)
361 419
362static void hdmi_phy_off(void) 420static void hdmi_phy_off(void)
363{ 421{
422 free_irq(gpio_to_irq(hdmi.hpd_gpio), NULL);
364 hdmi_set_phy_pwr(HDMI_PHYPWRCMD_OFF); 423 hdmi_set_phy_pwr(HDMI_PHYPWRCMD_OFF);
424 hdmi.phy_tx_enabled = false;
365} 425}
366 426
367static int hdmi_core_ddc_edid(u8 *pedid, int ext) 427static int hdmi_core_ddc_edid(u8 *pedid, int ext)
@@ -1236,12 +1296,15 @@ void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev)
1236 1296
1237int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev) 1297int omapdss_hdmi_display_enable(struct omap_dss_device *dssdev)
1238{ 1298{
1299 struct omap_dss_hdmi_data *priv = dssdev->data;
1239 int r = 0; 1300 int r = 0;
1240 1301
1241 DSSDBG("ENTER hdmi_display_enable\n"); 1302 DSSDBG("ENTER hdmi_display_enable\n");
1242 1303
1243 mutex_lock(&hdmi.lock); 1304 mutex_lock(&hdmi.lock);
1244 1305
1306 hdmi.hpd_gpio = priv->hpd_gpio;
1307
1245 r = omap_dss_start_device(dssdev); 1308 r = omap_dss_start_device(dssdev);
1246 if (r) { 1309 if (r) {
1247 DSSERR("failed to start device\n"); 1310 DSSERR("failed to start device\n");
diff --git a/include/video/omapdss.h b/include/video/omapdss.h
index 892b97f8e15..c0d8014f5cc 100644
--- a/include/video/omapdss.h
+++ b/include/video/omapdss.h
@@ -514,6 +514,11 @@ struct omap_dss_device {
514 int (*get_backlight)(struct omap_dss_device *dssdev); 514 int (*get_backlight)(struct omap_dss_device *dssdev);
515}; 515};
516 516
517struct omap_dss_hdmi_data
518{
519 int hpd_gpio;
520};
521
517struct omap_dss_driver { 522struct omap_dss_driver {
518 struct device_driver driver; 523 struct device_driver driver;
519 524