Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mattst88...
[sfrench/cifs-2.6.git] / drivers / gpu / drm / omapdrm / displays / connector-hdmi.c
index 79cb69f1acf5c68ceedc51623cfc20f58ca94cb9..d9d25df6fc1b5311cb1a97c6517fb3587f8bf708 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/platform_device.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
+#include <linux/mutex.h>
 
 #include <drm/drm_edid.h>
 
@@ -37,6 +38,10 @@ static const struct videomode hdmic_default_vm = {
 struct panel_drv_data {
        struct omap_dss_device dssdev;
        struct omap_dss_device *in;
+       void (*hpd_cb)(void *cb_data, enum drm_connector_status status);
+       void *hpd_cb_data;
+       bool hpd_enabled;
+       struct mutex hpd_lock;
 
        struct device *dev;
 
@@ -167,6 +172,70 @@ static bool hdmic_detect(struct omap_dss_device *dssdev)
                return in->ops.hdmi->detect(in);
 }
 
+static int hdmic_register_hpd_cb(struct omap_dss_device *dssdev,
+                                void (*cb)(void *cb_data,
+                                           enum drm_connector_status status),
+                                void *cb_data)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (gpio_is_valid(ddata->hpd_gpio)) {
+               mutex_lock(&ddata->hpd_lock);
+               ddata->hpd_cb = cb;
+               ddata->hpd_cb_data = cb_data;
+               mutex_unlock(&ddata->hpd_lock);
+               return 0;
+       } else if (in->ops.hdmi->register_hpd_cb) {
+               return in->ops.hdmi->register_hpd_cb(in, cb, cb_data);
+       }
+
+       return -ENOTSUPP;
+}
+
+static void hdmic_unregister_hpd_cb(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (gpio_is_valid(ddata->hpd_gpio)) {
+               mutex_lock(&ddata->hpd_lock);
+               ddata->hpd_cb = NULL;
+               ddata->hpd_cb_data = NULL;
+               mutex_unlock(&ddata->hpd_lock);
+       } else if (in->ops.hdmi->unregister_hpd_cb) {
+               in->ops.hdmi->unregister_hpd_cb(in);
+       }
+}
+
+static void hdmic_enable_hpd(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (gpio_is_valid(ddata->hpd_gpio)) {
+               mutex_lock(&ddata->hpd_lock);
+               ddata->hpd_enabled = true;
+               mutex_unlock(&ddata->hpd_lock);
+       } else if (in->ops.hdmi->enable_hpd) {
+               in->ops.hdmi->enable_hpd(in);
+       }
+}
+
+static void hdmic_disable_hpd(struct omap_dss_device *dssdev)
+{
+       struct panel_drv_data *ddata = to_panel_data(dssdev);
+       struct omap_dss_device *in = ddata->in;
+
+       if (gpio_is_valid(ddata->hpd_gpio)) {
+               mutex_lock(&ddata->hpd_lock);
+               ddata->hpd_enabled = false;
+               mutex_unlock(&ddata->hpd_lock);
+       } else if (in->ops.hdmi->disable_hpd) {
+               in->ops.hdmi->disable_hpd(in);
+       }
+}
+
 static int hdmic_set_hdmi_mode(struct omap_dss_device *dssdev, bool hdmi_mode)
 {
        struct panel_drv_data *ddata = to_panel_data(dssdev);
@@ -197,10 +266,34 @@ static struct omap_dss_driver hdmic_driver = {
 
        .read_edid              = hdmic_read_edid,
        .detect                 = hdmic_detect,
+       .register_hpd_cb        = hdmic_register_hpd_cb,
+       .unregister_hpd_cb      = hdmic_unregister_hpd_cb,
+       .enable_hpd             = hdmic_enable_hpd,
+       .disable_hpd            = hdmic_disable_hpd,
        .set_hdmi_mode          = hdmic_set_hdmi_mode,
        .set_hdmi_infoframe     = hdmic_set_infoframe,
 };
 
+static irqreturn_t hdmic_hpd_isr(int irq, void *data)
+{
+       struct panel_drv_data *ddata = data;
+
+       mutex_lock(&ddata->hpd_lock);
+       if (ddata->hpd_enabled && ddata->hpd_cb) {
+               enum drm_connector_status status;
+
+               if (hdmic_detect(&ddata->dssdev))
+                       status = connector_status_connected;
+               else
+                       status = connector_status_disconnected;
+
+               ddata->hpd_cb(ddata->hpd_cb_data, status);
+       }
+       mutex_unlock(&ddata->hpd_lock);
+
+       return IRQ_HANDLED;
+}
+
 static int hdmic_probe_of(struct platform_device *pdev)
 {
        struct panel_drv_data *ddata = platform_get_drvdata(pdev);
@@ -246,11 +339,22 @@ static int hdmic_probe(struct platform_device *pdev)
        if (r)
                return r;
 
+       mutex_init(&ddata->hpd_lock);
+
        if (gpio_is_valid(ddata->hpd_gpio)) {
                r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio,
                                GPIOF_DIR_IN, "hdmi_hpd");
                if (r)
                        goto err_reg;
+
+               r = devm_request_threaded_irq(&pdev->dev,
+                               gpio_to_irq(ddata->hpd_gpio),
+                               NULL, hdmic_hpd_isr,
+                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+                               IRQF_ONESHOT,
+                               "hdmic hpd", ddata);
+               if (r)
+                       goto err_reg;
        }
 
        ddata->vm = hdmic_default_vm;