/*
 * Copyright 2007  Luc Verhaegen <lverhaegen@novell.com>
 * Copyright 2007  Matthias Hopf <mhopf@novell.com>
 * Copyright 2007  Egbert Eich   <eich@novell.com>
 * Copyright 2007  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.
 */

//ld -T ld.x -s --shared --image-base 0 --file-alignment 32 -o test.exe test.obj core.lib

#include "common.h"
#include "rhd.h"
#include "edid.h"

#include "rhd_atombios.h"
#include "rhd_regs.h"
#include "rhd_mc.h"
#include "rhd_atombios.h"
#include "rhd_connector.h"
#include "rhd_output.h"
#include "rhd_biosscratch.h"
#include "rhd_card.h"
#include "rhd_vga.h"
#include "rhd_crtc.h"
#include "rhd_monitor.h"
#include "rhd_modes.h"
#include "rhd_pll.h"
#include "rhd_lut.h"
#include "rhd_i2c.h"

#define PG_SW       0x003
#define PG_NOCACHE  0x018

void sysSetScreen(int width, int height);

static void rhdModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode);
static void rhdSetMode(ScrnInfoPtr pScrn, DisplayModePtr mode);
static void RHDAdjustFrame(RHDPtr rhdPtr, int x, int y, int flags);
static Bool rhdMapFB(RHDPtr rhdPtr);
static void rhdGetIGPNorthBridgeInfo(RHDPtr rhdPtr);

Bool OldSetupConnectors(RHDPtr rhdPtr);
Bool OldConnectorsInit(RHDPtr rhdPtr);

int rhdInitHeap(RHDPtr rhdPtr);

static enum rhdCardType rhdGetCardType(RHDPtr rhdPtr);


static u32_t _PciApi(int cmd);
static int SupportedModes;

int __stdcall drvEntry(int)__asm__("_drvEntry");

typedef struct
{
  unsigned      handle;
  unsigned      io_code;
  void          *input;
  int           inp_size;
  void          *output;
  int           out_size;
}ioctl_t;

typedef int (_stdcall *srv_proc_t)(ioctl_t *);

int _stdcall srv_proc(ioctl_t *io);

extern PciChipset_t   RHDPCIchipsets[];
extern struct rhdCard *RHDCardIdentify(RHDPtr rhdPtr);

static struct RHDRec       rhd;
static struct _ScrnInfoRec Scrn;

void sysSetScreen(int width, int height)
{
  asm __volatile__
  (
    "dec eax \n\t"
    "dec edx \n\t"
    "call [DWORD PTR __imp__SetScreen] \n\t"
    :
    :"a" (width),"d"(height)
    :"memory","cc"
  );
}

static int RegService(char *name, srv_proc_t proc)
{
  int retval;

  asm __volatile__
  (
    "push %[t] \n\t"
    "push %[t1] \n\t"
    "call [DWORD PTR __imp__RegService] \n\t"
    :"=eax" (retval)
    :[t] "g" (proc),[t1] "g" (name)
    :"memory", "ebx"
  );
  return retval;
};

static u32_t _PciApi(int cmd)
{
  u32_t retval;

  asm __volatile__
  (
    "call [DWORD PTR __imp__PciApi]"
    :"=eax" (retval)
    :"a" (cmd)
    :"memory"
  );
  return retval;
};

const PciChipset_t *PciDevMatch(CARD16 dev,const PciChipset_t *list)
{
  while(list->device)
  {
    if(dev==list->device)
      return list;
    list++;
  }
  return 0;
}

const char *
xf86TokenToString(SymTabPtr table, int token)
{
    int i;

    for (i = 0; table[i].token >= 0 && table[i].token != token; i++){};

    if (table[i].token < 0)
      return NULL;
    else
      return(table[i].name);
}

int FindPciDevice()
{
  const PciChipset_t *dev;
  u32_t bus, last_bus;

  if( (last_bus = _PciApi(1))==-1)
    return 0;

  for(bus=0;bus<=last_bus;bus++)
  {
    u32_t devfn;

    for(devfn=0;devfn<256;devfn++)
    {
      u32_t id;
      id = PciRead32(bus,devfn, 0);

      if( (CARD16)id != VENDOR_ATI)
        continue;

      if( (dev=PciDevMatch(id>>16,RHDPCIchipsets))!=0)
      {

        rhd.PciDeviceID = (id>>16);

        rhd.bus = bus;
        rhd.pci.bus = bus;
        rhd.devfn = devfn;
        rhd.pci.devfn = devfn;
        rhd.PciTag = pciTag(bus,(devfn>>3)&0x1F,devfn&0x7);

        rhd.ChipSet = dev->family;
       // rhd.IsMobility  = dev->mobility;
       // rhd.IsIGP       = dev->igp;
       // rhd.HasCRTC2    = !dev->nocrtc2;
       // rhd.HasSingleDAC = dev->singledac;
       // rhd.InternalTVOut = !dev->nointtvout;

        pciGetInfo(&rhd.pci);

        rhd.subvendor_id = rhd.pci.subsysVendor;
        rhd.subdevice_id = rhd.pci.subsysCard;

        //rhd.chipset = (char*)xf86TokenToString(RADEONChipsets, rhd.device_id);

        return 1;
      };
    };
  };

  dbgprintf("Device not found\n");

  return 0;
}


