drivers/gpu/drm/via/via_display.c |    1 
 drivers/gpu/drm/via/via_drv.c     |   10 +
 drivers/gpu/drm/via/via_drv.h     |    2 
 drivers/gpu/drm/via/via_fb.c      |    1 
 drivers/gpu/drm/via/via_hdmi.c    |    2 
 drivers/gpu/drm/via/via_irq.c     |  233 ++++++++++++++++++++++++++++++++++++++
 6 files changed, 249 insertions(+)

New commits:
commit 02076320d157654b7bb97a7e1e5f9ac60d803b51
Author: James Simmons <jsimm...@infradead.org>
Date:   Sat Mar 30 14:26:03 2013 -0400

    Enabling HDMI support had a side effect of setting off a irq storm due to 
the driver not handling hot plug. So the solution to enable hot plug support.

diff --git a/drivers/gpu/drm/via/via_display.c 
b/drivers/gpu/drm/via/via_display.c
index 8be5eb2..a402475 100644
--- a/drivers/gpu/drm/via/via_display.c
+++ b/drivers/gpu/drm/via/via_display.c
@@ -565,6 +565,7 @@ void via_modeset_fini(struct drm_device *dev)
        struct drm_connector *connector, *ot;
        struct drm_encoder *encoder, *enct;
 
+       drm_kms_helper_poll_fini(dev);
        via_framebuffer_fini(dev);
 
        /* We need to cleanup the connectors before the encoders */
diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c
index 36fb3eb..323c363 100644
--- a/drivers/gpu/drm/via/via_drv.c
+++ b/drivers/gpu/drm/via/via_drv.c
@@ -270,6 +270,9 @@ static int via_driver_unload(struct drm_device *dev)
 
        drm_irq_uninstall(dev);
 
