/*
 * Copyright 2007, 2008  Luc Verhaegen <lverhaegen@novell.com>
 * Copyright 2007, 2008  Matthias Hopf <mhopf@novell.com>
 * Copyright 2007, 2008  Egbert Eich   <eich@novell.com>
 * Copyright 2007, 2008  Advanced Micro Devices, Inc.
 *
 * 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
 * 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 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 NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "xf86.h"

/* for usleep */
#if HAVE_XF86_ANSIC_H
# include "xf86_ansic.h"
#else
# include <unistd.h>
#endif

#include "rhd.h"
#include "rhd_crtc.h"
#include "rhd_pll.h"
#include "rhd_lut.h"
#include "rhd_regs.h"
#include "rhd_modes.h"
#include "rhd_mc.h"
#ifdef ATOM_BIOS
# include "rhd_atombios.h"
#endif

#define D1_REG_OFFSET 0x0000
#define D2_REG_OFFSET 0x0800
#define FMT1_REG_OFFSET 0x0000
#define FMT2_REG_OFFSET 0x800

struct rhdCrtcFMTPrivate {
    CARD32 StoreControl;
    CARD32 StoreBitDepthControl;
    CARD32 StoreClampCntl;
};

struct rhdCrtcFBPrivate {
    CARD32 StoreGrphEnable;
    CARD32 StoreGrphControl;
    CARD32 StoreGrphXStart;
    CARD32 StoreGrphYStart;
    CARD32 StoreGrphXEnd;
    CARD32 StoreGrphYEnd;
    CARD32 StoreGrphSwap;
    CARD32 StoreGrphPrimarySurfaceAddress;
    CARD32 StoreGrphSurfaceOffsetX;
    CARD32 StoreGrphSurfaceOffsetY;
    CARD32 StoreGrphPitch;
    CARD32 StoreModeDesktopHeight;
};

struct rhdCrtcLUTPrivate {
    CARD32 StoreGrphLutSel;
};

struct rhdCrtcScalePrivate {
    CARD32 StoreModeViewPortSize;

    CARD32 StoreModeOverScanH;
    CARD32 StoreModeOverScanV;

    CARD32 StoreModeViewPortStart;
    CARD32 StoreScaleEnable;
    CARD32 StoreScaleTapControl;
    CARD32 StoreModeCenter;
    CARD32 StoreScaleHV;
    CARD32 StoreScaleHFilter;
    CARD32 StoreScaleVFilter;
    CARD32 StoreScaleDither;
};

struct rhdCrtcModePrivate {
    CARD32 StoreCrtcControl;

    CARD32 StoreCrtcHTotal;
    CARD32 StoreCrtcHBlankStartEnd;
    CARD32 StoreCrtcHSyncA;
    CARD32 StoreCrtcHSyncACntl;
    CARD32 StoreCrtcHSyncB;
    CARD32 StoreCrtcHSyncBCntl;

    CARD32 StoreCrtcVTotal;
    CARD32 StoreCrtcVBlankStartEnd;
    CARD32 StoreCrtcVSyncA;
    CARD32 StoreCrtcVSyncACntl;
    CARD32 StoreCrtcVSyncB;
    CARD32 StoreCrtcVSyncBCntl;
    CARD32 StoreCrtcCountControl;

    CARD32 StoreModeDataFormat;
    CARD32 StoreCrtcInterlaceControl;

    CARD32 StoreCrtcBlackColor;
    CARD32 StoreCrtcBlankControl;
};

/*
 * Checks whether Width, Height are within boundaries.
 * If MODE_OK is returned and pPitch is not NULL, it is set.
 */
static ModeStatus
DxFBValid(struct rhdCrtc *Crtc, CARD16 Width, CARD16 Height, int bpp,
	  CARD32 Offset, CARD32 Size, CARD32 *pPitch)
{
    RHDPtr rhdPtr = RHDPTRI(Crtc);
    ScrnInfoPtr pScrn = rhdPtr->pScrn;

    CARD16 Pitch;
    unsigned int BytesPerPixel;
    CARD8 PitchMask = 0xFF;

    RHDDebug(Crtc->scrnIndex, "FUNCTION: %s: %s\n", __func__, Crtc->Name);

    /* If we hit this, then the memory claimed so far is not properly aligned */
    if (Offset & 0xFFF) {
	xf86DrvMsg(Crtc->scrnIndex, X_ERROR, "%s: Offset (0x%08X) is invalid!\n",
		   __func__, (int) Offset);
      return MODE_ERROR;
    }

    switch (pScrn->bitsPerPixel) {
      case 8:
        BytesPerPixel = 1;
        break;
      case 15:
      case 16:
        BytesPerPixel = 2;
        PitchMask /= BytesPerPixel;
        break;
     case 24:
     case 32:
       BytesPerPixel = 4;
       PitchMask /= BytesPerPixel;
       break;
     default:
	xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s: %dbpp is not implemented!\n",
		   __func__, pScrn->bitsPerPixel);
       return MODE_BAD;
    }

    if((Width==720)&&(Height==400))    //skip textmode
      return MODE_BAD;

     /* Be reasonable */
    if (Width < 640)
      return MODE_H_ILLEGAL;
    if (Height < 480)
      return MODE_V_ILLEGAL;

    /* D1GRPH_X_START is 14bits while D1_MODE_VIEWPORT_X_START is only 13 bits.
     * Since it is reasonable to assume that modes will be at least 1x1
     * limit at 13bits + 1 */
    if (Width > 0x2000)
      return MODE_VIRTUAL_X;

    /* D1GRPH_Y_START is 14bits while D1_MODE_VIEWPORT_Y_START is only 13 bits.
     * Since it is reasonable to assume that modes will be at least 1x1
     * limit at 13bits + 1 */
    if (Height > 0x2000)
      return MODE_VIRTUAL_Y;

    Pitch = (Width + PitchMask) & ~PitchMask;
    /* D1_PITCH limit: should never happen after clamping Width to 0x2000 */
    if (Pitch >= 0x4000)
      return MODE_VIRTUAL_X;

    if ((Pitch * BytesPerPixel * Height) > Size)
      return MODE_MEM_VIRT;

    if (pPitch)
      *pPitch = Pitch;
    return MODE_OK;
}

/*
 *
 */
static void
DxFBSet(struct rhdCrtc *Crtc, CARD16 Pitch, CARD16 Width, CARD16 Height,
	int bpp, CARD32 Offset)
{
    RHDPtr rhdPtr = RHDPTRI(Crtc);
    CARD16 RegOff;

    RHDDebug(Crtc->scrnIndex, "FUNCTION: %s: %s (%i[%i]x%i@%ibpp)  +0x%x )\n",
	     __func__, Crtc->Name, Width, Pitch, Height, bpp, Offset);

    if (Crtc->Id == RHD_CRTC_1)
      RegOff = D1_REG_OFFSET;
    else
      RegOff = D2_REG_OFFSET;

    RHDRegMask(Crtc, RegOff + D1GRPH_ENABLE, 1, 0x00000001);

    /* disable R/B swap, disable tiling, disable 16bit alpha, etc. */
    RHDRegWrite(Crtc, RegOff + D1GRPH_CONTROL, 0);

    switch (bpp) {
      case 8:
        RHDRegMask(Crtc, RegOff + D1GRPH_CONTROL, 0, 0x00000703);
        break;
      case 15:
        RHDRegMask(Crtc, RegOff + D1GRPH_CONTROL, 0x000001, 0x00000703);
        break;
      case 16:
        RHDRegMask(Crtc, RegOff + D1GRPH_CONTROL, 0x000101, 0x00000703);
        break;
      case 24:
      case 32:
        default:
        RHDRegMask(Crtc, RegOff + D1GRPH_CONTROL, 0x000002, 0x00000703);
        break;
    /* TODO: 64bpp ;p */
    }

    /* Make sure that we are not swapping colours around */
    if (rhdPtr->ChipSet > RHD_R600)
      RHDRegWrite(Crtc, RegOff + D1GRPH_SWAP_CNTL, 0);
    /* R5xx - RS690 case is GRPH_CONTROL bit 16 */

    RHDRegWrite(Crtc, RegOff + D1GRPH_PRIMARY_SURFACE_ADDRESS,
                rhdPtr->FbIntAddress + Offset);
    RHDRegWrite(Crtc, RegOff + D1GRPH_PITCH, Pitch);
    RHDRegWrite(Crtc, RegOff + D1GRPH_SURFACE_OFFSET_X, 0);
    RHDRegWrite(Crtc, RegOff + D1GRPH_SURFACE_OFFSET_Y, 0);
    RHDRegWrite(Crtc, RegOff + D1GRPH_X_START, 0);
    RHDRegWrite(Crtc, RegOff + D1GRPH_Y_START, 0);
    RHDRegWrite(Crtc, RegOff + D1GRPH_X_END, Width);
    RHDRegWrite(Crtc, RegOff + D1GRPH_Y_END, Height);

    /* D1Mode registers */
    RHDRegWrite(Crtc, RegOff + D1MODE_DESKTOP_HEIGHT, Height);

    Crtc->Pitch = Pitch;
    Crtc->Width = Width;
    Crtc->Height = Height;
    Crtc->bpp = bpp;
    Crtc->Offset = Offset;
}

