imx-drm: imx-hdmi: add hotplug support to HDMI component
authorRussell King <rmk+kernel@arm.linux.org.uk>
Sun, 3 Nov 2013 22:23:24 +0000 (22:23 +0000)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Mon, 24 Feb 2014 12:08:02 +0000 (12:08 +0000)
Add hotplug support.  We have to make the interrupt handler threaded so
we can call drm_helper_hpd_irq_event().  Keeping in mind that we will
want to share the interrupt with other HDMI interface drivers (eg, audio
and CEC) put the groundwork in now for that, rather than just using
IRQF_ONESHOT.

Also, we must not call drm_helper_hpd_irq_event() until we have fully
setup the connector; keep the interrupt(s) muted until after that point.

Acked-by: Philipp Zabel <p.zabel@pengutronix.de>
Acked-by: Shawn Guo <shawn.guo@linaro.org>
Reviewed-by: Fabio Estevam <fabio.estevam@freescale.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
drivers/staging/imx-drm/imx-hdmi.c

index 8c586453df1133f5fbaa7f4e59476e8ae20d9088..ab16aba7788db20162d41eef64ed0087bb4b4a3c 100644 (file)
@@ -120,6 +120,8 @@ struct imx_hdmi {
        struct clk *isfr_clk;
        struct clk *iahb_clk;
 
+       enum drm_connector_status connector_status;
+
        struct hdmi_data_info hdmi_data;
        int vic;
 
@@ -1301,9 +1303,6 @@ static int imx_hdmi_fb_registered(struct imx_hdmi *hdmi)
        /* Clear Hotplug interrupts */
        hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
 
-       /* Unmute interrupts */
-       hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
-
        return 0;
 }
 
@@ -1372,8 +1371,9 @@ static void imx_hdmi_poweroff(struct imx_hdmi *hdmi)
 static enum drm_connector_status imx_hdmi_connector_detect(struct drm_connector
                                                        *connector, bool force)
 {
-       /* FIXME */
-       return connector_status_connected;
+       struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi,
+                                            connector);
+       return hdmi->connector_status;
 }
 
 static int imx_hdmi_connector_get_modes(struct drm_connector *connector)
@@ -1487,6 +1487,18 @@ static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = {
        .best_encoder = imx_hdmi_connector_best_encoder,
 };
 
+static irqreturn_t imx_hdmi_hardirq(int irq, void *dev_id)
+{
+       struct imx_hdmi *hdmi = dev_id;
+       u8 intr_stat;
+
+       intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
+       if (intr_stat)
+               hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
+
+       return intr_stat ? IRQ_WAKE_THREAD : IRQ_NONE;
+}
+
 static irqreturn_t imx_hdmi_irq(int irq, void *dev_id)
 {
        struct imx_hdmi *hdmi = dev_id;
@@ -1503,17 +1515,21 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id)
 
                        hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
 
+                       hdmi->connector_status = connector_status_connected;
                        imx_hdmi_poweron(hdmi);
                } else {
                        dev_dbg(hdmi->dev, "EVENT=plugout\n");
 
                        hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD, HDMI_PHY_POL0);
 
+                       hdmi->connector_status = connector_status_disconnected;
                        imx_hdmi_poweroff(hdmi);
                }
+               drm_helper_hpd_irq_event(hdmi->connector.dev);
        }
 
        hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
+       hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
 
        return IRQ_HANDLED;
 }
@@ -1527,6 +1543,8 @@ static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi)
        if (ret)
                return ret;
 
+       hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
        drm_encoder_helper_add(&hdmi->encoder, &imx_hdmi_encoder_helper_funcs);
        drm_encoder_init(drm, &hdmi->encoder, &imx_hdmi_encoder_funcs,
                         DRM_MODE_ENCODER_TMDS);
@@ -1578,6 +1596,7 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
                return -ENOMEM;
 
        hdmi->dev = dev;
+       hdmi->connector_status = connector_status_disconnected;
        hdmi->sample_rate = 48000;
        hdmi->ratio = 100;
 
@@ -1601,8 +1620,9 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
        if (irq < 0)
                return -EINVAL;
 
-       ret = devm_request_irq(dev, irq, imx_hdmi_irq, 0,
-                              dev_name(dev), hdmi);
+       ret = devm_request_threaded_irq(dev, irq, imx_hdmi_hardirq,
+                                       imx_hdmi_irq, IRQF_SHARED,
+                                       dev_name(dev), hdmi);
        if (ret)
                return ret;
 
@@ -1678,6 +1698,9 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
        if (ret)
                goto err_iahb;
 
+       /* Unmute interrupts */
+       hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+
        dev_set_drvdata(dev, hdmi);
 
        return 0;
@@ -1695,6 +1718,9 @@ static void imx_hdmi_unbind(struct device *dev, struct device *master,
 {
        struct imx_hdmi *hdmi = dev_get_drvdata(dev);
 
+       /* Disable all interrupts */
+       hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
+
        hdmi->connector.funcs->destroy(&hdmi->connector);
        hdmi->encoder.funcs->destroy(&hdmi->encoder);