+       /* destroy work queue. */
+       destroy_workqueue(dev_priv->wq);
+
        bo = dev_priv->vq.bo;
        if (bo) {
                ttm_bo_unpin(bo, &dev_priv->vq);
@@ -380,6 +383,13 @@ via_driver_load(struct drm_device *dev, unsigned long 
chipset)
 
        via_engine_init(dev);
 
+       /* setup workqueue */
+       dev_priv->wq = create_workqueue("viadrm");
+       if (dev_priv->wq == NULL) {
+               DRM_ERROR("create_workqueue failed !\n");
+               goto out_err;
+       }
+
        ret = drm_irq_install(dev);
        if (ret)
                goto out_err;
diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h
index c39352d..6cd8034 100644
--- a/drivers/gpu/drm/via/via_drv.h
+++ b/drivers/gpu/drm/via/via_drv.h
@@ -145,6 +145,8 @@ struct drm_via_private {
        uint32_t num_fire_offsets;
 
        drm_via_irq_t via_irqs[VIA_NUM_IRQS];
+       struct work_struct hotplug_work;
+       struct workqueue_struct *wq;
        unsigned num_irqs;
        maskarray_t *irq_masks;
        uint32_t irq_enable_mask;
diff --git a/drivers/gpu/drm/via/via_fb.c b/drivers/gpu/drm/via/via_fb.c
index 15cbb88..2179761 100644
--- a/drivers/gpu/drm/via/via_fb.c
+++ b/drivers/gpu/drm/via/via_fb.c
@@ -1217,6 +1217,7 @@ via_framebuffer_init(struct drm_device *dev, struct 
drm_fb_helper **ptr)
        drm_fb_helper_single_add_all_connectors(helper);
        drm_helper_disable_unused_functions(dev);
        drm_fb_helper_initial_config(helper, 32);
+       drm_kms_helper_poll_init(dev);
        *ptr = helper;
 out_err:
        if (ret)
diff --git a/drivers/gpu/drm/via/via_hdmi.c b/drivers/gpu/drm/via/via_hdmi.c
index 398ea53..8caaf9f 100644
--- a/drivers/gpu/drm/via/via_hdmi.c
+++ b/drivers/gpu/drm/via/via_hdmi.c
@@ -680,6 +680,7 @@ via_hdmi_init(struct drm_device *dev, int diport)
        drm_connector_helper_add(&hdmi->base, &via_hdmi_connector_helper_funcs);
        drm_sysfs_connector_add(&hdmi->base);
 
+       hdmi->base.polled = DRM_CONNECTOR_POLL_HPD;
        hdmi->base.doublescan_allowed = false;
        switch (dev->pdev->device) {
        case PCI_DEVICE_ID_VIA_VT3157:
@@ -698,6 +699,7 @@ via_hdmi_init(struct drm_device *dev, int diport)
        drm_connector_helper_add(&dvi->base, &via_hdmi_connector_helper_funcs);
        drm_sysfs_connector_add(&dvi->base);
 
+       dvi->base.polled = DRM_CONNECTOR_POLL_HPD;
        dvi->base.doublescan_allowed = false;
        switch (dev->pdev->device) {
        case PCI_DEVICE_ID_VIA_VT3157:
diff --git a/drivers/gpu/drm/via/via_irq.c b/drivers/gpu/drm/via/via_irq.c
index e68245d..79ac545 100644
--- a/drivers/gpu/drm/via/via_irq.c
+++ b/drivers/gpu/drm/via/via_irq.c
@@ -69,6 +69,12 @@
 #define VIA_IRQ_DMA1_DD_STATUS         BIT(6)
 #define VIA_IRQ_DMA1_TD_STATUS         BIT(7)
 
+#define VIA_IRQ_LVDS_ENABLE            BIT(30)
+#define VIA_IRQ_TMDS_ENABLE            BIT(16)
+
+#define VIA_IRQ_LVDS_STATUS            BIT(27)
+#define VIA_IRQ_TMDS_STATUS            BIT(0)
+
 #define INTR_ENABLE_MASK (VIA_IRQ_DMA0_TD_ENABLE | VIA_IRQ_DMA1_TD_ENABLE | \
                        VIA_IRQ_DMA0_DD_ENABLE | VIA_IRQ_DMA1_DD_ENABLE | \
                        VIA_IRQ_IGA1_VSYNC_ENABLE | VIA_IRQ_IGA2_VSYNC_ENABLE)
@@ -85,6 +91,24 @@
                                VIA_IRQ_HQV0_STATUS | VIA_IRQ_HQV1_STATUS | \
                                INTR_STATUS_MASK)
 
+/* mmio 0x1280 IRQ enabe and status bits. */
+#define INTERRUPT_CTRL_REG3            0x1280
+
+/* MM1280[9], internal TMDS interrupt status = SR3E[6] */
+#define INTERRUPT_TMDS_STATUS          0x200
+/* MM1280[30], internal TMDS interrupt control = SR3E[7] */
+#define INTERNAL_TMDS_INT_CONTROL      0x40000000
+
+#define VIA_IRQ_DP1_ENABLE             BIT(24)
+#define VIA_IRQ_DP2_ENABLE             BIT(26)
+#define VIA_IRQ_IN_TMDS_ENABLE         BIT(30)
+#define VIA_IRQ_CRT_ENABLE             BIT(20)
+
+#define VIA_IRQ_DP1_STATUS             BIT(11)
+#define VIA_IRQ_DP2_STATUS             BIT(13)
+#define VIA_IRQ_IN_TMDS_STATUS         BIT(9)
+#define VIA_IRQ_CRT_STATUS             BIT(4)
+
 /*
  * Device-specific IRQs go here. This type might need to be extended with
  * the register if there are multiple IRQ control registers.
@@ -113,6 +137,128 @@ static maskarray_t via_pro_group_a_irqs[] = {
 static int via_num_pro_group_a = ARRAY_SIZE(via_pro_group_a_irqs);
 static int via_irqmap_pro_group_a[] = {0, 1, -1, 2, -1, 3};
 
+static irqreturn_t
+via_hpd_irq_process(struct drm_via_private *dev_priv)
+{
+       uint32_t mm_1280 = VIA_READ(0x1280);
+       uint32_t mm_200 = VIA_READ(0x200);
+       uint32_t mm_c730, mm_c7b0;
+       irqreturn_t ret = IRQ_NONE;
+
+       /* DVI sense using sequence register */
+       if (vga_rseq(VGABASE, 0x2B) & BIT(6)) {
+               DRM_DEBUG("VIA_IRQ_DVI_SENSE_IRQ!\n");
+               ret = IRQ_HANDLED;
+       }
+
+       /* LVDS sense using sequence register */
+       if (vga_rseq(VGABASE, 0x2B) & BIT(4)) {
+               DRM_DEBUG("VIA_IRQ_LCD_SENSE_IRQ!\n");
+               ret = IRQ_HANDLED;
+       }
+
+       /* CRT sense interrupt */
+       if (vga_rseq(VGABASE, 0x2B) & BIT(2)) {
+               DRM_DEBUG("VIA_IRQ_VGA_SENSE_IRQ!\n");
+               ret = IRQ_HANDLED;
+       }
+
+       /* External LVDS device sense */
+       if (mm_200 & VIA_IRQ_LVDS_ENABLE) {
+               if (mm_200 & VIA_IRQ_LVDS_STATUS) {
+                       DRM_DEBUG("VIA_IRQ_LVDS_SENSE_IRQ!\n");
+                       ret = IRQ_HANDLED;
+               }
+       }
+
+       /* External DVI sense */
+       if (mm_200 & VIA_IRQ_TMDS_ENABLE) {
+               if (mm_200 & VIA_IRQ_TMDS_STATUS) {
+                       DRM_DEBUG("VIA_IRQ_TMDS_SENSE_IRQ!\n");
+                       ret = IRQ_HANDLED;
+               }
+       }
+
+       /* clear interrupt status on 0x200. */
+       VIA_WRITE(0x200, mm_200);
+
+       /* CRT sense */
+       if (mm_1280 & VIA_IRQ_CRT_ENABLE) {
+               if (mm_1280 & VIA_IRQ_CRT_STATUS) {
+                       DRM_DEBUG("VIA_IRQ_CRT_HOT_PLUG!\n");
+               }
+       }
+
+       /* DP1 or Internal HDMI sense */
+       if (mm_1280 & VIA_IRQ_DP1_ENABLE) {
+               if (mm_1280 & VIA_IRQ_DP1_STATUS) {
+                       mm_c730 = VIA_READ(0xc730);
+
+                       switch (mm_c730 & 0xC0000000) {
+                       case VIA_IRQ_DP_HOT_IRQ:
+                               DRM_DEBUG("VIA_IRQ_DP1_HOT_IRQ!\n");
+                               break;
+
+                       case VIA_IRQ_DP_HOT_UNPLUG:
+                               DRM_DEBUG("VIA_IRQ_DP1(HDMI)_HOT_UNPLUG!\n");
+                               break;
+
+                       case VIA_IRQ_DP_HOT_PLUG:
+                               DRM_DEBUG("VIA_IRQ_DP1(HDMI)_HOT_PLUG!\n");
+                               break;
+
+                       case VIA_IRQ_DP_NO_INT:
+                               DRM_DEBUG("VIA_IRQ_DP1_NO_INT!\n");
+                               break;
+                       }
+                       ret = IRQ_HANDLED;
+               }
+       }
+
+       /* DP2 sense */
+       if (mm_1280 & VIA_IRQ_DP2_ENABLE) {
+               if (mm_1280 & VIA_IRQ_DP2_STATUS) {
+                       mm_c7b0 = VIA_READ(0xc7b0);
+
+                       switch (mm_c7b0 & 0xC0000000) {
+                       case VIA_IRQ_DP_HOT_IRQ:
+                               DRM_DEBUG("VIA_IRQ_DP2_HOT_IRQ!\n");
+                               break;
+
+                       case VIA_IRQ_DP_HOT_UNPLUG:
+                               DRM_DEBUG("VIA_IRQ_DP2_HOT_UNPLUG!\n");
+                               break;
+
+                       case VIA_IRQ_DP_HOT_PLUG:
+                               DRM_DEBUG("VIA_IRQ_DP2_HOT_PLUG!\n");
+                               break;
+
+                       case VIA_IRQ_DP_NO_INT:
+                               DRM_DEBUG("VIA_IRQ_DP2_NO_INT!\n");
+                               break;
+                       }
+                       ret = IRQ_HANDLED;
+               }
+       }
+
+       /* internal TMDS sense */
+       if ((dev_priv->dev->pci_device != PCI_DEVICE_ID_VIA_VX875) ||
+           (dev_priv->dev->pci_device != PCI_DEVICE_ID_VIA_VX900)) {
+               if (VIA_IRQ_IN_TMDS_ENABLE & mm_1280) {
+                       if (VIA_IRQ_IN_TMDS_STATUS & mm_1280) {
+                               ret = IRQ_HANDLED;
+                       }
+               }
+       }
+
+       /* clear interrupt status on 0x1280. */
+       VIA_WRITE(0x1280, mm_1280);
+
+       if (ret == IRQ_HANDLED)
+               queue_work(dev_priv->wq, &dev_priv->hotplug_work);
+       return ret;
+}
+
 irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS)
 {
        struct drm_device *dev = (struct drm_device *) arg;
@@ -122,6 +268,10 @@ irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS)
        irqreturn_t ret = IRQ_NONE;
        int i;
 
