summaryrefslogtreecommitdiffstats
path: root/drivers/video/fbdev/omap2/dss/hdmi4.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/fbdev/omap2/dss/hdmi4.c')
-rw-r--r--drivers/video/fbdev/omap2/dss/hdmi4.c113
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);