static Bool
rhdMapMMIO()
{

  rhd.MMIOMapSize = 1 << rhd.pci.size[RHD_MMIO_BAR];
  rhd.MMIOBase = MapIoMem(rhd.pci.memBase[RHD_MMIO_BAR],
                          rhd.MMIOMapSize,PG_SW+PG_NOCACHE);
  if( !rhd.MMIOBase)
    return 0;

  DBG(dbgprintf("Mapped IO at %x (size %x)\n", rhd.MMIOBase, rhd.MMIOMapSize));

  return 1;
}

#define RADEON_NB_TOM             0x15c
static CARD32
rhdGetVideoRamSize(RHDPtr rhdPtr)
{
  CARD32 RamSize, BARSize;

  if (rhdPtr->ChipSet == RHD_RS690)
    RamSize = (_RHDRegRead(rhdPtr, R5XX_CONFIG_MEMSIZE))>>10;
  else
    if (rhdPtr->IsIGP)
    {
      CARD32 tom = _RHDRegRead(rhdPtr, RADEON_NB_TOM);
      RamSize = (((tom >> 16) - (tom & 0xffff) + 1) << 6);
      _RHDRegWrite(rhdPtr,R5XX_CONFIG_MEMSIZE, RamSize<<10);
    }
    else
    {
      if (rhdPtr->ChipSet < RHD_R600)
      {
        RamSize = (_RHDRegRead(rhdPtr, R5XX_CONFIG_MEMSIZE)) >> 10;
        if(RamSize==0) RamSize=8192;
      }
      else
        RamSize = (_RHDRegRead(rhdPtr, R6XX_CONFIG_MEMSIZE)) >> 10;
    };

  BARSize = 1 << (rhdPtr->pci.size[RHD_FB_BAR] - 10);
  if(BARSize==0)
    BARSize = 0x20000;

  if (RamSize > BARSize) {
    DBG(dbgprintf("The detected amount of videoram"
           " exceeds the PCI BAR aperture.\n"));
    DBG(dbgprintf("Using only %dkB of the total "
           "%dkB.\n", (int) BARSize, (int) RamSize));
    return BARSize;
  }
  else return RamSize;
}


Bool RHDScalePolicy(struct rhdMonitor *Monitor, struct rhdConnector *Connector)
{
    if (!Monitor || !Monitor->UseFixedModes || !Monitor->NativeMode)
	return FALSE;

    if (Connector->Type != RHD_CONNECTOR_PANEL)
	return FALSE;

    return TRUE;
}

static void
rhdOutputConnectorCheck(struct rhdConnector *Connector)
{
    struct rhdOutput *Output;
    int i;

    /* First, try to sense */
    for (i = 0; i < 2; i++) {
        Output = Connector->Output[i];
        if (Output && Output->Sense) {
	    /*
	     * This is ugly and needs to change when the TV support patches are in.
	     * The problem here is that the Output struct can be used for two connectors
	     * and thus two different devices
	     */
            if (Output->SensedType == RHD_SENSED_NONE) {
             /* Do this before sensing as AtomBIOS sense needs this info */
                if ((Output->SensedType = Output->Sense(Output, Connector)) != RHD_SENSED_NONE) {
                    RHDOutputPrintSensedType(Output);
                    Output->Connector = Connector;
                    break;
                 }
            }
        }
    }

    if (i == 2) {
	/* now just enable the ones without sensing */
	for (i = 0; i < 2; i++) {
	    Output = Connector->Output[i];
	    if (Output && !Output->Sense) {
		Output->Connector = Connector;
		break;
	    }
	}
    }
}

/*
 *
 */
static Bool
rhdModeLayoutSelect(RHDPtr rhdPtr)
{
  struct rhdOutput *Output;
  struct rhdConnector *Connector;
  Bool Found = FALSE;
  char *ignore = NULL;
  Bool ConnectorIsDMS59 = FALSE;
  int i = 0;

  RHDFUNC(rhdPtr);

  /* housekeeping */
  rhdPtr->Crtc[0]->PLL = rhdPtr->PLLs[0];
  rhdPtr->Crtc[0]->LUT = rhdPtr->LUT[0];

  rhdPtr->Crtc[1]->PLL = rhdPtr->PLLs[1];
  rhdPtr->Crtc[1]->LUT = rhdPtr->LUT[1];

  /* start layout afresh */
  for (Output = rhdPtr->Outputs; Output; Output = Output->Next)
  {
    Output->Active = FALSE;
    Output->Crtc = NULL;
    Output->Connector = NULL;
  }

    /* quick and dirty option so that some output choice exists */
//  ignore = xf86GetOptValString(rhdPtr->Options, OPTION_IGNORECONNECTOR);

    /* handle cards with DMS-59 connectors appropriately. The DMS-59 to VGA
       adapter does not raise HPD at all, so we need a fallback there. */
  if (rhdPtr->Card)
  {
    ConnectorIsDMS59 = rhdPtr->Card->flags & RHD_CARD_FLAG_DMS59;
    if (ConnectorIsDMS59)
	    xf86DrvMsg(rhdPtr->scrnIndex, X_INFO, "Card %s has a DMS-59"
		       " connector.\n", rhdPtr->Card->name);
  }

    /* Check on the basis of Connector->HPD */
  for (i = 0; i < RHD_CONNECTORS_MAX; i++)
  {
    Connector = rhdPtr->Connector[i];

    if (!Connector)
	    continue;


	if (Connector->HPDCheck) {
	    if (Connector->HPDCheck(Connector)) {
          Connector->HPDAttached = TRUE;
          rhdOutputConnectorCheck(Connector);
	    } else {
          Connector->HPDAttached = FALSE;
          if (ConnectorIsDMS59)
            rhdOutputConnectorCheck(Connector);
	    }
	} else
      rhdOutputConnectorCheck(Connector);
  }

  i = 0; /* counter for CRTCs */
  for (Output = rhdPtr->Outputs; Output; Output = Output->Next)
	if (Output->Connector) {
      struct rhdMonitor *Monitor = NULL;

      Connector = Output->Connector;

      Monitor = RHDMonitorInit(Connector);

	    if (!Monitor && (Connector->Type == RHD_CONNECTOR_PANEL)) {
		xf86DrvMsg(rhdPtr->scrnIndex, X_WARNING, "Unable to attach a"
			   " monitor to connector \"%s\"\n", Connector->Name);
        Output->Active = FALSE;
	    } else if (!Output->AllocFree || Output->AllocFree(Output, RHD_OUTPUT_ALLOC)){
        Connector->Monitor = Monitor;

        Output->Active = TRUE;

        Output->Crtc = rhdPtr->Crtc[i & 1]; /* ;) */
        i++;

        Output->Crtc->Active = TRUE;

		if (RHDScalePolicy(Monitor, Connector)) {
		    Output->Crtc->ScaledToMode = RHDModeCopy(Monitor->NativeMode);
		    xf86DrvMsg(rhdPtr->scrnIndex, X_INFO,
			       "Crtc[%i]: found native mode from Monitor[%s]: ",
			       Output->Crtc->Id, Monitor->Name);
		    RHDPrintModeline(Output->Crtc->ScaledToMode);
		}
        Found = TRUE;

		if (Monitor) {
  /* If this is a DVI attached monitor, enable reduced blanking.
   * TODO: iiyama vm pro 453: CRT with DVI-D == No reduced.
   */
          if ((Output->Id == RHD_OUTPUT_TMDSA) ||
              (Output->Id == RHD_OUTPUT_LVTMA) ||
              (Output->Id == RHD_OUTPUT_KLDSKP_LVTMA) ||
              (Output->Id == RHD_OUTPUT_UNIPHYA) ||
              (Output->Id == RHD_OUTPUT_UNIPHYB))
            Monitor->ReducedAllowed = TRUE;

		    /* allow user to override settings globally */
          if (rhdPtr->forceReduced.set)
            Monitor->ReducedAllowed = rhdPtr->forceReduced.val.bool;

          xf86DrvMsg(rhdPtr->scrnIndex, X_INFO,
                     "Connector \"%s\" uses Monitor \"%s\":\n",
                      Connector->Name, Monitor->Name);
          RHDMonitorPrint(Monitor);
		} else
          xf86DrvMsg(rhdPtr->scrnIndex, X_WARNING,
                     "Connector \"%s\": Failed to retrieve Monitor"
                     " information.\n", Connector->Name);
      }
    }

    /* Now validate the scaled modes attached to crtcs */
    for (i = 0; i < 2; i++) {
      struct rhdCrtc *crtc = rhdPtr->Crtc[i];
	if (crtc->ScaledToMode && RHDValidateScaledToMode(crtc, crtc->ScaledToMode) != MODE_OK) {
	    xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "Crtc[%i]: scaled mode invalid.\n", crtc->Id);
	    xfree(crtc->ScaledToMode);
	    crtc->ScaledToMode = NULL;
      }
    }
  return Found;
}

     /*
 *
 */
static void
rhdModeLayoutPrint(RHDPtr rhdPtr)
{
    struct rhdCrtc *Crtc;
    struct rhdOutput *Output;
    Bool Found;

    xf86DrvMsg(rhdPtr->scrnIndex, X_INFO, "Listing modesetting layout:\n\n");

    /* CRTC 1 */
    Crtc = rhdPtr->Crtc[0];
    if (Crtc->Active) {
	xf86Msg(X_NONE, "\t%s: tied to %s and %s:\n",
		Crtc->Name, Crtc->PLL->Name, Crtc->LUT->Name);

	Found = FALSE;
	for (Output = rhdPtr->Outputs; Output; Output = Output->Next)
	    if (Output->Active && (Output->Crtc == Crtc)) {
		if (!Found) {
		    xf86Msg(X_NONE, "\t\tOutputs: %s (%s)",
			    Output->Name, Output->Connector->Name);
		    Found = TRUE;
		} else
		    xf86Msg(X_NONE, ", %s (%s)", Output->Name,
			    Output->Connector->Name);
	    }

	if (!Found)
	    xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR,
		       "%s is active without outputs\n", Crtc->Name);
	else
	     xf86Msg(X_NONE, "\n");
    } else
	xf86Msg(X_NONE, "\t%s: unused\n", Crtc->Name);
    xf86Msg(X_NONE, "\n");

    /* CRTC 2 */
    Crtc = rhdPtr->Crtc[1];
    if (Crtc->Active) {
	xf86Msg(X_NONE, "\t%s: tied to %s and %s:\n",
		Crtc->Name, Crtc->PLL->Name, Crtc->LUT->Name);

	Found = FALSE;
	for (Output = rhdPtr->Outputs; Output; Output = Output->Next)
	    if (Output->Active && (Output->Crtc == Crtc)) {
		if (!Found) {
		    xf86Msg(X_NONE, "\t\tOutputs: %s (%s)",
			    Output->Name, Output->Connector->Name);
		    Found = TRUE;
		} else
		    xf86Msg(X_NONE, ", %s (%s)", Output->Name,
			    Output->Connector->Name);
	    }

	if (!Found)
	    xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR,
		       "%s is active without outputs\n", Crtc->Name);
	else
	    xf86Msg(X_NONE, "\n");
    } else
	xf86Msg(X_NONE, "\t%s: unused\n", Crtc->Name);
    xf86Msg(X_NONE, "\n");

    /* Print out unused Outputs */
    Found = FALSE;
    for (Output = rhdPtr->Outputs; Output; Output = Output->Next)
	if (!Output->Active) {
	    if (!Found) {
		xf86Msg(X_NONE, "\t\tUnused Outputs: %s", Output->Name);
		Found = TRUE;
	    } else
		xf86Msg(X_NONE, ", %s", Output->Name);
	}

    if (Found)
	xf86Msg(X_NONE, "\n");
    xf86Msg(X_NONE, "\n");
}


DisplayModePtr
rhdCreateModesListAndValidate(ScrnInfoPtr pScrn, Bool Silent);
void RHDPrintModeline(DisplayModePtr mode);

int RHDPreInit()
{
    RHDI2CDataArg i2cArg;
    RHDPtr rhdPtr = &rhd;

    /* We need access to IO space already */
    if (!rhdMapMMIO()) {
        dbgprintf("Failed to map MMIO.\n");
        return 0;
    };


    if (RHDIsIGP(rhd.ChipSet))
        rhdGetIGPNorthBridgeInfo(&rhd);

    rhd.Card = RHDCardIdentify(&rhd);
    if (rhd.Card)
        dbgprintf("Detected an %s on a %s\n", rhd.chipset_name, rhd.Card->name);
    else
        dbgprintf("Detected an %s on an unidentified card\n", rhd.chipset_name);

    if (rhdPtr->Card && rhdPtr->Card->flags & RHD_CARD_FLAG_HPDSWAP &&
	rhdPtr->hpdUsage == RHD_HPD_USAGE_AUTO)
	rhdPtr->hpdUsage = RHD_HPD_USAGE_AUTO_SWAP;
    if (rhdPtr->Card && rhdPtr->Card->flags & RHD_CARD_FLAG_HPDOFF &&
	rhdPtr->hpdUsage == RHD_HPD_USAGE_AUTO)
	rhdPtr->hpdUsage = RHD_HPD_USAGE_AUTO_OFF;

    rhdPtr->cardType = rhdGetCardType(rhdPtr);


    {
        AtomBiosArgRec atomBiosArg;

        rhd.UseAtomFlags = (RHD_ATOMBIOS_ON << RHD_ATOMBIOS_CRTC)    |
                           (RHD_ATOMBIOS_ON << RHD_ATOMBIOS_OUTPUT)  |
                           (RHD_ATOMBIOS_ON << RHD_ATOMBIOS_PLL);

   // rhd.UseAtomFlags = 0;

        if (RHDAtomBiosFunc(&rhd, NULL, ATOMBIOS_INIT, &atomBiosArg) == ATOM_SUCCESS)
        {
            rhd.atomBIOS = atomBiosArg.atomhandle;
        }
    }

    rhd.videoRam = rhdGetVideoRamSize(&rhd);
    if (!rhd.videoRam)
    {
        dbgprintf("No Video RAM detected.\n");
	    goto error1;
	}
    dbgprintf("VideoRAM: %d kByte\n",rhd.videoRam);

    if (rhd.atomBIOS)                        /* for testing functions */
    {
        AtomBiosArgRec atomBiosArg;

        atomBiosArg.fb.start = rhd.FbFreeStart;
        atomBiosArg.fb.size = rhd.FbFreeSize;
        if (RHDAtomBiosFunc(&rhd, rhd.atomBIOS, ATOMBIOS_ALLOCATE_FB_SCRATCH,
                            &atomBiosArg) == ATOM_SUCCESS)
        {
            rhd.FbFreeStart = atomBiosArg.fb.start;
            rhd.FbFreeSize = atomBiosArg.fb.size;
        };
        RHDAtomBiosFunc(&rhd, rhd.atomBIOS, GET_DEFAULT_ENGINE_CLOCK, &atomBiosArg);
        RHDAtomBiosFunc(&rhd, rhd.atomBIOS, GET_DEFAULT_MEMORY_CLOCK, &atomBiosArg);
        RHDAtomBiosFunc(&rhd, rhd.atomBIOS, GET_MAX_PIXEL_CLOCK_PLL_OUTPUT, &atomBiosArg);
        RHDAtomBiosFunc(&rhd, rhd.atomBIOS, GET_MIN_PIXEL_CLOCK_PLL_OUTPUT, &atomBiosArg);
        RHDAtomBiosFunc(&rhd, rhd.atomBIOS, GET_MAX_PIXEL_CLOCK_PLL_INPUT, &atomBiosArg);
        RHDAtomBiosFunc(&rhd, rhd.atomBIOS, GET_MIN_PIXEL_CLOCK_PLL_INPUT, &atomBiosArg);
        RHDAtomBiosFunc(&rhd, rhd.atomBIOS, GET_MAX_PIXEL_CLK, &atomBiosArg);
        RHDAtomBiosFunc(&rhd, rhd.atomBIOS, GET_REF_CLOCK, &atomBiosArg);
    }


    rhd.FbFreeStart = 0;
    rhd.FbFreeSize = rhd.videoRam << 10;


    if (RHDI2CFunc((int)&rhd, NULL, RHD_I2C_INIT, &i2cArg) == RHD_I2C_SUCCESS)
        rhd.I2C = i2cArg.I2CBusList;
    else
    {
        dbgprintf("I2C init failed\n");
        goto error1;
    };

    if (!rhd.atomBIOS)
    {
        dbgprintf("No ATOMBIOS detected.  Done.\n");
        return 0;
    }

//  rhdMapFB(&rhd);

    Scrn.rhdPtr       = &rhd;
    Scrn.driverName   = "Radeon HD driver";
    Scrn.bitsPerPixel = 32;
    Scrn.depth        = 32;
    Scrn.virtualX     = 1280;
    Scrn.virtualY     = 1024;
    Scrn.displayWidth = 1280;

    rhd.pScrn = &Scrn;

    rhd.FbScanoutStart = 0;
    rhd.FbScanoutSize  = 8*1024*1024;
    rhd.FbFreeStart    = 8*1024*1024;
    rhd.FbFreeSize     = rhd.FbMapSize - 8*1024*1024;

    rhdInitHeap(&rhd);

    RHDVGAInit(&rhd);
    RHDMCInit(&rhd);

    if (!RHDCrtcsInit(&rhd))
        RHDAtomCrtcsInit(&rhd);
    if (!RHDPLLsInit(&rhd))
        RHDAtomPLLsInit(&rhd);

    RHDLUTsInit(&rhd);

    if (!RHDConnectorsInit(&rhd, rhd.Card))
    {
        dbgprintf("Card information has invalid connector information\n");
        goto error1;
    }

    {
        struct rhdAtomOutputDeviceList *OutputDeviceList = NULL;

        if (rhdPtr->Card
            && rhdPtr->Card->ConnectorInfo[0].Type != RHD_CONNECTOR_NONE
            && (rhdPtr->Card->DeviceInfo[0][0] != atomNone
            || rhdPtr->Card->DeviceInfo[0][1] != atomNone))
        {
            int i, k = 0;

            for (i = 0; i < RHD_CONNECTORS_MAX; i++)
            {
                int j;
                if (rhdPtr->Card->ConnectorInfo[i].Type == RHD_CONNECTOR_NONE)
                    break;
                for (j = 0; j < MAX_OUTPUTS_PER_CONNECTOR; j++)
                {
                    if (rhdPtr->Card->ConnectorInfo[i].Output[j] != RHD_OUTPUT_NONE)
                    {
                        if (!(OutputDeviceList =
                             (struct rhdAtomOutputDeviceList *)
                              xrealloc(OutputDeviceList,
                                       sizeof (struct rhdAtomOutputDeviceList) * (k + 1))))
                            break;
                        OutputDeviceList[k].ConnectorType = rhdPtr->Card->ConnectorInfo[i].Type;
                        OutputDeviceList[k].DeviceId = rhdPtr->Card->DeviceInfo[i][j];
                        OutputDeviceList[k].OutputType = rhdPtr->Card->ConnectorInfo[i].Output[j];
                        dbgprintf("OutputDevice: C: 0x%2.2x O: 0x%2.2x DevID: 0x%2.2x\n",
                                   OutputDeviceList[k].ConnectorType,
                                   OutputDeviceList[k].OutputType,
                                   OutputDeviceList[k].DeviceId);
                        k++;
                    }
                }
            }
        }
        else
        {
            AtomBiosArgRec data;

            data.chipset = rhdPtr->ChipSet;
            if (RHDAtomBiosFunc(rhdPtr, rhdPtr->atomBIOS,
				ATOMBIOS_GET_OUTPUT_DEVICE_LIST, &data) == ATOM_SUCCESS)
                OutputDeviceList = data.OutputDeviceList;
        }

        if (OutputDeviceList)
        {
            struct rhdOutput *Output;

            for (Output = rhdPtr->Outputs; Output; Output = Output->Next)
                RHDAtomSetupOutputDriverPrivate(OutputDeviceList, Output);
            xfree(OutputDeviceList);
        }
    }


    if (!rhdModeLayoutSelect(&rhd))
    {
        dbgprintf("Failed to detect a connected monitor\n");
        goto error1;
	}

    RHDConfigMonitorSet(&rhd, FALSE);
    rhdModeLayoutPrint(&rhd);

    {
        DisplayModePtr Modes, tmp;

        Modes = RHDModesPoolCreate(&Scrn, FALSE);
        Scrn.modePool = Modes;

        tmp = Modes;
        SupportedModes=0;
        while(tmp)
        {
            dbgprintf("%dx%d@%3.1fHz\n",tmp->CrtcHDisplay,
                      tmp->CrtcVDisplay,tmp->VRefresh);
            tmp=tmp->next;
            SupportedModes++;
        };
//    rhdModeInit(&Scrn,Modes);
    //RHDAdjustFrame(&rhd,0,0,0);
    };
    dbgprintf("All done\n");
    return 1;

error1:
    return 0;
};

int __stdcall drvEntry(int action)
{
    int i;

    if(action != 1)
        return 0;

    if(!dbg_open("/rd/1/drivers/ati.txt"))
    {
        printf("Can't open /rd/1/drivers/ati.txt\nExit\n");
        return 0;
    }
    if(!FindPciDevice())
        return 0;

    rhd.scrnIndex = (int)&rhd;

    for(i=0;i<6;i++)
    {
        if(rhd.pci.memBase[i])
            dbgprintf("Memory base_%d 0x%x size 0x%x\n",
                      i,rhd.pci.memBase[i],(1<<rhd.pci.size[i]));
    };
    for(i=0;i<6;i++)
    {
        if(rhd.pci.ioBase[i])
            dbgprintf("Io base_%d 0x%x size 0x%x\n",
                      i,rhd.pci.ioBase[i],(1<<rhd.pci.size[i]));
    };
    if(RHDPreInit()==0)
        return 0;

    return RegService("RHD", srv_proc);
};


void usleep(u32_t delay)
{
  if(!delay) delay++;
  delay*=1000;

  asm(
     "1:\n\t"
      "xor eax, eax \n\t"
      "cpuid \n\t"
      "dec edi \n\t"
      "jnz 1b"
      :
      :"D"(delay)
      :"eax","ebx","ecx","edx"
     );
}


//git://anongit.freedesktop.org/git/xorg/xserver
//git://anongit.freedesktop.org/git/xorg/lib/libpciaccess

int KernelFree(void *p)
{

  return 0;
}

static void
rhdPrepareMode(RHDPtr rhdPtr)
{
    RHDFUNC(rhdPtr);

    /* no active outputs == no mess */
    RHDOutputsPower(rhdPtr, RHD_POWER_RESET);
}

/*
 * */static void
rhdModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode)
{
    RHDPtr rhdPtr = pScrn->rhdPtr;

    RHDFUNC(rhdPtr);

    rhdSetMode(pScrn, mode);
}

/* * */static void
rhdSetMode(ScrnInfoPtr pScrn, DisplayModePtr mode)
{
    RHDPtr rhdPtr = RHDPTR(pScrn);
    int i;

    RHDFUNC(rhdPtr);

    xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Setting up \"%s\" (%dx%d@%3.1fHz)\n",
	       mode->name, mode->CrtcHDisplay, mode->CrtcVDisplay,
	       mode->VRefresh);

    /* Set up D1/D2 and appendages */
    for (i = 0; i < 2; i++) {
	struct rhdCrtc *Crtc;

	Crtc = rhdPtr->Crtc[i];
	if (Crtc->Active) {
	    Crtc->FBSet(Crtc, pScrn->displayWidth, pScrn->virtualX, pScrn->virtualY,
			pScrn->depth, rhdPtr->FbScanoutStart);
	    if (Crtc->ScaledToMode) {
		Crtc->ModeSet(Crtc, Crtc->ScaledToMode);
		if (Crtc->ScaleSet)
		    Crtc->ScaleSet(Crtc, Crtc->ScaleType, mode, Crtc->ScaledToMode);
	    } else {
		Crtc->ModeSet(Crtc, mode);
		if (Crtc->ScaleSet)
		    Crtc->ScaleSet(Crtc, RHD_CRTC_SCALE_TYPE_NONE, mode, NULL);
	    }
	    RHDPLLSet(Crtc->PLL, mode->Clock);
	    Crtc->LUTSelect(Crtc, Crtc->LUT);
	    RHDOutputsMode(rhdPtr, Crtc, Crtc->ScaledToMode
			   ? Crtc->ScaledToMode : mode);
	}
    }

    /* shut down that what we don't use */
    RHDPLLsShutdownInactive(rhdPtr);
    RHDOutputsShutdownInactive(rhdPtr);

    if (rhdPtr->Crtc[0]->Active)
	rhdPtr->Crtc[0]->Power(rhdPtr->Crtc[0], RHD_POWER_ON);
    else
	rhdPtr->Crtc[0]->Power(rhdPtr->Crtc[0], RHD_POWER_SHUTDOWN);

    if (rhdPtr->Crtc[1]->Active)
	rhdPtr->Crtc[1]->Power(rhdPtr->Crtc[1], RHD_POWER_ON);
    else
	rhdPtr->Crtc[1]->Power(rhdPtr->Crtc[1], RHD_POWER_SHUTDOWN);

    RHDOutputsPower(rhdPtr, RHD_POWER_ON);
}



static void
RHDAdjustFrame(RHDPtr rhdPtr, int x, int y, int flags)
{
//    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
//    RHDPtr rhdPtr = RHDPTR(pScrn);
    struct rhdCrtc *Crtc;

	Crtc = rhdPtr->Crtc[0];
  if (Crtc->Active)
    Crtc->FrameSet(Crtc, x, y);

	Crtc = rhdPtr->Crtc[1];
  if ( Crtc->Active)
	    Crtc->FrameSet(Crtc, x, y);

}

static Bool
rhdMapFB(RHDPtr rhdPtr)
{
    CARD32 membase;
    RHDFUNC(rhdPtr);

    rhdPtr->FbMapSize = 1 << rhdPtr->pci.size[RHD_FB_BAR];
    membase = rhdPtr->pci.memBase[RHD_FB_BAR];

    rhdPtr->FbBase = MapIoMem(membase, rhdPtr->FbMapSize,PG_SW+PG_NOCACHE);

    if (!rhdPtr->FbBase)
        return FALSE;

    /* These devices have an internal address reference, which some other
     * address registers in there also use. This can be different from the
     * address in the BAR */
    if (rhdPtr->ChipSet < RHD_R600)
      rhdPtr->FbIntAddress = _RHDRegRead(rhdPtr, HDP_FB_LOCATION)<< 16;
    else
      rhdPtr->FbIntAddress = _RHDRegRead(rhdPtr, R6XX_CONFIG_FB_BASE);

    if (rhdPtr->FbIntAddress != membase)
      dbgprintf("PCI FB Address (BAR) is at "
                "0x%08X while card Internal Address is 0x%08X\n",
                (unsigned int) membase,rhdPtr->FbIntAddress);
    dbgprintf("Mapped FB at %p (size 0x%08X)\n",rhdPtr->FbBase, rhdPtr->FbMapSize);
    return TRUE;
}


#define ERR_PARAM  -1

#pragma pack (push,1)
typedef struct
{
  short width;
  short height;
  short bpp;
  short freq;
}mode_t;
#pragma pack (pop)

int get_modes(mode_t *mode, int count)
{
  if(count==0)
    count = SupportedModes;
  else
  {
    DisplayModePtr tmp;
    int i;

    if(count>SupportedModes)
      count = SupportedModes;

    for(i=0,tmp = Scrn.modePool;i<count;i++,tmp=tmp->next,mode++)
    {
      mode->width = tmp->CrtcHDisplay;
      mode->height = tmp->CrtcVDisplay;
      mode->bpp = 32;
      mode->freq = (short)__builtin_ceilf(tmp->VRefresh);
    }
  }
  return count;
}

int set_mode(mode_t *mode)
{
  DisplayModePtr tmp;
  int i;

  for(i=0,tmp = Scrn.modePool;i<SupportedModes;i++,tmp=tmp->next)
  {
    if( (mode->width == tmp->CrtcHDisplay) &&
        (mode->height == tmp->CrtcVDisplay) &&
        (mode->freq ==  (short)__builtin_ceilf(tmp->VRefresh)))
    {
      Scrn.virtualX = mode->width ;
      Scrn.virtualY = mode->height;
      Scrn.displayWidth = mode->width;
      rhdModeInit(&Scrn,tmp);
      sysSetScreen(mode->width,mode->height);
      dbgprintf("set_mode OK\n");
      return 1;
    };
  }
  return 0;
};

#define API_VERSION     0x01000100

#define SRV_GETVERSION  0
#define SRV_ENUM_MODES  1
#define SRV_SET_MODE    2

int _stdcall srv_proc(ioctl_t *io)
{
  u32_t *inp;
  u32_t *outp;

  inp = io->input;
  outp = io->output;

  switch(io->io_code)
  {
    case SRV_GETVERSION:
      if(io->out_size==4)
      {
        *(u32_t*)io->output = API_VERSION;
        return 0;
      }
      break;
    case SRV_ENUM_MODES:
      if(io->inp_size==8)
      {
        int count;
        count = get_modes((mode_t*)(*inp),(int)*(inp+1));

        if(io->out_size==4)
        {
          *outp = count;
          return 0;
        }
      };
      break;
    case SRV_SET_MODE:
      if(io->inp_size==8)
      {
        int err;
        err = set_mode((mode_t*)inp);

        if(io->out_size==4)
        {
          *outp = err;
          return 0;
        }
      };
      break;

  };

  return -1;
}


