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