diff options
Diffstat (limited to 'drivers/video/fbdev/omap2/dss/hdmi4.c')
-rw-r--r-- | drivers/video/fbdev/omap2/dss/hdmi4.c | 113 |
1 files changed, 107 insertions, 6 deletions
diff --git a/drivers/video/fbdev/omap2/dss/hdmi4.c b/drivers/video/fbdev/omap2/dss/hdmi4.c index f5f7944a1fd..626aad2bef4 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi4.c +++ b/drivers/video/fbdev/omap2/dss/hdmi4.c @@ -81,8 +81,40 @@ 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; struct regulator *reg; if (hdmi.vdda_hdmi_dac_reg != NULL) @@ -96,6 +128,15 @@ static int hdmi_init_regulator(void) return PTR_ERR(reg); } + if (regulator_can_change_voltage(reg)) { + r = regulator_set_voltage(reg, 1800000, 1800000); + if (r) { + devm_regulator_put(reg); + DSSWARN("can't set the regulator voltage\n"); + return r; + } + } + hdmi.vdda_hdmi_dac_reg = reg; return 0; @@ -140,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); @@ -161,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 */ @@ -183,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); @@ -200,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); @@ -600,15 +658,44 @@ static void __exit hdmi_uninit_output(struct platform_device *pdev) omapdss_unregister_output(out); } +static int hdmi_probe_of(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct device_node *ep; + int r; + + ep = omapdss_of_get_first_endpoint(node); + if (!ep) + return 0; + + r = hdmi_parse_lanes_of(pdev, ep, &hdmi.phy); + if (r) + goto err; + + of_node_put(ep); + return 0; + +err: + of_node_put(ep); + return r; +} + /* HDMI HW IP initialisation */ static int omapdss_hdmihw_probe(struct platform_device *pdev) { int r; + int irq; hdmi.pdev = pdev; mutex_init(&hdmi.lock); + if (pdev->dev.of_node) { + r = hdmi_probe_of(pdev); + if (r) + return r; + } + r = hdmi_wp_init(pdev, &hdmi.wp); if (r) return r; @@ -631,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); |