/*
 *
 */
static void
DxFBSave(struct rhdCrtc *Crtc)
{
    struct rhdCrtcFBPrivate *FBPriv;
    CARD32 RegOff;

    if (!Crtc->FBPriv)
	FBPriv = xnfcalloc(1, sizeof(struct rhdCrtcFBPrivate));
    else
	FBPriv = Crtc->FBPriv;

    if (Crtc->Id == RHD_CRTC_1)
	RegOff = D1_REG_OFFSET;
    else
	RegOff = D2_REG_OFFSET;

    FBPriv->StoreGrphEnable = RHDRegRead(Crtc, RegOff + D1GRPH_ENABLE);
    FBPriv->StoreGrphControl = RHDRegRead(Crtc, RegOff + D1GRPH_CONTROL);
    FBPriv->StoreGrphXStart = RHDRegRead(Crtc, RegOff + D1GRPH_X_START);
    FBPriv->StoreGrphYStart = RHDRegRead(Crtc, RegOff + D1GRPH_Y_START);
    FBPriv->StoreGrphXEnd = RHDRegRead(Crtc, RegOff + D1GRPH_X_END);
    FBPriv->StoreGrphYEnd = RHDRegRead(Crtc, RegOff + D1GRPH_Y_END);
    if (RHDPTRI(Crtc)->ChipSet >= RHD_R600)
	FBPriv->StoreGrphSwap = RHDRegRead(Crtc, RegOff + D1GRPH_SWAP_CNTL);
    FBPriv->StoreGrphPrimarySurfaceAddress =
	RHDRegRead(Crtc, RegOff + D1GRPH_PRIMARY_SURFACE_ADDRESS);
    FBPriv->StoreGrphSurfaceOffsetX =
	RHDRegRead(Crtc, RegOff + D1GRPH_SURFACE_OFFSET_X);
    FBPriv->StoreGrphSurfaceOffsetY =
	RHDRegRead(Crtc, RegOff + D1GRPH_SURFACE_OFFSET_Y);
    FBPriv->StoreGrphPitch = RHDRegRead(Crtc, RegOff + D1GRPH_PITCH);
    FBPriv->StoreModeDesktopHeight = RHDRegRead(Crtc, RegOff + D1MODE_DESKTOP_HEIGHT);

    Crtc->FBPriv = FBPriv;
}

/*
 *
 */
static void
DxFBRestore(struct rhdCrtc *Crtc)
{
    struct rhdCrtcFBPrivate *FBPriv = Crtc->FBPriv;
    CARD32 RegOff;

    if (!FBPriv) {
	xf86DrvMsg(Crtc->scrnIndex, X_ERROR, "%s: no registers stored!\n",
		   __func__);
	return;
    }

    if (Crtc->Id == RHD_CRTC_1)
	RegOff = D1_REG_OFFSET;
    else
	RegOff = D2_REG_OFFSET;

    /* FBSet */
    RHDRegWrite(Crtc, RegOff + D1GRPH_CONTROL, FBPriv->StoreGrphControl);
    RHDRegWrite(Crtc, RegOff + D1GRPH_X_START, FBPriv->StoreGrphXStart);
    RHDRegWrite(Crtc, RegOff + D1GRPH_Y_START, FBPriv->StoreGrphYStart);
    RHDRegWrite(Crtc, RegOff + D1GRPH_X_END, FBPriv->StoreGrphXEnd);
    RHDRegWrite(Crtc, RegOff + D1GRPH_Y_END, FBPriv->StoreGrphYEnd);
    if (RHDPTRI(Crtc)->ChipSet >= RHD_R600)
	RHDRegWrite(Crtc, RegOff + D1GRPH_SWAP_CNTL, FBPriv->StoreGrphSwap);

    /* disable read requests */
    RHDRegMask(Crtc, RegOff + D1CRTC_CONTROL, 0x01000000, 0x01000000);
    RHDRegMask(Crtc, RegOff + D1GRPH_ENABLE, 0, 0x00000001);
    usleep (10);

    RHDRegWrite(Crtc, RegOff + D1GRPH_PRIMARY_SURFACE_ADDRESS,
		FBPriv->StoreGrphPrimarySurfaceAddress);
    usleep(10);

    RHDRegWrite(Crtc, RegOff + D1GRPH_ENABLE, FBPriv->StoreGrphEnable);

    RHDRegWrite(Crtc, RegOff + D1GRPH_SURFACE_OFFSET_X,
		FBPriv->StoreGrphSurfaceOffsetX);
    RHDRegWrite(Crtc, RegOff + D1GRPH_SURFACE_OFFSET_Y,
		FBPriv->StoreGrphSurfaceOffsetY);

    RHDRegWrite(Crtc, RegOff + D1GRPH_PITCH, FBPriv->StoreGrphPitch);
    RHDRegWrite(Crtc, RegOff + D1MODE_DESKTOP_HEIGHT, FBPriv->StoreModeDesktopHeight);
}

/*
 *
 */
static void
DxFBDestroy(struct rhdCrtc *Crtc)
{
    if (Crtc->FBPriv)
	xfree(Crtc->FBPriv);
    Crtc->FBPriv = NULL;
}

/*
 *
 */
static ModeStatus
DxModeValid(struct rhdCrtc *Crtc, DisplayModePtr Mode)
{
  CARD32 tmp;

    RHDDebug(Crtc->scrnIndex, "%s: %s\n", __func__, Crtc->Name);

    /* Work around HW bug: need at least 2 lines of front porch
       for interlaced mode */
    if ((Mode->Flags & V_INTERLACE)
	&& (Mode->CrtcVSyncStart < (Mode->CrtcVDisplay + 2))) {
	Mode->CrtcVSyncStart = Mode->CrtcVDisplay + 2;
	Mode->CrtcVAdjusted = TRUE;
    }

    /* D1CRTC_H_TOTAL - 1 : 13bits */
  if (Mode->CrtcHTotal > 0x2000)
    return MODE_BAD_HVALUE;

  tmp = Mode->CrtcHTotal + Mode->CrtcHBlankStart - Mode->CrtcHSyncStart;
    /* D1CRTC_H_BLANK_START: 13bits */
  if (tmp >= 0x2000)
    return MODE_BAD_HVALUE;

  tmp = Mode->CrtcHBlankEnd - Mode->CrtcHSyncStart;
    /* D1CRTC_H_BLANK_END: 13bits */
  if (tmp >= 0x2000)
    return MODE_BAD_HVALUE;

  tmp = Mode->CrtcHSyncEnd - Mode->CrtcHSyncStart;
    /* D1CRTC_H_SYNC_A_END: 13bits */
  if (tmp >= 0x2000)
    return MODE_HSYNC_WIDE;

    /* D1CRTC_V_TOTAL - 1 : 13bits */
  if (Mode->CrtcVTotal > 0x2000)
    return MODE_BAD_VVALUE;

  tmp = Mode->CrtcVTotal + Mode->CrtcVBlankStart - Mode->CrtcVSyncStart;
    /* D1CRTC_V_BLANK_START: 13bits */
  if (tmp >= 0x2000)
    return MODE_BAD_VVALUE;

  tmp = Mode->CrtcVBlankEnd - Mode->CrtcVSyncStart;
    /* D1CRTC_V_BLANK_END: 13bits */
  if (tmp >= 0x2000)
    return MODE_BAD_VVALUE;

  tmp = Mode->CrtcVSyncEnd - Mode->CrtcVSyncStart;
    /* D1CRTC_V_SYNC_A_END: 13bits */
  if (tmp >= 0x2000)
    return MODE_VSYNC_WIDE;

  return MODE_OK;
}

/*
 *
 */
static void
DxModeSet(struct rhdCrtc *Crtc, DisplayModePtr Mode)
{
    RHDPtr rhdPtr = RHDPTRI(Crtc);
  CARD16 BlankStart, BlankEnd;
  CARD16 RegOff;

    RHDDebug(Crtc->scrnIndex, "FUNCTION: %s: %s\n", __func__, Crtc->Name);

    if (rhdPtr->verbosity > 6) {
	xf86DrvMsg(Crtc->scrnIndex, X_INFO, "%s: Setting ",__func__);
	RHDPrintModeline(Mode);
    }

  if (Crtc->Id == RHD_CRTC_1)
    RegOff = D1_REG_OFFSET;
  else
    RegOff = D2_REG_OFFSET;

    /* enable read requests */
  RHDRegMask(Crtc, RegOff + D1CRTC_CONTROL, 0, 0x01000000);

    /* Horizontal */
  RHDRegWrite(Crtc, RegOff + D1CRTC_H_TOTAL, Mode->CrtcHTotal - 1);

  BlankStart = Mode->CrtcHTotal + Mode->CrtcHBlankStart - Mode->CrtcHSyncStart;
  BlankEnd = Mode->CrtcHBlankEnd - Mode->CrtcHSyncStart;
  RHDRegWrite(Crtc, RegOff + D1CRTC_H_BLANK_START_END,
  BlankStart | (BlankEnd << 16));

  RHDRegWrite(Crtc, RegOff + D1CRTC_H_SYNC_A,
             (Mode->CrtcHSyncEnd - Mode->CrtcHSyncStart) << 16);
  RHDRegWrite(Crtc, RegOff + D1CRTC_H_SYNC_A_CNTL, Mode->Flags & V_NHSYNC);

    /* Vertical */
  RHDRegWrite(Crtc, RegOff + D1CRTC_V_TOTAL, Mode->CrtcVTotal - 1);

  BlankStart = Mode->CrtcVTotal + Mode->CrtcVBlankStart - Mode->CrtcVSyncStart;
  BlankEnd = Mode->CrtcVBlankEnd - Mode->CrtcVSyncStart;
  RHDRegWrite(Crtc, RegOff + D1CRTC_V_BLANK_START_END,
              BlankStart | (BlankEnd << 16));

    /* set interlaced */
    if (Mode->Flags & V_INTERLACE) {
	RHDRegWrite(Crtc, RegOff + D1CRTC_INTERLACE_CONTROL, 0x1);
	RHDRegWrite(Crtc, RegOff + D1MODE_DATA_FORMAT, 0x1);
    } else {
	RHDRegWrite(Crtc, RegOff + D1CRTC_INTERLACE_CONTROL, 0x0);
	RHDRegWrite(Crtc, RegOff + D1MODE_DATA_FORMAT, 0x0);
    }

  RHDRegWrite(Crtc, RegOff + D1CRTC_V_SYNC_A,
             (Mode->CrtcVSyncEnd - Mode->CrtcVSyncStart) << 16);
  RHDRegWrite(Crtc, RegOff + D1CRTC_V_SYNC_A_CNTL, Mode->Flags & V_NVSYNC);

    /* set D1CRTC_HORZ_COUNT_BY2_EN to 0; should only be set to 1 on 30bpp DVI modes */
    RHDRegMask(Crtc, RegOff + D1CRTC_COUNT_CONTROL, 0x0, 0x1);

  Crtc->CurrentMode = Mode;
}

/*
 *
 */
static void
DxModeSave(struct rhdCrtc *Crtc)
{
    struct rhdCrtcModePrivate *ModePriv;
    CARD32 RegOff;

    if (!Crtc->ModePriv)
	ModePriv = xnfcalloc(1, sizeof(struct rhdCrtcModePrivate));
    else
	ModePriv = Crtc->ModePriv;

    if (Crtc->Id == RHD_CRTC_1)
	RegOff = D1_REG_OFFSET;
    else
	RegOff = D2_REG_OFFSET;

    ModePriv->StoreCrtcControl = RHDRegRead(Crtc, RegOff + D1CRTC_CONTROL);

    ModePriv->StoreCrtcHTotal = RHDRegRead(Crtc, RegOff + D1CRTC_H_TOTAL);
    ModePriv->StoreCrtcHBlankStartEnd =
	RHDRegRead(Crtc, RegOff + D1CRTC_H_BLANK_START_END);
    ModePriv->StoreCrtcHSyncA = RHDRegRead(Crtc, RegOff + D1CRTC_H_SYNC_A);
    ModePriv->StoreCrtcHSyncACntl = RHDRegRead(Crtc, RegOff + D1CRTC_H_SYNC_A_CNTL);
    ModePriv->StoreCrtcHSyncB = RHDRegRead(Crtc, RegOff + D1CRTC_H_SYNC_B);
    ModePriv->StoreCrtcHSyncBCntl = RHDRegRead(Crtc, RegOff + D1CRTC_H_SYNC_B_CNTL);

    ModePriv->StoreModeDataFormat = RHDRegRead(Crtc, RegOff + D1MODE_DATA_FORMAT);
    ModePriv->StoreCrtcInterlaceControl = RHDRegRead(Crtc, RegOff + D1CRTC_INTERLACE_CONTROL);

    ModePriv->StoreCrtcVTotal = RHDRegRead(Crtc, RegOff + D1CRTC_V_TOTAL);
    ModePriv->StoreCrtcVBlankStartEnd =
	RHDRegRead(Crtc, RegOff + D1CRTC_V_BLANK_START_END);
    ModePriv->StoreCrtcVSyncA = RHDRegRead(Crtc, RegOff + D1CRTC_V_SYNC_A);
    ModePriv->StoreCrtcVSyncACntl = RHDRegRead(Crtc, RegOff + D1CRTC_V_SYNC_A_CNTL);
    ModePriv->StoreCrtcVSyncB = RHDRegRead(Crtc, RegOff + D1CRTC_V_SYNC_B);
    ModePriv->StoreCrtcVSyncBCntl = RHDRegRead(Crtc, RegOff + D1CRTC_V_SYNC_B_CNTL);

    ModePriv->StoreCrtcBlackColor = RHDRegRead(Crtc, RegOff + D1CRTC_BLACK_COLOR);
    ModePriv->StoreCrtcBlankControl = RHDRegRead(Crtc, RegOff + D1CRTC_BLANK_CONTROL);

    ModePriv->StoreCrtcCountControl = RHDRegRead(Crtc, RegOff + D1CRTC_COUNT_CONTROL);
    RHDDebug(Crtc->scrnIndex, "Saved CrtcCountControl[%i] = 0x%8.8x\n",
	     Crtc->Id,ModePriv->StoreCrtcCountControl);

    Crtc->ModePriv = ModePriv;
}

/*
 *
 */
static void
DxModeRestore(struct rhdCrtc *Crtc)
{
    struct rhdCrtcModePrivate *ModePriv = Crtc->ModePriv;
    CARD32 RegOff;

    if (!ModePriv) {
	xf86DrvMsg(Crtc->scrnIndex, X_ERROR, "%s: no registers stored!\n",
		   __func__);
	return;
    }

    if (Crtc->Id == RHD_CRTC_1)
	RegOff = D1_REG_OFFSET;
    else
	RegOff = D2_REG_OFFSET;

    /* ModeSet */
    RHDRegWrite(Crtc, RegOff + D1CRTC_CONTROL, ModePriv->StoreCrtcControl);

    RHDRegWrite(Crtc, RegOff + D1CRTC_H_TOTAL, ModePriv->StoreCrtcHTotal);
    RHDRegWrite(Crtc, RegOff + D1CRTC_H_BLANK_START_END,
		ModePriv->StoreCrtcHBlankStartEnd);
    RHDRegWrite(Crtc, RegOff + D1CRTC_H_SYNC_A, ModePriv->StoreCrtcHSyncA);
    RHDRegWrite(Crtc, RegOff + D1CRTC_H_SYNC_A_CNTL, ModePriv->StoreCrtcHSyncACntl);
    RHDRegWrite(Crtc, RegOff + D1CRTC_H_SYNC_B, ModePriv->StoreCrtcHSyncB);
    RHDRegWrite(Crtc, RegOff + D1CRTC_H_SYNC_B_CNTL, ModePriv->StoreCrtcHSyncBCntl);

    RHDRegWrite(Crtc, RegOff + D1MODE_DATA_FORMAT, ModePriv->StoreModeDataFormat);
    RHDRegWrite(Crtc, RegOff + D1CRTC_INTERLACE_CONTROL, ModePriv->StoreCrtcInterlaceControl);

    RHDRegWrite(Crtc, RegOff + D1CRTC_V_TOTAL, ModePriv->StoreCrtcVTotal);
    RHDRegWrite(Crtc, RegOff + D1CRTC_V_BLANK_START_END,
		ModePriv->StoreCrtcVBlankStartEnd);
    RHDRegWrite(Crtc, RegOff + D1CRTC_V_SYNC_A, ModePriv->StoreCrtcVSyncA);
    RHDRegWrite(Crtc, RegOff + D1CRTC_V_SYNC_A_CNTL, ModePriv->StoreCrtcVSyncACntl);
    RHDRegWrite(Crtc, RegOff + D1CRTC_V_SYNC_B, ModePriv->StoreCrtcVSyncB);
    RHDRegWrite(Crtc, RegOff + D1CRTC_V_SYNC_B_CNTL, ModePriv->StoreCrtcVSyncBCntl);

    RHDRegWrite(Crtc, RegOff + D1CRTC_COUNT_CONTROL, ModePriv->StoreCrtcCountControl);

    /* Blank */
    RHDRegWrite(Crtc, RegOff + D1CRTC_BLACK_COLOR, ModePriv->StoreCrtcBlackColor);
    RHDRegWrite(Crtc, RegOff + D1CRTC_BLANK_CONTROL, ModePriv->StoreCrtcBlankControl);

    /* When VGA is enabled, it imposes its timing on us, so our CRTC SYNC
     * timing can be set to 0. This doesn't always restore properly...
     * Workaround is to set a valid sync length for a bit so VGA can
     * latch in. */
    if (!ModePriv->StoreCrtcVSyncA && (ModePriv->StoreCrtcControl & 0x00000001)) {
	RHDRegWrite(Crtc, RegOff + D1CRTC_V_SYNC_A, 0x00040000);
	usleep(300000); /* seems a reliable timeout here */
	RHDRegWrite(Crtc, RegOff + D1CRTC_V_SYNC_A, ModePriv->StoreCrtcVSyncA);
    }
}

/*
 *
 */
static void
DxModeDestroy(struct rhdCrtc *Crtc)
{
    RHDFUNC(Crtc);

    if (Crtc->ModePriv)
	xfree(Crtc->ModePriv);
    Crtc->ModePriv = NULL;
}

/*
 *
 */
struct rhdScalerOverscan
rhdCalculateOverscan(DisplayModePtr Mode, DisplayModePtr ScaledToMode, enum rhdCrtcScaleType Type)
{
    struct rhdScalerOverscan Overscan;
    int tmp;

    Overscan.OverscanTop = Overscan.OverscanBottom = Overscan.OverscanLeft = Overscan.OverscanRight = 0;
    Overscan.Type = Type;

    if (ScaledToMode) {
	Overscan.OverscanTop = ScaledToMode->CrtcVDisplay - Mode->CrtcVDisplay;
	Overscan.OverscanLeft = ScaledToMode->CrtcHDisplay - Mode->CrtcHDisplay;

	if (!Overscan.OverscanTop && !Overscan.OverscanLeft)
	    Overscan.Type = RHD_CRTC_SCALE_TYPE_NONE;

	/* handle down scaling */
	if (Overscan.OverscanTop < 0) {
	    Overscan.Type = RHD_CRTC_SCALE_TYPE_SCALE;
	    Overscan.OverscanTop = 0;
	}
	if (Overscan.OverscanLeft < 0) {
	    Overscan.Type = RHD_CRTC_SCALE_TYPE_SCALE;
	    Overscan.OverscanLeft = 0;
	}
    }

    switch (Type) {
	case RHD_CRTC_SCALE_TYPE_NONE:
	    break;

	case RHD_CRTC_SCALE_TYPE_CENTER:
	    tmp = Overscan.OverscanTop;
	    Overscan.OverscanTop >>= 1;
	    Overscan.OverscanBottom = tmp - Overscan.OverscanTop;
	    tmp = Overscan.OverscanLeft;
	    Overscan.OverscanLeft >>= 1;
	    Overscan.OverscanRight = tmp - Overscan.OverscanLeft;
	    break;

	case RHD_CRTC_SCALE_TYPE_SCALE:
	    Overscan.OverscanLeft = Overscan.OverscanRight = Overscan.OverscanTop = Overscan.OverscanBottom = 0;
	    break;
	case RHD_CRTC_SCALE_TYPE_SCALE_KEEP_ASPECT_RATIO:
	{
	    int p1, p2, tmp;
	    Overscan.OverscanLeft = Overscan.OverscanRight = Overscan.OverscanTop = Overscan.OverscanBottom = 0;
	    p1 = Mode->CrtcVDisplay * ScaledToMode->CrtcHDisplay;
	    p2 = ScaledToMode->CrtcVDisplay * Mode->CrtcHDisplay;
	    if (p1 == p2) {
		Overscan.Type = RHD_CRTC_SCALE_TYPE_SCALE;
	    } else if (p1 > p2) {
		tmp = (p2 / Mode->CrtcVDisplay);
		tmp = ScaledToMode->CrtcHDisplay - tmp;
		Overscan.OverscanLeft = tmp >> 1;
		Overscan.OverscanRight = tmp - Overscan.OverscanLeft;
		ErrorF("HScale %i %i\n", Overscan.OverscanLeft, Overscan.OverscanRight);
	    } else {
		tmp = (p1 / Mode->CrtcHDisplay);
		tmp = ScaledToMode->CrtcVDisplay - tmp;
		Overscan.OverscanTop = tmp >> 1;
		Overscan.OverscanBottom = tmp - Overscan.OverscanTop;
		ErrorF("VScale %i %i\n", Overscan.OverscanTop, Overscan.OverscanBottom);
	    }
	    break;
	}
    }

    return Overscan;
}

/*
 *
 */
static ModeStatus
DxScaleValid(struct rhdCrtc *Crtc, enum rhdCrtcScaleType Type,
	     DisplayModePtr Mode, DisplayModePtr ScaledToMode)
{
    struct rhdScalerOverscan Overscan;

    /* D1_MODE_VIEWPORT_WIDTH: 14bits */
    if (Mode->CrtcHDisplay >= 0x4000)
	return MODE_BAD_HVALUE;

    /* D1_MODE_VIEWPORT_HEIGHT: 14bits */
    if (Mode->CrtcVDisplay >= 0x4000)
	return MODE_BAD_VVALUE;

    Overscan = rhdCalculateOverscan(Mode, ScaledToMode, Type);

    if (Overscan.OverscanLeft >= 4096 || Overscan.OverscanRight >= 4096)
	return MODE_HBLANK_WIDE;

    if (Overscan.OverscanTop >= 4096 || Overscan.OverscanBottom >= 4096)
	return MODE_VBLANK_WIDE;

    if ((Type == RHD_CRTC_SCALE_TYPE_SCALE
	 || Type == RHD_CRTC_SCALE_TYPE_SCALE_KEEP_ASPECT_RATIO)
	&& (Mode->Flags & V_INTERLACE))
	return MODE_NO_INTERLACE;

    /* should we also fail of Type != Overscan.Type? */

    return MODE_OK;
}

/*
 *
 */
static void
DxScaleSet(struct rhdCrtc *Crtc, enum rhdCrtcScaleType Type,
	   DisplayModePtr Mode, DisplayModePtr ScaledToMode)
{
    RHDPtr rhdPtr = RHDPTRI(Crtc);
    CARD16 RegOff;
    struct rhdScalerOverscan Overscan;

    RHDDebug(Crtc->scrnIndex, "FUNCTION: %s: %s viewport: %ix%i\n", __func__, Crtc->Name,
	     Mode->CrtcHDisplay, Mode->CrtcVDisplay);

    if (Crtc->Id == RHD_CRTC_1)
	RegOff = D1_REG_OFFSET;
    else
	RegOff = D2_REG_OFFSET;

    Overscan = rhdCalculateOverscan(Mode, ScaledToMode, Type);
    Type = Overscan.Type;

    RHDDebug(Crtc->scrnIndex, "FUNCTION: %s: %s viewport: %ix%i - OverScan: T: %i B: %i R: %i L: %i\n",
	     __func__, Crtc->Name, Mode->CrtcHDisplay, Mode->CrtcVDisplay,
	     Overscan.OverscanTop, Overscan.OverscanBottom,
	     Overscan.OverscanLeft, Overscan.OverscanRight);

    /* D1Mode registers */
    RHDRegWrite(Crtc, RegOff + D1MODE_VIEWPORT_SIZE,
		Mode->CrtcVDisplay | (Mode->CrtcHDisplay << 16));
    RHDRegWrite(Crtc, RegOff + D1MODE_VIEWPORT_START, 0);

    RHDRegWrite(Crtc, RegOff + D1MODE_EXT_OVERSCAN_LEFT_RIGHT,
		(Overscan.OverscanLeft << 16) | Overscan.OverscanRight);
    RHDRegWrite(Crtc, RegOff + D1MODE_EXT_OVERSCAN_TOP_BOTTOM,
		(Overscan.OverscanTop << 16) | Overscan.OverscanBottom);

    switch (Type) {
	case RHD_CRTC_SCALE_TYPE_NONE:  /* No scaling whatsoever */
	    ErrorF("None\n");
	    RHDRegWrite(Crtc, RegOff + D1SCL_ENABLE, 0);
	    RHDRegWrite(Crtc, RegOff + D1SCL_TAP_CONTROL, 0);
	    RHDRegWrite(Crtc, RegOff + D1MODE_CENTER, 0);
	    break;
	case RHD_CRTC_SCALE_TYPE_CENTER: /* center of the actual mode */
	    ErrorF("Center\n");
	    RHDRegWrite(Crtc, RegOff + D1SCL_ENABLE, 0);
	    RHDRegWrite(Crtc, RegOff + D1SCL_TAP_CONTROL, 0);
	    RHDRegWrite(Crtc, RegOff + D1MODE_CENTER, 1);
	    break;
	case RHD_CRTC_SCALE_TYPE_SCALE_KEEP_ASPECT_RATIO: /* scaled to fullscreen */
	case RHD_CRTC_SCALE_TYPE_SCALE: /* scaled to fullscreen */
	    ErrorF("Full\n");
	    if (Type == RHD_CRTC_SCALE_TYPE_SCALE_KEEP_ASPECT_RATIO)
		RHDRegWrite(Crtc, RegOff + D1MODE_CENTER, 1);
	    else
		RHDRegWrite(Crtc, RegOff + D1MODE_CENTER, 0);

	    RHDRegWrite(Crtc, RegOff + D1SCL_UPDATE, 0);
	    RHDRegWrite(Crtc, RegOff + D1SCL_DITHER, 0);

	    RHDRegWrite(Crtc, RegOff + D1SCL_ENABLE, 1);
	    RHDRegWrite(Crtc, RegOff + D1SCL_HVSCALE, 0x00010001); /* both h/v */

	    RHDRegWrite(Crtc, RegOff + D1SCL_TAP_CONTROL, 0x00000101);

	    RHDRegWrite(Crtc, RegOff + D1SCL_HFILTER, 0x00030100);
	    RHDRegWrite(Crtc, RegOff + D1SCL_VFILTER, 0x00030100);

	    RHDRegWrite(Crtc, RegOff + D1SCL_DITHER, 0x00001010);
	    break;
    }
    RHDMCTuneAccessForDisplay(rhdPtr, Crtc->Id, Mode,
			      ScaledToMode ? ScaledToMode : Mode);
}

/*
 *
 */
static void
DxScaleSave(struct rhdCrtc *Crtc)
{
    struct rhdCrtcScalePrivate *ScalePriv;
    CARD32 RegOff;

    if (!Crtc->ScalePriv)
	ScalePriv =  xnfcalloc(1, sizeof(struct rhdCrtcScalePrivate));
    else
	ScalePriv = Crtc->ScalePriv;

    if (Crtc->Id == RHD_CRTC_1)
	RegOff = D1_REG_OFFSET;
    else
	RegOff = D2_REG_OFFSET;

    ScalePriv->StoreModeViewPortSize = RHDRegRead(Crtc, RegOff + D1MODE_VIEWPORT_SIZE);
    ScalePriv->StoreModeViewPortStart = RHDRegRead(Crtc, RegOff + D1MODE_VIEWPORT_START);
    ScalePriv->StoreModeOverScanH =
	RHDRegRead(Crtc, RegOff + D1MODE_EXT_OVERSCAN_LEFT_RIGHT);
    ScalePriv->StoreModeOverScanV =
	RHDRegRead(Crtc, RegOff + D1MODE_EXT_OVERSCAN_TOP_BOTTOM);

    ScalePriv->StoreScaleEnable = RHDRegRead(Crtc, RegOff + D1SCL_ENABLE);
    ScalePriv->StoreScaleTapControl = RHDRegRead(Crtc, RegOff + D1SCL_TAP_CONTROL);
    ScalePriv->StoreModeCenter = RHDRegRead(Crtc, RegOff + D1MODE_CENTER);
    ScalePriv->StoreScaleHV = RHDRegRead(Crtc, RegOff + D1SCL_HVSCALE);
    ScalePriv->StoreScaleHFilter = RHDRegRead(Crtc, RegOff + D1SCL_HFILTER);
    ScalePriv->StoreScaleVFilter = RHDRegRead(Crtc, RegOff + D1SCL_VFILTER);
    ScalePriv->StoreScaleDither = RHDRegRead(Crtc, RegOff + D1SCL_DITHER);

    Crtc->ScalePriv = ScalePriv;
}

/*
 *
 */
static void
DxScaleRestore(struct rhdCrtc *Crtc)
{
    struct rhdCrtcScalePrivate *ScalePriv = Crtc->ScalePriv;
    CARD32 RegOff;

    if (!ScalePriv) {
	xf86DrvMsg(Crtc->scrnIndex, X_ERROR, "%s: no registers stored!\n",
		   __func__);
	return;
    }

    if (Crtc->Id == RHD_CRTC_1)
	RegOff = D1_REG_OFFSET;
    else
	RegOff = D2_REG_OFFSET;

    /* ScaleSet */
    RHDRegWrite(Crtc, RegOff + D1MODE_VIEWPORT_SIZE, ScalePriv->StoreModeViewPortSize);

    /* ScaleSet/ViewPortStart */
    RHDRegWrite(Crtc, RegOff + D1MODE_VIEWPORT_START, ScalePriv->StoreModeViewPortStart);

    /* ScaleSet */
    RHDRegWrite(Crtc, RegOff + D1MODE_EXT_OVERSCAN_LEFT_RIGHT,
		ScalePriv->StoreModeOverScanH);
    RHDRegWrite(Crtc, RegOff + D1MODE_EXT_OVERSCAN_TOP_BOTTOM,
		ScalePriv->StoreModeOverScanV);

    RHDRegWrite(Crtc, RegOff + D1SCL_ENABLE, ScalePriv->StoreScaleEnable);
    RHDRegWrite(Crtc, RegOff + D1SCL_TAP_CONTROL, ScalePriv->StoreScaleTapControl);
    RHDRegWrite(Crtc, RegOff + D1MODE_CENTER, ScalePriv->StoreModeCenter);
    RHDRegWrite(Crtc, RegOff + D1SCL_HVSCALE, ScalePriv->StoreScaleHV);
    RHDRegWrite(Crtc, RegOff + D1SCL_HFILTER, ScalePriv->StoreScaleHFilter);
    RHDRegWrite(Crtc, RegOff + D1SCL_VFILTER, ScalePriv->StoreScaleVFilter);
    RHDRegWrite(Crtc, RegOff + D1SCL_DITHER, ScalePriv->StoreScaleDither);
}

/*
 *
 */
static void
DxScaleDestroy(struct rhdCrtc *Crtc)
{
    RHDFUNC(Crtc);

    if (Crtc->ScalePriv)
	xfree(Crtc->ScalePriv);
    Crtc->ScalePriv = NULL;
}

/*
 *
 */
static void
D1LUTSelect(struct rhdCrtc *Crtc, struct rhdLUT *LUT)
{
    RHDFUNC(Crtc);

    RHDRegWrite(Crtc, D1GRPH_LUT_SEL, LUT->Id & 1);
    Crtc->LUT = LUT;
}

/*
 *
 */
static void
D2LUTSelect(struct rhdCrtc *Crtc, struct rhdLUT *LUT)
{
    RHDFUNC(Crtc);

    RHDRegWrite(Crtc, D2GRPH_LUT_SEL, LUT->Id & 1);
    Crtc->LUT = LUT;
}

/*
 *
 */
static void
DxLUTSave(struct rhdCrtc *Crtc)
{
    struct rhdCrtcLUTPrivate *LUTPriv;
    CARD32 RegOff;

    if (!Crtc->LUTPriv)
	LUTPriv =  xnfcalloc(1, sizeof(struct rhdCrtcLUTPrivate));
    else
	LUTPriv = Crtc->LUTPriv;

    if (Crtc->Id == RHD_CRTC_1)
	RegOff = D1_REG_OFFSET;
    else
	RegOff = D2_REG_OFFSET;

    LUTPriv->StoreGrphLutSel = RHDRegRead(Crtc, RegOff + D1GRPH_LUT_SEL);

    Crtc->LUTPriv = LUTPriv;
}

/*
 *
 */
static void
DxLUTRestore(struct rhdCrtc *Crtc)
{
    struct rhdCrtcLUTPrivate *LUTPriv = Crtc->LUTPriv;
    CARD32 RegOff;

    if (!LUTPriv) {
	xf86DrvMsg(Crtc->scrnIndex, X_ERROR, "%s: no registers stored!\n",
		   __func__);
	return;
    }

    if (Crtc->Id == RHD_CRTC_1)
	RegOff = D1_REG_OFFSET;
    else
	RegOff = D2_REG_OFFSET;

    /* LUTSelect */
    RHDRegWrite(Crtc, RegOff + D1GRPH_LUT_SEL, LUTPriv->StoreGrphLutSel);
}

/*
 *
 */
static void
DxLUTDestroy(struct rhdCrtc *Crtc)
{
    RHDFUNC(Crtc);

    if (Crtc->LUTPriv)
	xfree(Crtc->LUTPriv);
    Crtc->LUTPriv = NULL;
}

/*
 *
 */
static void
D1ViewPortStart(struct rhdCrtc *Crtc, CARD16 X, CARD16 Y)
{
    RHDFUNC(Crtc);

    /* not as granular as docs make it seem to be.
     * if the lower two bits are set the line buffer might screw up, requiring
     * a power cycle. */
    X = (X + 0x02) & ~0x03;
    Y &= ~0x01;

    RHDRegMask(Crtc, D1SCL_UPDATE, 0x00010000, 0x0001000);
    RHDRegWrite(Crtc, D1MODE_VIEWPORT_START, (X << 16) | Y);
    RHDRegMask(Crtc, D1SCL_UPDATE, 0, 0x0001000);

    Crtc->X = X;
    Crtc->Y = Y;
}

/*
 *
 */
static void
D2ViewPortStart(struct rhdCrtc *Crtc, CARD16 X, CARD16 Y)
{
    RHDFUNC(Crtc);

    /* not as granular as docs make it seem to be. */
    X = (X + 0x02) & ~0x03;
    Y &= ~0x01;

    RHDRegMask(Crtc, D2SCL_UPDATE, 0x00010000, 0x0001000);
    RHDRegWrite(Crtc, D2MODE_VIEWPORT_START, (X << 16) | Y);
    RHDRegMask(Crtc, D2SCL_UPDATE, 0, 0x0001000);

    Crtc->X = X;
    Crtc->Y = Y;
}

#define CRTC_SYNC_WAIT 0x100000
/*
 *
 */
static Bool
D1CRTCDisable(struct rhdCrtc *Crtc)
{
    if (RHDRegRead(Crtc, D1CRTC_CONTROL) & 0x00000001) {
    CARD32 Control = RHDRegRead(Crtc, D1CRTC_CONTROL);
    int i;

	RHDRegMask(Crtc, D1CRTC_CONTROL, 0, 0x00000301);
	(void)RHDRegRead(Crtc, D1CRTC_CONTROL);

    for (i = 0; i < CRTC_SYNC_WAIT; i++)
	    if (!(RHDRegRead(Crtc, D1CRTC_CONTROL) & 0x00010000)) {
		RHDDebug(Crtc->scrnIndex, "%s: %d loops\n", __func__, i);
		RHDRegMask(Crtc, D1CRTC_CONTROL, Control, 0x00000300);
		return TRUE;
	    }
	xf86DrvMsg(Crtc->scrnIndex, X_ERROR,
		   "%s: Failed to Unsync %s\n", __func__, Crtc->Name);
	RHDRegMask(Crtc, D1CRTC_CONTROL, Control, 0x00000300);
	return FALSE;
    }
    return TRUE;
}

/*
 *
 */
static Bool
D2CRTCDisable(struct rhdCrtc *Crtc)
{
    if (RHDRegRead(Crtc, D2CRTC_CONTROL) & 0x00000001) {
    CARD32 Control = RHDRegRead(Crtc, D2CRTC_CONTROL);
    int i;

	RHDRegMask(Crtc, D2CRTC_CONTROL, 0, 0x00000301);
	(void)RHDRegRead(Crtc, D2CRTC_CONTROL);

    for (i = 0; i < CRTC_SYNC_WAIT; i++)
	    if (!(RHDRegRead(Crtc, D2CRTC_CONTROL) & 0x00010000)) {
		RHDDebug(Crtc->scrnIndex, "%s: %d loops\n", __func__, i);
		RHDRegMask(Crtc, D2CRTC_CONTROL, Control, 0x00000300);
		return TRUE;
	    }
	xf86DrvMsg(Crtc->scrnIndex, X_ERROR,
		   "%s: Failed to Unsync %s\n", __func__, Crtc->Name);
	RHDRegMask(Crtc, D2CRTC_CONTROL, Control, 0x00000300);
	return FALSE;
    }
    return TRUE;
}

/*
 *
 */
static Bool
D1Power(struct rhdCrtc *Crtc, int Power)
{
    Bool ret;
    RHDFUNC(Crtc);

    switch (Power) {
    case RHD_POWER_ON:
	RHDRegMask(Crtc, D1GRPH_ENABLE, 0x00000001, 0x00000001);
	usleep(2);
	RHDRegMask(Crtc, D1CRTC_CONTROL, 0, 0x01000000); /* enable read requests */
	RHDRegMask(Crtc, D1CRTC_CONTROL, 1, 1);
	return TRUE;
    case RHD_POWER_RESET:
	RHDRegMask(Crtc, D1CRTC_CONTROL, 0x01000000, 0x01000000); /* disable read requests */
	return D1CRTCDisable(Crtc);
    case RHD_POWER_SHUTDOWN:
    default:
	RHDRegMask(Crtc, D1CRTC_CONTROL, 0x01000000, 0x01000000); /* disable read requests */
	ret = D1CRTCDisable(Crtc);
	RHDRegMask(Crtc, D1GRPH_ENABLE, 0, 0x00000001);
	return ret;
    }
}

/*
 *
 */
static Bool
D2Power(struct rhdCrtc *Crtc, int Power)
{
    Bool ret;
    RHDFUNC(Crtc);

    switch (Power) {
    case RHD_POWER_ON:
	RHDRegMask(Crtc, D2GRPH_ENABLE, 0x00000001, 0x00000001);
	usleep(2);
	RHDRegMask(Crtc, D2CRTC_CONTROL, 0, 0x01000000); /* enable read requests */
	RHDRegMask(Crtc, D2CRTC_CONTROL, 1, 1);
	return TRUE;
    case RHD_POWER_RESET:
	RHDRegMask(Crtc, D2CRTC_CONTROL, 0x01000000, 0x01000000); /* disable read requests */
	return D2CRTCDisable(Crtc);
    case RHD_POWER_SHUTDOWN:
    default:
	RHDRegMask(Crtc, D2CRTC_CONTROL, 0x01000000, 0x01000000); /* disable read requests */
	ret = D2CRTCDisable(Crtc);
	RHDRegMask(Crtc, D2GRPH_ENABLE, 0, 0x00000001);
	return ret;
    }
}

/*
 * This is quite different from Power. Power disables and enables things,
 * this here makes the hw send out black, and can switch back and forth
 * immediately. Useful for covering up a framebuffer that is not filled
 * in yet.
 */
static void
D1Blank(struct rhdCrtc *Crtc, Bool Blank)
{
    RHDFUNC(Crtc);

    RHDRegWrite(Crtc, D1CRTC_BLACK_COLOR, 0);
    if (Blank)
	RHDRegMask(Crtc, D1CRTC_BLANK_CONTROL, 0x00000100, 0x00000100);
    else
	RHDRegMask(Crtc, D1CRTC_BLANK_CONTROL, 0, 0x00000100);
}

/*
 *
 */
static void
D2Blank(struct rhdCrtc *Crtc, Bool Blank)
{
    RHDFUNC(Crtc);

    RHDRegWrite(Crtc, D2CRTC_BLACK_COLOR, 0);
    if (Blank)
	RHDRegMask(Crtc, D2CRTC_BLANK_CONTROL, 0x00000100, 0x00000100);
    else
	RHDRegMask(Crtc, D2CRTC_BLANK_CONTROL, 0, 0x00000100);
}

/*
 *
 */
static void
DxFMTSet(struct rhdCrtc *Crtc, struct rhdFMTDither *FMTDither)
{
    CARD32 RegOff;
    CARD32 fmt_cntl = 0;

    RHDFUNC(Crtc);

    if (Crtc->Id == RHD_CRTC_1)
	RegOff = FMT1_REG_OFFSET;
    else
	RegOff = FMT2_REG_OFFSET;

    if (FMTDither) {

	/* set dither depth to 18/24 */
	fmt_cntl = FMTDither->LVDS24Bit
	    ? (RV62_FMT_SPATIAL_DITHER_DEPTH | RV62_FMT_TEMPORAL_DITHER_DEPTH)
	    : 0;
	RHDRegMask(Crtc, RegOff + RV620_FMT1_BIT_DEPTH_CONTROL, fmt_cntl,
	       RV62_FMT_SPATIAL_DITHER_DEPTH | RV62_FMT_TEMPORAL_DITHER_DEPTH);

	/* set temporal dither */
	if (FMTDither->LVDSTemporalDither) {
	    fmt_cntl = FMTDither->LVDSGreyLevel ? RV62_FMT_TEMPORAL_LEVEL : 0x0;
	    /* grey level */
	    RHDRegMask(Crtc, RegOff + RV620_FMT1_BIT_DEPTH_CONTROL,
		       fmt_cntl, RV62_FMT_TEMPORAL_LEVEL);
	    /* turn on temporal dither and reset */
	    RHDRegMask(Crtc, RegOff + RV620_FMT1_BIT_DEPTH_CONTROL,
		       RV62_FMT_TEMPORAL_DITHER_EN | RV62_FMT_TEMPORAL_DITHER_RESET,
		       RV62_FMT_TEMPORAL_DITHER_EN | RV62_FMT_TEMPORAL_DITHER_RESET);
	    usleep(20);
	    /* turn off reset */
	    RHDRegMask(Crtc, RegOff + RV620_FMT1_BIT_DEPTH_CONTROL, 0x0,
		       RV62_FMT_TEMPORAL_DITHER_RESET);
	}
	/* spatial dither */
	RHDRegMask(Crtc, RegOff + RV620_FMT1_BIT_DEPTH_CONTROL,
		   FMTDither->LVDSSpatialDither ? RV62_FMT_SPATIAL_DITHER_EN : 0,
		   RV62_FMT_SPATIAL_DITHER_EN);
    } else
	RHDRegWrite(Crtc, RegOff + RV620_FMT1_BIT_DEPTH_CONTROL, 0);

    /* 4:4:4 encoding */
    RHDRegMask(Crtc,  RegOff + RV620_FMT1_CONTROL, 0, RV62_FMT_PIXEL_ENCODING);
    /* disable color clamping */
    RHDRegWrite(Crtc, RegOff + RV620_FMT1_CLAMP_CNTL, 0);
}

/*
 *
 */
static void
DxFMTSave(struct rhdCrtc *Crtc)
{
    struct rhdCrtcFMTPrivate *FMTPrivate;
    CARD32 RegOff;

    RHDFUNC(Crtc);

    if (!Crtc->FMTPriv)
	FMTPrivate = xnfcalloc(sizeof (struct rhdCrtcFMTPrivate),1);
    else
	FMTPrivate = Crtc->FMTPriv;

    if (Crtc->Id == RHD_CRTC_1)
	RegOff = FMT1_REG_OFFSET;
    else
	RegOff = FMT2_REG_OFFSET;

    FMTPrivate->StoreControl         = RHDRegRead(Crtc, RegOff + RV620_FMT1_CONTROL);
    FMTPrivate->StoreBitDepthControl = RHDRegRead(Crtc, RegOff + RV620_FMT1_BIT_DEPTH_CONTROL);
    FMTPrivate->StoreClampCntl       = RHDRegRead(Crtc, RegOff + RV620_FMT1_CLAMP_CNTL);

    Crtc->FMTPriv = FMTPrivate;
}

/*
 *
 */
static void
DxFMTRestore(struct rhdCrtc *Crtc)
{
    struct rhdCrtcFMTPrivate *FMTPrivate = Crtc->FMTPriv;
    CARD32 RegOff;

    RHDFUNC(Crtc);

    if (!FMTPrivate)
	return;

    if (Crtc->Id == RHD_CRTC_1)
	RegOff = FMT1_REG_OFFSET;
    else
	RegOff = FMT2_REG_OFFSET;

    RHDRegWrite(Crtc, RegOff + RV620_FMT1_CONTROL, FMTPrivate->StoreControl);
    RHDRegWrite(Crtc, RegOff + RV620_FMT1_BIT_DEPTH_CONTROL, FMTPrivate->StoreBitDepthControl);
    RHDRegWrite(Crtc, RegOff + RV620_FMT1_CLAMP_CNTL, FMTPrivate->StoreClampCntl);
}

/*
 *
 */
static void
DxFMTDestroy(struct rhdCrtc *Crtc)
{
    RHDFUNC(Crtc);

    if (Crtc->FMTPriv)
	xfree(Crtc->FMTPriv);
    Crtc->FMTPriv = NULL;
}

/*
 *
 */
static enum rhdCrtcScaleType
rhdInitScaleType(RHDPtr rhdPtr)
{
    RHDFUNC(rhdPtr);
/*
    if (rhdPtr->scaleTypeOpt.set) {
	if (!strcasecmp(rhdPtr->scaleTypeOpt.val.string, "none"))
	    return RHD_CRTC_SCALE_TYPE_NONE;
	else if (!strcasecmp(rhdPtr->scaleTypeOpt.val.string, "center"))
	    return RHD_CRTC_SCALE_TYPE_CENTER;
	else if (!strcasecmp(rhdPtr->scaleTypeOpt.val.string, "scale"))
	    return RHD_CRTC_SCALE_TYPE_SCALE;
	else if (!strcasecmp(rhdPtr->scaleTypeOpt.val.string, "scale_keep_aspect_ratio"))
	    return RHD_CRTC_SCALE_TYPE_SCALE_KEEP_ASPECT_RATIO;
	else if (!strcasecmp(rhdPtr->scaleTypeOpt.val.string, "default"))
	    return RHD_CRTC_SCALE_TYPE_DEFAULT;
	else {
	    xf86DrvMsgVerb(rhdPtr->scrnIndex, X_ERROR, 0,
			   "Unknown scale type: %s\n", rhdPtr->scaleTypeOpt.val.string);
	    return RHD_CRTC_SCALE_TYPE_DEFAULT;
	}
    } else  */
  return RHD_CRTC_SCALE_TYPE_SCALE;
}

/*
 *
 */
Bool
RHDCrtcsInit(RHDPtr rhdPtr)
{
    struct rhdCrtc *Crtc;
    enum rhdCrtcScaleType ScaleType;
    Bool useAtom;

    RHDFUNC(rhdPtr);

    useAtom = RHDUseAtom(rhdPtr, NULL, atomUsageCrtc);

    ScaleType = rhdInitScaleType(rhdPtr);

    Crtc = xnfcalloc(sizeof(struct rhdCrtc), 1);
    Crtc->scrnIndex = rhdPtr->scrnIndex;
    Crtc->Name = "CRTC 1";
    Crtc->Id = RHD_CRTC_1;

    Crtc->ScaleType = ScaleType;

    if (rhdPtr->ChipSet >= RHD_RV620) {
	Crtc->FMTDestroy = DxFMTDestroy;
	Crtc->FMTSave = DxFMTSave;
	Crtc->FMTRestore = DxFMTRestore;
	Crtc->FMTModeSet = DxFMTSet;
    }
    Crtc->FMTPriv = NULL;

    Crtc->FBValid   = DxFBValid;
    Crtc->FBSet     = DxFBSet;
    Crtc->FBSave = DxFBSave;
    Crtc->FBRestore = DxFBRestore;
    Crtc->FBDestroy = DxFBDestroy;

    Crtc->ModeValid = DxModeValid;
    Crtc->ModeSet   = DxModeSet;
    Crtc->ModeSave = DxModeSave;
    Crtc->ModeRestore = DxModeRestore;
    Crtc->ModeDestroy = DxModeDestroy;
    Crtc->ModePriv = NULL;

    Crtc->ScaleValid = DxScaleValid;
    Crtc->ScaleSet = DxScaleSet;
    Crtc->ScaleSave = DxScaleSave;
    Crtc->ScaleRestore = DxScaleRestore;
    Crtc->ScaleDestroy = DxScaleDestroy;
    Crtc->ScalePriv = NULL;

    Crtc->LUTSelect = D1LUTSelect;
    Crtc->LUTSave = DxLUTSave;
    Crtc->LUTRestore = DxLUTRestore;
    Crtc->LUTDestroy = DxLUTDestroy;
    Crtc->LUTPriv = NULL;

    Crtc->FrameSet  = D1ViewPortStart;

    Crtc->Power     = D1Power;
    Crtc->Blank     = D1Blank;

    rhdPtr->Crtc[0] = Crtc;

    Crtc = xnfcalloc(sizeof(struct rhdCrtc), 1);
    Crtc->scrnIndex = rhdPtr->scrnIndex;
    Crtc->Name      = "CRTC 2";
    Crtc->Id        = RHD_CRTC_2;

    Crtc->ScaleType = ScaleType;

    if (rhdPtr->ChipSet >= RHD_RV620) {
	Crtc->FMTDestroy = DxFMTDestroy;
	Crtc->FMTSave = DxFMTSave;
	Crtc->FMTRestore = DxFMTRestore;
	Crtc->FMTModeSet = DxFMTSet;
    }
    Crtc->FMTPriv = NULL;

    Crtc->FBValid   = DxFBValid;
    Crtc->FBSet     = DxFBSet;
    Crtc->FBSave = DxFBSave;
    Crtc->FBRestore = DxFBRestore;
    Crtc->FBDestroy = DxFBDestroy;

    Crtc->ModeValid = DxModeValid;
    Crtc->ModeSet   = DxModeSet;
    Crtc->ModeSave = DxModeSave;
    Crtc->ModeRestore = DxModeRestore;
    Crtc->ModeDestroy = DxModeDestroy;
    Crtc->ModePriv = NULL;

    Crtc->ScaleValid = DxScaleValid;
    Crtc->ScaleSet = DxScaleSet;
    Crtc->ScaleSave = DxScaleSave;
    Crtc->ScaleRestore = DxScaleRestore;
    Crtc->ScaleDestroy = DxScaleDestroy;
    Crtc->ScalePriv = NULL;

    Crtc->LUTSelect = D2LUTSelect;
    Crtc->LUTSave = DxLUTSave;
    Crtc->LUTRestore = DxLUTRestore;
    Crtc->LUTDestroy = DxLUTDestroy;
    Crtc->LUTPriv = NULL;

    Crtc->FrameSet  = D2ViewPortStart;

    Crtc->Power     = D2Power;
    Crtc->Blank     = D2Blank;

    rhdPtr->Crtc[1] = Crtc;

    return !useAtom;
}

/*
 *
 */
void
RHDCrtcsDestroy(RHDPtr rhdPtr)
{
    struct rhdCrtc *Crtc;
    int i;

    RHDFUNC(rhdPtr);

    for (i = 0; i < 2; i++) {
	Crtc = rhdPtr->Crtc[i];
    if (Crtc) {
	    if (Crtc->FMTDestroy)
		Crtc->FMTDestroy(Crtc);

	    if (Crtc->LUTDestroy)
		Crtc->LUTDestroy(Crtc);

	    if (Crtc->FBDestroy)
		Crtc->FBDestroy(Crtc);

	    if (Crtc->ScaleDestroy)
		Crtc->ScaleDestroy(Crtc);

	    if (Crtc->ModeDestroy)
		Crtc->ModeDestroy(Crtc);

	    xfree(Crtc);
	    rhdPtr->Crtc[i] = NULL;
	}
    }
}


/*
 *
 */
void
RHDCrtcSave(struct rhdCrtc *Crtc)
{
    RHDDebug(Crtc->scrnIndex, "%s: %s\n", __func__, Crtc->Name);

    if (Crtc->FMTSave)
	Crtc->FMTSave(Crtc);

    if (Crtc->FBSave)
	Crtc->FBSave(Crtc);

    if (Crtc->LUTSave)
	Crtc->LUTSave(Crtc);

    if (Crtc->ScaleSave)
	Crtc->ScaleSave(Crtc);

    if (Crtc->ModeSave)
	Crtc->ModeSave(Crtc);
}

/*
 *
 */
void
RHDCrtcRestore(struct rhdCrtc *Crtc)
{

    RHDDebug(Crtc->scrnIndex, "%s: %s\n", __func__, Crtc->Name);

    if (Crtc->FMTRestore)
	Crtc->FMTRestore(Crtc);

    if (Crtc->FBRestore)
	Crtc->FBRestore(Crtc);

    if (Crtc->LUTRestore)
	Crtc->LUTRestore(Crtc);

    if (Crtc->ScaleRestore)
	Crtc->ScaleRestore(Crtc);

    if (Crtc->ModeRestore)
	Crtc->ModeRestore(Crtc);
}