CARD32
_RHDReadMC(int scrnIndex, CARD32 addr)
{
    RHDPtr rhdPtr = (RHDPtr)scrnIndex;
    CARD32 ret;

    if (rhdPtr->ChipSet < RHD_RS600) {
      _RHDRegWrite(rhdPtr, MC_IND_INDEX, addr);
      ret = _RHDRegRead(rhdPtr, MC_IND_DATA);
    } else if (rhdPtr->ChipSet == RHD_RS600) {
      _RHDRegWrite(rhdPtr, RS60_MC_NB_MC_INDEX, addr);
      ret = _RHDRegRead(rhdPtr, RS60_MC_NB_MC_DATA);
    } else if (rhdPtr->ChipSet == RHD_RS690 || rhdPtr->ChipSet == RHD_RS740) {
	pciWriteLong(rhdPtr->NBPciTag, RS69_MC_INDEX, addr & ~RS69_MC_IND_WR_EN);
	ret = pciReadLong(rhdPtr->NBPciTag, RS69_MC_DATA);
    } else {
	pciWriteLong(rhdPtr->NBPciTag, RS78_NB_MC_IND_INDEX, (addr & ~RS78_MC_IND_WR_EN));
	ret = pciReadLong(rhdPtr->NBPciTag, RS78_NB_MC_IND_DATA);
    }

    RHDDebug(scrnIndex,"%s(0x%08X) = 0x%08X\n",__func__,(unsigned int)addr,
	     (unsigned int)ret);
    return ret;
}

void
_RHDWriteMC(int scrnIndex, CARD32 addr, CARD32 data)
{
    RHDPtr rhdPtr = (RHDPtr)scrnIndex;

    RHDDebug(scrnIndex,"%s(0x%08X, 0x%08X)\n",__func__,(unsigned int)addr,
	     (unsigned int)data);

    if (rhdPtr->ChipSet < RHD_RS600) {
      _RHDRegWrite(rhdPtr, MC_IND_INDEX, addr | MC_IND_WR_EN);
      _RHDRegWrite(rhdPtr, MC_IND_DATA, data);
    } else if (rhdPtr->ChipSet == RHD_RS600) {
      _RHDRegWrite(rhdPtr, RS60_MC_NB_MC_INDEX, addr | RS60_NB_MC_IND_WR_EN);
      _RHDRegWrite(rhdPtr, RS60_MC_NB_MC_DATA, data);
    } else if (rhdPtr->ChipSet == RHD_RS690 || rhdPtr->ChipSet == RHD_RS740) {
      pciWriteLong(rhdPtr->NBPciTag, RS69_MC_INDEX, addr | RS69_MC_IND_WR_EN);
      pciWriteLong(rhdPtr->NBPciTag, RS69_MC_DATA, data);
    } else  {
      pciWriteLong(rhdPtr->NBPciTag, RS78_NB_MC_IND_INDEX, addr | RS78_MC_IND_WR_EN);
      pciWriteLong(rhdPtr->NBPciTag, RS78_NB_MC_IND_DATA, data);
    }
}

/*
 *
 */
static void
rhdGetIGPNorthBridgeInfo(RHDPtr rhdPtr)
{
    switch (rhdPtr->ChipSet)
    {
        case RHD_RS600:
            break;
        case RHD_RS690:
        case RHD_RS740:
        case RHD_RS780:
            rhdPtr->NBPciTag = pciTag(0,0,0);
            break;
        default:
            break;
    }
}

static enum rhdCardType
rhdGetCardType(RHDPtr rhdPtr)
{
    CARD32 cmd_stat;

    if (rhdPtr->ChipSet == RHD_RS780)
        return RHD_CARD_PCIE;

    cmd_stat = pciReadLong(rhdPtr->PciTag, PCI_CMD_STAT_REG);

    if (cmd_stat & 0x100000) {
        CARD32 cap_ptr, cap_id;

        cap_ptr = pciReadLong(rhdPtr->PciTag, 0x34);
        cap_ptr &= 0xfc;

        while (cap_ptr)
        {
            cap_id = pciReadLong(rhdPtr->PciTag, cap_ptr);
            switch (cap_id & 0xff) {
                case RHD_PCI_CAPID_AGP:
                    xf86DrvMsg(rhdPtr->scrnIndex, X_INFO, "AGP Card Detected\n");
                    return RHD_CARD_AGP;
                case RHD_PCI_CAPID_PCIE:
                    xf86DrvMsg(rhdPtr->scrnIndex, X_INFO, "PCIE Card Detected\n");
                    return RHD_CARD_PCIE;
            }
            cap_ptr = (cap_id >> 8) & 0xff;
        }
    }
    return RHD_CARD_NONE;
}