+       /* Handle hot plug if KMS available */
+       if (drm_core_check_feature(dev, DRIVER_MODESET))
+               ret = via_hpd_irq_process(dev_priv);
+
        if (status & VIA_IRQ_IGA1_VSYNC_STATUS) {
                drm_handle_vblank(dev, 0);
                ret = IRQ_HANDLED;
@@ -195,6 +345,74 @@ via_disable_vblank(struct drm_device *dev, int crtc)
        VIA_WRITE(INTERRUPT_CTRL_REG1, status);
 }
 
+/**
+ * when we set the irq mask enable bit, the irq status bit will be enabled
+ * as well, whether the device was connected or not, so we then trigger
+ * call the interrupt right now. so we should write 1 to clear the status
+ * bit when enable irq mask.
+ */
+void
+via_hpd_irq_state(struct drm_via_private *dev_priv, bool enable)
+{
+       uint32_t mask = BIT(7) | BIT(5) | BIT(3) | BIT(1);
+       uint32_t value = (enable ? mask : 0);
+       uint32_t mm_1280 = VIA_READ(0x1280);
+       uint32_t mm_200 = VIA_READ(0x200);
+
+       /* Turn off/on DVI sense [7], LVDS sense [5], CRT sense [3],
+        * and CRT hotplug [1] */
+       svga_wseq_mask(VGABASE, 0x2B, value, mask);
+
+       /* Handle external LVDS */
+       mask = VIA_IRQ_LVDS_ENABLE | VIA_IRQ_LVDS_STATUS;
+       /* Handle external TMDS on DVP1 port */
+       mask |= VIA_IRQ_TMDS_ENABLE | VIA_IRQ_TMDS_STATUS;
+
+       if (enable)
+               mm_200 |= mask;
+       else
+               mm_200 &= ~mask;
+
+       /**
+        * only when 0x200[31] = 1 can these IRQs can be triggered.
+        */
+       mask = VIA_IRQ_CRT_ENABLE | VIA_IRQ_CRT_STATUS;
+
+       if ((dev_priv->dev->pci_device != PCI_DEVICE_ID_VIA_VX875) ||
+           (dev_priv->dev->pci_device != PCI_DEVICE_ID_VIA_VX900)) {
+               /* Internal DVI - DFPL port */
+               mask |= VIA_IRQ_IN_TMDS_ENABLE | VIA_IRQ_IN_TMDS_STATUS;
+       } else {
+               /* For both HDMI encoder and DisplayPort */
+               mask |= VIA_IRQ_DP1_ENABLE | VIA_IRQ_DP1_STATUS;
+               mask |= VIA_IRQ_DP2_ENABLE | VIA_IRQ_DP2_STATUS;
+       }
+
+       if (enable)
+               mm_1280 |= mask;
+       else
+               mm_1280 &= ~mask;
+
+       VIA_WRITE(0x1280, mm_1280);
+       VIA_WRITE(0x200, mm_200);
+}
+
+/*
+ * Handle hotplug events outside the interrupt handler proper.
+ */
+static void
+via_hotplug_work_func(struct work_struct *work)
+{
+       struct drm_via_private *dev_priv = container_of(work,
+               struct drm_via_private, hotplug_work);
+       struct drm_device *dev = dev_priv->dev;
+
+       DRM_DEBUG("Sending Hotplug event\n");
+
+       /* Fire off a uevent and let userspace tell us what to do */
+       drm_helper_hpd_irq_event(dev);
+}
+
 void
 via_driver_irq_preinstall(struct drm_device *dev)
 {
@@ -238,6 +456,18 @@ via_driver_irq_preinstall(struct drm_device *dev)
        /* Acknowledge interrupts */
        status = VIA_READ(INTERRUPT_CTRL_REG1);
        VIA_WRITE(INTERRUPT_CTRL_REG1, status | dev_priv->irq_pending_mask);
+
+       /* Clear hotplug settings */
+       if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+               dev_priv->irq_pending_mask |= VIA_IRQ_TMDS_STATUS | 
VIA_IRQ_LVDS_STATUS;
+               dev_priv->irq_enable_mask |= VIA_IRQ_TMDS_ENABLE | 
VIA_IRQ_LVDS_ENABLE;
+
+               INIT_WORK(&dev_priv->hotplug_work, via_hotplug_work_func);
+
+               via_hpd_irq_state(dev_priv, true);
+
+               status = via_hpd_irq_process(dev_priv);
+       }
 }
 
 int
@@ -264,6 +494,9 @@ via_driver_irq_uninstall(struct drm_device *dev)
        status = VIA_READ(INTERRUPT_CTRL_REG1);
        VIA_WRITE(INTERRUPT_CTRL_REG1, status &
                  ~(VIA_IRQ_IGA1_VSYNC_ENABLE | dev_priv->irq_enable_mask));
+
+       if (drm_core_check_feature(dev, DRIVER_MODESET))
+               via_hpd_irq_state(dev_priv, false);
 }
 
 static int
_______________________________________________
Openchrome-devel mailing list
Openchrome-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/openchrome-devel

Reply via email to