Commit d4877cf2 authored by Alex Deucher's avatar Alex Deucher Committed by Dave Airlie

drm/radeon/kms: enable hpd support

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: default avatarAlex Deucher <alexdeucher@gmail.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 429770b3
...@@ -507,6 +507,18 @@ static bool atom_dp_get_link_status(struct radeon_connector *radeon_connector, ...@@ -507,6 +507,18 @@ static bool atom_dp_get_link_status(struct radeon_connector *radeon_connector,
return true; 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) static void dp_set_power(struct radeon_connector *radeon_connector, u8 power_state)
{ {
struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv; struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
......
...@@ -289,6 +289,7 @@ static inline uint32_t r100_irq_ack(struct radeon_device *rdev) ...@@ -289,6 +289,7 @@ static inline uint32_t r100_irq_ack(struct radeon_device *rdev)
int r100_irq_process(struct radeon_device *rdev) int r100_irq_process(struct radeon_device *rdev)
{ {
uint32_t status, msi_rearm; uint32_t status, msi_rearm;
bool queue_hotplug = false;
status = r100_irq_ack(rdev); status = r100_irq_ack(rdev);
if (!status) { if (!status) {
...@@ -310,13 +311,17 @@ int r100_irq_process(struct radeon_device *rdev) ...@@ -310,13 +311,17 @@ int r100_irq_process(struct radeon_device *rdev)
drm_handle_vblank(rdev->ddev, 1); drm_handle_vblank(rdev->ddev, 1);
} }
if (status & RADEON_FP_DETECT_STAT) { if (status & RADEON_FP_DETECT_STAT) {
DRM_INFO("HPD1\n"); queue_hotplug = true;
DRM_DEBUG("HPD1\n");
} }
if (status & RADEON_FP2_DETECT_STAT) { if (status & RADEON_FP2_DETECT_STAT) {
DRM_INFO("HPD2\n"); queue_hotplug = true;
DRM_DEBUG("HPD2\n");
} }
status = r100_irq_ack(rdev); status = r100_irq_ack(rdev);
} }
if (queue_hotplug)
queue_work(rdev->wq, &rdev->hotplug_work);
if (rdev->msi_enabled) { if (rdev->msi_enabled) {
switch (rdev->family) { switch (rdev->family) {
case CHIP_RS400: case CHIP_RS400:
......
...@@ -2674,6 +2674,7 @@ int r600_irq_process(struct radeon_device *rdev) ...@@ -2674,6 +2674,7 @@ int r600_irq_process(struct radeon_device *rdev)
u32 last_entry = rdev->ih.ring_size - 16; u32 last_entry = rdev->ih.ring_size - 16;
u32 ring_index, disp_int, disp_int_cont, disp_int_cont2; u32 ring_index, disp_int, disp_int_cont, disp_int_cont2;
unsigned long flags; unsigned long flags;
bool queue_hotplug = false;
DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr); DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
...@@ -2745,37 +2746,43 @@ int r600_irq_process(struct radeon_device *rdev) ...@@ -2745,37 +2746,43 @@ int r600_irq_process(struct radeon_device *rdev)
case 0: case 0:
if (disp_int & DC_HPD1_INTERRUPT) { if (disp_int & DC_HPD1_INTERRUPT) {
disp_int &= ~DC_HPD1_INTERRUPT; disp_int &= ~DC_HPD1_INTERRUPT;
DRM_INFO("IH: HPD1\n"); queue_hotplug = true;
DRM_DEBUG("IH: HPD1\n");
} }
break; break;
case 1: case 1:
if (disp_int & DC_HPD2_INTERRUPT) { if (disp_int & DC_HPD2_INTERRUPT) {
disp_int &= ~DC_HPD2_INTERRUPT; disp_int &= ~DC_HPD2_INTERRUPT;
DRM_INFO("IH: HPD2\n"); queue_hotplug = true;
DRM_DEBUG("IH: HPD2\n");
} }
break; break;
case 4: case 4:
if (disp_int_cont & DC_HPD3_INTERRUPT) { if (disp_int_cont & DC_HPD3_INTERRUPT) {
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; break;
case 5: case 5:
if (disp_int_cont & DC_HPD4_INTERRUPT) { if (disp_int_cont & DC_HPD4_INTERRUPT) {
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; break;
case 10: case 10:
if (disp_int_cont2 & DC_HPD5_INTERRUPT) { if (disp_int_cont2 & DC_HPD5_INTERRUPT) {
disp_int_cont &= ~DC_HPD5_INTERRUPT; disp_int_cont &= ~DC_HPD5_INTERRUPT;
DRM_INFO("IH: HPD5\n"); queue_hotplug = true;
DRM_DEBUG("IH: HPD5\n");
} }
break; break;
case 12: case 12:
if (disp_int_cont2 & DC_HPD6_INTERRUPT) { if (disp_int_cont2 & DC_HPD6_INTERRUPT) {
disp_int_cont &= ~DC_HPD6_INTERRUPT; disp_int_cont &= ~DC_HPD6_INTERRUPT;
DRM_INFO("IH: HPD6\n"); queue_hotplug = true;
DRM_DEBUG("IH: HPD6\n");
} }
break; break;
default: default:
...@@ -2807,6 +2814,8 @@ int r600_irq_process(struct radeon_device *rdev) ...@@ -2807,6 +2814,8 @@ int r600_irq_process(struct radeon_device *rdev)
wptr = r600_get_ih_wptr(rdev); wptr = r600_get_ih_wptr(rdev);
if (wptr != rdev->ih.wptr) if (wptr != rdev->ih.wptr)
goto restart_ih; goto restart_ih;
if (queue_hotplug)
queue_work(rdev->wq, &rdev->hotplug_work);
rdev->ih.rptr = rptr; rdev->ih.rptr = rptr;
WREG32(IH_RB_RPTR, rdev->ih.rptr); WREG32(IH_RB_RPTR, rdev->ih.rptr);
spin_unlock_irqrestore(&rdev->ih.lock, flags); spin_unlock_irqrestore(&rdev->ih.lock, flags);
......
...@@ -809,6 +809,8 @@ struct radeon_device { ...@@ -809,6 +809,8 @@ struct radeon_device {
struct r600_blit r600_blit; struct r600_blit r600_blit;
int msi_enabled; /* msi enabled */ int msi_enabled; /* msi enabled */
struct r600_ih ih; /* r6/700 interrupt ring */ 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, int radeon_device_init(struct radeon_device *rdev,
......
...@@ -40,6 +40,26 @@ radeon_atombios_connected_scratch_regs(struct drm_connector *connector, ...@@ -40,6 +40,26 @@ radeon_atombios_connected_scratch_regs(struct drm_connector *connector,
struct drm_encoder *encoder, struct drm_encoder *encoder,
bool connected); 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) static void radeon_property_change_mode(struct drm_encoder *encoder)
{ {
struct drm_crtc *crtc = encoder->crtc; struct drm_crtc *crtc = encoder->crtc;
......
...@@ -570,6 +570,11 @@ int radeon_device_init(struct radeon_device *rdev, ...@@ -570,6 +570,11 @@ int radeon_device_init(struct radeon_device *rdev,
rwlock_init(&rdev->fence_drv.lock); rwlock_init(&rdev->fence_drv.lock);
INIT_LIST_HEAD(&rdev->gem.objects); INIT_LIST_HEAD(&rdev->gem.objects);
/* setup workqueue */
rdev->wq = create_workqueue("radeon");
if (rdev->wq == NULL)
return -ENOMEM;
/* Set asic functions */ /* Set asic functions */
r = radeon_asic_init(rdev); r = radeon_asic_init(rdev);
if (r) { if (r) {
...@@ -643,6 +648,7 @@ void radeon_device_fini(struct radeon_device *rdev) ...@@ -643,6 +648,7 @@ void radeon_device_fini(struct radeon_device *rdev)
DRM_INFO("radeon: finishing device.\n"); DRM_INFO("radeon: finishing device.\n");
rdev->shutdown = true; rdev->shutdown = true;
radeon_fini(rdev); radeon_fini(rdev);
destroy_workqueue(rdev->wq);
vga_client_register(rdev->pdev, NULL, NULL, NULL); vga_client_register(rdev->pdev, NULL, NULL, NULL);
iounmap(rdev->rmmio); iounmap(rdev->rmmio);
rdev->rmmio = NULL; rdev->rmmio = NULL;
...@@ -689,6 +695,7 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state) ...@@ -689,6 +695,7 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
radeon_save_bios_scratch_regs(rdev); radeon_save_bios_scratch_regs(rdev);
radeon_suspend(rdev); radeon_suspend(rdev);
radeon_hpd_fini(rdev);
/* evict remaining vram memory */ /* evict remaining vram memory */
radeon_bo_evict_vram(rdev); radeon_bo_evict_vram(rdev);
...@@ -723,6 +730,8 @@ int radeon_resume_kms(struct drm_device *dev) ...@@ -723,6 +730,8 @@ int radeon_resume_kms(struct drm_device *dev)
fb_set_suspend(rdev->fbdev_info, 0); fb_set_suspend(rdev->fbdev_info, 0);
release_console_sem(); release_console_sem();
/* reset hpd state */
radeon_hpd_init(rdev);
/* blat the mode back in */ /* blat the mode back in */
drm_helper_resume_force_mode(dev); drm_helper_resume_force_mode(dev);
return 0; return 0;
......
...@@ -741,6 +741,8 @@ int radeon_modeset_init(struct radeon_device *rdev) ...@@ -741,6 +741,8 @@ int radeon_modeset_init(struct radeon_device *rdev)
if (!ret) { if (!ret) {
return ret; return ret;
} }
/* initialize hpd */
radeon_hpd_init(rdev);
drm_helper_initial_config(rdev->ddev); drm_helper_initial_config(rdev->ddev);
return 0; return 0;
} }
...@@ -748,6 +750,7 @@ int radeon_modeset_init(struct radeon_device *rdev) ...@@ -748,6 +750,7 @@ int radeon_modeset_init(struct radeon_device *rdev)
void radeon_modeset_fini(struct radeon_device *rdev) void radeon_modeset_fini(struct radeon_device *rdev)
{ {
if (rdev->mode_info.mode_config_initialized) { if (rdev->mode_info.mode_config_initialized) {
radeon_hpd_fini(rdev);
drm_mode_config_cleanup(rdev->ddev); drm_mode_config_cleanup(rdev->ddev);
rdev->mode_info.mode_config_initialized = false; rdev->mode_info.mode_config_initialized = false;
} }
......
...@@ -39,11 +39,32 @@ irqreturn_t radeon_driver_irq_handler_kms(DRM_IRQ_ARGS) ...@@ -39,11 +39,32 @@ irqreturn_t radeon_driver_irq_handler_kms(DRM_IRQ_ARGS)
return radeon_irq_process(rdev); 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) void radeon_driver_irq_preinstall_kms(struct drm_device *dev)
{ {
struct radeon_device *rdev = dev->dev_private; struct radeon_device *rdev = dev->dev_private;
unsigned i; unsigned i;
INIT_WORK(&rdev->hotplug_work, radeon_hotplug_work_func);
/* Disable *all* interrupts */ /* Disable *all* interrupts */
rdev->irq.sw_int = false; rdev->irq.sw_int = false;
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
......
...@@ -392,6 +392,8 @@ struct radeon_framebuffer { ...@@ -392,6 +392,8 @@ struct radeon_framebuffer {
struct drm_gem_object *obj; 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, extern int radeon_dp_mode_valid_helper(struct radeon_connector *radeon_connector,
struct drm_display_mode *mode); struct drm_display_mode *mode);
extern void radeon_dp_set_link_config(struct drm_connector *connector, extern void radeon_dp_set_link_config(struct drm_connector *connector,
......
...@@ -388,6 +388,7 @@ int rs600_irq_process(struct radeon_device *rdev) ...@@ -388,6 +388,7 @@ int rs600_irq_process(struct radeon_device *rdev)
{ {
uint32_t status, msi_rearm; uint32_t status, msi_rearm;
uint32_t r500_disp_int; uint32_t r500_disp_int;
bool queue_hotplug = false;
status = rs600_irq_ack(rdev, &r500_disp_int); status = rs600_irq_ack(rdev, &r500_disp_int);
if (!status && !r500_disp_int) { if (!status && !r500_disp_int) {
...@@ -403,13 +404,17 @@ int rs600_irq_process(struct radeon_device *rdev) ...@@ -403,13 +404,17 @@ int rs600_irq_process(struct radeon_device *rdev)
if (G_007EDC_LB_D2_VBLANK_INTERRUPT(r500_disp_int)) if (G_007EDC_LB_D2_VBLANK_INTERRUPT(r500_disp_int))
drm_handle_vblank(rdev->ddev, 1); drm_handle_vblank(rdev->ddev, 1);
if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(r500_disp_int)) { 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)) { 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); status = rs600_irq_ack(rdev, &r500_disp_int);
} }
if (queue_hotplug)
queue_work(rdev->wq, &rdev->hotplug_work);
if (rdev->msi_enabled) { if (rdev->msi_enabled) {
switch (rdev->family) { switch (rdev->family) {
case CHIP_RS600: case CHIP_RS600:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment