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;
int r100_irq_process(struct radeon_device *rdev)
{
uint32_t status, msi_rearm;
+ bool queue_hotplug = false;
status = r100_irq_ack(rdev);
if (!status) {
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:
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);
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:
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);
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,
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;
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) {
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;
radeon_save_bios_scratch_regs(rdev);
radeon_suspend(rdev);
+ radeon_hpd_fini(rdev);
/* evict remaining vram memory */
radeon_bo_evict_vram(rdev);
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;
if (!ret) {
return ret;
}
+ /* initialize hpd */
+ radeon_hpd_init(rdev);
drm_helper_initial_config(rdev->ddev);
return 0;
}
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;
}
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++) {
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,
{
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) {
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: