summaryrefslogtreecommitdiffstats
path: root/drm-radeon-pm.patch
blob: df12ec8a8ffd3041b3442436bc618b1645b5a226 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/atombios_crtc.c.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/atombios_crtc.c
--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/atombios_crtc.c.mjg	2009-03-03 19:41:48.000000000 +0000
+++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/atombios_crtc.c	2009-03-03 20:53:05.000000000 +0000
@@ -441,14 +441,23 @@ static bool atombios_crtc_mode_fixup(str
 
 static void atombios_crtc_prepare(struct drm_crtc *crtc)
 {
+	struct drm_device *dev = crtc->dev;
+	struct drm_radeon_private *dev_priv = dev->dev_private;
+
+	mutex_lock(&dev_priv->mode_info.power.pll_mutex);
+
 	atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
 	atombios_lock_crtc(crtc, 1);
 }
 
 static void atombios_crtc_commit(struct drm_crtc *crtc)
 {
+	struct drm_device *dev = crtc->dev;
+	struct drm_radeon_private *dev_priv = dev->dev_private;
+
 	atombios_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
 	atombios_lock_crtc(crtc, 0);
+	mutex_unlock(&dev_priv->mode_info.power.pll_mutex);
 }
 
 static const struct drm_crtc_helper_funcs atombios_helper_funcs = {
diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_atombios.c.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_atombios.c
--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_atombios.c.mjg	2009-03-03 19:41:48.000000000 +0000
+++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_atombios.c	2009-03-03 20:53:05.000000000 +0000
@@ -620,6 +620,34 @@ void radeon_atom_static_pwrmgt_setup(str
 	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args);
 }
 
+void radeon_atom_get_mc_arb_info(struct drm_device *dev)
+{
+	struct drm_radeon_private *dev_priv = dev->dev_private;
+	struct radeon_mode_info *mode_info = &dev_priv->mode_info;
+	struct atom_context *ctx = mode_info->atom_context;
+	int index = GetIndexIntoMasterTable(DATA, MC_InitParameter);
+	uint8_t frev, crev;
+	uint16_t size, data_offset;
+
+	atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset);
+	dev_priv->mode_info.power.mc_arb_init_values =
+		kmalloc(size*sizeof(int), GFP_KERNEL);
+	memcpy(dev_priv->mode_info.power.mc_arb_init_values,
+	       ctx->bios + data_offset, size * sizeof(int));
+}
+
+void radeon_atom_get_engine_clock(struct drm_device *dev, int *engine_clock)
+{
+	struct drm_radeon_private *dev_priv = dev->dev_private;
+	struct radeon_mode_info *mode_info = &dev_priv->mode_info;
+	struct atom_context *ctx = mode_info->atom_context;
+	GET_ENGINE_CLOCK_PS_ALLOCATION args;
+	int index = GetIndexIntoMasterTable(COMMAND, GetEngineClock);
+
+	atom_execute_table(ctx, index, (uint32_t *)&args);
+	*engine_clock = args.ulReturnEngineClock;
+}
+
 void radeon_atom_set_engine_clock(struct drm_device *dev, int eng_clock)
 {
 	struct drm_radeon_private *dev_priv = dev->dev_private;
@@ -633,6 +661,18 @@ void radeon_atom_set_engine_clock(struct
 	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args);
 }
 
+void radeon_atom_get_memory_clock(struct drm_device *dev, int *mem_clock)
+{
+	struct drm_radeon_private *dev_priv = dev->dev_private;
+	struct radeon_mode_info *mode_info = &dev_priv->mode_info;
+	struct atom_context *ctx = mode_info->atom_context;
+	GET_MEMORY_CLOCK_PS_ALLOCATION args;
+	int index = GetIndexIntoMasterTable(COMMAND, GetMemoryClock);
+	
+	atom_execute_table(ctx, index, (uint32_t *)&args);
+	*mem_clock = args.ulReturnMemoryClock;
+}
+
 void radeon_atom_set_memory_clock(struct drm_device *dev, int mem_clock)
 {
 	struct drm_radeon_private *dev_priv = dev->dev_private;
@@ -646,6 +686,16 @@ void radeon_atom_set_memory_clock(struct
 	atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args);
 }
 
