[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