summaryrefslogtreecommitdiffstats
path: root/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
diff options
context:
space:
mode:
authorTomi Valkeinen2013-06-06 12:08:35 +0200
committerTomi Valkeinen2013-06-17 13:00:54 +0200
commitddb1d5ca99f515442592b487171481dbf3a309d5 (patch)
tree1609a35b8e43ef3fc631880bd895505c3af53a59 /drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
parentOMAPFB: use EPROBE_DEFER if default display is not present (diff)
downloadkernel-qcow2-linux-ddb1d5ca99f515442592b487171481dbf3a309d5.tar.gz
kernel-qcow2-linux-ddb1d5ca99f515442592b487171481dbf3a309d5.tar.xz
kernel-qcow2-linux-ddb1d5ca99f515442592b487171481dbf3a309d5.zip
OMAPDSS: HDMI: clean up PHY power handling
The TRM tells to set PHY to TXON only after getting LINK_CONNECT, and to set PHY to OFF or LDOON after getting LINK_DISCONNECT, in order to avoid damage to the PHY. We don't currently do it quite like that. Instead of using the HDMI interrupts, we use HPD signal. This works, but is not actually quite correct, as HPD comes at a different time than LINK_CONNECT and LINK_DISCONNECT interrupts. Also, the HPD GPIO is a property of the TPD level shifter, not HDMI IP, so handling the GPIO in the HDMI driver is wrong. This patch implements the PHY power handling correctly, using the interrupts. There is a corner case that causes some additional difficulties: we may get both LINK_CONNECT and LINK_DISCONNECT interrupts at the same time. This is handled in the code by retrying: turning off the PHY, clearing the interrupt status, and re-enabling the PHY. This causes a new LINK_CONNECT interrupt to happen if a cable is connected. Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Diffstat (limited to 'drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c')
-rw-r--r--drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c81
1 files changed, 43 insertions, 38 deletions
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
index e18b222ed739..052f2db35d62 100644
--- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
@@ -38,6 +38,9 @@
#include "dss.h"
#include "dss_features.h"
+#define HDMI_IRQ_LINK_CONNECT (1 << 25)
+#define HDMI_IRQ_LINK_DISCONNECT (1 << 26)
+
static inline void hdmi_write_reg(void __iomem *base_addr,
const u16 idx, u32 val)
{
@@ -233,37 +236,39 @@ 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)
+static irqreturn_t hdmi_irq_handler(int irq, void *data)
{
- bool hpd;
- int r;
-
- mutex_lock(&ip_data->lock);
-
- hpd = gpio_get_value(ip_data->hpd_gpio);
+ struct hdmi_ip_data *ip_data = data;
+ void __iomem *wp_base = hdmi_wp_base(ip_data);
+ u32 irqstatus;
+
+ irqstatus = hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS);
+ hdmi_write_reg(wp_base, HDMI_WP_IRQSTATUS, irqstatus);
+ /* flush posted write */
+ hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS);
+
+ if ((irqstatus & HDMI_IRQ_LINK_CONNECT) &&
+ irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
+ /*
+ * If we get both connect and disconnect interrupts at the same
+ * time, turn off the PHY, clear interrupts, and restart, which
+ * raises connect interrupt if a cable is connected, or nothing
+ * if cable is not connected.
+ */
+ hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
- if (hpd)
- r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON);
- else
- r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
+ hdmi_write_reg(wp_base, HDMI_WP_IRQSTATUS,
+ HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
+ /* flush posted write */
+ hdmi_read_reg(wp_base, HDMI_WP_IRQSTATUS);
- if (r) {
- DSSERR("Failed to %s PHY TX power\n",
- hpd ? "enable" : "disable");
- goto err;
+ hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
+ } else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
+ hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_TXON);
+ } else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
+ hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
}
-err:
- mutex_unlock(&ip_data->lock);
- 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;
}
@@ -272,6 +277,12 @@ int ti_hdmi_4xxx_phy_enable(struct hdmi_ip_data *ip_data)
u16 r = 0;
void __iomem *phy_base = hdmi_phy_base(ip_data);
+ hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQENABLE_CLR,
+ 0xffffffff);
+
+ hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQSTATUS,
+ HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
+
r = hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_LDOON);
if (r)
return r;
@@ -297,29 +308,23 @@ 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_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
- IRQF_ONESHOT, "hpd", ip_data);
+ r = request_threaded_irq(ip_data->irq, NULL, hdmi_irq_handler,
+ IRQF_ONESHOT, "OMAP HDMI", ip_data);
if (r) {
- DSSERR("HPD IRQ request failed\n");
+ DSSERR("HDMI 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;
- }
+ hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_IRQENABLE_SET,
+ HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
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);
+ free_irq(ip_data->irq, ip_data);
hdmi_set_phy_pwr(ip_data, HDMI_PHYPWRCMD_OFF);
}