drm/radeon/kms: enable hpd support
authorAlex Deucher <alexdeucher@gmail.com>
Fri, 4 Dec 2009 21:56:37 +0000 (16:56 -0500)
committerDave Airlie <airlied@redhat.com>
Tue, 8 Dec 2009 00:48:22 +0000 (10:48 +1000)
This enabled interrupt driven hpd support for all
radeon chips.  Assuming the hpd pin is wired up
correctly, the driver will generate uevents on
digital monitor connect and disconnect and retrain
DP monitors automatically.

Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/radeon/atombios_dp.c
drivers/gpu/drm/radeon/r100.c
drivers/gpu/drm/radeon/r600.c
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_connectors.c
drivers/gpu/drm/radeon/radeon_device.c
drivers/gpu/drm/radeon/radeon_display.c
drivers/gpu/drm/radeon/radeon_irq_kms.c
drivers/gpu/drm/radeon/radeon_mode.h
drivers/gpu/drm/radeon/rs600.c

index 784ba80afcb6516ea1d3b1ec6e723a7d4066dab9..0d63c4436e7cdc0db11a99657463d6f8537becdb 100644 (file)
@@ -507,6 +507,18 @@ static bool atom_dp_get_link_status(struct radeon_connector *radeon_connector,
        return true;
 }
 
+bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector)
+{
+       struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
+       u8 link_status[DP_LINK_STATUS_SIZE];
+
+       if (!atom_dp_get_link_status(radeon_connector, link_status))
+               return false;
+       if (dp_channel_eq_ok(link_status, dig_connector->dp_lane_count))
+               return false;
+       return true;
+}
+
 static void dp_set_power(struct radeon_connector *radeon_connector, u8 power_state)
 {
        struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
index 2b534c528aafeea9b1dd23089531b27950e79c84..b7baf16c11d7f144b81de1cc006657ad3b9d272f 100644 (file)
@@ -289,6 +289,7 @@ static inline uint32_t r100_irq_ack(struct radeon_device *rdev)
 int r100_irq_process(struct radeon_device *rdev)
 {
        uint32_t status, msi_rearm;
+       bool queue_hotplug = false;
 
        status = r100_irq_ack(rdev);
        if (!status) {
@@ -310,13 +311,17 @@ int r100_irq_process(struct radeon_device *rdev)
                        drm_handle_vblank(rdev->ddev, 1);
                }
                if (status & RADEON_FP_DETECT_STAT) {
-                       DRM_INFO("HPD1\n");
+                       queue_hotplug = true;
+                       DRM_DEBUG("HPD1\n");
                }
                if (status & RADEON_FP2_DETECT_STAT) {
-                       DRM_INFO("HPD2\n");
+                       queue_hotplug = true;
+                       DRM_DEBUG("HPD2\n");
                }
                status = r100_irq_ack(rdev);
        }
+       if (queue_hotplug)
+               queue_work(rdev->wq, &rdev->hotplug_work);
        if (rdev->msi_enabled) {
                switch (rdev->family) {
                case CHIP_RS400:
index c11715fb29c743bb257ea1bcbec86c1576583737..250ec3fe1a165448e2ae0d368f206dfafa68581e 100644 (file)
@@ -2674,6 +2674,7 @@ int r600_irq_process(struct radeon_device *rdev)
        u32 last_entry = rdev->ih.ring_size - 16;
        u32 ring_index, disp_int, disp_int_cont, disp_int_cont2;
        unsigned long flags;
+       bool queue_hotplug = false;
 
        DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
 
@@ -2745,37 +2746,43 @@ restart_ih:
                        case 0:
                                if (disp_int & DC_HPD1_INTERRUPT) {
                                        disp_int &= ~DC_HPD1_INTERRUPT;
-                                       DRM_INFO("IH: HPD1\n");
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD1\n");
                                }
                                break;
                        case 1:
                                if (disp_int & DC_HPD2_INTERRUPT) {
                                        disp_int &= ~DC_HPD2_INTERRUPT;
-                                       DRM_INFO("IH: HPD2\n");
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD2\n");
                                }
                                break;
                        case 4:
                                if (disp_int_cont & DC_HPD3_INTERRUPT) {
                                        disp_int_cont &= ~DC_HPD3_INTERRUPT;
-                                       DRM_INFO("IH: HPD3\n");
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD3\n");
                                }
                                break;
                        case 5:
                                if (disp_int_cont & DC_HPD4_INTERRUPT) {
                                        disp_int_cont &= ~DC_HPD4_INTERRUPT;
-                                       DRM_INFO("IH: HPD4\n");
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD4\n");
                                }
                                break;
                        case 10:
                                if (disp_int_cont2 & DC_HPD5_INTERRUPT) {
                                        disp_int_cont &= ~DC_HPD5_INTERRUPT;
-                                       DRM_INFO("IH: HPD5\n");
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD5\n");
                                }
                                break;
                        case 12:
                                if (disp_int_cont2 & DC_HPD6_INTERRUPT) {
                                        disp_int_cont &= ~DC_HPD6_INTERRUPT;
-                                       DRM_INFO("IH: HPD6\n");
+                                       queue_hotplug = true;
+                                       DRM_DEBUG("IH: HPD6\n");
                                }
                                break;
                        default:
@@ -2807,6 +2814,8 @@ restart_ih:
        wptr = r600_get_ih_wptr(rdev);
        if (wptr != rdev->ih.wptr)
                goto restart_ih;
+       if (queue_hotplug)
+               queue_work(rdev->wq, &rdev->hotplug_work);
        rdev->ih.rptr = rptr;
        WREG32(IH_RB_RPTR, rdev->ih.rptr);
        spin_unlock_irqrestore(&rdev->ih.lock, flags);
index 29c6e0af3755e92802cfae496b3a21236156b51c..a15cf9ceb9a72357691f0a485a009fa61b5b584f 100644 (file)
@@ -809,6 +809,8 @@ struct radeon_device {
        struct r600_blit r600_blit;
        int msi_enabled; /* msi enabled */
        struct r600_ih ih; /* r6/700 interrupt ring */
+       struct workqueue_struct *wq;
+       struct work_struct hotplug_work;
 };
 
 int radeon_device_init(struct radeon_device *rdev,
index 7328d1528a85b8fcd37989fb190c244e985df287..cfa2ebb259fed81fd6ad186eee272c8e7cc95e9c 100644 (file)
@@ -40,6 +40,26 @@ radeon_atombios_connected_scratch_regs(struct drm_connector *connector,
                                       struct drm_encoder *encoder,
                                       bool connected);
 
+void radeon_connector_hotplug(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       struct radeon_device *rdev = dev->dev_private;
+       struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+
+       if (radeon_connector->hpd.hpd != RADEON_HPD_NONE)
+               radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
+
+       if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
+               if (radeon_dp_getsinktype(radeon_connector) == CONNECTOR_OBJECT_ID_DISPLAYPORT) {
+                       if (radeon_dp_needs_link_train(radeon_connector)) {
+                               if (connector->encoder)
+                                       dp_link_train(connector->encoder, connector);
+                       }
+               }
+       }
+
+}
+
 static void radeon_property_change_mode(struct drm_encoder *encoder)
 {
        struct drm_crtc *crtc = encoder->crtc;
index 60ee6a8b4f7f0e60265ef70f02aee16cb2fd2dfb..7e55647f118ec2c4cc14173fa248180c9e60676f 100644 (file)
@@ -570,6 +570,11 @@ int radeon_device_init(struct radeon_device *rdev,
        rwlock_init(&rdev->fence_drv.lock);
        INIT_LIST_HEAD(&rdev->gem.objects);
 
+       /* setup workqueue */
+       rdev->wq = create_workqueue("radeon");
+       if (rdev->wq == NULL)
+               return -ENOMEM;
+
        /* Set asic functions */
        r = radeon_asic_init(rdev);
        if (r) {
@@ -643,6 +648,7 @@ void radeon_device_fini(struct radeon_device *rdev)
        DRM_INFO("radeon: finishing device.\n");
        rdev->shutdown = true;
        radeon_fini(rdev);
+       destroy_workqueue(rdev->wq);
        vga_client_register(rdev->pdev, NULL, NULL, NULL);
        iounmap(rdev->rmmio);
        rdev->rmmio = NULL;
@@ -689,6 +695,7 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
        radeon_save_bios_scratch_regs(rdev);
 
        radeon_suspend(rdev);
+       radeon_hpd_fini(rdev);
        /* evict remaining vram memory */
        radeon_bo_evict_vram(rdev);
 
@@ -723,6 +730,8 @@ int radeon_resume_kms(struct drm_device *dev)
        fb_set_suspend(rdev->fbdev_info, 0);
        release_console_sem();
 
+       /* reset hpd state */
+       radeon_hpd_init(rdev);
        /* blat the mode back in */
        drm_helper_resume_force_mode(dev);
        return 0;
index d4f4fb1c54c742f308f10638d0c1df35059d3f1b..c115f2e442ebaf471a0495bfde2843540d16fadf 100644 (file)
@@ -741,6 +741,8 @@ int radeon_modeset_init(struct radeon_device *rdev)
        if (!ret) {
                return ret;
        }
+       /* initialize hpd */
+       radeon_hpd_init(rdev);
        drm_helper_initial_config(rdev->ddev);
        return 0;
 }
@@ -748,6 +750,7 @@ int radeon_modeset_init(struct radeon_device *rdev)
 void radeon_modeset_fini(struct radeon_device *rdev)
 {
        if (rdev->mode_info.mode_config_initialized) {
+               radeon_hpd_fini(rdev);
                drm_mode_config_cleanup(rdev->ddev);
                rdev->mode_info.mode_config_initialized = false;
        }
index 26789970c5cf60f5f57c9e9a390d86e4a1512b89..9223296fe37be087253481647ca32c3f3e657061 100644 (file)
@@ -39,11 +39,32 @@ irqreturn_t radeon_driver_irq_handler_kms(DRM_IRQ_ARGS)
        return radeon_irq_process(rdev);
 }
 
+/*
+ * Handle hotplug events outside the interrupt handler proper.
+ */
+static void radeon_hotplug_work_func(struct work_struct *work)
+{
+       struct radeon_device *rdev = container_of(work, struct radeon_device,
+                                                 hotplug_work);
+       struct drm_device *dev = rdev->ddev;
+       struct drm_mode_config *mode_config = &dev->mode_config;
+       struct drm_connector *connector;
+
+       if (mode_config->num_connector) {
+               list_for_each_entry(connector, &mode_config->connector_list, head)
+                       radeon_connector_hotplug(connector);
+       }
+       /* Just fire off a uevent and let userspace tell us what to do */
+       drm_sysfs_hotplug_event(dev);
+}
+
 void radeon_driver_irq_preinstall_kms(struct drm_device *dev)
 {
        struct radeon_device *rdev = dev->dev_private;
        unsigned i;
 
+       INIT_WORK(&rdev->hotplug_work, radeon_hotplug_work_func);
+
        /* Disable *all* interrupts */
        rdev->irq.sw_int = false;
        for (i = 0; i < 2; i++) {
index 61b90343f7942d32a60edb35fda289fadcf0f468..15ec7ca18a957a37768321f2c0b2f3588472be7b 100644 (file)
@@ -392,6 +392,8 @@ struct radeon_framebuffer {
        struct drm_gem_object *obj;
 };
 
+extern void radeon_connector_hotplug(struct drm_connector *connector);
+extern bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector);
 extern int radeon_dp_mode_valid_helper(struct radeon_connector *radeon_connector,
                                       struct drm_display_mode *mode);
 extern void radeon_dp_set_link_config(struct drm_connector *connector,
index 6364ba1d41531158152c5e64f0ff443cb87397c6..fd5ab01f6ad10a1d6d9dd4c12a999d24cd348f65 100644 (file)
@@ -388,6 +388,7 @@ int rs600_irq_process(struct radeon_device *rdev)
 {
        uint32_t status, msi_rearm;
        uint32_t r500_disp_int;
+       bool queue_hotplug = false;
 
        status = rs600_irq_ack(rdev, &r500_disp_int);
        if (!status && !r500_disp_int) {
@@ -403,13 +404,17 @@ int rs600_irq_process(struct radeon_device *rdev)
                if (G_007EDC_LB_D2_VBLANK_INTERRUPT(r500_disp_int))
                        drm_handle_vblank(rdev->ddev, 1);
                if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(r500_disp_int)) {
-                       DRM_INFO("HPD1\n");
+                       queue_hotplug = true;
+                       DRM_DEBUG("HPD1\n");
                }
                if (G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(r500_disp_int)) {
-                       DRM_INFO("HPD2\n");
+                       queue_hotplug = true;
+                       DRM_DEBUG("HPD2\n");
                }
                status = rs600_irq_ack(rdev, &r500_disp_int);
        }
+       if (queue_hotplug)
+               queue_work(rdev->wq, &rdev->hotplug_work);
        if (rdev->msi_enabled) {
                switch (rdev->family) {
                case CHIP_RS600: