summaryrefslogtreecommitdiff
path: root/linux-core/drm_drv.c
AgeCommit message (Expand)Author
2007-12-06take down stuff after asking driver to unloadDave Airlie
2007-12-01drm: Add _DRM_DRIVER map flag.Robert Noland
2007-11-22drm: major whitespace/coding style realignment with kernelDave Airlie
2007-11-05drm: remove lots of spurious whitespace.Dave Airlie
2007-10-26update DRM sysfs supportJesse Barnes
2007-10-25Tighten permissions on some buffer manager ioctls.Thomas Hellstrom
2007-10-25Buffer manager:Thomas Hellstrom
2007-10-17Remove the op ioctl, and replace it with a setuser ioctl.Thomas Hellstrom
2007-10-17Revert "Replace NO_MOVE/NO_EVICT flags to buffer objects with an ioctl to set...Thomas Hellstrom
2007-10-16Drop destroy ioctls for fences and buffer objects.Kristian Høgsberg
2007-10-04linux-drm: Obey device class requirements when detecting devices.Maarten Maathuis
2007-09-26Add bracketsAlan Hourihane
2007-09-26don't copy back if an error was returned.Alan Hourihane
2007-08-16Merge branch 'master' into bo-set-pinEric Anholt
2007-08-02drm: add unlocked ioctl code path - not used yetDave Airlie
2007-07-26debug print ioctl return value as -integer rather than fffffwhatever.Eric Anholt
2007-07-26Replace NO_MOVE/NO_EVICT flags to buffer objects with an ioctl to set pinning.Eric Anholt
2007-07-21Fix misc ioctl issues, makes Nouveau run.Pekka Paalanen
2007-07-21fix missing brace placement for IOC_INDave Airlie
2007-07-21fix drm no-compile due to BSD :-)Dave Airlie
2007-07-20Replace DRM_IOCTL_ARGS with (dev, data, file_priv) and remove DRM_DEVICE.Eric Anholt
2007-07-20Replace filp in ioctl arguments with drm_file *file_priv.Eric Anholt
2007-07-16drm: remove drmP.h internal typedefsDave Airlie
2007-07-16drm: detypedef drm.h and fixup all problemsDave Airlie
2007-07-11Merge branch 'master' into cleanupDave Airlie
2007-07-11Made drm_sg_alloc accessible from inside the DRM - drm_sg_alloc_ioctl is the ...Arthur Huillet
2007-07-02Fix must-check warnings and implement a few error paths.Kristian Høgsberg
2007-06-10use krh's idr mods to remove lists from idr codeDave Airlie
2007-06-03Revert "drm: add new drm_wait_on function to replace macro"root
2007-05-27drm: convert drawable handling to use Linux idrDave Airlie
2007-05-26Revert "drm/ttm: cleanup mm_ioctl ioctls to be separate ioctls."Dave Airlie
2007-05-26Revert "drm/ttm: cleanup most of fence ioctl split out"Dave Airlie
2007-05-26whitespace fixups from kernelDave Airlie
2007-05-26drm/ttm: cleanup most of fence ioctl split outDave Airlie
2007-05-26drm/ttm: cleanup mm_ioctl ioctls to be separate ioctls.Dave Airlie
2007-05-26drm: cleanup use of Linux list handling macrosDave Airlie
2007-05-08ttm: complete drm buffer object ioctl splitDave Airlie
2007-05-06drm/ttm: cleanup most of fence ioctl split outDave Airlie
2007-05-06drm/ttm: cleanup mm_ioctl ioctls to be separate ioctls.Dave Airlie
2007-04-28drm: add new drm_wait_on function to replace macroDave Airlie
2007-04-28remove DRM_GETSAREA and replace with drm_getsarea functionDave Airlie
2007-03-20rename badly named defineDave Airlie
2007-03-19cleanup ioctl expansion codeDave Airlie
2007-03-19make drm fops const from kernelDave Airlie
2007-01-25Remove a scary error printed when we were leaking memory caches.Thomas Hellstrom
2006-12-15Remove the memory caches for fence objects and memory manager nodes,Thomas Hellstrom
2006-10-20Bug #8707, 2.6.19-rc compatibility for memory manager code.Thomas Hellstrom
2006-10-17Add vma list memory usage to memory accounting.Thomas Hellstrom
2006-10-17Add memory usage accounting to avoid DOS problems.Thomas Hellstrom
2006-10-17Extend generality for more memory types.Thomas Hellstrom
> 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 587 588 589
/*
 * Copyright 2007 Jérôme Glisse
 * Copyright 2007 Alex Deucher
 * Copyright 2007 Dave Airlie
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation on the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NON-INFRINGEMENT.  IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR
 * THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
#include "radeon_ms.h"

static int radeon_ms_gpu_address_space_init(struct drm_device *dev)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;
	struct radeon_state *state = &dev_priv->driver_state;

	/* initialize gpu mapping */
	dev_priv->gpu_vram_start = dev_priv->vram.offset;
	dev_priv->gpu_vram_end = dev_priv->gpu_vram_start + dev_priv->vram.size;
	/* align it on 16Mo boundary (clamp memory which is then
	 * unreachable but not manufacturer should use strange
	 * memory size */
	dev_priv->gpu_vram_end = dev_priv->gpu_vram_end & (~0xFFFFFF);
	dev_priv->gpu_vram_end -= 1;
	dev_priv->gpu_vram_size = dev_priv->gpu_vram_end -
				  dev_priv->gpu_vram_start + 1;
	/* set gart size to 32Mo FIXME: should make a parameter for this */
	dev_priv->gpu_gart_size = 1024 * 1024 * 32;
	if (dev_priv->gpu_gart_size > (0xffffffffU - dev_priv->gpu_vram_end)) {
		/* align gart start to next 4Ko in gpu address space */
		dev_priv->gpu_gart_start = (dev_priv->gpu_vram_end + 1) + 0xfff;
		dev_priv->gpu_gart_start = dev_priv->gpu_gart_start & (~0xfff);
		dev_priv->gpu_gart_end = dev_priv->gpu_gart_start +
					 dev_priv->gpu_gart_size;
		dev_priv->gpu_gart_end = (dev_priv->gpu_gart_end & (~0xfff)) -
					 0x1000;
	} else {
		/* align gart start to next 4Ko in gpu address space */
		dev_priv->gpu_gart_start = (dev_priv->gpu_vram_start & ~0xfff) -
					   dev_priv->gpu_gart_size;
		dev_priv->gpu_gart_start = dev_priv->gpu_gart_start & (~0xfff);
		dev_priv->gpu_gart_end = dev_priv->gpu_gart_start +
					 dev_priv->gpu_gart_size;
		dev_priv->gpu_gart_end = (dev_priv->gpu_gart_end & (~0xfff)) -
					 0x1000;
	}
	state->mc_fb_location =
		REG_S(MC_FB_LOCATION, MC_FB_START,
				dev_priv->gpu_vram_start >> 16) |
		REG_S(MC_FB_LOCATION, MC_FB_TOP, dev_priv->gpu_vram_end >> 16);
	state->display_base_addr =
		REG_S(DISPLAY_BASE_ADDR, DISPLAY_BASE_ADDR,
				dev_priv->gpu_vram_start);
	state->config_aper_0_base = dev_priv->gpu_vram_start;
	state->config_aper_1_base = dev_priv->gpu_vram_start;
	state->config_aper_size = dev_priv->gpu_vram_size;
	DRM_INFO("[radeon_ms] gpu vram start 0x%08X\n",
		 dev_priv->gpu_vram_start);
	DRM_INFO("[radeon_ms] gpu vram end   0x%08X\n",
		 dev_priv->gpu_vram_end);
	DRM_INFO("[radeon_ms] gpu gart start 0x%08X\n",
		 dev_priv->gpu_gart_start);
	DRM_INFO("[radeon_ms] gpu gart end   0x%08X\n",
		 dev_priv->gpu_gart_end);
	return 0;
}

static void radeon_ms_gpu_reset(struct drm_device *dev)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;
	uint32_t clock_cntl_index, mclk_cntl, rbbm_soft_reset;
	uint32_t reset_mask, host_path_cntl, cache_mode;

	radeon_ms_cp_stop(dev);
	radeon_ms_gpu_flush(dev);

	/* reset clock */
	clock_cntl_index = MMIO_R(CLOCK_CNTL_INDEX);
	pll_index_errata(dev_priv);
	mclk_cntl = PPLL_R(MCLK_CNTL);
	PPLL_W(MCLK_CNTL,
			mclk_cntl |
			MCLK_CNTL__FORCE_MCLKA |
			MCLK_CNTL__FORCE_MCLKB |
			MCLK_CNTL__FORCE_YCLKA |
			MCLK_CNTL__FORCE_YCLKB |
			MCLK_CNTL__FORCE_MC |
			MCLK_CNTL__FORCE_AIC);
	PPLL_W(SCLK_CNTL,
			PPLL_R(SCLK_CNTL) |
			SCLK_CNTL__FORCE_CP |
			SCLK_CNTL__FORCE_VIP);

	/* Soft resetting HDP thru RBBM_SOFT_RESET register can cause some
	 * unexpected behaviour on some machines.  Here we use
	 * RADEON_HOST_PATH_CNTL to reset it.
	 */
	host_path_cntl = MMIO_R(HOST_PATH_CNTL);
	rbbm_soft_reset = MMIO_R(RBBM_SOFT_RESET);
	reset_mask = RBBM_SOFT_RESET__SOFT_RESET_CP |
		RBBM_SOFT_RESET__SOFT_RESET_HI |
		RBBM_SOFT_RESET__SOFT_RESET_VAP |
		RBBM_SOFT_RESET__SOFT_RESET_SE |
		RBBM_SOFT_RESET__SOFT_RESET_RE |
		RBBM_SOFT_RESET__SOFT_RESET_PP |
		RBBM_SOFT_RESET__SOFT_RESET_E2 |
		RBBM_SOFT_RESET__SOFT_RESET_RB;
	MMIO_W(RBBM_SOFT_RESET, rbbm_soft_reset | reset_mask);
	MMIO_R(RBBM_SOFT_RESET);
	MMIO_W(RBBM_SOFT_RESET, 0);
	MMIO_R(RBBM_SOFT_RESET);

	cache_mode = MMIO_R(RB2D_DSTCACHE_MODE);
	MMIO_W(RB2D_DSTCACHE_MODE,
			cache_mode | RB2D_DSTCACHE_MODE__DC_DISABLE_IGNORE_PE);

	MMIO_W(HOST_PATH_CNTL, host_path_cntl | HOST_PATH_CNTL__HDP_SOFT_RESET);
	MMIO_R(HOST_PATH_CNTL);
	MMIO_W(HOST_PATH_CNTL, host_path_cntl);
	MMIO_R(HOST_PATH_CNTL);

	MMIO_W(CLOCK_CNTL_INDEX, clock_cntl_index);
	pll_index_errata(dev_priv);
	PPLL_W(MCLK_CNTL, mclk_cntl);
}

static void radeon_ms_gpu_resume(struct drm_device *dev)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;
	uint32_t a;
	uint32_t i;

	/* make sure we have sane offset before restoring crtc */
	a = (MMIO_R(MC_FB_LOCATION) & MC_FB_LOCATION__MC_FB_START__MASK) << 16;
	MMIO_W(DISPLAY_BASE_ADDR, a);
	MMIO_W(CRTC2_DISPLAY_BASE_ADDR, a);
	MMIO_W(CRTC_OFFSET, 0);
	MMIO_W(CUR_OFFSET, 0);
	MMIO_W(CRTC_OFFSET_CNTL, CRTC_OFFSET_CNTL__CRTC_OFFSET_FLIP_CNTL);
	MMIO_W(CRTC2_OFFSET, 0);
	MMIO_W(CUR2_OFFSET, 0);
	MMIO_W(CRTC2_OFFSET_CNTL, CRTC2_OFFSET_CNTL__CRTC2_OFFSET_FLIP_CNTL);
	for (i = 0; i < dev_priv->usec_timeout; i++) {
		if (!(CRTC_OFFSET__CRTC_GUI_TRIG_OFFSET &
		      MMIO_R(CRTC_OFFSET))) {
			break;
		}
		DRM_UDELAY(1);
	}
	if (i >= dev_priv->usec_timeout) {
		DRM_ERROR("[radeon_ms] timeout waiting for crtc...\n");
	}
	for (i = 0; i < dev_priv->usec_timeout; i++) {
		if (!(CRTC2_OFFSET__CRTC2_GUI_TRIG_OFFSET &
		      MMIO_R(CRTC2_OFFSET))) {
			break;
		}
		DRM_UDELAY(1);
	}
	if (i >= dev_priv->usec_timeout) {
		DRM_ERROR("[radeon_ms] timeout waiting for crtc...\n");
	}
	DRM_UDELAY(10000);
}

static void radeon_ms_gpu_stop(struct drm_device *dev)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;
	uint32_t ov0_scale_cntl, crtc_ext_cntl, crtc_gen_cntl;
	uint32_t crtc2_gen_cntl, i;

	radeon_ms_wait_for_idle(dev);
	/* Capture MC_STATUS in case things go wrong ... */
	ov0_scale_cntl = dev_priv->ov0_scale_cntl = MMIO_R(OV0_SCALE_CNTL);
	crtc_ext_cntl = dev_priv->crtc_ext_cntl = MMIO_R(CRTC_EXT_CNTL);
	crtc_gen_cntl = dev_priv->crtc_gen_cntl = MMIO_R(CRTC_GEN_CNTL);
	crtc2_gen_cntl = dev_priv->crtc2_gen_cntl = MMIO_R(CRTC2_GEN_CNTL);
	ov0_scale_cntl &= ~OV0_SCALE_CNTL__OV0_OVERLAY_EN__MASK;
	crtc_ext_cntl |= CRTC_EXT_CNTL__CRTC_DISPLAY_DIS;
	crtc_gen_cntl &= ~CRTC_GEN_CNTL__CRTC_CUR_EN;
	crtc_gen_cntl &= ~CRTC_GEN_CNTL__CRTC_ICON_EN;
	crtc_gen_cntl |= CRTC_GEN_CNTL__CRTC_EXT_DISP_EN;
	crtc_gen_cntl |= CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B;
	crtc2_gen_cntl &= ~CRTC2_GEN_CNTL__CRTC2_CUR_EN;
	crtc2_gen_cntl &= ~CRTC2_GEN_CNTL__CRTC2_ICON_EN;
	crtc2_gen_cntl |= CRTC2_GEN_CNTL__CRTC2_DISP_REQ_EN_B;
	MMIO_W(OV0_SCALE_CNTL, ov0_scale_cntl);
	MMIO_W(CRTC_EXT_CNTL, crtc_ext_cntl);
	MMIO_W(CRTC_GEN_CNTL, crtc_gen_cntl);
	MMIO_W(CRTC2_GEN_CNTL, crtc2_gen_cntl);
	DRM_UDELAY(10000);
	switch (dev_priv->family) {
	case CHIP_R100:
	case CHIP_R200:
	case CHIP_RV200:
	case CHIP_RV250:
	case CHIP_RV280:
	case CHIP_RS300:
		for (i = 0; i < dev_priv->usec_timeout; i++) {
			if ((MC_STATUS__MC_IDLE & MMIO_R(MC_STATUS))) {
				DRM_INFO("[radeon_ms] gpu stoped in %d usecs\n",
					 i);
				return;
			}
			DRM_UDELAY(1);
		}
		break;
	case CHIP_R300:
	case CHIP_R350:
	case CHIP_R360:
	case CHIP_RV350:
	case CHIP_RV370:
	case CHIP_RV380:
	case CHIP_RS400:
	case CHIP_RV410:
	case CHIP_R420:
	case CHIP_R430:
	case CHIP_R480:
		for (i = 0; i < dev_priv->usec_timeout; i++) {
			if ((MC_STATUS__MC_IDLE_R3 & MMIO_R(MC_STATUS))) {
				DRM_INFO("[radeon_ms] gpu stoped in %d usecs\n",
					 i);
				return;
			}
			DRM_UDELAY(1);
		}
		break;
	default:
		DRM_ERROR("Unknown radeon family, aborting\n");
		return;
	}
	DRM_ERROR("[radeon_ms] failed to stop gpu...will proceed anyway\n");
	DRM_UDELAY(20000);
}

static int radeon_ms_wait_for_fifo(struct drm_device *dev, int num_fifo)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;
	int i;

	for (i = 0; i < dev_priv->usec_timeout; i++) {
		int t;
		t = RBBM_STATUS__CMDFIFO_AVAIL__MASK & MMIO_R(RBBM_STATUS);
		t = t >> RBBM_STATUS__CMDFIFO_AVAIL__SHIFT;
		if (t >= num_fifo)
			return 0;
		DRM_UDELAY(1);
	}
	DRM_ERROR("[radeon_ms] failed to wait for fifo\n");
	return -EBUSY;
}

int radeon_ms_gpu_initialize(struct drm_device *dev)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;
	struct radeon_state *state = &dev_priv->driver_state;
	int ret;

	state->disp_misc_cntl = DISP_MISC_CNTL__SYNC_PAD_FLOP_EN |
		REG_S(DISP_MISC_CNTL, SYNC_STRENGTH, 2) |
		REG_S(DISP_MISC_CNTL, PALETTE_MEM_RD_MARGIN, 0xb) |
		REG_S(DISP_MISC_CNTL, PALETTE2_MEM_RD_MARGIN, 0xb) |
		REG_S(DISP_MISC_CNTL, RMX_BUF_MEM_RD_MARGIN, 0x5);
	state->disp_merge_cntl = REG_S(DISP_MERGE_CNTL, DISP_GRPH_ALPHA, 0xff) |
		REG_S(DISP_MERGE_CNTL, DISP_OV0_ALPHA, 0xff);
	state->disp_pwr_man = DISP_PWR_MAN__DISP_PWR_MAN_D3_CRTC_EN |
		DISP_PWR_MAN__DISP2_PWR_MAN_D3_CRTC2_EN |
		REG_S(DISP_PWR_MAN, DISP_PWR_MAN_DPMS, DISP_PWR_MAN_DPMS__OFF) |
		DISP_PWR_MAN__DISP_D3_RST |
		DISP_PWR_MAN__DISP_D3_REG_RST |
		DISP_PWR_MAN__DISP_D3_GRPH_RST |
		DISP_PWR_MAN__DISP_D3_SUBPIC_RST |
		DISP_PWR_MAN__DISP_D3_OV0_RST |
		DISP_PWR_MAN__DISP_D1D2_GRPH_RST |
		DISP_PWR_MAN__DISP_D1D2_SUBPIC_RST |
		DISP_PWR_MAN__DISP_D1D2_OV0_RST |
		DISP_PWR_MAN__DISP_DVO_ENABLE_RST |
		DISP_PWR_MAN__TV_ENABLE_RST;
	state->disp2_merge_cntl = 0;
	ret = radeon_ms_gpu_address_space_init(dev);
	if (ret) {
		return ret;
	}

	/* initialize bus */
	ret = dev_priv->bus_init(dev);
	if (ret != 0) {
		return ret;
	}
	return 0;
}

void radeon_ms_gpu_dpms(struct drm_device *dev)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;
	struct radeon_state *state = &dev_priv->driver_state;

	if (dev_priv->crtc1_dpms == dev_priv->crtc2_dpms) {
		/* both crtc are in same state so use global display pwr */
		state->disp_pwr_man &= ~DISP_PWR_MAN__DISP_PWR_MAN_DPMS__MASK;
		switch(dev_priv->crtc1_dpms) {
		case DPMSModeOn:
			state->disp_pwr_man |= REG_S(DISP_PWR_MAN,
					DISP_PWR_MAN_DPMS,
					DISP_PWR_MAN_DPMS__ON);
			break;
		case DPMSModeStandby:
			state->disp_pwr_man |= REG_S(DISP_PWR_MAN,
					DISP_PWR_MAN_DPMS,
					DISP_PWR_MAN_DPMS__STANDBY);
			break;
		case DPMSModeSuspend:
			state->disp_pwr_man |= REG_S(DISP_PWR_MAN,
					DISP_PWR_MAN_DPMS,
					DISP_PWR_MAN_DPMS__SUSPEND);
			break;
		case DPMSModeOff:
			state->disp_pwr_man |= REG_S(DISP_PWR_MAN,
					DISP_PWR_MAN_DPMS,
					DISP_PWR_MAN_DPMS__OFF);
			break;
		default:
			/* error */
			break;
		}
		MMIO_W(DISP_PWR_MAN, state->disp_pwr_man);
	} else {
		state->disp_pwr_man &= ~DISP_PWR_MAN__DISP_PWR_MAN_DPMS__MASK;
		state->disp_pwr_man |= REG_S(DISP_PWR_MAN,
				DISP_PWR_MAN_DPMS,
				DISP_PWR_MAN_DPMS__ON);
		MMIO_W(DISP_PWR_MAN, state->disp_pwr_man);
	}
}

void radeon_ms_gpu_flush(struct drm_device *dev)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;
	uint32_t i;
	uint32_t purge2d;
	uint32_t purge3d;

	switch (dev_priv->family) {
	case CHIP_R100:
	case CHIP_R200:
	case CHIP_RV200:
	case CHIP_RV250:
	case CHIP_RV280:
	case CHIP_RS300:
		purge2d = REG_S(RB2D_DSTCACHE_CTLSTAT, DC_FLUSH, 3) |
			REG_S(RB2D_DSTCACHE_CTLSTAT, DC_FREE, 3);
		purge3d = REG_S(RB3D_DSTCACHE_CTLSTAT, DC_FLUSH, 3) |
			REG_S(RB3D_DSTCACHE_CTLSTAT, DC_FREE, 3);
		MMIO_W(RB2D_DSTCACHE_CTLSTAT, purge2d);
		MMIO_W(RB3D_DSTCACHE_CTLSTAT, purge3d);
		break;
	case CHIP_R300:
	case CHIP_R350:
	case CHIP_R360:
	case CHIP_RV350:
	case CHIP_RV370:
	case CHIP_RV380:
	case CHIP_RS400:
	case CHIP_RV410:
	case CHIP_R420:
	case CHIP_R430:
	case CHIP_R480:
		purge2d = REG_S(RB2D_DSTCACHE_CTLSTAT, DC_FLUSH, 3) |
			REG_S(RB2D_DSTCACHE_CTLSTAT, DC_FREE, 3);
		purge3d = REG_S(RB3D_DSTCACHE_CTLSTAT_R3, DC_FLUSH, 3) |
			REG_S(RB3D_DSTCACHE_CTLSTAT_R3, DC_FREE, 3);
		MMIO_W(RB2D_DSTCACHE_CTLSTAT, purge2d);
		MMIO_W(RB3D_DSTCACHE_CTLSTAT_R3, purge3d);
		break;
	default:
		DRM_ERROR("Unknown radeon family, aborting\n");
		return;
	}
	for (i = 0; i < dev_priv->usec_timeout; i++) {
		if (!(RB2D_DSTCACHE_CTLSTAT__DC_BUSY &
					MMIO_R(RB2D_DSTCACHE_CTLSTAT))) {
			return;
		}
		DRM_UDELAY(1);
	}
	DRM_ERROR("[radeon_ms] gpu flush timeout\n");
}

void radeon_ms_gpu_restore(struct drm_device *dev, struct radeon_state *state)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;
	uint32_t wait_until;
	uint32_t fbstart;
	int ret, ok = 1;

	radeon_ms_gpu_reset(dev);
	radeon_ms_wait_for_idle(dev);
	radeon_ms_gpu_stop(dev);

	MMIO_W(AIC_CTRL, state->aic_ctrl);
	MMIO_W(MC_FB_LOCATION, state->mc_fb_location);
	MMIO_R(MC_FB_LOCATION);
	MMIO_W(CONFIG_APER_0_BASE, state->config_aper_0_base);
	MMIO_W(CONFIG_APER_1_BASE, state->config_aper_1_base);
	MMIO_W(CONFIG_APER_SIZE, state->config_aper_size);
	MMIO_W(DISPLAY_BASE_ADDR, state->display_base_addr);
	if (dev_priv->bus_restore) {
		dev_priv->bus_restore(dev, state);
	}

	radeon_ms_gpu_reset(dev);
	radeon_ms_gpu_resume(dev);

	MMIO_W(BUS_CNTL, state->bus_cntl);
	wait_until = WAIT_UNTIL__WAIT_DMA_VIPH0_IDLE |
		WAIT_UNTIL__WAIT_DMA_VIPH1_IDLE |
		WAIT_UNTIL__WAIT_DMA_VIPH2_IDLE |
		WAIT_UNTIL__WAIT_DMA_VIPH3_IDLE |
		WAIT_UNTIL__WAIT_DMA_VID_IDLE |
		WAIT_UNTIL__WAIT_DMA_GUI_IDLE |
		WAIT_UNTIL__WAIT_2D_IDLE |
		WAIT_UNTIL__WAIT_3D_IDLE |
		WAIT_UNTIL__WAIT_2D_IDLECLEAN |
		WAIT_UNTIL__WAIT_3D_IDLECLEAN |
		WAIT_UNTIL__WAIT_HOST_IDLECLEAN;
	switch (dev_priv->family) {
	case CHIP_R100:
	case CHIP_R200:
	case CHIP_RV200:
	case CHIP_RV250:
	case CHIP_RV280:
	case CHIP_RS300:
		break;
	case CHIP_R300:
	case CHIP_R350:
	case CHIP_R360:
	case CHIP_RV350:
	case CHIP_RV370:
	case CHIP_RV380:
	case CHIP_RS400:
	case CHIP_RV410:
	case CHIP_R420:
	case CHIP_R430:
	case CHIP_R480:
		wait_until |= WAIT_UNTIL__WAIT_VAP_IDLE;
		break;
	}
	MMIO_W(WAIT_UNTIL, wait_until); 
	MMIO_W(DISP_MISC_CNTL, state->disp_misc_cntl);
	MMIO_W(DISP_PWR_MAN, state->disp_pwr_man);
	MMIO_W(DISP_MERGE_CNTL, state->disp_merge_cntl);
	MMIO_W(DISP_OUTPUT_CNTL, state->disp_output_cntl);
	MMIO_W(DISP2_MERGE_CNTL, state->disp2_merge_cntl);

	/* Setup engine location. This shouldn't be necessary since we
	 * set them appropriately before any accel ops, but let's avoid
	 * random bogus DMA in case we inadvertently trigger the engine
	 * in the wrong place (happened).
	 */
	ret = radeon_ms_wait_for_fifo(dev, 2);
	if (ret) {
		ok = 0;
		DRM_ERROR("[radeon_ms] no fifo for setting up dst & src gui\n");
		DRM_ERROR("[radeon_ms] proceed anyway\n");
	}
	fbstart = (MC_FB_LOCATION__MC_FB_START__MASK &
			MMIO_R(MC_FB_LOCATION)) << 16;
	MMIO_W(DST_PITCH_OFFSET,
		REG_S(DST_PITCH_OFFSET, DST_OFFSET, fbstart >> 10));
	MMIO_W(SRC_PITCH_OFFSET,
		REG_S(SRC_PITCH_OFFSET, SRC_OFFSET, fbstart >> 10));

	ret = radeon_ms_wait_for_fifo(dev, 1);
	if (ret) {
		ok = 0;
		DRM_ERROR("[radeon_ms] no fifo for setting up dp data type\n");
		DRM_ERROR("[radeon_ms] proceed anyway\n");
	}
#ifdef __BIG_ENDIAN
	MMIO_W(DP_DATATYPE, DP_DATATYPE__DP_BYTE_PIX_ORDER);
#else
	MMIO_W(DP_DATATYPE, 0);
#endif

	ret = radeon_ms_wait_for_fifo(dev, 1);
	if (ret) {
		ok = 0;
		DRM_ERROR("[radeon_ms] no fifo for setting up surface cntl\n");
		DRM_ERROR("[radeon_ms] proceed anyway\n");
	}
	MMIO_W(SURFACE_CNTL, SURFACE_CNTL__SURF_TRANSLATION_DIS);

	ret = radeon_ms_wait_for_fifo(dev, 2);
	if (ret) {
		ok = 0;
		DRM_ERROR("[radeon_ms] no fifo for setting scissor\n");
		DRM_ERROR("[radeon_ms] proceed anyway\n");
	}
	MMIO_W(DEFAULT_SC_BOTTOM_RIGHT, 0x1fff1fff);
	MMIO_W(DEFAULT2_SC_BOTTOM_RIGHT, 0x1fff1fff);

	ret = radeon_ms_wait_for_fifo(dev, 1);
	if (ret) {
		ok = 0;
		DRM_ERROR("[radeon_ms] no fifo for setting up gui cntl\n");
		DRM_ERROR("[radeon_ms] proceed anyway\n");
	}
	MMIO_W(DP_GUI_MASTER_CNTL, 0);

	ret = radeon_ms_wait_for_fifo(dev, 5);
	if (ret) {
		ok = 0;
		DRM_ERROR("[radeon_ms] no fifo for setting up clear color\n");
		DRM_ERROR("[radeon_ms] proceed anyway\n");
	}
	MMIO_W(DP_BRUSH_BKGD_CLR, 0x00000000);
	MMIO_W(DP_BRUSH_FRGD_CLR, 0xffffffff);
	MMIO_W(DP_SRC_BKGD_CLR, 0x00000000);
	MMIO_W(DP_SRC_FRGD_CLR, 0xffffffff);
	MMIO_W(DP_WRITE_MSK, 0xffffffff);

	if (!ok) {
		DRM_ERROR("[radeon_ms] engine restore not enough fifo\n");
	}
}

void radeon_ms_gpu_save(struct drm_device *dev, struct radeon_state *state)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;

	state->aic_ctrl = MMIO_R(AIC_CTRL);
	state->bus_cntl = MMIO_R(BUS_CNTL);
	state->mc_fb_location = MMIO_R(MC_FB_LOCATION);
	state->display_base_addr = MMIO_R(DISPLAY_BASE_ADDR);
	state->config_aper_0_base = MMIO_R(CONFIG_APER_0_BASE);
	state->config_aper_1_base = MMIO_R(CONFIG_APER_1_BASE);
	state->config_aper_size = MMIO_R(CONFIG_APER_SIZE);
	state->disp_misc_cntl = MMIO_R(DISP_MISC_CNTL);
	state->disp_pwr_man = MMIO_R(DISP_PWR_MAN);
	state->disp_merge_cntl = MMIO_R(DISP_MERGE_CNTL);
	state->disp_output_cntl = MMIO_R(DISP_OUTPUT_CNTL);
	state->disp2_merge_cntl = MMIO_R(DISP2_MERGE_CNTL);
	if (dev_priv->bus_save) {
		dev_priv->bus_save(dev, state);
	}
}

int radeon_ms_wait_for_idle(struct drm_device *dev)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;
	int i, j, ret;

	for (i = 0; i < 2; i++) {
		ret = radeon_ms_wait_for_fifo(dev, 64);
		if (ret) {
			DRM_ERROR("[radeon_ms] fifo not empty\n");
		}
		for (j = 0; j < dev_priv->usec_timeout; j++) {
			if (!(RBBM_STATUS__GUI_ACTIVE & MMIO_R(RBBM_STATUS))) {
				radeon_ms_gpu_flush(dev);
				return 0;
			}
			DRM_UDELAY(1);
		}
		DRM_ERROR("[radeon_ms] idle timed out:  status=0x%08x\n",
				MMIO_R(RBBM_STATUS));
		radeon_ms_gpu_stop(dev);
		radeon_ms_gpu_reset(dev);
	}
	return -EBUSY;
}