OMAPDSS: HDMI: move irq & phy pwr handling

HDMI IRQ handling was moved into hdmi_phy.c when restructuring the HDMI
driver. While this worked fine, it's not correct.

The HDMI IRQ handling should be either in the hdmi_wp, or in the main
hdmi driver. This patch moves the handling to the main hdmi driver, as I
feel it's a more appropriate choice.

This move also requires changing the handling of the PHY power, as that
was partly handled in the IRQ handler. The PHY power is handled via the
WP module. An option would be to give HDMI PHY driver function pointers
that it could use to manage the PHY power, but as the PHY power is not
needed to access the PHY registers, the handling was also moved to the
main HDMI driver. This could be changed later if need be.

Note that there's slightly similar power issue with the PLL: the HDMI
PLLs power is also handled via the WP module. For now, the PLL power
handling is still done inside the PLL driver.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
This commit is contained in:
Tomi Valkeinen 2013-10-28 11:47:34 +02:00
parent 543e761fb9
commit dcf5f7299e
3 changed files with 71 additions and 80 deletions

View File

@ -351,8 +351,6 @@ struct hdmi_pll_data {
struct hdmi_phy_data {
void __iomem *base;
int irq;
u8 lane_function[4];
u8 lane_polarity[4];
};
@ -420,9 +418,7 @@ void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy);
int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll);
/* HDMI PHY funcs */
int hdmi_phy_enable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp,
struct hdmi_config *cfg);
void hdmi_phy_disable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp);
int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg);
void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s);
int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy);
int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes);

View File

@ -81,6 +81,37 @@ static void hdmi_runtime_put(void)
WARN_ON(r < 0 && r != -ENOSYS);
}
static irqreturn_t hdmi_irq_handler(int irq, void *data)
{
struct hdmi_wp_data *wp = data;
u32 irqstatus;
irqstatus = hdmi_wp_get_irqstatus(wp);
hdmi_wp_set_irqstatus(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_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
HDMI_IRQ_LINK_DISCONNECT);
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
} else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
} else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
}
return IRQ_HANDLED;
}
static int hdmi_init_regulator(void)
{
int r;
@ -150,11 +181,16 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
struct omap_video_timings *p;
struct omap_overlay_manager *mgr = hdmi.output.manager;
unsigned long phy;
struct hdmi_wp_data *wp = &hdmi.wp;
r = hdmi_power_on_core(dssdev);
if (r)
return r;
/* disable and clear irqs */
hdmi_wp_clear_irqenable(wp, 0xffffffff);
hdmi_wp_set_irqstatus(wp, 0xffffffff);
p = &hdmi.cfg.timings;
DSSDBG("hdmi_power_on x_res= %d y_res = %d\n", p->x_res, p->y_res);
@ -171,12 +207,16 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
goto err_pll_enable;
}
r = hdmi_phy_enable(&hdmi.phy, &hdmi.wp, &hdmi.cfg);
r = hdmi_phy_configure(&hdmi.phy, &hdmi.cfg);
if (r) {
DSSDBG("Failed to start PHY\n");
goto err_phy_enable;
DSSDBG("Failed to configure PHY\n");
goto err_phy_cfg;
}
r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
if (r)
goto err_phy_pwr;
hdmi4_configure(&hdmi.core, &hdmi.wp, &hdmi.cfg);
/* bypass TV gamma table */
@ -193,13 +233,17 @@ static int hdmi_power_on_full(struct omap_dss_device *dssdev)
if (r)
goto err_mgr_enable;
hdmi_wp_set_irqenable(wp,
HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
return 0;
err_mgr_enable:
hdmi_wp_video_stop(&hdmi.wp);
err_vid_enable:
hdmi_phy_disable(&hdmi.phy, &hdmi.wp);
err_phy_enable:
err_phy_cfg:
hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
err_phy_pwr:
hdmi_pll_disable(&hdmi.pll, &hdmi.wp);
err_pll_enable:
hdmi_power_off_core(dssdev);
@ -210,10 +254,14 @@ static void hdmi_power_off_full(struct omap_dss_device *dssdev)
{
struct omap_overlay_manager *mgr = hdmi.output.manager;
hdmi_wp_clear_irqenable(&hdmi.wp, 0xffffffff);
dss_mgr_disable(mgr);
hdmi_wp_video_stop(&hdmi.wp);
hdmi_phy_disable(&hdmi.phy, &hdmi.wp);
hdmi_wp_set_phy_pwr(&hdmi.wp, HDMI_PHYPWRCMD_OFF);
hdmi_pll_disable(&hdmi.pll, &hdmi.wp);
hdmi_power_off_core(dssdev);
@ -636,6 +684,7 @@ err:
static int omapdss_hdmihw_probe(struct platform_device *pdev)
{
int r;
int irq;
hdmi.pdev = pdev;
@ -669,6 +718,20 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
return r;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
DSSERR("platform_get_irq failed\n");
return -ENODEV;
}
r = devm_request_threaded_irq(&pdev->dev, irq,
NULL, hdmi_irq_handler,
IRQF_ONESHOT, "OMAP HDMI", &hdmi.wp);
if (r) {
DSSERR("HDMI IRQ request failed\n");
return r;
}
pm_runtime_enable(&pdev->dev);
hdmi_init_output(pdev);

View File

@ -28,37 +28,6 @@ void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s)
DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL);
}
static irqreturn_t hdmi_irq_handler(int irq, void *data)
{
struct hdmi_wp_data *wp = data;
u32 irqstatus;
irqstatus = hdmi_wp_get_irqstatus(wp);
hdmi_wp_set_irqstatus(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_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
hdmi_wp_set_irqstatus(wp, HDMI_IRQ_LINK_CONNECT |
HDMI_IRQ_LINK_DISCONNECT);
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
} else if (irqstatus & HDMI_IRQ_LINK_CONNECT) {
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_TXON);
} else if (irqstatus & HDMI_IRQ_LINK_DISCONNECT) {
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
}
return IRQ_HANDLED;
}
int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes)
{
int i;
@ -150,21 +119,8 @@ static void hdmi_phy_configure_lanes(struct hdmi_phy_data *phy)
REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, pol_val, 30, 27);
}
int hdmi_phy_enable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp,
struct hdmi_config *cfg)
int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg)
{
u16 r = 0;
u32 irqstatus;
hdmi_wp_clear_irqenable(wp, 0xffffffff);
irqstatus = hdmi_wp_get_irqstatus(wp);
hdmi_wp_set_irqstatus(wp, irqstatus);
r = hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_LDOON);
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
@ -185,27 +141,9 @@ int hdmi_phy_enable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp,
hdmi_phy_configure_lanes(phy);
r = request_threaded_irq(phy->irq, NULL, hdmi_irq_handler,
IRQF_ONESHOT, "OMAP HDMI", wp);
if (r) {
DSSERR("HDMI IRQ request failed\n");
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
return r;
}
hdmi_wp_set_irqenable(wp,
HDMI_IRQ_LINK_CONNECT | HDMI_IRQ_LINK_DISCONNECT);
return 0;
}
void hdmi_phy_disable(struct hdmi_phy_data *phy, struct hdmi_wp_data *wp)
{
free_irq(phy->irq, wp);
hdmi_wp_set_phy_pwr(wp, HDMI_PHYPWRCMD_OFF);
}
#define PHY_OFFSET 0x300
#define PHY_SIZE 0x100
@ -240,11 +178,5 @@ int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy)
return -ENOMEM;
}
phy->irq = platform_get_irq(pdev, 0);
if (phy->irq < 0) {
DSSERR("platform_get_irq failed\n");
return -ENODEV;
}
return 0;
}