Il Mon, Dec 28, 2009 at 05:27:13PM -0500, Alex Deucher ha scritto: > 2009/12/28 Luca Tettamanti <kronos...@gmail.com>: > > On Mon, Dec 28, 2009 at 01:32:24PM -0500, Alex Deucher wrote: > >> 2009/12/28 Luca Tettamanti <kronos...@gmail.com>: > >> > 2009/12/28 Alex Deucher <alexdeuc...@gmail.com>: > >> >> On Mon, Dec 28, 2009 at 5:53 AM, Luca Tettamanti <kronos...@gmail.com> > >> >> wrote: > >> >>> On Sun, Dec 27, 2009 at 1:55 AM, Rafał Miłecki <zaj...@gmail.com> > >> >>> wrote: > >> >>>> W dniu 26 grudnia 2009 20:08 użytkownik Alex Deucher > >> >>>> <alexdeuc...@gmail.com> napisał: > >> >>>>> It may be that the engine doesn't like to be reclocked while it's > >> >>>>> running. Perhaps we should use the GUI idle interrupt rather than > >> >>>>> vblanks to reclock the engine. > >> >>>> > >> >>>> Could you say something more about GUI idle interrupt, please? > >> >>> > >> >>> It's mentioned in the documentation of the IH (see r600.c); I guess > >> >>> it's enabled by GUI_IDLE_INT_ENABLE. > >> >>> > >> >>>> What is this, do we already have code for that? > >> >>> > >> >>> Unless there are more subtleties all is needed it to enabled the > >> >>> interrupt and catch it in r600_irq_process. > >> >> > >> >> That's pretty much it. Pre-r6xx asics have a GUI interrupt as well. > >> >> I can look up the details for that if they are not already documented. > >> > > >> > I can't find a way to ack the interrupt; I see > >> > RADEON_GUI_IDLE_INT_TEST_ACK but I think it's for pre-r6xx cards, > >> > right? > >> > >> You don't have to ACK it as the CP generates the interrupt in > >> software; similar to the sw interrupts used for fences. > > > > Ok, good: I've got the stub running on my M76. r100 and rs600 parts are > > untested. Comments? > > > > Looks pretty good. I've included the proper defines from the register > database below and you'll need to ack the gui idle interrupts on > pre-r600 chips. Now you just have to do something when you get the > idle interrupt.
I've adapted Rafał's patch to do the reclock when the idle interrupt is fired (which btw should take care of the special case for nr CRTCs > 1). Unfortunately I still see the black frame when reclocking is performed. So I tried recloking directly from the IH (yeah, I'm ashamed of myself...); this got rid of the black frame, but causes corruption of a horizontal block of the screen (during the reclock, before and after the screen looks fine). In this second case I've added a spinlock to guard the access to the CP ring, so nothing touches it while reclocking is performed; however by the time we process the idle interrupt - especially considering that multiple events might be queued in the IH ring - someone else (i.e. one of the other cores) might already have submitted more work; what do you think? I'm attaching 3 patches; the first one contains the stub idle IH, the second one is Rafał's patch adapted for idle interrupt (it's not very polished), the third one moves reclocking to IH (and as is it's just an ugly hack). Luca -- "In linea di principio sarei indifferente al natale, se solo il natale ricambiasse la cortesia e mi lasciasse in pace." -- Marco d'Itri
--- linux-2.6.git.orig/drivers/gpu/drm/radeon/r600.c 2009-12-28 16:38:38.388825742 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/r600.c 2009-12-28 22:03:12.936157804 +0100 @@ -2458,6 +2458,7 @@ int r600_irq_set(struct radeon_device *rdev) { u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE; + u32 grbm_int_cntl; u32 mode_int = 0; u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0; @@ -2465,6 +2466,8 @@ if (!rdev->ih.enabled) return 0; + grbm_int_cntl = RREG32(GRBM_INT_CNTL) & ~GUI_IDLE_INT_ENABLE; + if (ASIC_IS_DCE3(rdev)) { hpd1 = RREG32(DC_HPD1_INT_CONTROL) & ~DC_HPDx_INT_EN; hpd2 = RREG32(DC_HPD2_INT_CONTROL) & ~DC_HPDx_INT_EN; @@ -2484,6 +2487,10 @@ DRM_DEBUG("r600_irq_set: sw int\n"); cp_int_cntl |= RB_INT_ENABLE; } + if (rdev->irq.idle_int) { + DRM_DEBUG("r600_irq_set: GUI idle int\n"); + grbm_int_cntl |= GUI_IDLE_INT_ENABLE; + } if (rdev->irq.crtc_vblank_int[0]) { DRM_DEBUG("r600_irq_set: vblank 0\n"); mode_int |= D1MODE_VBLANK_INT_MASK; @@ -2518,6 +2525,7 @@ } WREG32(CP_INT_CNTL, cp_int_cntl); + WREG32(GRBM_INT_CNTL, grbm_int_cntl); WREG32(DxMODE_INT_MASK, mode_int); if (ASIC_IS_DCE3(rdev)) { WREG32(DC_HPD1_INT_CONTROL, hpd1); @@ -2806,6 +2814,9 @@ case 181: /* CP EOP event */ DRM_DEBUG("IH: CP EOP\n"); break; + case 233: /* GUI idle event */ + DRM_DEBUG("IH: GUI idle\n"); + break; default: DRM_ERROR("Unhandled interrupt: %d %d\n", src_id, src_data); break; --- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon.h 2009-12-28 16:38:13.945836481 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/radeon.h 2009-12-28 22:02:48.964001788 +0100 @@ -343,6 +343,7 @@ struct radeon_irq { bool installed; bool sw_int; + bool idle_int; /* FIXME: use a define max crtc rather than hardcode it */ bool crtc_vblank_int[2]; /* FIXME: use defines for max hpd/dacs */ --- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_irq_kms.c 2009-12-28 16:37:10.669823739 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_irq_kms.c 2009-12-28 22:03:01.508077338 +0100 @@ -67,6 +67,7 @@ /* Disable *all* interrupts */ rdev->irq.sw_int = false; + rdev->irq.idle_int = false; for (i = 0; i < 2; i++) { rdev->irq.crtc_vblank_int[i] = false; } @@ -81,6 +82,7 @@ dev->max_vblank_count = 0x001fffff; rdev->irq.sw_int = true; + rdev->irq.idle_int = true; radeon_irq_set(rdev); return 0; } @@ -95,6 +97,7 @@ } /* Disable *all* interrupts */ rdev->irq.sw_int = false; + rdev->irq.idle_int = false; for (i = 0; i < 2; i++) { rdev->irq.crtc_vblank_int[i] = false; } --- linux-2.6.git.orig/drivers/gpu/drm/radeon/r100.c 2009-12-28 22:59:45.076740411 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/r100.c 2009-12-29 14:14:42.600043339 +0100 @@ -246,6 +246,9 @@ if (rdev->irq.sw_int) { tmp |= RADEON_SW_INT_ENABLE; } + if (rdev->irq.idle_int) { + tmp |= RADEON_GUI_IDLE_INT_MASK; + } if (rdev->irq.crtc_vblank_int[0]) { tmp |= RADEON_CRTC_VBLANK_MASK; } @@ -278,7 +281,8 @@ uint32_t irqs = RREG32(RADEON_GEN_INT_STATUS); uint32_t irq_mask = RADEON_SW_INT_TEST | RADEON_CRTC_VBLANK_STAT | RADEON_CRTC2_VBLANK_STAT | - RADEON_FP_DETECT_STAT | RADEON_FP2_DETECT_STAT; + RADEON_FP_DETECT_STAT | RADEON_FP2_DETECT_STAT | + RADEON_GUI_IDLE_STAT; if (irqs) { WREG32(RADEON_GEN_INT_STATUS, irqs); @@ -318,6 +322,9 @@ queue_hotplug = true; DRM_DEBUG("HPD2\n"); } + if (status & RADEON_GUI_IDLE_STAT) { + DRM_DEBUG("GUI idle\n"); + } status = r100_irq_ack(rdev); } if (queue_hotplug) --- linux-2.6.git.orig/drivers/gpu/drm/radeon/rs600.c 2009-12-28 22:59:45.052738246 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/rs600.c 2009-12-29 14:22:09.240002197 +0100 @@ -318,6 +318,9 @@ if (rdev->irq.sw_int) { tmp |= S_000040_SW_INT_EN(1); } + if (rdev->irq.idle_int) { + tmp |= S_000040_GUI_IDLE(1); + } if (rdev->irq.crtc_vblank_int[0]) { mode_int |= S_006540_D1MODE_VBLANK_INT_MASK(1); } @@ -340,7 +343,7 @@ static inline uint32_t rs600_irq_ack(struct radeon_device *rdev, u32 *r500_disp_int) { uint32_t irqs = RREG32(R_000044_GEN_INT_STATUS); - uint32_t irq_mask = ~C_000044_SW_INT; + uint32_t irq_mask = ~C_000044_SW_INT | ~C_000044_GUI_IDLE_STAT; u32 tmp; if (G_000044_DISPLAY_INT_STAT(irqs)) { @@ -411,6 +414,9 @@ queue_hotplug = true; DRM_DEBUG("HPD2\n"); } + if (G_000044_GUI_IDLE_STAT(status)) { + DRM_DEBUG("GUI idle\n"); + } status = rs600_irq_ack(rdev, &r500_disp_int); } if (queue_hotplug) --- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_reg.h 2009-12-28 22:59:45.060748373 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_reg.h 2009-12-29 14:13:35.801002197 +0100 @@ -994,6 +994,7 @@ # define RADEON_FP_DETECT_MASK (1 << 4) # define RADEON_CRTC2_VBLANK_MASK (1 << 9) # define RADEON_FP2_DETECT_MASK (1 << 10) +# define RADEON_GUI_IDLE_INT_MASK (1 << 19) # define RADEON_SW_INT_ENABLE (1 << 25) #define RADEON_GEN_INT_STATUS 0x0044 # define AVIVO_DISPLAY_INT_STATUS (1 << 0) @@ -1005,6 +1006,8 @@ # define RADEON_CRTC2_VBLANK_STAT_ACK (1 << 9) # define RADEON_FP2_DETECT_STAT (1 << 10) # define RADEON_FP2_DETECT_STAT_ACK (1 << 10) +# define RADEON_GUI_IDLE_STAT (1 << 19) +# define RADEON_GUI_IDLE_STAT_ACK (1 << 19) # define RADEON_SW_INT_FIRE (1 << 26) # define RADEON_SW_INT_TEST (1 << 25) # define RADEON_SW_INT_TEST_ACK (1 << 25)
Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon.h =================================================================== --- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon.h 2009-12-29 14:11:49.113001762 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/radeon.h 2009-12-29 19:08:02.904889536 +0100 @@ -89,6 +89,7 @@ extern int radeon_connector_table; extern int radeon_tv; extern int radeon_new_pll; +extern int radeon_dynpm; extern int radeon_audio; /* @@ -148,6 +149,7 @@ * Power management */ int radeon_pm_init(struct radeon_device *rdev); +void radeon_pm_compute_clocks(struct radeon_device *rdev); /* * Fences. @@ -567,7 +569,33 @@ * Equation between gpu/memory clock and available bandwidth is hw dependent * (type of memory, bus size, efficiency, ...) */ +enum radeon_pm_state { + PM_STATE_DISABLED, + PM_STATE_MINIMUM, + PM_STATE_PAUSED, + PM_STATE_ACTIVE +}; +enum radeon_pm_action { + PM_ACTION_NONE, + PM_ACTION_MINIMUM, + PM_ACTION_DOWNCLOCK, + PM_ACTION_UPCLOCK +}; struct radeon_pm { + struct mutex reclock_mutex; + struct spinlock lock; + struct work_struct reclock_work; + struct delayed_work idle_work; + enum radeon_pm_state state; + enum radeon_pm_action planned_action; + unsigned long action_timeout; + bool downclocked; + int active_crtcs; + int req_vblank; + uint32_t min_gpu_engine_clock; + uint32_t min_gpu_memory_clock; + uint32_t min_mode_engine_clock; + uint32_t min_mode_memory_clock; fixed20_12 max_bandwidth; fixed20_12 igp_sideport_mclk; fixed20_12 igp_system_mclk; Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon_device.c =================================================================== --- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_device.c 2009-12-28 11:40:27.826946593 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_device.c 2009-12-29 19:05:43.772754474 +0100 @@ -640,6 +640,8 @@ if (rdev->family >= CHIP_R600) spin_lock_init(&rdev->ih.lock); mutex_init(&rdev->gem.mutex); + mutex_init(&rdev->pm.reclock_mutex); + spin_lock_init(&rdev->pm.lock); rwlock_init(&rdev->fence_drv.lock); INIT_LIST_HEAD(&rdev->gem.objects); Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon_drv.c =================================================================== --- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_drv.c 2009-12-28 11:40:27.826946593 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_drv.c 2009-12-29 15:19:02.393011113 +0100 @@ -87,6 +87,7 @@ int radeon_connector_table = 0; int radeon_tv = 1; int radeon_new_pll = 1; +int radeon_dynpm = -1; int radeon_audio = 1; MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers"); @@ -125,6 +126,9 @@ MODULE_PARM_DESC(new_pll, "Select new PLL code for AVIVO chips"); module_param_named(new_pll, radeon_new_pll, int, 0444); +MODULE_PARM_DESC(dynpm, "Disable/Enable dynamic power management (1 = enable)"); +module_param_named(dynpm, radeon_dynpm, int, 0444); + MODULE_PARM_DESC(audio, "Audio enable (0 = disable)"); module_param_named(audio, radeon_audio, int, 0444); Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon_encoders.c =================================================================== --- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_encoders.c 2009-12-28 11:40:27.826946593 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_encoders.c 2009-12-29 15:19:49.512042228 +0100 @@ -216,6 +216,9 @@ struct drm_device *dev = encoder->dev; struct radeon_device *rdev = dev->dev_private; + /* adjust pm to upcoming mode change */ + radeon_pm_compute_clocks(rdev); + /* set the active encoder to connector routing */ radeon_encoder_set_active_device(encoder); drm_mode_set_crtcinfo(adjusted_mode, 0); @@ -1027,6 +1030,9 @@ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); } radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); + + /* adjust pm to dpms change */ + radeon_pm_compute_clocks(rdev); } union crtc_sourc_param { Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon_legacy_encoders.c =================================================================== --- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_legacy_encoders.c 2009-12-28 11:40:27.826946593 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_legacy_encoders.c 2009-12-29 15:19:02.393011113 +0100 @@ -96,6 +96,9 @@ radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); else radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); + + /* adjust pm to dpms change */ + radeon_pm_compute_clocks(rdev); } static void radeon_legacy_lvds_prepare(struct drm_encoder *encoder) @@ -195,6 +198,11 @@ struct drm_display_mode *adjusted_mode) { struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_device *dev = encoder->dev; + struct radeon_device *rdev = dev->dev_private; + + /* adjust pm to upcoming mode change */ + radeon_pm_compute_clocks(rdev); /* set the active encoder to connector routing */ radeon_encoder_set_active_device(encoder); @@ -266,6 +274,9 @@ radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); else radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); + + /* adjust pm to dpms change */ + radeon_pm_compute_clocks(rdev); } static void radeon_legacy_primary_dac_prepare(struct drm_encoder *encoder) @@ -451,6 +462,9 @@ radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); else radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); + + /* adjust pm to dpms change */ + radeon_pm_compute_clocks(rdev); } static void radeon_legacy_tmds_int_prepare(struct drm_encoder *encoder) @@ -616,6 +630,9 @@ radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); else radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); + + /* adjust pm to dpms change */ + radeon_pm_compute_clocks(rdev); } static void radeon_legacy_tmds_ext_prepare(struct drm_encoder *encoder) @@ -823,6 +840,9 @@ radeon_atombios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); else radeon_combios_encoder_dpms_scratch_regs(encoder, (mode == DRM_MODE_DPMS_ON) ? true : false); + + /* adjust pm to dpms change */ + radeon_pm_compute_clocks(rdev); } static void radeon_legacy_tv_dac_prepare(struct drm_encoder *encoder) Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon_pm.c =================================================================== --- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_pm.c 2009-12-28 11:40:27.830951642 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_pm.c 2009-12-29 19:07:39.496885146 +0100 @@ -22,17 +22,260 @@ #include "drmP.h" #include "radeon.h" -int radeon_debugfs_pm_init(struct radeon_device *rdev); +#define RADEON_IDLE_LOOP_MS 100 +#define RADEON_RECLOCK_DELAY_MS 500 + +static void radeon_pm_check_limits(struct radeon_device *rdev); +static void radeon_pm_set_clocks_locked(struct radeon_device *rdev); +static void radeon_pm_set_clocks(struct radeon_device *rdev); +static void radeon_pm_reclock_work_handler(struct work_struct *work); +static void radeon_pm_idle_work_handler(struct work_struct *work); +static int radeon_debugfs_pm_init(struct radeon_device *rdev); + +static const char *pm_state_names[4] = { + "PM_STATE_DISABLED", + "PM_STATE_MINIMUM", + "PM_STATE_PAUSED", + "PM_STATE_ACTIVE" +}; int radeon_pm_init(struct radeon_device *rdev) { + rdev->pm.state = PM_STATE_DISABLED; + rdev->pm.planned_action = PM_ACTION_NONE; + rdev->pm.downclocked = false; + + radeon_pm_check_limits(rdev); + if (radeon_debugfs_pm_init(rdev)) { DRM_ERROR("Failed to register debugfs file for PM!\n"); } + INIT_WORK(&rdev->pm.reclock_work, radeon_pm_reclock_work_handler); + INIT_DELAYED_WORK(&rdev->pm.idle_work, radeon_pm_idle_work_handler); + + if (radeon_dynpm != -1 && radeon_dynpm) { + rdev->pm.state = PM_STATE_PAUSED; + DRM_INFO("radeon: dynamic power management enabled\n"); + } + + DRM_INFO("radeon: power management initialized\n"); + return 0; } +static void radeon_pm_check_limits(struct radeon_device *rdev) +{ + rdev->pm.min_gpu_engine_clock = rdev->clock.default_sclk - 10000; + rdev->pm.min_gpu_memory_clock = rdev->clock.default_mclk - 5000; +} + +void radeon_pm_compute_clocks(struct radeon_device *rdev) +{ + struct drm_device *ddev = rdev->ddev; + struct drm_connector *connector; + struct radeon_crtc *radeon_crtc; + int count = 0; + + if (rdev->pm.state == PM_STATE_DISABLED) + return; + + spin_lock_irq(&rdev->pm.lock); + + rdev->pm.active_crtcs = 0; + list_for_each_entry(connector, + &ddev->mode_config.connector_list, head) { + if (connector->encoder && + connector->dpms != DRM_MODE_DPMS_OFF) { + radeon_crtc = to_radeon_crtc(connector->encoder->crtc); + rdev->pm.active_crtcs |= (1 << radeon_crtc->crtc_id); + ++count; + } + } + + DRM_INFO("Active CRTCs: %d\n", count); + DRM_INFO("Current state: %s\n", pm_state_names[rdev->pm.state]); + + if (count > 1) { + if (rdev->pm.state == PM_STATE_ACTIVE) { + wait_queue_head_t wait; + init_waitqueue_head(&wait); + + cancel_delayed_work(&rdev->pm.idle_work); + + rdev->pm.state = PM_STATE_PAUSED; + rdev->pm.planned_action = PM_ACTION_UPCLOCK; + + spin_unlock_irq(&rdev->pm.lock); + + DRM_INFO("radeon: dynamic power management deactivated\n"); + } else { + spin_unlock_irq(&rdev->pm.lock); + //mutex_unlock(&rdev->pm.mutex); + } + } else if (count == 1) { + rdev->pm.min_mode_engine_clock = rdev->pm.min_gpu_engine_clock; + rdev->pm.min_mode_memory_clock = rdev->pm.min_gpu_memory_clock; + /* TODO: Increase clocks if needed for current mode */ + + if (rdev->pm.state == PM_STATE_MINIMUM) { + rdev->pm.state = PM_STATE_ACTIVE; + rdev->pm.planned_action = PM_ACTION_UPCLOCK; + + queue_delayed_work(rdev->wq, &rdev->pm.idle_work, + msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); + } else if (rdev->pm.state == PM_STATE_PAUSED) { + rdev->pm.state = PM_STATE_ACTIVE; + queue_delayed_work(rdev->wq, &rdev->pm.idle_work, + msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); + DRM_INFO("radeon: dynamic power management activated\n"); + } + + spin_unlock_irq(&rdev->pm.lock); + } else { /* count == 0 */ + if (rdev->pm.state != PM_STATE_MINIMUM) { + cancel_delayed_work(&rdev->pm.idle_work); + + rdev->pm.state = PM_STATE_MINIMUM; + rdev->pm.planned_action = PM_ACTION_MINIMUM; + } + + spin_unlock_irq(&rdev->pm.lock); + } +} + +static void radeon_pm_set_clocks(struct radeon_device *rdev) +{ + unsigned long flags; + enum radeon_pm_action action; + + spin_lock_irqsave(&rdev->pm.lock, flags); + /* TODO: retrieve the full target power state */ + action = rdev->pm.planned_action; + rdev->pm.planned_action = PM_ACTION_NONE; + spin_unlock_irqrestore(&rdev->pm.lock, flags); + + mutex_lock(&rdev->cp.mutex); + /*radeon_fence_wait_last(rdev);*/ + switch (action) { + case PM_ACTION_UPCLOCK: + DRM_DEBUG_DRIVER("PM_ACTION_UPCLOCK\n"); + radeon_set_engine_clock(rdev, rdev->clock.default_sclk); + rdev->pm.downclocked = false; + break; + case PM_ACTION_DOWNCLOCK: + DRM_DEBUG_DRIVER("PM_ACTION_DOWNCLOCK\n"); + radeon_set_engine_clock(rdev, rdev->pm.min_mode_engine_clock); + rdev->pm.downclocked = true; + break; + case PM_ACTION_MINIMUM: + DRM_DEBUG_DRIVER("PM_ACTION_MINIMUM\n"); + radeon_set_engine_clock(rdev, rdev->pm.min_gpu_engine_clock); + break; + case PM_ACTION_NONE: + DRM_ERROR("%s: PM_ACTION_NONE\n", __func__); + break; + } + + mutex_unlock(&rdev->cp.mutex); +} + +#if 0 +static void radeon_pm_set_clocks(struct radeon_device *rdev) +{ + radeon_pm_set_clocks_locked(rdev); + mutex_lock(&rdev->pm.mutex); + /* new VBLANK irq may come before handling previous one */ + if (rdev->pm.vblank_callback) { + mutex_lock(&rdev->cp.mutex); + if (rdev->pm.req_vblank & (1 << 0)) { + rdev->pm.req_vblank &= ~(1 << 0); + drm_vblank_put(rdev->ddev, 0); + } + if (rdev->pm.req_vblank & (1 << 1)) { + rdev->pm.req_vblank &= ~(1 << 1); + drm_vblank_put(rdev->ddev, 1); + } + rdev->pm.vblank_callback = false; + radeon_pm_set_clocks_locked(rdev); + mutex_unlock(&rdev->cp.mutex); + } + mutex_unlock(&rdev->pm.mutex); +} +#endif + +static void radeon_pm_reclock_work_handler(struct work_struct *work) +{ + struct radeon_device *rdev; + rdev = container_of(work, struct radeon_device, + pm.reclock_work); + + mutex_lock(&rdev->pm.reclock_mutex); + radeon_pm_set_clocks(rdev); + mutex_unlock(&rdev->pm.reclock_mutex); +} + +static void radeon_pm_idle_work_handler(struct work_struct *work) +{ + struct radeon_device *rdev; + unsigned long irq_flags; + rdev = container_of(work, struct radeon_device, pm.idle_work.work); + + spin_lock_irqsave(&rdev->pm.lock, irq_flags); + if (rdev->pm.state == PM_STATE_ACTIVE) { + int not_processed = 0; + + read_lock(&rdev->fence_drv.lock); + if (!list_empty(&rdev->fence_drv.emited)) { + struct list_head *ptr; + list_for_each(ptr, &rdev->fence_drv.emited) { + /* count up to 3, that's enought info */ + if (++not_processed >= 3) + break; + } + } + read_unlock(&rdev->fence_drv.lock); + + if (not_processed >= 3) { /* should upclock */ + if (rdev->pm.planned_action == PM_ACTION_DOWNCLOCK) { + rdev->pm.planned_action = PM_ACTION_NONE; + } else if (rdev->pm.planned_action == PM_ACTION_NONE && + rdev->pm.downclocked) { + rdev->pm.planned_action = PM_ACTION_UPCLOCK; + rdev->pm.action_timeout = jiffies + + msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS); + } + } else if (not_processed == 0) { /* should downclock */ + if (rdev->pm.planned_action == PM_ACTION_UPCLOCK) { + rdev->pm.planned_action = PM_ACTION_NONE; + } else if (rdev->pm.planned_action == PM_ACTION_NONE && + !rdev->pm.downclocked) { + rdev->pm.planned_action = PM_ACTION_DOWNCLOCK; + rdev->pm.action_timeout = jiffies + + msecs_to_jiffies(RADEON_RECLOCK_DELAY_MS); + } + } +#if 0 + if (rdev->pm.planned_action != PM_ACTION_NONE && + jiffies > rdev->pm.action_timeout) { + if (rdev->pm.active_crtcs & (1 << 0)) { + rdev->pm.req_vblank |= (1 << 0); + drm_vblank_get(rdev->ddev, 0); + } + if (rdev->pm.active_crtcs & (1 << 1)) { + rdev->pm.req_vblank |= (1 << 1); + drm_vblank_get(rdev->ddev, 1); + } + rdev->pm.vblank_callback = true; + } +#endif + } + spin_unlock_irqrestore(&rdev->pm.lock, irq_flags); + + queue_delayed_work(rdev->wq, &rdev->pm.idle_work, + msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); +} + /* * Debugfs info */ @@ -44,6 +287,7 @@ struct drm_device *dev = node->minor->dev; struct radeon_device *rdev = dev->dev_private; + seq_printf(m, "state: %s\n", pm_state_names[rdev->pm.state]); seq_printf(m, "default engine clock: %u0 kHz\n", rdev->clock.default_sclk); seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev)); seq_printf(m, "default memory clock: %u0 kHz\n", rdev->clock.default_mclk); @@ -58,7 +302,7 @@ }; #endif -int radeon_debugfs_pm_init(struct radeon_device *rdev) +static int radeon_debugfs_pm_init(struct radeon_device *rdev) { #if defined(CONFIG_DEBUG_FS) return radeon_debugfs_add_files(rdev, radeon_pm_info_list, ARRAY_SIZE(radeon_pm_info_list)); Index: linux-2.6.git/drivers/gpu/drm/radeon/r600.c =================================================================== --- linux-2.6.git.orig/drivers/gpu/drm/radeon/r600.c 2009-12-29 15:54:56.036020552 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/r600.c 2009-12-29 19:05:43.732756081 +0100 @@ -2689,6 +2689,7 @@ u32 last_entry = rdev->ih.ring_size - 16; u32 ring_index, disp_int, disp_int_cont, disp_int_cont2; unsigned long flags; + enum radeon_pm_action pm_action; bool queue_hotplug = false; DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr); @@ -2816,6 +2817,15 @@ break; case 233: /* GUI idle event */ DRM_DEBUG("IH: GUI idle\n"); + + spin_lock(&rdev->pm.lock); + pm_action = rdev->pm.planned_action; + spin_unlock(&rdev->pm.lock); + + /* Kick off the reclock if needed */ + if (pm_action != PM_ACTION_NONE) + queue_work(rdev->wq, &rdev->pm.reclock_work); + break; default: DRM_ERROR("Unhandled interrupt: %d %d\n", src_id, src_data);
Index: linux-2.6.git/drivers/gpu/drm/radeon/atom.c =================================================================== --- linux-2.6.git.orig/drivers/gpu/drm/radeon/atom.c 2009-12-29 19:05:43.828736246 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/atom.c 2009-12-29 19:08:27.560753496 +0100 @@ -608,7 +608,8 @@ uint8_t count = U8((*ptr)++); SDEBUG(" count: %d\n", count); if (arg == ATOM_UNIT_MICROSEC) - schedule_timeout_uninterruptible(usecs_to_jiffies(count)); + //schedule_timeout_uninterruptible(usecs_to_jiffies(count)); + mdelay(count); else schedule_timeout_uninterruptible(msecs_to_jiffies(count)); } @@ -1065,7 +1066,7 @@ ectx.start = base; ectx.ps = params; if (ws) - ectx.ws = kzalloc(4 * ws, GFP_KERNEL); + ectx.ws = kzalloc(4 * ws, in_atomic() ? GFP_ATOMIC : GFP_KERNEL); else ectx.ws = NULL; @@ -1095,9 +1096,9 @@ void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params) { - mutex_lock(&ctx->mutex); +// mutex_lock(&ctx->mutex); atom_execute_table_locked(ctx, index, params); - mutex_unlock(&ctx->mutex); +// mutex_unlock(&ctx->mutex); } static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 }; Index: linux-2.6.git/drivers/gpu/drm/radeon/r600.c =================================================================== --- linux-2.6.git.orig/drivers/gpu/drm/radeon/r600.c 2009-12-29 19:05:43.732756081 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/r600.c 2009-12-29 19:09:04.501787866 +0100 @@ -74,6 +74,8 @@ void r600_gpu_init(struct radeon_device *rdev); void r600_fini(struct radeon_device *rdev); +void radeon_pm_set_clocks(struct radeon_device *rdev, enum radeon_pm_action action); + /* hpd for digital panel detect/disconnect */ bool r600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd) { @@ -2824,7 +2826,7 @@ /* Kick off the reclock if needed */ if (pm_action != PM_ACTION_NONE) - queue_work(rdev->wq, &rdev->pm.reclock_work); + radeon_pm_set_clocks(rdev, pm_action); break; default: Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon_device.c =================================================================== --- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_device.c 2009-12-29 19:05:43.772754474 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_device.c 2009-12-29 19:08:27.560753496 +0100 @@ -637,6 +637,7 @@ mutex_init(&rdev->cs_mutex); mutex_init(&rdev->ib_pool.mutex); mutex_init(&rdev->cp.mutex); + spin_lock_init(&rdev->cp.ring_lock); if (rdev->family >= CHIP_R600) spin_lock_init(&rdev->ih.lock); mutex_init(&rdev->gem.mutex); Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon.h =================================================================== --- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon.h 2009-12-29 19:08:02.904889536 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/radeon.h 2009-12-29 19:08:27.560753496 +0100 @@ -397,6 +397,7 @@ uint32_t align_mask; uint32_t ptr_mask; struct mutex mutex; + struct spinlock ring_lock; bool ready; }; Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon_pm.c =================================================================== --- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_pm.c 2009-12-29 19:07:39.496885146 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_pm.c 2009-12-29 19:12:32.592787422 +0100 @@ -27,7 +27,7 @@ static void radeon_pm_check_limits(struct radeon_device *rdev); static void radeon_pm_set_clocks_locked(struct radeon_device *rdev); -static void radeon_pm_set_clocks(struct radeon_device *rdev); +void radeon_pm_set_clocks(struct radeon_device *rdev, enum radeon_pm_action action); static void radeon_pm_reclock_work_handler(struct work_struct *work); static void radeon_pm_idle_work_handler(struct work_struct *work); static int radeon_debugfs_pm_init(struct radeon_device *rdev); @@ -51,7 +51,7 @@ DRM_ERROR("Failed to register debugfs file for PM!\n"); } - INIT_WORK(&rdev->pm.reclock_work, radeon_pm_reclock_work_handler); +// INIT_WORK(&rdev->pm.reclock_work, radeon_pm_reclock_work_handler); INIT_DELAYED_WORK(&rdev->pm.idle_work, radeon_pm_idle_work_handler); if (radeon_dynpm != -1 && radeon_dynpm) { @@ -144,8 +144,9 @@ } } -static void radeon_pm_set_clocks(struct radeon_device *rdev) +void radeon_pm_set_clocks(struct radeon_device *rdev, enum radeon_pm_action action) { +#if 0 unsigned long flags; enum radeon_pm_action action; @@ -154,8 +155,12 @@ action = rdev->pm.planned_action; rdev->pm.planned_action = PM_ACTION_NONE; spin_unlock_irqrestore(&rdev->pm.lock, flags); +#endif + rdev->pm.planned_action = PM_ACTION_NONE; + + /* Lock the CP ring */ + spin_lock(&rdev->cp.ring_lock); - mutex_lock(&rdev->cp.mutex); /*radeon_fence_wait_last(rdev);*/ switch (action) { case PM_ACTION_UPCLOCK: @@ -176,8 +181,7 @@ DRM_ERROR("%s: PM_ACTION_NONE\n", __func__); break; } - - mutex_unlock(&rdev->cp.mutex); + spin_unlock(&rdev->cp.ring_lock); } #if 0 @@ -204,6 +208,7 @@ } #endif +#if 0 static void radeon_pm_reclock_work_handler(struct work_struct *work) { struct radeon_device *rdev; @@ -214,6 +219,7 @@ radeon_pm_set_clocks(rdev); mutex_unlock(&rdev->pm.reclock_mutex); } +#endif static void radeon_pm_idle_work_handler(struct work_struct *work) { Index: linux-2.6.git/drivers/gpu/drm/radeon/radeon_ring.c =================================================================== --- linux-2.6.git.orig/drivers/gpu/drm/radeon/radeon_ring.c 2009-12-29 19:05:43.796755382 +0100 +++ linux-2.6.git/drivers/gpu/drm/radeon/radeon_ring.c 2009-12-29 19:08:27.564755871 +0100 @@ -279,11 +279,13 @@ /* We pad to match fetch size */ count_dw_pad = (rdev->cp.align_mask + 1) - (rdev->cp.wptr & rdev->cp.align_mask); + spin_lock_irq(&rdev->cp.ring_lock); for (i = 0; i < count_dw_pad; i++) { radeon_ring_write(rdev, 2 << 30); } DRM_MEMORYBARRIER(); radeon_cp_commit(rdev); + spin_unlock_irq(&rdev->cp.ring_lock); mutex_unlock(&rdev->cp.mutex); }
------------------------------------------------------------------------------ This SF.Net email is sponsored by the Verizon Developer Community Take advantage of Verizon's best-in-class app development support A streamlined, 14 day to market process makes app distribution fast and easy Join now and get one step closer to millions of Verizon customers http://p.sf.net/sfu/verizon-dev2dev
-- _______________________________________________ Dri-devel mailing list Dri-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/dri-devel