[Openchrome-devel] drm-openchrome: drivers/gpu/drm

James Simmons jsimmons at kemper.freedesktop.org
Sat Mar 30 11:26:16 PDT 2013


 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 <jsimmons at 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


More information about the Openchrome-devel mailing list