+void radeon_atom_initialize_memory_controller(struct drm_device *dev)
+{
+	struct drm_radeon_private *dev_priv = dev->dev_private;
+	struct atom_context *ctx = dev_priv->mode_info.atom_context;
+	int index = GetIndexIntoMasterTable(COMMAND, MemoryDeviceInit);
+	MEMORY_PLLINIT_PS_ALLOCATION args;
+
+	atom_execute_table(ctx, index, (uint32_t *)&args);
+}
+
 void radeon_atom_initialize_bios_scratch_regs(struct drm_device *dev)
 {
 	struct drm_radeon_private *dev_priv = dev->dev_private;
diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_cp.c.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_cp.c
--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_cp.c.mjg	2009-03-03 19:41:48.000000000 +0000
+++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_cp.c	2009-03-03 20:53:05.000000000 +0000
@@ -3223,6 +3223,8 @@ int radeon_driver_load(struct drm_device
 		if (ret)
 			goto modeset_fail;
 
+		mutex_init(&dev_priv->mode_info.power.pll_mutex);
+
 		radeon_modeset_init(dev);
 
 		radeon_modeset_cp_init(dev);
@@ -3231,7 +3233,7 @@ int radeon_driver_load(struct drm_device
 		drm_irq_install(dev);
 	}
 
-
+	radeon_pm_init(dev);
 	return ret;
 modeset_fail:
 	dev->driver->driver_features &= ~DRIVER_MODESET;
@@ -3303,6 +3305,8 @@ int radeon_driver_unload(struct drm_devi
 {
 	drm_radeon_private_t *dev_priv = dev->dev_private;
 
+	radeon_pm_exit(dev);
+
 	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
 		drm_irq_uninstall(dev);
 		radeon_modeset_cleanup(dev);
diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_cs.c.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_cs.c
--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_cs.c.mjg	2009-03-03 19:41:48.000000000 +0000
+++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_cs.c	2009-03-03 20:53:05.000000000 +0000
@@ -41,6 +41,8 @@ int radeon_cs_ioctl(struct drm_device *d
 	long size;
 	int r, i;
 
+	radeon_pm_timer_reset(dev);
+
 	mutex_lock(&dev_priv->cs.cs_mutex);
 	/* set command stream id to 0 which is fake id */
 	cs_id = 0;
diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_drv.h.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_drv.h
--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_drv.h.mjg	2009-03-03 19:41:48.000000000 +0000
+++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_drv.h	2009-03-03 20:53:05.000000000 +0000
@@ -612,6 +612,9 @@ extern int radeon_modeset_cp_resume(stru
 /* radeon_pm.c */
 int radeon_suspend(struct drm_device *dev, pm_message_t state);
 int radeon_resume(struct drm_device *dev);
+void radeon_pm_init(struct drm_device *dev);
+void radeon_pm_exit(struct drm_device *dev);
+void radeon_pm_timer_reset(struct drm_device *dev);
 
 /* Flags for stats.boxes
  */
diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_irq.c.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_irq.c
--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_irq.c.mjg	2009-03-03 19:41:48.000000000 +0000
+++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_irq.c	2009-03-03 20:53:05.000000000 +0000
@@ -185,8 +185,10 @@ irqreturn_t radeon_driver_irq_handler(DR
 	struct drm_device *dev = (struct drm_device *) arg;
 	drm_radeon_private_t *dev_priv =
 	    (drm_radeon_private_t *) dev->dev_private;
+	struct radeon_powermanagement_info *power = &dev_priv->mode_info.power;
 	u32 stat;
 	u32 r500_disp_int;
+	unsigned long flags;
 
 	/* Only consider the bits we're interested in - others could be used
 	 * outside the DRM
@@ -206,15 +208,47 @@ irqreturn_t radeon_driver_irq_handler(DR
 
 	/* VBLANK interrupt */
 	if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS690) {
-		if (r500_disp_int & R500_D1_VBLANK_INTERRUPT)
+		if (r500_disp_int & R500_D1_VBLANK_INTERRUPT) {
+			spin_lock_irqsave(&power->power_lock, flags);
+			if (power->reclock_head & 1) {
+				power->reclock_head &= ~1;
+				schedule_work(&power->reclock_work);
+				drm_vblank_put(dev, 0);
+			}
+			spin_unlock_irqrestore(&power->power_lock, flags);
 			drm_handle_vblank(dev, 0);
-		if (r500_disp_int & R500_D2_VBLANK_INTERRUPT)
+		}
+		if (r500_disp_int & R500_D2_VBLANK_INTERRUPT) {
+			spin_lock_irqsave(&power->power_lock, flags);
+			if (power->reclock_head & 2) {
+				power->reclock_head &= ~2;
+				schedule_work(&power->reclock_work);
+				drm_vblank_put(dev, 1);
+			}
+			spin_unlock_irqrestore(&power->power_lock, flags);
 			drm_handle_vblank(dev, 1);
+		}
 	} else {
-		if (stat & RADEON_CRTC_VBLANK_STAT)
+		if (stat & RADEON_CRTC_VBLANK_STAT) {
+			spin_lock_irqsave(&power->power_lock, flags);
+			if (power->reclock_head & 1) {
+				power->reclock_head &= ~1;
+				schedule_work(&power->reclock_work);
+				drm_vblank_put(dev, 0);
+			}
+			spin_unlock_irqrestore(&power->power_lock, flags);
 			drm_handle_vblank(dev, 0);
-		if (stat & RADEON_CRTC2_VBLANK_STAT)
+		}
+		if (stat & RADEON_CRTC2_VBLANK_STAT) {
+			spin_lock_irqsave(&power->power_lock, flags);
+			if (power->reclock_head & 2) {
+				power->reclock_head &= ~2;
+				schedule_work(&power->reclock_work);
+				drm_vblank_put(dev, 1);
+			}
+			spin_unlock_irqrestore(&power->power_lock, flags);
 			drm_handle_vblank(dev, 1);
+		}
 	}
 	return IRQ_HANDLED;
 }
diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_mode.h.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_mode.h
--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_mode.h.mjg	2009-03-03 19:41:48.000000000 +0000
+++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_mode.h	2009-03-03 20:53:05.000000000 +0000
@@ -173,6 +173,22 @@ struct radeon_i2c_chan {
 	struct radeon_i2c_bus_rec rec;
 };
 
+struct radeon_powermanagement_info {
+	struct timer_list idle_power_timer;
+	struct work_struct reclock_work;
+	struct drm_device *dev;
+	uint32_t orig_memory_clock;
+	uint32_t orig_engine_clock;
+	uint32_t *mc_arb_init_values;
+	uint8_t orig_fbdiv;
+	int new_mem_clock;
+	int new_engine_clock;
+	int current_clock_state;
+	int reclock_head;
+	struct mutex pll_mutex;
+	spinlock_t power_lock;
+};
+
 struct radeon_mode_info {
 	struct atom_context *atom_context;
 	struct radeon_bios_connector bios_connector[RADEON_MAX_BIOS_CONNECTOR];
@@ -182,6 +198,9 @@ struct radeon_mode_info {
 	struct radeon_pll mpll;
 	uint32_t mclk;
 	uint32_t sclk;
+
+	/* power management */
+	struct radeon_powermanagement_info power;
 };
 
 struct radeon_crtc {
@@ -307,6 +326,12 @@ extern int radeon_crtc_cursor_move(struc
 
 extern bool radeon_atom_get_clock_info(struct drm_device *dev);
 extern bool radeon_combios_get_clock_info(struct drm_device *dev);
+extern void radeon_atom_get_engine_clock(struct drm_device *dev, int *engine_clock);
+extern void radeon_atom_get_memory_clock(struct drm_device *dev, int *memory_clock);
+extern void radeon_atom_set_engine_clock(struct drm_device *dev, int engine_clock);
+extern void radeon_atom_set_memory_clock(struct drm_device *dev, int memory_clock);
+extern void radeon_atom_initialize_memory_controller(struct drm_device *dev);
+extern void radeon_atom_get_mc_arb_info(struct drm_device *dev);
 extern void radeon_atombios_get_lvds_info(struct radeon_encoder *encoder);
 extern void radeon_atombios_get_tmds_info(struct radeon_encoder *encoder);
 extern bool radeon_combios_get_lvds_info(struct radeon_encoder *encoder);
diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_pm.c.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_pm.c
--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_pm.c.mjg	2009-03-03 19:41:48.000000000 +0000
+++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_pm.c	2009-03-03 20:53:05.000000000 +0000
@@ -31,6 +31,8 @@
 
 #include "drm_crtc_helper.h"
 
+#define RADEON_DOWNCLOCK_IDLE_MS 30
+
 int radeon_suspend(struct drm_device *dev, pm_message_t state)
 {
 	struct drm_radeon_private *dev_priv = dev->dev_private;
@@ -255,3 +257,214 @@ bool radeon_set_pcie_lanes(struct drm_de
 		return false;
 }
 
+static void radeon_pm_set_engine_clock(struct drm_device *dev, int freq)
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+
+	if (dev_priv->is_atom_bios)
+		radeon_atom_set_engine_clock(dev, freq);
+}
+
+static void radeon_pm_set_memory_clock(struct drm_device *dev, int freq)
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	struct radeon_powermanagement_info *power = &dev_priv->mode_info.power;
+
+	mutex_lock(&power->pll_mutex);
+	radeon_do_cp_idle(dev_priv);
+	if (dev_priv->is_atom_bios) {
+		int mpll, spll, hclk, sclk, fbdiv, index, factor;
+		switch (dev_priv->chip_family) {
+		case CHIP_R520:
+		case CHIP_RV530:
+		case CHIP_RV560:
+		case CHIP_RV570:
+		case CHIP_R580:
+			mpll = RADEON_READ_PLL(dev_priv, MPLL_FUNC_CNTL);
+			fbdiv = (mpll & 0x1fe0) >> 5;
+
+			/* Set new fbdiv */
+			factor = power->orig_memory_clock / freq;
+			fbdiv = power->orig_fbdiv / factor;
+
+			mpll &= ~0x1fe0;
+			mpll |= ((fbdiv << 5) | (1 << 24));
+			mpll &= ~(1 << 25);
+
+			spll = RADEON_READ_PLL(dev_priv, SPLL_FUNC_CNTL);
+
+			hclk = fbdiv << 5;
+			hclk += 0x20;
+			hclk *= 8;
+
+			sclk = spll & 0x1fe0;
+			sclk += 0x20;
+			sclk *= 6;
+			sclk = sclk >> 5;
+
+			index = (hclk/sclk);
+
+			R500_WRITE_MCIND(R530_MC_ARB_RATIO_CLK_SEQ,
+					 power->mc_arb_init_values[index]);
+			RADEON_WRITE_PLL(dev_priv, MPLL_FUNC_CNTL, mpll);
+			radeon_atom_initialize_memory_controller(dev);
+			break;
+		}
+	}
+
+	mutex_unlock(&power->pll_mutex);
+}
+
+static int radeon_pm_get_active_crtcs(struct drm_device *dev, int *crtcs)
+{
+	struct drm_crtc *crtc;
+	int count = 0;
+	struct radeon_crtc *radeon_crtc;
+	
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+		radeon_crtc = to_radeon_crtc(crtc);
+		if (crtc->enabled) {
+			count++;
+			*crtcs |= (1 << radeon_crtc->crtc_id);
+		}
+	}
+	return count;
+}
+
+
+static void radeon_pm_perform_transition(struct drm_device *dev)
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	struct radeon_powermanagement_info *power = &dev_priv->mode_info.power;
+	int crtcs = 0, count;
+	unsigned long flags;
+
+	count = radeon_pm_get_active_crtcs(dev, &crtcs);
+
+	spin_lock_irqsave(&power->power_lock, flags);
+	switch (count) {
+	case 0:
+		schedule_work(&power->reclock_work);
+		break;
+	case 1:
+		if (power->reclock_head)
+			break;
+		if (crtcs & 1) {
+			power->reclock_head |= 1;
+			drm_vblank_get(dev, 0);
+		} else {
+			power->reclock_head |= 2;
+			drm_vblank_get(dev, 1);
+		}
+		break;
+	default:
+		/* Too many active heads */
+		break;
+	}
+	spin_unlock_irqrestore(&power->power_lock, flags);
+}
+
+
+static int radeon_pm_set_runtime_power(struct drm_device *dev, int value)
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	struct radeon_powermanagement_info *power = &dev_priv->mode_info.power;
+
+	if (power->current_clock_state == value)
+		return 1;
+
+	switch (value) {
+	case 0:
+		power->new_engine_clock = 100*100;
+		power->new_mem_clock = 100*100;
+		break;
+	case 1:
+		power->new_engine_clock = power->orig_engine_clock;
+		power->new_mem_clock = power->orig_memory_clock;
+		break;
+	}
+
+	power->current_clock_state = value;
+	radeon_pm_perform_transition(dev);
+
+	return 0;
+}
+
+static void radeon_pm_idle_timeout(unsigned long d)
+{
+	struct drm_device *dev = (struct drm_device *)d;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+
+	radeon_pm_set_runtime_power(dev, 0);
+}
+
+static void radeon_pm_reclock_callback(struct work_struct *work)
+{
+	struct radeon_powermanagement_info *power =
+		container_of(work, struct radeon_powermanagement_info,
+			     reclock_work);
+	struct drm_device *dev = power->dev;	
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+
+	mutex_lock(&dev_priv->cs.cs_mutex);
+	radeon_pm_set_memory_clock(dev, power->new_mem_clock);
+	radeon_pm_set_engine_clock(dev, power->new_engine_clock);
+	mutex_unlock(&dev_priv->cs.cs_mutex);
+}
+
+void radeon_pm_timer_reset(struct drm_device *dev)
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	struct radeon_powermanagement_info *power = &dev_priv->mode_info.power;
+
+	if (!drm_core_check_feature(dev, DRIVER_MODESET))
+		return;
+
+	radeon_pm_set_runtime_power(dev, 1);
+
+	mod_timer(&power->idle_power_timer,
+		  jiffies + msecs_to_jiffies(RADEON_DOWNCLOCK_IDLE_MS));
+}
+
+void radeon_pm_init(struct drm_device *dev)
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	struct radeon_powermanagement_info *power = &dev_priv->mode_info.power;
+
+	power->dev = dev;
+
+	if (!drm_core_check_feature(dev, DRIVER_MODESET))
+		return;
+
+	if (dev_priv->is_atom_bios) {
+		int mpll;
+		radeon_atom_get_mc_arb_info(dev);
+		radeon_atom_get_engine_clock(dev, &power->orig_engine_clock);
+		radeon_atom_get_memory_clock(dev, &power->orig_memory_clock);
+
+		mpll = RADEON_READ_PLL(dev_priv, MPLL_FUNC_CNTL);
+		dev_priv->mode_info.power.orig_fbdiv = (mpll & 0x1fe0) >> 5;
+	}
+
+	setup_timer(&power->idle_power_timer, radeon_pm_idle_timeout,
+		    (unsigned long)dev);
+	INIT_WORK(&power->reclock_work, radeon_pm_reclock_callback);
+
+	spin_lock_init(&power->power_lock);
+
+	power->current_clock_state = 1;
+	power->reclock_head = 0;
+
+	radeon_pm_timer_reset(dev);
+}
+
+void radeon_pm_exit(struct drm_device *dev)
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	struct radeon_powermanagement_info *power = &dev_priv->mode_info.power;
+
+	if (!drm_core_check_feature(dev, DRIVER_MODESET))
+		return;
+
+	del_timer_sync(&power->idle_power_timer);
+}
diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_reg.h.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_reg.h
--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_reg.h.mjg	2009-03-03 19:41:48.000000000 +0000
+++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_reg.h	2009-03-03 20:53:05.000000000 +0000
@@ -303,6 +303,28 @@
 #       define RADEON_PLL_WR_EN             (1 << 7)
 #       define RADEON_PLL_DIV_SEL           (3 << 8)
 #       define RADEON_PLL2_DIV_SEL_MASK     ~(3 << 8)
+#define SPLL_FUNC_CNTL                     0x0000
+#define MPLL_FUNC_CNTL                     0x0004
+#define GENERAL_PWRMGT                     0x0008
+#      define RADEON_GLOBAL_PWRMGT_EN      (1 << 0)
+#define SCLK_PWRMGT_CNTL                   0x0009
+#      define RADEON_SCLK_PWRMGT_OFF       (1 << 0)
+#define MCLK_PWRMGT_CNTL                   0x000a
+#      define RADEON_MCLK_PWRMGT_OFF       (1 << 0)
+#define DYN_PWRMGT_SCLK_CNTL               0x000b
+#      define RADEON_ENGINE_DYNCLK_MODE    (1 << 0)
+#      define RADEON_STATIC_SCREEN_EN      (1 << 20)
+#      define RADEON_CLIENT_SELECT_POWER_EN (1 << 21)
+#define DYN_SCLK_PWMEN_PIPE                0x000d
+#      define RADEON_PIPE_3D_NOT_AUTO      (1 << 8)
+#define DYN_SCLK_VOL_CNTL                  0x000e
+#      define RADEON_IO_CG_VOLTAGE_DROP    (1 << 0)
+#      define RADEON_VOLTAGE_DROP_SYNC     (1 << 2)
+#define CP_DYN_CNTL                        0x000f
+#      define RADEON_CP_FORCEON            (1 << 0)
+#      define RADEON_CP_LOWER_POWER_IGNORE (1 << 20)
+#      define RADEON_CP_NORMAL_POWER_IGNORE (1 << 21)
+#      define RADEON_CP_NORMAL_POWER_BUSY  (1 << 24)
 #define RADEON_CLK_PWRMGT_CNTL              0x0014
 #       define RADEON_ENGIN_DYNCLK_MODE     (1 << 12)
 #       define RADEON_ACTIVE_HILO_LAT_MASK  (3 << 13)
@@ -3961,7 +3983,48 @@
 #	define AVIVO_I2C_RESET						(1 << 8)
 
 #define R600_GENERAL_PWRMGT                                        0x618
+#	define R600_GLOBAL_PWRMGT_EN                               (1 << 0)
+#	define R600_STATIC_PM_EN                                   (1 << 1)
+#	define R600_MOBILE_SU                                      (1 << 2)
+#	define R600_THERMAL_PROTECTION_DIS                         (1 << 3)
+#	define R600_THERMAL_PROTECTION_TYPE                        (1 << 4)
+#	define R600_ENABLE_GEN2PCIE                                (1 << 5)
+#	define R600_SW_GPIO_INDEX                                  (1 << 6)
+#	define R600_LOW_VOLT_D2_ACPI                               (1 << 8)
+#	define R600_LOW_VOLT_D3_ACPI                               (1 << 9)
+#	define R600_VOLT_PWRMGT_EN                                 (1 << 10)
 #	define R600_OPEN_DRAIN_PADS				   (1 << 11)
+#	define R600_AVP_SCLK_EN                                    (1 << 12)
+#	define R600_IDCT_SCLK_EN                                   (1 << 13)
+#	define R600_GPU_COUNTER_ACPI                               (1 << 14)
+#	define R600_COUNTER_CLK                                    (1 << 15)
+#	define R600_BACKBIAS_PAD_EN                                (1 << 16)
+#	define R600_BACKBIAS_VALUE                                 (1 << 17)
+#	define R600_BACKBIAS_DPM_CNTL                              (1 << 18)
+#	define R600_SPREAD_SPECTRUM_INDEX                          (1 << 19)
+#	define R600_DYN_SPREAD_SPECTRUM_EN                         (1 << 21)
+
+#define R600_SCLK_PWRMGT_CNTL                                     0x620
+#	define R600_SCLK_PWRMGT_OFF                                (1 << 0)
+#	define R600_SCLK_TURNOFF                                   (1 << 1)
+#	define R600_SPLL_TURNOFF                                   (1 << 2)
+#	define R600_SU_SCLK_USE_BCLK                               (1 << 3)
+#	define R600_DYNAMIC_GFX_ISLAND_PWR_DOWN                    (1 << 4)
+#	define R600_DYNAMIC_GFX_ISLAND_LP                          (1 << 5)
+#	define R600_CLK_TURN_ON_STAGGER                            (1 << 6)
+#	define R600_CLK_TURN_OFF_STAGGER                           (1 << 7)
+#	define R600_FIR_FORCE_TREND_SEL                            (1 << 8)
+#	define R600_FIR_TREND_MODE                                 (1 << 9)
+#	define R600_DYN_GFX_CLK_OFF_EN                             (1 << 10)
+#	define R600_VDDC3D_TURNOFF_D1                              (1 << 11)
+#	define R600_VDDC3D_TURNOFF_D2                              (1 << 12)
+#	define R600_VDDC3D_TURNOFF_D3                              (1 << 13)
+#	define R600_SPLL_TURNOFF_D2                                (1 << 14)
+#	define R600_SCLK_LOW_D1                                    (1 << 15)
+#	define R600_DYN_GFX_CLK_OFF_MC_EN                          (1 << 16)
+
+#define R600_MCLK_PWRMGT_CNTL                                     0x624
+#      define R600_MPLL_PWRMGT_OFF                                (1 << 0)
 
 #define R600_LOWER_GPIO_ENABLE                                     0x710
 #define R600_CTXSW_VID_LOWER_GPIO_CNTL                             0x718
@@ -5331,5 +5394,6 @@
 #   define R500_RS_IP_OFFSET_EN 			(1 << 31)
 
 #define R500_DYN_SCLK_PWMEM_PIPE                        0x000d /* PLL */
+#define R530_MC_ARB_RATIO_CLK_SEQ			0x0016 /* MC */
 
 #endif