/*
 * Copyright 2003 Alex Deucher.
 *
 * 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 ALEX DEUCHER, OR ANY OTHER 
 * CONTRIBUTORS 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

/*
 * Authors:
 *   Alex Deucher <agd5f@yahoo.com>
 *   Based, in large part, on the sis driver by Thomas Winischhofer.
 */

#include <string.h>
#include <stdio.h>

#include "xf86.h"
#include "xf86Priv.h"
#include "xf86Resources.h"
#include "xf86_OSproc.h"
#include "extnsionst.h"  	/* required */
#include <X11/extensions/panoramiXproto.h>  	/* required */
#include "dixstruct.h"
#include "vbe.h"


#include "radeon.h"
#include "radeon_reg.h"
#include "radeon_macros.h"
#include "radeon_mergedfb.h"

/* psuedo xinerama support */
static unsigned char 	RADEONXineramaReqCode = 0;
int 			RADEONXineramaPixWidth = 0;
int 			RADEONXineramaPixHeight = 0;
int 			RADEONXineramaNumScreens = 0;
RADEONXineramaData	*RADEONXineramadataPtr = NULL;
static int 		RADEONXineramaGeneration;
Bool 		RADEONnoPanoramiXExtension = TRUE;

int RADEONProcXineramaQueryVersion(ClientPtr client);
int RADEONProcXineramaGetState(ClientPtr client);
int RADEONProcXineramaGetScreenCount(ClientPtr client);
int RADEONProcXineramaGetScreenSize(ClientPtr client);
int RADEONProcXineramaIsActive(ClientPtr client);
int RADEONProcXineramaQueryScreens(ClientPtr client);
int RADEONSProcXineramaDispatch(ClientPtr client);

static void
RADEONChooseCursorCRTC(ScrnInfoPtr pScrn1, int x, int y);

/* mergedfb functions */
/* Helper function for CRT2 monitor vrefresh/hsync options
 * (Taken from mga, sis drivers)
 */
int
RADEONStrToRanges(range *r, char *s, int max)
{
   float num = 0.0;
   int rangenum = 0;
   Bool gotdash = FALSE;
   Bool nextdash = FALSE;
   char* strnum = NULL;
   do {
     switch(*s) {
     case '0':
     case '1':
     case '2':
     case '3':
     case '4':
     case '5':
     case '6':
     case '7':
     case '8':
     case '9':
     case '.':
        if(strnum == NULL) {
           strnum = s;
           gotdash = nextdash;
           nextdash = FALSE;
        }
        break;
     case '-':
     case ' ':
     case 0:
        if(strnum == NULL) break;
	sscanf(strnum, "%f", &num);
	strnum = NULL;
        if(gotdash)
           r[rangenum - 1].hi = num;
        else {
           r[rangenum].lo = num;
           r[rangenum].hi = num;
           rangenum++;
        }
        if(*s == '-') nextdash = (rangenum != 0);
        else if(rangenum >= max) return rangenum;
        break;
     default :
        return 0;
     }
   } while(*(s++) != 0);

   return rangenum;
}

/* Copy and link two modes (i, j) for merged-fb mode
 * (Taken from mga, sis drivers)
 * Copys mode i, merges j to copy of i, links the result to dest, and returns it.
 * Links i and j in Private record.
 * If dest is NULL, return value is copy of i linked to itself.
 * For mergedfb auto-config, we only check the dimension
 * against virtualX/Y, if they were user-provided.
 */
static DisplayModePtr
RADEONCopyModeNLink(ScrnInfoPtr pScrn, DisplayModePtr dest,
                 DisplayModePtr i, DisplayModePtr j,
		 RADEONScrn2Rel srel)
{
    DisplayModePtr mode;
    int dx = 0,dy = 0;
    RADEONInfoPtr  info       = RADEONPTR(pScrn);

    if(!((mode = xalloc(sizeof(DisplayModeRec))))) return dest;
    memcpy(mode, i, sizeof(DisplayModeRec));
    if(!((mode->Private = xalloc(sizeof(RADEONMergedDisplayModeRec))))) {
       xfree(mode);
       return dest;
    }
    ((RADEONMergedDisplayModePtr)mode->Private)->CRT1 = i;
    ((RADEONMergedDisplayModePtr)mode->Private)->CRT2 = j;
    ((RADEONMergedDisplayModePtr)mode->Private)->CRT2Position = srel;
    mode->PrivSize = 0;

    switch(srel) {
    case radeonLeftOf:
    case radeonRightOf:
       if(!(pScrn->display->virtualX)) {
          dx = i->HDisplay + j->HDisplay;
       } else {
          dx = min(pScrn->virtualX, i->HDisplay + j->HDisplay);
       }
       dx -= mode->HDisplay;
       if(!(pScrn->display->virtualY)) {
          dy = max(i->VDisplay, j->VDisplay);
       } else {
          dy = min(pScrn->virtualY, max(i->VDisplay, j->VDisplay));
       }
       dy -= mode->VDisplay;
       break;
    case radeonAbove:
    case radeonBelow:
       if(!(pScrn->display->virtualY)) {
          dy = i->VDisplay + j->VDisplay;
       } else {
          dy = min(pScrn->virtualY, i->VDisplay + j->VDisplay);
       }
       dy -= mode->VDisplay;
       if(!(pScrn->display->virtualX)) {
          dx = max(i->HDisplay, j->HDisplay);
       } else {
          dx = min(pScrn->virtualX, max(i->HDisplay, j->HDisplay));
       }
       dx -= mode->HDisplay;
       break;
    case radeonClone:
       if(!(pScrn->display->virtualX)) {
          dx = max(i->HDisplay, j->HDisplay);
       } else {
          dx = min(pScrn->virtualX, max(i->HDisplay, j->HDisplay));
       }
       dx -= mode->HDisplay;
       if(!(pScrn->display->virtualY)) {
          dy = max(i->VDisplay, j->VDisplay);
       } else {
	  dy = min(pScrn->virtualY, max(i->VDisplay, j->VDisplay));
       }
       dy -= mode->VDisplay;
       break;
    }
    mode->HDisplay += dx;
    mode->HSyncStart += dx;
    mode->HSyncEnd += dx;
    mode->HTotal += dx;
    mode->VDisplay += dy;
    mode->VSyncStart += dy;
    mode->VSyncEnd += dy;
    mode->VTotal += dy;

     /* Provide a fake VRefresh/DotClock in order to trick the vidmode
      * extension to allow selecting among a number of modes whose merged result
      * looks identical but consists of different modes for CRT1 and CRT2
      */
    mode->VRefresh = (float)((i->Clock * 1000.0 / i->HTotal / i->VTotal) * 100 +
	(j->Clock * 1000.0 / j->HTotal / j->VTotal));

    mode->Clock = (int)(mode->VRefresh * 0.001 * mode->HTotal * mode->VTotal);

    if( ((mode->HDisplay * ((pScrn->bitsPerPixel + 7) / 8) * mode->VDisplay) > 
	(pScrn->videoRam * 1024)) ||
        (mode->HDisplay > 8191) ||
	(mode->VDisplay > 8191) ) {

       xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
	        "Skipped \"%s\" (%dx%d), not enough video RAM or beyond hardware specs\n",
		mode->name, mode->HDisplay, mode->VDisplay);
       xfree(mode->Private);
       xfree(mode);

       return dest;
    }

    if(srel != radeonClone) {
       info->AtLeastOneNonClone = TRUE;
    }

    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
    	"Merged \"%s\" (%dx%d) and \"%s\" (%dx%d) to %dx%d%s\n",
	i->name, i->HDisplay, i->VDisplay, j->name, j->HDisplay, j->VDisplay,
	mode->HDisplay, mode->VDisplay, (srel == radeonClone) ? " (Clone)" : "");

    mode->next = mode;
    mode->prev = mode;

    if(dest) {
        mode->next = dest->next; 	/* Insert node after "dest" */
        dest->next->prev = mode;
        mode->prev = dest;
        dest->next = mode;
    }

    return mode;
}

/* Helper function to find a mode from a given name
 * (Taken from mga, sis drivers)
 */
static DisplayModePtr
RADEONGetModeFromName(char* str, DisplayModePtr i)
{
    DisplayModePtr c = i;
    if(!i) return NULL;
    do {
       if(strcmp(str, c->name) == 0) return c;
       c = c->next;
    } while(c != i);
    return NULL;
}

static DisplayModePtr
RADEONFindWidestTallestMode(DisplayModePtr i, Bool tallest)
{
    DisplayModePtr c = i, d = NULL;
    int max = 0;
    if(!i) return NULL;
    do {
       if(tallest) {
          if(c->VDisplay > max) {
             max = c->VDisplay;
	     d = c;
          }
       } else {
          if(c->HDisplay > max) {
             max = c->HDisplay;
	     d = c;
          }
       }
       c = c->next;
    } while(c != i);
    return d;
}

static void
RADEONFindWidestTallestCommonMode(DisplayModePtr i, DisplayModePtr j, Bool tallest,
				DisplayModePtr *a, DisplayModePtr *b)
{
    DisplayModePtr c = i, d;
    int max = 0;
    Bool foundone;

    (*a) = (*b) = NULL;

    if(!i || !j) return;

    do {
       d = j;
       foundone = FALSE;
       do {
	  if( (c->HDisplay == d->HDisplay) &&
	      (c->VDisplay == d->VDisplay) ) {
	     foundone = TRUE;
	     break;
	  }
	  d = d->next;
       } while(d != j);
       if(foundone) {
	  if(tallest) {
	     if(c->VDisplay > max) {
		max = c->VDisplay;
		(*a) = c;
		(*b) = d;
	     }
	  } else {
	     if(c->HDisplay > max) {
		max = c->HDisplay;
		(*a) = c;
		(*b) = d;
	     }
	  }
       }
       c = c->next;
    } while(c != i);
}

static DisplayModePtr
RADEONGenerateModeListFromLargestModes(ScrnInfoPtr pScrn,
		    DisplayModePtr i, DisplayModePtr j,
		    RADEONScrn2Rel srel)
{

    RADEONInfoPtr  info       = RADEONPTR(pScrn);
    DisplayModePtr mode1 = NULL;
    DisplayModePtr mode2 = NULL;
    DisplayModePtr mode3 = NULL;
    DisplayModePtr mode4 = NULL;
    DisplayModePtr result = NULL;

    info->AtLeastOneNonClone = FALSE;

    /* Now build a default list of MetaModes.
     * - Non-clone: If the user enabled NonRectangular, we use the
     * largest mode for each CRT1 and CRT2. If not, we use the largest
     * common mode for CRT1 and CRT2 (if available). Additionally, and
     * regardless if the above, we produce a clone mode consisting of
     * the largest common mode (if available) in order to use DGA.
     * - Clone: If the (global) CRT2Position is Clone, we use the
     * largest common mode if available, otherwise the first two modes
     * in each list.
     */

    switch(srel) {
    case radeonLeftOf:
    case radeonRightOf:
       mode1 = RADEONFindWidestTallestMode(i, FALSE);
       mode2 = RADEONFindWidestTallestMode(j, FALSE);
       RADEONFindWidestTallestCommonMode(i, j, FALSE, &mode3, &mode4);
       break;
    case radeonAbove:
    case radeonBelow:
       mode1 = RADEONFindWidestTallestMode(i, TRUE);
       mode2 = RADEONFindWidestTallestMode(j, TRUE);
       RADEONFindWidestTallestCommonMode(i, j, TRUE, &mode3, &mode4);
       break;
    case radeonClone:
       RADEONFindWidestTallestCommonMode(i, j, FALSE, &mode3, &mode4);
       if(mode3 && mode4) {
	  mode1 = mode3;
	  mode2 = mode4;
       } else {
	  mode1 = i;
	  mode2 = j;
       }
    }

    if(srel != radeonClone) {
       if(mode3 && mode4 && !info->NonRect) {
	  mode1 = mode3;
	  mode2 = mode2;
       }
    }

    if(mode1 && mode2) {
       result = RADEONCopyModeNLink(pScrn, result, mode1, mode2, srel);
    }

    if(srel != radeonClone) {
       if(mode3 && mode4) {
	  result = RADEONCopyModeNLink(pScrn, result, mode3, mode4, radeonClone);
       }
    }
    return result;
}

/* Generate the merged-fb mode modelist
 * (Taken from mga, sis drivers)
 */
static DisplayModePtr
RADEONGenerateModeListFromMetaModes(ScrnInfoPtr pScrn, char* str,
		    DisplayModePtr i, DisplayModePtr j,
		    RADEONScrn2Rel srel)
{
    char* strmode = str;
    char modename[256];
    Bool gotdash = FALSE;
    char gotsep = 0;
    RADEONScrn2Rel sr;
    DisplayModePtr mode1 = NULL;
    DisplayModePtr mode2 = NULL;
    DisplayModePtr result = NULL;
    int myslen;
    RADEONInfoPtr  info       = RADEONPTR(pScrn);

    info->AtLeastOneNonClone = FALSE;

    do {
        switch(*str) {
        case 0:
        case '-':
	case '+':
        case ' ':
	case ',':
	case ';':
           if(strmode != str) {

              myslen = str - strmode;
              if(myslen > 255) myslen = 255;
  	      strncpy(modename, strmode, myslen);
  	      modename[myslen] = 0;

              if(gotdash) {
                 if(mode1 == NULL) {
  	             xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
  	                        "Error parsing MetaModes parameter\n");
  	             return NULL;
  	         }
                 mode2 = RADEONGetModeFromName(modename, j);
                 if(!mode2) {
                    xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
                        "Mode \"%s\" is not a supported mode for CRT2\n", modename);
                    xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
                        "\t(Skipping metamode \"%s%c%s\")\n", mode1->name, gotsep, modename);
                    mode1 = NULL;
		    gotsep = 0;
                 }
              } else {
                 mode1 = RADEONGetModeFromName(modename, i);
                 if(!mode1) {
                    char* tmps = str;
                    xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
                        "Mode \"%s\" is not a supported mode for CRT1\n", modename);
                    while(*tmps == ' ' || *tmps == ';') tmps++;
                    /* skip the next mode */
  	            if(*tmps == '-' || *tmps == '+' || *tmps == ',') {
                       tmps++;
		       /* skip spaces */
		       while(*tmps == ' ' || *tmps == ';') tmps++;
		       /* skip modename */
		       while(*tmps && *tmps != ' ' && *tmps != ';' && *tmps != '-' && *tmps != '+' && *tmps != ',') tmps++;
  	               myslen = tmps - strmode;
  	               if(myslen > 255) myslen = 255;
  	               strncpy(modename,strmode,myslen);
  	               modename[myslen] = 0;
                       str = tmps-1;
                    }
                    xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
                        "\t(Skipping metamode \"%s\")\n", modename);
                    mode1 = NULL;
		    gotsep = 0;
                 }
              }
              gotdash = FALSE;
           }
           strmode = str + 1;
           gotdash |= (*str == '-' || *str == '+' || *str == ',');
	   if (*str == '-' || *str == '+' || *str == ',')
  	      gotsep = *str;

           if(*str != 0) break;
	   /* Fall through otherwise */

        default:
           if(!gotdash && mode1) {
              sr = srel;
	      if(gotsep == '+') sr = radeonClone;
              if(!mode2) {
                 mode2 = RADEONGetModeFromName(mode1->name, j);
                 sr = radeonClone;
              }
              if(!mode2) {
                 xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
                     "Mode \"%s\" is not a supported mode for CRT2\n", mode1->name);
                 xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
                     "\t(Skipping metamode \"%s\")\n", modename);
                 mode1 = NULL;
              } else {
                 result = RADEONCopyModeNLink(pScrn, result, mode1, mode2, sr);
                 mode1 = NULL;
                 mode2 = NULL;
              }
	      gotsep = 0;
           }
           break;

        }

    } while(*(str++) != 0);
     
    return result;
}

DisplayModePtr
RADEONGenerateModeList(ScrnInfoPtr pScrn, char* str,
		    DisplayModePtr i, DisplayModePtr j,
		    RADEONScrn2Rel srel)
{
    RADEONInfoPtr  info   = RADEONPTR(pScrn);

   if(str != NULL) {
      return(RADEONGenerateModeListFromMetaModes(pScrn, str, i, j, srel));
   } else {
        if (srel == radeonClone ) {
           DisplayModePtr p, q, result = NULL;

           xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                "Clone mode, list all common modes\n");
           for (p = i; p->next != i; p = p->next)
                for (q = j; q->next != j; q = q->next)
                   if ((p->HDisplay == q->HDisplay) &&
                        (p->VDisplay == q->VDisplay))
                        result = RADEONCopyModeNLink(pScrn, result, p, q, srel);
           return result;
        } else {
           xf86DrvMsg(pScrn->scrnIndex, X_INFO,
                "No MetaModes given, linking %s modes by default\n",
                (info->NonRect ?
		(((srel == radeonLeftOf) || (srel == radeonRightOf)) ? "widest" :  "tallest")
		:
		(((srel == radeonLeftOf) || (srel == radeonRightOf)) ? "widest common" :  "tallest common")) );
           return(RADEONGenerateModeListFromLargestModes(pScrn, i, j, srel));
        }
    }
}

void
RADEONRecalcDefaultVirtualSize(ScrnInfoPtr pScrn)
{
    RADEONInfoPtr  info   = RADEONPTR(pScrn);
    DisplayModePtr mode, bmode;
    int maxh, maxv;
    static const char *str = "MergedFB: Virtual %s %d\n";
    static const char *errstr = "Virtual %s to small for given CRT2Position offset\n";

    mode = bmode = pScrn->modes;
    maxh = maxv = 0;
    do {
        if(mode->HDisplay > maxh) maxh = mode->HDisplay;
	if(mode->VDisplay > maxv) maxv = mode->VDisplay;
        mode = mode->next;
    } while(mode != bmode);
    maxh += info->CRT1XOffs + info->CRT2XOffs;
    maxv += info->CRT1YOffs + info->CRT2YOffs;

    if(!(pScrn->display->virtualX)) {
	if(maxh > 8191) {
  	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
  	               "Virtual width with CRT2Position offset beyond hardware specs\n");
  	               info->CRT1XOffs = info->CRT2XOffs = 0;
  	               maxh -= (info->CRT1XOffs + info->CRT2XOffs);
  	}
  	pScrn->virtualX = maxh;
  	pScrn->displayWidth = maxh;
  	xf86DrvMsg(pScrn->scrnIndex, X_PROBED, str, "width", maxh);
    } else {
  	if(maxh < pScrn->display->virtualX) {
  	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR, errstr, "width");
  	    info->CRT1XOffs = info->CRT2XOffs = 0;
	}
    }

    if(!(pScrn->display->virtualY)) {
        pScrn->virtualY = maxv;
	xf86DrvMsg(pScrn->scrnIndex, X_PROBED, str, "height", maxv);
    } else {
	if(maxv < pScrn->display->virtualY) {
	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR, errstr, "height");
	    info->CRT1YOffs = info->CRT2YOffs = 0;
	}
    }
}

/* Pseudo-Xinerama extension for MergedFB mode */
void
RADEONUpdateXineramaScreenInfo(ScrnInfoPtr pScrn1)
{
    RADEONInfoPtr  info   = RADEONPTR(pScrn1);
    ScrnInfoPtr pScrn2 = NULL;
    int crt1scrnnum = 0, crt2scrnnum = 1;
    int x1=0, x2=0, y1=0, y2=0, h1=0, h2=0, w1=0, w2=0;
    int realvirtX, realvirtY;
    DisplayModePtr currentMode, firstMode;
    Bool infochanged = FALSE;
    Bool usenonrect = info->NonRect;
    const char *rectxine = "\t... setting up rectangular Xinerama layout\n";

    info->MBXNR1XMAX = info->MBXNR1YMAX = info->MBXNR2XMAX = info->MBXNR2YMAX = 65536;
    info->HaveNonRect = info->HaveOffsRegions = FALSE;

    if(!info->MergedFB) return;

    if(RADEONnoPanoramiXExtension) return;

    if(!RADEONXineramadataPtr) return;

    if(info->CRT2IsScrn0) {
       crt1scrnnum = 1;
       crt2scrnnum = 0;
    }

    pScrn2 = info->CRT2pScrn;

    /* Attention: Usage of RandR may lead into virtual X and Y values
     * actually smaller than our MetaModes! To avoid this, we calculate
     * the maxCRT fields here (and not somewhere else, like in CopyNLink)
     */

     /* "Real" virtual: Virtual without the Offset */
    realvirtX = pScrn1->virtualX - info->CRT1XOffs - info->CRT2XOffs;
    realvirtY = pScrn1->virtualY - info->CRT1YOffs - info->CRT2YOffs;

    if((info->RADEONXineramaVX != pScrn1->virtualX) || (info->RADEONXineramaVY != pScrn1->virtualY)) {

      if(!(pScrn1->modes)) {
          xf86DrvMsg(pScrn1->scrnIndex, X_ERROR,
       	     "Internal error: RADEONUpdateXineramaScreenInfo(): pScrn->modes is NULL\n");
	  return;
       }

       info->maxCRT1_X1 = info->maxCRT1_X2 = 0;
       info->maxCRT1_Y1 = info->maxCRT1_Y2 = 0;
       info->maxCRT2_X1 = info->maxCRT2_X2 = 0;
       info->maxCRT2_Y1 = info->maxCRT2_Y2 = 0;
       info->maxClone_X1 = info->maxClone_X2 = 0;
       info->maxClone_Y1 = info->maxClone_Y2 = 0;

       currentMode = firstMode = pScrn1->modes;

       do {

          DisplayModePtr p = currentMode->next;
          DisplayModePtr i = ((RADEONMergedDisplayModePtr)currentMode->Private)->CRT1;
          DisplayModePtr j = ((RADEONMergedDisplayModePtr)currentMode->Private)->CRT2;
          RADEONScrn2Rel srel = ((RADEONMergedDisplayModePtr)currentMode->Private)->CRT2Position;

          if((currentMode->HDisplay <= realvirtX) && (currentMode->VDisplay <= realvirtY) &&
	     (i->HDisplay <= realvirtX) && (j->HDisplay <= realvirtX) &&
	     (i->VDisplay <= realvirtY) && (j->VDisplay <= realvirtY)) {

	     if(srel != radeonClone) {
		if(info->maxCRT1_X1 == i->HDisplay) {
		   if(info->maxCRT1_X2 < j->HDisplay) {
		      info->maxCRT1_X2 = j->HDisplay;   /* Widest CRT2 mode displayed with widest CRT1 mode */
		   }
		} else if(info->maxCRT1_X1 < i->HDisplay) {
		   info->maxCRT1_X1 = i->HDisplay;      /* Widest CRT1 mode */
		   info->maxCRT1_X2 = j->HDisplay;
		}
		if(info->maxCRT2_X2 == j->HDisplay) {
		   if(info->maxCRT2_X1 < i->HDisplay) {
		      info->maxCRT2_X1 = i->HDisplay;   /* Widest CRT1 mode displayed with widest CRT2 mode */
		   }
		} else if(info->maxCRT2_X2 < j->HDisplay) {
		   info->maxCRT2_X2 = j->HDisplay;      /* Widest CRT2 mode */
		   info->maxCRT2_X1 = i->HDisplay;
		}
		if(info->maxCRT1_Y1 == i->VDisplay) {   /* Same as above, but tallest instead of widest */
		   if(info->maxCRT1_Y2 < j->VDisplay) {
		      info->maxCRT1_Y2 = j->VDisplay;
		   }
		} else if(info->maxCRT1_Y1 < i->VDisplay) {
		   info->maxCRT1_Y1 = i->VDisplay;
		   info->maxCRT1_Y2 = j->VDisplay;
		}
		if(info->maxCRT2_Y2 == j->VDisplay) {
		   if(info->maxCRT2_Y1 < i->VDisplay) {
		      info->maxCRT2_Y1 = i->VDisplay;
		   }
		} else if(info->maxCRT2_Y2 < j->VDisplay) {
		   info->maxCRT2_Y2 = j->VDisplay;
		   info->maxCRT2_Y1 = i->VDisplay;
		}
	     } else {
		if(info->maxClone_X1 < i->HDisplay) {
		   info->maxClone_X1 = i->HDisplay;
		}
		if(info->maxClone_X2 < j->HDisplay) {
		   info->maxClone_X2 = j->HDisplay;
		}
		if(info->maxClone_Y1 < i->VDisplay) {
		   info->maxClone_Y1 = i->VDisplay;
		}
		if(info->maxClone_Y2 < j->VDisplay) {
		   info->maxClone_Y2 = j->VDisplay;
		}
	     }
	  }
	  currentMode = p;

       } while((currentMode) && (currentMode != firstMode));

       info->RADEONXineramaVX = pScrn1->virtualX;
       info->RADEONXineramaVY = pScrn1->virtualY;
       infochanged = TRUE;

    }

    if((usenonrect) && (info->CRT2Position != radeonClone) && info->maxCRT1_X1) {
     switch(info->CRT2Position) {
       case radeonLeftOf:
       case radeonRightOf:
	  if((info->maxCRT1_Y1 != realvirtY) && (info->maxCRT2_Y2 != realvirtY)) {
	     usenonrect = FALSE;
	  }
	  break;
       case radeonAbove:
       case radeonBelow:
	  if((info->maxCRT1_X1 != realvirtX) && (info->maxCRT2_X2 != realvirtX)) {
	     usenonrect = FALSE;
	  }
	  break;
       case radeonClone:
	  break;
       }

       if(infochanged && !usenonrect) {
	  xf86DrvMsg(pScrn1->scrnIndex, X_INFO,
			"Virtual screen size does not match maximum display modes...\n");
	  xf86DrvMsg(pScrn1->scrnIndex, X_INFO, rectxine);

       }
    } else if(infochanged && usenonrect) {
       usenonrect = FALSE;
       xf86DrvMsg(pScrn1->scrnIndex, X_INFO,
		"Only clone modes available for this virtual screen size...\n");
       xf86DrvMsg(pScrn1->scrnIndex, X_INFO, rectxine);
    }

    if(info->maxCRT1_X1) {		/* Means we have at least one non-clone mode */
       switch(info->CRT2Position) {
       case radeonLeftOf:
	  x1 = min(info->maxCRT1_X2, pScrn1->virtualX - info->maxCRT1_X1);
	  if(x1 < 0) x1 = 0;
	  y1 = info->CRT1YOffs;
	  w1 = pScrn1->virtualX - x1;
	  h1 = realvirtY;
	  if((usenonrect) && (info->maxCRT1_Y1 != realvirtY)) {
	     h1 = info->MBXNR1YMAX = info->maxCRT1_Y1;
	     info->NonRectDead.x0 = x1;
	     info->NonRectDead.x1 = x1 + w1 - 1;
	     info->NonRectDead.y0 = y1 + h1;
	     info->NonRectDead.y1 = pScrn1->virtualY - 1;
	     info->HaveNonRect = TRUE;
	  }
	  x2 = 0;
	  y2 = info->CRT2YOffs;
	  w2 = max(info->maxCRT2_X2, pScrn1->virtualX - info->maxCRT2_X1);
	  if(w2 > pScrn1->virtualX) w2 = pScrn1->virtualX;
	  h2 = realvirtY;
	  if((usenonrect) && (info->maxCRT2_Y2 != realvirtY)) {
	     h2 = info->MBXNR2YMAX = info->maxCRT2_Y2;
	     info->NonRectDead.x0 = x2;
	     info->NonRectDead.x1 = x2 + w2 - 1;
	     info->NonRectDead.y0 = y2 + h2;
	     info->NonRectDead.y1 = pScrn1->virtualY - 1;
	     info->HaveNonRect = TRUE;
	  }
	  break;
       case radeonRightOf:
	  x1 = 0;
	  y1 = info->CRT1YOffs;
	  w1 = max(info->maxCRT1_X1, pScrn1->virtualX - info->maxCRT1_X2);
	  if(w1 > pScrn1->virtualX) w1 = pScrn1->virtualX;
	  h1 = realvirtY;
	  if((usenonrect) && (info->maxCRT1_Y1 != realvirtY)) {
	     h1 = info->MBXNR1YMAX = info->maxCRT1_Y1;
	     info->NonRectDead.x0 = x1;
	     info->NonRectDead.x1 = x1 + w1 - 1;
	     info->NonRectDead.y0 = y1 + h1;
	     info->NonRectDead.y1 = pScrn1->virtualY - 1;
	     info->HaveNonRect = TRUE;
	  }
	  x2 = min(info->maxCRT2_X1, pScrn1->virtualX - info->maxCRT2_X2);
	  if(x2 < 0) x2 = 0;
	  y2 = info->CRT2YOffs;
	  w2 = pScrn1->virtualX - x2;
	  h2 = realvirtY;
	  if((usenonrect) && (info->maxCRT2_Y2 != realvirtY)) {
	     h2 = info->MBXNR2YMAX = info->maxCRT2_Y2;
	     info->NonRectDead.x0 = x2;
	     info->NonRectDead.x1 = x2 + w2 - 1;
	     info->NonRectDead.y0 = y2 + h2;
	     info->NonRectDead.y1 = pScrn1->virtualY - 1;
	     info->HaveNonRect = TRUE;
	  }
	  break;
       case radeonAbove:
	  x1 = info->CRT1XOffs;
	  y1 = min(info->maxCRT1_Y2, pScrn1->virtualY - info->maxCRT1_Y1);
	  if(y1 < 0) y1 = 0;
	  w1 = realvirtX;
	  h1 = pScrn1->virtualY - y1;
	  if((usenonrect) && (info->maxCRT1_X1 != realvirtX)) {
	     w1 = info->MBXNR1XMAX = info->maxCRT1_X1;
	     info->NonRectDead.x0 = x1 + w1;
	     info->NonRectDead.x1 = pScrn1->virtualX - 1;
	     info->NonRectDead.y0 = y1;
	     info->NonRectDead.y1 = y1 + h1 - 1;
	     info->HaveNonRect = TRUE;
	  }
	  x2 = info->CRT2XOffs;
	  y2 = 0;
	  w2 = realvirtX;
	  h2 = max(info->maxCRT2_Y2, pScrn1->virtualY - info->maxCRT2_Y1);
	  if(h2 > pScrn1->virtualY) h2 = pScrn1->virtualY;
	  if((usenonrect) && (info->maxCRT2_X2 != realvirtX)) {
	     w2 = info->MBXNR2XMAX = info->maxCRT2_X2;
	     info->NonRectDead.x0 = x2 + w2;
	     info->NonRectDead.x1 = pScrn1->virtualX - 1;
	     info->NonRectDead.y0 = y2;
	     info->NonRectDead.y1 = y2 + h2 - 1;
	     info->HaveNonRect = TRUE;
	  }
	  break;
       case radeonBelow:
	  x1 = info->CRT1XOffs;
	  y1 = 0;
	  w1 = realvirtX;
	  h1 = max(info->maxCRT1_Y1, pScrn1->virtualY - info->maxCRT1_Y2);
	  if(h1 > pScrn1->virtualY) h1 = pScrn1->virtualY;
	  if((usenonrect) && (info->maxCRT1_X1 != realvirtX)) {
	     w1 = info->MBXNR1XMAX = info->maxCRT1_X1;
	     info->NonRectDead.x0 = x1 + w1;
	     info->NonRectDead.x1 = pScrn1->virtualX - 1;
	     info->NonRectDead.y0 = y1;
	     info->NonRectDead.y1 = y1 + h1 - 1;
	     info->HaveNonRect = TRUE;
	  }
	  x2 = info->CRT2XOffs;
	  y2 = min(info->maxCRT2_Y1, pScrn1->virtualY - info->maxCRT2_Y2);
	  if(y2 < 0) y2 = 0;
	  w2 = realvirtX;
	  h2 = pScrn1->virtualY - y2;
	  if((usenonrect) && (info->maxCRT2_X2 != realvirtX)) {
	     w2 = info->MBXNR2XMAX = info->maxCRT2_X2;
	     info->NonRectDead.x0 = x2 + w2;
	     info->NonRectDead.x1 = pScrn1->virtualX - 1;
	     info->NonRectDead.y0 = y2;
	     info->NonRectDead.y1 = y2 + h2 - 1;
	     info->HaveNonRect = TRUE;
	  }
       default:
	  break;
       }

       switch(info->CRT2Position) {
       case radeonLeftOf:
       case radeonRightOf:
	  if(info->CRT1YOffs) {
	     info->OffDead1.x0 = x1;
	     info->OffDead1.x1 = x1 + w1 - 1;
	     info->OffDead1.y0 = 0;
	     info->OffDead1.y1 = y1 - 1;
	     info->OffDead2.x0 = x2;
	     info->OffDead2.x1 = x2 + w2 - 1;
	     info->OffDead2.y0 = y2 + h2;
	     info->OffDead2.y1 = pScrn1->virtualY - 1;
	     info->HaveOffsRegions = TRUE;
	  } else if(info->CRT2YOffs) {
	     info->OffDead1.x0 = x2;
	     info->OffDead1.x1 = x2 + w2 - 1;
	     info->OffDead1.y0 = 0;
	     info->OffDead1.y1 = y2 - 1;
	     info->OffDead2.x0 = x1;
	     info->OffDead2.x1 = x1 + w1 - 1;
	     info->OffDead2.y0 = y1 + h1;
	     info->OffDead2.y1 = pScrn1->virtualY - 1;
	     info->HaveOffsRegions = TRUE;
	  }
	  break;
       case radeonAbove:
       case radeonBelow:
	  if(info->CRT1XOffs) {
	     info->OffDead1.x0 = x2 + w2;
	     info->OffDead1.x1 = pScrn1->virtualX - 1;
	     info->OffDead1.y0 = y2;
	     info->OffDead1.y1 = y2 + h2 - 1;
	     info->OffDead2.x0 = 0;
	     info->OffDead2.x1 = x1 - 1;
	     info->OffDead2.y0 = y1;
	     info->OffDead2.y1 = y1 + h1 - 1;
	     info->HaveOffsRegions = TRUE;
	  } else if(info->CRT2XOffs) {
	     info->OffDead1.x0 = x1 + w1;
	     info->OffDead1.x1 = pScrn1->virtualX - 1;
	     info->OffDead1.y0 = y1;
	     info->OffDead1.y1 = y1 + h1 - 1;
	     info->OffDead2.x0 = 0;
	     info->OffDead2.x1 = x2 - 1;
	     info->OffDead2.y0 = y2;
	     info->OffDead2.y1 = y2 + h2 - 1;
	     info->HaveOffsRegions = TRUE;
	  }
       default:
	  break;
       }

    } else {	/* Only clone-modes left */

       x1 = x2 = 0;
       y1 = y2 = 0;
       w1 = w2 = max(info->maxClone_X1, info->maxClone_X2);
       h1 = h2 = max(info->maxClone_Y1, info->maxClone_Y2);

    }

    RADEONXineramadataPtr[crt1scrnnum].x = x1;
    RADEONXineramadataPtr[crt1scrnnum].y = y1;
    RADEONXineramadataPtr[crt1scrnnum].width = w1;
    RADEONXineramadataPtr[crt1scrnnum].height = h1;
    RADEONXineramadataPtr[crt2scrnnum].x = x2;
    RADEONXineramadataPtr[crt2scrnnum].y = y2;
    RADEONXineramadataPtr[crt2scrnnum].width = w2;
    RADEONXineramadataPtr[crt2scrnnum].height = h2;

    if(infochanged) {
       xf86DrvMsg(pScrn1->scrnIndex, X_INFO,
	  "Pseudo-Xinerama: CRT1 (Screen %d) (%d,%d)-(%d,%d)\n",
	  crt1scrnnum, x1, y1, w1+x1-1, h1+y1-1);
       xf86DrvMsg(pScrn1->scrnIndex, X_INFO,
	  "Pseudo-Xinerama: CRT2 (Screen %d) (%d,%d)-(%d,%d)\n",
	  crt2scrnnum, x2, y2, w2+x2-1, h2+y2-1);
       if(info->HaveNonRect) {
	  xf86DrvMsg(pScrn1->scrnIndex, X_INFO,
		"Pseudo-Xinerama: Inaccessible area (%d,%d)-(%d,%d)\n",
		info->NonRectDead.x0, info->NonRectDead.y0,
		info->NonRectDead.x1, info->NonRectDead.y1);
       }
       if(info->HaveOffsRegions) {
	  xf86DrvMsg(pScrn1->scrnIndex, X_INFO,
		"Pseudo-Xinerama: Inaccessible offset area (%d,%d)-(%d,%d)\n",
		info->OffDead1.x0, info->OffDead1.y0,
		info->OffDead1.x1, info->OffDead1.y1);
	  xf86DrvMsg(pScrn1->scrnIndex, X_INFO,
		"Pseudo-Xinerama: Inaccessible offset area (%d,%d)-(%d,%d)\n",
		info->OffDead2.x0, info->OffDead2.y0,
		info->OffDead2.x1, info->OffDead2.y1);
       }
       if(info->HaveNonRect || info->HaveOffsRegions) {
	  xf86DrvMsg(pScrn1->scrnIndex, X_INFO,
		"Mouse restriction for inaccessible areas is %s\n",
		info->MouseRestrictions ? "enabled" : "disabled");
       }
    }
}
/* Proc */

int
RADEONProcXineramaQueryVersion(ClientPtr client)
{
    xPanoramiXQueryVersionReply	  rep;
    register int		  n;

    REQUEST_SIZE_MATCH(xPanoramiXQueryVersionReq);
    rep.type = X_Reply;
    rep.length = 0;
    rep.sequenceNumber = client->sequence;
    rep.majorVersion = RADEON_XINERAMA_MAJOR_VERSION;
    rep.minorVersion = RADEON_XINERAMA_MINOR_VERSION;
    if(client->swapped) {
        swaps(&rep.sequenceNumber, n);
        swapl(&rep.length, n);
        swaps(&rep.majorVersion, n);
        swaps(&rep.minorVersion, n);
    }
    WriteToClient(client, sizeof(xPanoramiXQueryVersionReply), (char *)&rep);
    return (client->noClientException);
}

int
RADEONProcXineramaGetState(ClientPtr client)
{
    REQUEST(xPanoramiXGetStateReq);
    WindowPtr			pWin;
    xPanoramiXGetStateReply	rep;
    register int		n;

    REQUEST_SIZE_MATCH(xPanoramiXGetStateReq);
    pWin = LookupWindow(stuff->window, client);
    if(!pWin) return BadWindow;

    rep.type = X_Reply;
    rep.length = 0;
    rep.sequenceNumber = client->sequence;
    rep.state = !RADEONnoPanoramiXExtension;
    if(client->swapped) {
       swaps (&rep.sequenceNumber, n);
       swapl (&rep.length, n);
       swaps (&rep.state, n);
    }
    WriteToClient(client, sizeof(xPanoramiXGetStateReply), (char *)&rep);
    return client->noClientException;
}

int
RADEONProcXineramaGetScreenCount(ClientPtr client)
{
    REQUEST(xPanoramiXGetScreenCountReq);
    WindowPtr				pWin;
    xPanoramiXGetScreenCountReply	rep;
    register int			n;

    REQUEST_SIZE_MATCH(xPanoramiXGetScreenCountReq);
    pWin = LookupWindow(stuff->window, client);
    if(!pWin) return BadWindow;

    rep.type = X_Reply;
    rep.length = 0;
    rep.sequenceNumber = client->sequence;
    rep.ScreenCount = RADEONXineramaNumScreens;
    if(client->swapped) {
       swaps(&rep.sequenceNumber, n);
       swapl(&rep.length, n);
       swaps(&rep.ScreenCount, n);
    }
    WriteToClient(client, sizeof(xPanoramiXGetScreenCountReply), (char *)&rep);
    return client->noClientException;
}

int
RADEONProcXineramaGetScreenSize(ClientPtr client)
{
    REQUEST(xPanoramiXGetScreenSizeReq);
    WindowPtr				pWin;
    xPanoramiXGetScreenSizeReply	rep;
    register int			n;

    REQUEST_SIZE_MATCH(xPanoramiXGetScreenSizeReq);
    pWin = LookupWindow (stuff->window, client);
    if(!pWin)  return BadWindow;

    rep.type = X_Reply;
    rep.length = 0;
    rep.sequenceNumber = client->sequence;
    rep.width  = RADEONXineramadataPtr[stuff->screen].width;
    rep.height = RADEONXineramadataPtr[stuff->screen].height;
    if(client->swapped) {
       swaps(&rep.sequenceNumber, n);
       swapl(&rep.length, n);
       swaps(&rep.width, n);
       swaps(&rep.height, n);
    }
    WriteToClient(client, sizeof(xPanoramiXGetScreenSizeReply), (char *)&rep);
    return client->noClientException;
}

int
RADEONProcXineramaIsActive(ClientPtr client)
{
    xXineramaIsActiveReply	rep;

    REQUEST_SIZE_MATCH(xXineramaIsActiveReq);

    rep.type = X_Reply;
    rep.length = 0;
    rep.sequenceNumber = client->sequence;
    rep.state = !RADEONnoPanoramiXExtension;
    if(client->swapped) {
	register int n;
	swaps(&rep.sequenceNumber, n);
	swapl(&rep.length, n);
	swapl(&rep.state, n);
    }
    WriteToClient(client, sizeof(xXineramaIsActiveReply), (char *) &rep);
    return client->noClientException;
}

int
RADEONProcXineramaQueryScreens(ClientPtr client)
{
    xXineramaQueryScreensReply	rep;

    REQUEST_SIZE_MATCH(xXineramaQueryScreensReq);

    rep.type = X_Reply;
    rep.sequenceNumber = client->sequence;
    rep.number = (RADEONnoPanoramiXExtension) ? 0 : RADEONXineramaNumScreens;
    rep.length = rep.number * sz_XineramaScreenInfo >> 2;
    if(client->swapped) {
       register int n;
       swaps(&rep.sequenceNumber, n);
       swapl(&rep.length, n);
       swapl(&rep.number, n);
    }
    WriteToClient(client, sizeof(xXineramaQueryScreensReply), (char *)&rep);

    if(!RADEONnoPanoramiXExtension) {
       xXineramaScreenInfo scratch;
       int i;

       for(i = 0; i < RADEONXineramaNumScreens; i++) {
	  scratch.x_org  = RADEONXineramadataPtr[i].x;
	  scratch.y_org  = RADEONXineramadataPtr[i].y;
	  scratch.width  = RADEONXineramadataPtr[i].width;
	  scratch.height = RADEONXineramadataPtr[i].height;
	  if(client->swapped) {
	     register int n;
	     swaps(&scratch.x_org, n);
	     swaps(&scratch.y_org, n);
	     swaps(&scratch.width, n);
    	     swaps(&scratch.height, n);
	  }
	  WriteToClient(client, sz_XineramaScreenInfo, (char *)&scratch);
       }
    }

    return client->noClientException;
}

static int
RADEONProcXineramaDispatch(ClientPtr client)
{
    REQUEST(xReq);
    switch (stuff->data)
    {
	case X_PanoramiXQueryVersion:
	     return RADEONProcXineramaQueryVersion(client);
	case X_PanoramiXGetState:
	     return RADEONProcXineramaGetState(client);
	case X_PanoramiXGetScreenCount:
	     return RADEONProcXineramaGetScreenCount(client);
	case X_PanoramiXGetScreenSize:
	     return RADEONProcXineramaGetScreenSize(client);
	case X_XineramaIsActive:
	     return RADEONProcXineramaIsActive(client);
	case X_XineramaQueryScreens:
	     return RADEONProcXineramaQueryScreens(client);
    }
    return BadRequest;
}

/* SProc */

static int
RADEONSProcXineramaQueryVersion (ClientPtr client)
{
	REQUEST(xPanoramiXQueryVersionReq);
	register int n;
	swaps(&stuff->length,n);
	REQUEST_SIZE_MATCH (xPanoramiXQueryVersionReq);
	return RADEONProcXineramaQueryVersion(client);
}

static int
RADEONSProcXineramaGetState(ClientPtr client)
{
	REQUEST(xPanoramiXGetStateReq);
	register int n;
 	swaps (&stuff->length, n);
	REQUEST_SIZE_MATCH(xPanoramiXGetStateReq);
	return RADEONProcXineramaGetState(client);
}

static int
RADEONSProcXineramaGetScreenCount(ClientPtr client)
{
	REQUEST(xPanoramiXGetScreenCountReq);
	register int n;
	swaps (&stuff->length, n);
	REQUEST_SIZE_MATCH(xPanoramiXGetScreenCountReq);
	return RADEONProcXineramaGetScreenCount(client);
}

static int
RADEONSProcXineramaGetScreenSize(ClientPtr client)
{
	REQUEST(xPanoramiXGetScreenSizeReq);
	register int n;
	swaps (&stuff->length, n);
	REQUEST_SIZE_MATCH(xPanoramiXGetScreenSizeReq);
	return RADEONProcXineramaGetScreenSize(client);
}

static int
RADEONSProcXineramaIsActive(ClientPtr client)
{
	REQUEST(xXineramaIsActiveReq);
	register int n;
	swaps (&stuff->length, n);
	REQUEST_SIZE_MATCH(xXineramaIsActiveReq);
	return RADEONProcXineramaIsActive(client);
}

static int
RADEONSProcXineramaQueryScreens(ClientPtr client)
{
	REQUEST(xXineramaQueryScreensReq);
	register int n;
	swaps (&stuff->length, n);
	REQUEST_SIZE_MATCH(xXineramaQueryScreensReq);
	return RADEONProcXineramaQueryScreens(client);
}

int
RADEONSProcXineramaDispatch(ClientPtr client)
{
    REQUEST(xReq);
    switch (stuff->data) {
	case X_PanoramiXQueryVersion:
	     return RADEONSProcXineramaQueryVersion(client);
	case X_PanoramiXGetState:
	     return RADEONSProcXineramaGetState(client);
	case X_PanoramiXGetScreenCount:
	     return RADEONSProcXineramaGetScreenCount(client);
	case X_PanoramiXGetScreenSize:
	     return RADEONSProcXineramaGetScreenSize(client);
	case X_XineramaIsActive:
	     return RADEONSProcXineramaIsActive(client);
	case X_XineramaQueryScreens:
	     return RADEONSProcXineramaQueryScreens(client);
    }
    return BadRequest;
}

static void
RADEONXineramaResetProc(ExtensionEntry* extEntry)
{
    if(RADEONXineramadataPtr) {
       Xfree(RADEONXineramadataPtr);
       RADEONXineramadataPtr = NULL;
    }
}

void
RADEONXineramaExtensionInit(ScrnInfoPtr pScrn)
{
    RADEONInfoPtr info = RADEONPTR(pScrn);
    Bool	success = FALSE;

    if(!(RADEONXineramadataPtr)) {

       if(!info->MergedFB) {
          RADEONnoPanoramiXExtension = TRUE;
	  info->MouseRestrictions = FALSE;
          return;
       }

#ifdef PANORAMIX
       if(!noPanoramiXExtension) {
          xf86DrvMsg(pScrn->scrnIndex, X_INFO,
       	     "Xinerama active, not initializing Radeon Pseudo-Xinerama\n");
          RADEONnoPanoramiXExtension = TRUE;
	  info->MouseRestrictions = FALSE;
          return;
       }
#endif

       if(RADEONnoPanoramiXExtension) {
          xf86DrvMsg(pScrn->scrnIndex, X_INFO,
       	      "Radeon Pseudo-Xinerama disabled\n");
	  info->MouseRestrictions = FALSE;
          return;
       }

       if(info->CRT2Position == radeonClone) {
          xf86DrvMsg(pScrn->scrnIndex, X_INFO,
       	     "Running MergedFB in Clone mode, Radeon Pseudo-Xinerama disabled\n");
          RADEONnoPanoramiXExtension = TRUE;
	  info->MouseRestrictions = FALSE;
          return;
       }

       if(!(info->AtLeastOneNonClone)) {
          xf86DrvMsg(pScrn->scrnIndex, X_INFO,
       	     "Only Clone modes defined, Radeon Pseudo-Xinerama disabled\n");
          RADEONnoPanoramiXExtension = TRUE;
	  info->MouseRestrictions = FALSE;
          return;
       }

       RADEONXineramaNumScreens = 2;

       while(RADEONXineramaGeneration != serverGeneration) {

	  info->XineramaExtEntry = AddExtension(PANORAMIX_PROTOCOL_NAME, 0,0,
					RADEONProcXineramaDispatch,
					RADEONSProcXineramaDispatch,
					RADEONXineramaResetProc,
					StandardMinorOpcode);

	  if(!info->XineramaExtEntry) break;

	  RADEONXineramaReqCode = (unsigned char)info->XineramaExtEntry->base;

	  if(!(RADEONXineramadataPtr = (RADEONXineramaData *)
	        xcalloc(RADEONXineramaNumScreens, sizeof(RADEONXineramaData)))) break;

	  RADEONXineramaGeneration = serverGeneration;
	  success = TRUE;
       }

       if(!success) {
          xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
	    	"Failed to initialize Radeon Pseudo-Xinerama extension\n");
          RADEONnoPanoramiXExtension = TRUE;
	  info->MouseRestrictions = FALSE;
          return;
       }

       xf86DrvMsg(pScrn->scrnIndex, X_INFO,
    	  "Initialized Radeon Pseudo-Xinerama extension\n");

       info->RADEONXineramaVX = 0;
       info->RADEONXineramaVY = 0;

    }

    RADEONUpdateXineramaScreenInfo(pScrn);

}
/* End of PseudoXinerama */

static Bool
InRegion(int x, int y, region r)
{
    return (r.x0 <= x) && (x <= r.x1) && (r.y0 <= y) && (y <= r.y1);
}

void
RADEONMergePointerMoved(int scrnIndex, int x, int y)
{
  ScrnInfoPtr   pScrn1 = xf86Screens[scrnIndex];
  RADEONInfoPtr info = RADEONPTR(pScrn1);
  ScrnInfoPtr   pScrn2 = info->CRT2pScrn;
  region 	out, in1, in2, f2, f1;
  int 		deltax, deltay;
  int           temp1, temp2;
  int           old1x0, old1y0, old2x0, old2y0;
  int		CRT1XOffs = 0, CRT1YOffs = 0, CRT2XOffs = 0, CRT2YOffs = 0;
  int		HVirt = pScrn1->virtualX;
  int		VVirt = pScrn1->virtualY;
  int		sigstate;
  Bool		doit = FALSE, HaveNonRect = FALSE, HaveOffsRegions = FALSE;
  RADEONScrn2Rel   srel = ((RADEONMergedDisplayModePtr)info->CurrentLayout.mode->Private)->CRT2Position;

  if(info->DGAactive) {
     return;
     /* DGA: There is no cursor and no panning while DGA is active. */
     /* If it were, we would need to do: */
     /* HVirt = info->CurrentLayout.displayWidth;
        VVirt = info->CurrentLayout.displayHeight;
        BOUND(x, info->CurrentLayout.DGAViewportX, HVirt);
        BOUND(y, info->CurrentLayout.DGAViewportY, VVirt); */
  } else {
     CRT1XOffs = info->CRT1XOffs;
     CRT1YOffs = info->CRT1YOffs;
     CRT2XOffs = info->CRT2XOffs;
     CRT2YOffs = info->CRT2YOffs;
     HaveNonRect = info->HaveNonRect;
     HaveOffsRegions = info->HaveOffsRegions;
  }

  /* Check if the pointer is inside our dead areas */
  if((info->MouseRestrictions) && (srel != radeonClone) && !RADEONnoPanoramiXExtension) {
     if(HaveNonRect) {
	if(InRegion(x, y, info->NonRectDead)) {
	   switch(srel) {
	   case radeonLeftOf:
	   case radeonRightOf: y = info->NonRectDead.y0 - 1;
			    doit = TRUE;
			    break;
	   case radeonAbove:
	   case radeonBelow:   x = info->NonRectDead.x0 - 1;
			    doit = TRUE;
	   default:	    break;
	   }
	}
     }
     if(HaveOffsRegions) {
	if(InRegion(x, y, info->OffDead1)) {
	   switch(srel) {
	   case radeonLeftOf:
	   case radeonRightOf: y = info->OffDead1.y1;
			    doit = TRUE;
			    break;
	   case radeonAbove:
	   case radeonBelow:   x = info->OffDead1.x1;
			    doit = TRUE;
	   default:	    break;
	   }
	} else if(InRegion(x, y, info->OffDead2)) {
	   switch(srel) {
	   case radeonLeftOf:
	   case radeonRightOf: y = info->OffDead2.y0 - 1;
			    doit = TRUE;
			    break;
	   case radeonAbove:
	   case radeonBelow:   x = info->OffDead2.x0 - 1;
			    doit = TRUE;
	   default:	    break;
	   }
	}
     }
     if(doit) {
	UpdateCurrentTime();
	sigstate = xf86BlockSIGIO();
	miPointerAbsoluteCursor(x, y, currentTime.milliseconds);
	xf86UnblockSIGIO(sigstate);
	return;
     }
  }

  f1.x0 = old1x0 = info->CRT1frameX0;
  f1.x1 = info->CRT1frameX1;
  f1.y0 = old1y0 = info->CRT1frameY0;
  f1.y1 = info->CRT1frameY1;
  f2.x0 = old2x0 = pScrn2->frameX0;
  f2.x1 = pScrn2->frameX1;
  f2.y0 = old2y0 = pScrn2->frameY0;
  f2.y1 = pScrn2->frameY1;

  /* Define the outer region. Crossing this causes all frames to move */
  out.x0 = pScrn1->frameX0;
  out.x1 = pScrn1->frameX1;
  out.y0 = pScrn1->frameY0;
  out.y1 = pScrn1->frameY1;

  /*
   * Define the inner sliding window. Being outsize both frames but
   * inside the outer clipping window will slide corresponding frame
   */
  in1 = out;
  in2 = out;
  switch(srel) {
     case radeonLeftOf:
        in1.x0 = f1.x0;
        in2.x1 = f2.x1;
        break;
     case radeonRightOf:
        in1.x1 = f1.x1;
        in2.x0 = f2.x0;
        break;
     case radeonBelow:
        in1.y1 = f1.y1;
        in2.y0 = f2.y0;
        break;
     case radeonAbove:
        in1.y0 = f1.y0;
        in2.y1 = f2.y1;
        break;
     case radeonClone:
        break;
  }

  deltay = 0;
  deltax = 0;

  if(InRegion(x, y, out)) {	/* inside outer region */

     /* xf86DrvMsg(0, X_INFO, "1: %d %d | %d %d %d %d | %d %d %d %d\n",
     	x, y, in1.x0, in1.x1, in1.y0, in1.y1, f1.x0, f1.x1, f1.y0, f1.y1); */

     if(InRegion(x, y, in1) && !InRegion(x, y, f1)) {
        REBOUND(f1.x0, f1.x1, x);
        REBOUND(f1.y0, f1.y1, y);
        deltax = 1;
	/* xf86DrvMsg(0, X_INFO, "2: %d %d | %d %d %d %d | %d %d %d %d\n",
     		x, y, in1.x0, in1.x1, in1.y0, in1.y1, f1.x0, f1.x1, f1.y0, f1.y1); */
     }
     if(InRegion(x, y, in2) && !InRegion(x, y, f2)) {
        REBOUND(f2.x0, f2.x1, x);
        REBOUND(f2.y0, f2.y1, y);
        deltax = 1;
     }

  } else {			/* outside outer region */

     /* xf86DrvMsg(0, X_INFO, "3: %d %d | %d %d %d %d | %d %d %d %d\n",
     	x, y, in1.x0, in1.x1, in1.y0, in1.y1, f1.x0, f1.x1, f1.y0, f1.y1);
     xf86DrvMsg(0, X_INFO, "3-out: %d %d %d %d\n",
     	out.x0, out.x1, out.y0, out.y1); */

     if(out.x0 > x) {
        deltax = x - out.x0;
     }
     if(out.x1 < x) {
        deltax = x - out.x1;
     }
     if(deltax) {
        pScrn1->frameX0 += deltax;
        pScrn1->frameX1 += deltax;
	f1.x0 += deltax;
        f1.x1 += deltax;
        f2.x0 += deltax;
        f2.x1 += deltax;
     }

     if(out.y0 > y) {
        deltay = y - out.y0;
     }
     if(out.y1 < y) {
        deltay = y - out.y1;
     }
     if(deltay) {
        pScrn1->frameY0 += deltay;
        pScrn1->frameY1 += deltay;
	f1.y0 += deltay;
        f1.y1 += deltay;
        f2.y0 += deltay;
        f2.y1 += deltay;
     }

     switch(srel) {
        case radeonLeftOf:
	   if(x >= f1.x0) { REBOUND(f1.y0, f1.y1, y); }
	   if(x <= f2.x1) { REBOUND(f2.y0, f2.y1, y); }
           break;
        case radeonRightOf:
	   if(x <= f1.x1) { REBOUND(f1.y0, f1.y1, y); }
	   if(x >= f2.x0) { REBOUND(f2.y0, f2.y1, y); }
           break;
        case radeonBelow:
	   if(y <= f1.y1) { REBOUND(f1.x0, f1.x1, x); }
	   if(y >= f2.y0) { REBOUND(f2.x0, f2.x1, x); }
           break;
        case radeonAbove:
	   if(y >= f1.y0) { REBOUND(f1.x0, f1.x1, x); }
	   if(y <= f2.y1) { REBOUND(f2.x0, f2.x1, x); }
           break;
        case radeonClone:
           break;
     }

  }

  if(deltax || deltay) {
     info->CRT1frameX0 = f1.x0;
     info->CRT1frameY0 = f1.y0;
     pScrn2->frameX0 = f2.x0;
     pScrn2->frameY0 = f2.y0;

     switch(((RADEONMergedDisplayModePtr)info->CurrentLayout.mode->Private)->CRT2Position) {
	case radeonLeftOf:
	case radeonRightOf:
	    if(info->CRT1YOffs || info->CRT2YOffs || HaveNonRect) {
		if(info->CRT1frameY0 != old1y0) {
		    if(info->CRT1frameY0 < info->CRT1YOffs)
  	                info->CRT1frameY0 = info->CRT1YOffs;
  	            temp1 = info->CRT1frameY0 + CDMPTR->CRT1->VDisplay;
  	            /*temp2 = pScrn1->virtualY - info->CRT2YOffs;*/
		    temp2 = min((VVirt - CRT2YOffs), (CRT1YOffs + info->MBXNR1YMAX));
  	            if(temp1 > temp2)
  	                info->CRT1frameY0 -= (temp1 - temp2);
  	        }
  	        if(pScrn2->frameY0 != old2y0) {
  	            if(pScrn2->frameY0 < info->CRT2YOffs)
  	                pScrn2->frameY0 = info->CRT2YOffs;
  	            temp1 = pScrn2->frameY0   + CDMPTR->CRT2->VDisplay;
  	            /*temp2 = pScrn1->virtualY - info->CRT1YOffs;*/
		    temp2 = min((VVirt - CRT1YOffs), (CRT2YOffs + info->MBXNR2YMAX));
  	            if(temp1 > temp2)
  	                pScrn2->frameY0 -= (temp1 - temp2);
  	        }
  	    }
	    break;
	case radeonBelow:
	case radeonAbove:
	    if(info->CRT1XOffs || info->CRT2XOffs || HaveNonRect) {
		if(info->CRT1frameX0 != old1x0) {
		    if(info->CRT1frameX0 < info->CRT1XOffs)
			info->CRT1frameX0 = info->CRT1XOffs;
		    temp1 = info->CRT1frameX0 + CDMPTR->CRT1->HDisplay;
  	            /*temp2 = pScrn1->virtualX - info->CRT2XOffs;*/
                    temp2 = min((HVirt - CRT2XOffs), (CRT1XOffs + info->MBXNR1XMAX));
  	            if(temp1 > temp2)
  	            	info->CRT1frameX0 -= (temp1 - temp2);
  	        }
  	        if(pScrn2->frameX0 != old2x0) {
  	            if(pScrn2->frameX0 < info->CRT2XOffs)
  	       		pScrn2->frameX0 = info->CRT2XOffs;
  	            temp1 = pScrn2->frameX0   + CDMPTR->CRT2->HDisplay;
  	            /*temp2 = pScrn1->virtualX - info->CRT1XOffs;*/
		    temp2 = min((HVirt - CRT1XOffs), (CRT2XOffs + info->MBXNR2XMAX));
  	            if(temp1 > temp2)
  	            	pScrn2->frameX0 -= (temp1 - temp2);
  	        }
  	    }
  	    break;
  	    case radeonClone:
  	    	break;
     }

     info->CRT1frameX1 = info->CRT1frameX0 + CDMPTR->CRT1->HDisplay - 1;
     info->CRT1frameY1 = info->CRT1frameY0 + CDMPTR->CRT1->VDisplay - 1;
     pScrn2->frameX1   = pScrn2->frameX0   + CDMPTR->CRT2->HDisplay - 1;
     pScrn2->frameY1   = pScrn2->frameY0   + CDMPTR->CRT2->VDisplay - 1;
#if 0
     pScrn1->frameX1   = pScrn1->frameX0   + info->CurrentLayout.mode->HDisplay  - 1;
     pScrn1->frameY1   = pScrn1->frameY0   + info->CurrentLayout.mode->VDisplay  - 1;
#endif

     RADEONDoAdjustFrame(pScrn1, info->CRT1frameX0, info->CRT1frameY0, FALSE);
     RADEONDoAdjustFrame(pScrn1, pScrn2->frameX0, pScrn2->frameY0, TRUE);
  }
}

static void
RADEONAdjustFrameMergedHelper(int scrnIndex, int x, int y, int flags)
{
    ScrnInfoPtr pScrn1 = xf86Screens[scrnIndex];
    RADEONInfoPtr info = RADEONPTR(pScrn1);
    ScrnInfoPtr pScrn2 = info->CRT2pScrn;
    int VTotal = info->CurrentLayout.mode->VDisplay;
    int HTotal = info->CurrentLayout.mode->HDisplay;
    int VMax = VTotal;
    int HMax = HTotal;
    int HVirt = pScrn1->virtualX;
    int VVirt = pScrn1->virtualY;
    int x1 = x, x2 = x;
    int y1 = y, y2 = y;
    int CRT1XOffs = 0, CRT1YOffs = 0, CRT2XOffs = 0, CRT2YOffs = 0;
    int MBXNR1XMAX = 65536, MBXNR1YMAX = 65536, MBXNR2XMAX = 65536, MBXNR2YMAX = 65536;

    if(info->DGAactive) {
       HVirt = info->CurrentLayout.displayWidth;
       VVirt = info->CurrentLayout.displayHeight;
    } else {
       CRT1XOffs = info->CRT1XOffs;
       CRT1YOffs = info->CRT1YOffs;
       CRT2XOffs = info->CRT2XOffs;
       CRT2YOffs = info->CRT2YOffs;
       MBXNR1XMAX = info->MBXNR1XMAX;
       MBXNR1YMAX = info->MBXNR1YMAX;
       MBXNR2XMAX = info->MBXNR2XMAX;
       MBXNR2YMAX = info->MBXNR2YMAX;
    }


    BOUND(x, 0, pScrn1->virtualX - HTotal);
    BOUND(y, 0, pScrn1->virtualY - VTotal);
    if(SDMPTR(pScrn1)->CRT2Position != radeonClone) {
#if 0
	BOUND(x1, info->CRT1XOffs, pScrn1->virtualX - HTotal - info->CRT2XOffs);
	BOUND(y1, info->CRT1YOffs, pScrn1->virtualY - VTotal - info->CRT2YOffs);
	BOUND(x2, info->CRT2XOffs, pScrn1->virtualX - HTotal - info->CRT1XOffs);
	BOUND(y2, info->CRT2YOffs, pScrn1->virtualY - VTotal - info->CRT1YOffs);
#endif
       BOUND(x1, CRT1XOffs, min(HVirt, MBXNR1XMAX + CRT1XOffs) - min(HTotal, MBXNR1XMAX) - CRT2XOffs);
       BOUND(y1, CRT1YOffs, min(VVirt, MBXNR1YMAX + CRT1YOffs) - min(VTotal, MBXNR1YMAX) - CRT2YOffs);
       BOUND(x2, CRT2XOffs, min(HVirt, MBXNR2XMAX + CRT2XOffs) - min(HTotal, MBXNR2XMAX) - CRT1XOffs);
       BOUND(y2, CRT2YOffs, min(VVirt, MBXNR2YMAX + CRT2YOffs) - min(VTotal, MBXNR2YMAX) - CRT1YOffs);
    }

    switch(SDMPTR(pScrn1)->CRT2Position) {
        case radeonLeftOf:
            pScrn2->frameX0 = x2;
            /*BOUND(pScrn2->frameY0,   y2, y2 + VMax - CDMPTR->CRT2->VDisplay);*/
            BOUND(pScrn2->frameY0,   y2, y2 + min(VMax, MBXNR2YMAX) - CDMPTR->CRT2->VDisplay);
            info->CRT1frameX0 = x1 + CDMPTR->CRT2->HDisplay;
            /*BOUND(info->CRT1frameY0, y1, y1 + VMax - CDMPTR->CRT1->VDisplay);*/
            BOUND(info->CRT1frameY0, y1, y1 + min(VMax, MBXNR1YMAX) - CDMPTR->CRT1->VDisplay);
            break;
        case radeonRightOf:
            info->CRT1frameX0 = x1;
            /*BOUND(info->CRT1frameY0, y1, y1 + VMax - CDMPTR->CRT1->VDisplay);*/
            BOUND(info->CRT1frameY0, y1, y1 + min(VMax, MBXNR1YMAX) - CDMPTR->CRT1->VDisplay);
            pScrn2->frameX0 = x2 + CDMPTR->CRT1->HDisplay;
            /*BOUND(pScrn2->frameY0,   y2, y2 + VMax - CDMPTR->CRT2->VDisplay);*/
            BOUND(pScrn2->frameY0,   y2, y2 + min(VMax, MBXNR2YMAX) - CDMPTR->CRT2->VDisplay);
            break;
        case radeonAbove:
            /*BOUND(pScrn2->frameX0,   x2, x2 + HMax - CDMPTR->CRT2->HDisplay);*/
            BOUND(pScrn2->frameX0,   x2, x2 + min(HMax, MBXNR2XMAX) - CDMPTR->CRT2->HDisplay);
            pScrn2->frameY0 = y2;
            /*BOUND(info->CRT1frameX0, x1, x1  + HMax - CDMPTR->CRT1->HDisplay);*/
            BOUND(info->CRT1frameX0, x1, x1 + min(HMax, MBXNR1XMAX) - CDMPTR->CRT1->HDisplay);
            info->CRT1frameY0 = y1 + CDMPTR->CRT2->VDisplay;
            break;
        case radeonBelow:
            /*BOUND(info->CRT1frameX0, x1, x1 + HMax - CDMPTR->CRT1->HDisplay);*/
            BOUND(info->CRT1frameX0, x1, x1 + min(HMax, MBXNR1XMAX) - CDMPTR->CRT1->HDisplay);
            info->CRT1frameY0 = y1;
            /*BOUND(pScrn2->frameX0,   x2, x2 + HMax - CDMPTR->CRT2->HDisplay);*/
	    BOUND(pScrn2->frameX0,   x2, x2 + min(HMax, MBXNR2XMAX) - CDMPTR->CRT2->HDisplay);
            pScrn2->frameY0 = y2 + CDMPTR->CRT1->VDisplay;
            break;
        case radeonClone:
            BOUND(info->CRT1frameX0, x,  x + HMax - CDMPTR->CRT1->HDisplay);
            BOUND(info->CRT1frameY0, y,  y + VMax - CDMPTR->CRT1->VDisplay);
            BOUND(pScrn2->frameX0,   x,  x + HMax - CDMPTR->CRT2->HDisplay);
            BOUND(pScrn2->frameY0,   y,  y + VMax - CDMPTR->CRT2->VDisplay);
            break;
    }

    BOUND(info->CRT1frameX0, 0, pScrn1->virtualX - CDMPTR->CRT1->HDisplay);
    BOUND(info->CRT1frameY0, 0, pScrn1->virtualY - CDMPTR->CRT1->VDisplay);
    BOUND(pScrn2->frameX0,   0, pScrn1->virtualX - CDMPTR->CRT2->HDisplay);
    BOUND(pScrn2->frameY0,   0, pScrn1->virtualY - CDMPTR->CRT2->VDisplay);
    
    pScrn1->frameX0 = x;
    pScrn1->frameY0 = y;

    info->CRT1frameX1 = info->CRT1frameX0 + CDMPTR->CRT1->HDisplay - 1;
    info->CRT1frameY1 = info->CRT1frameY0 + CDMPTR->CRT1->VDisplay - 1;
    pScrn2->frameX1   = pScrn2->frameX0   + CDMPTR->CRT2->HDisplay - 1;
    pScrn2->frameY1   = pScrn2->frameY0   + CDMPTR->CRT2->VDisplay - 1;
    pScrn1->frameX1   = pScrn1->frameX0   + info->CurrentLayout.mode->HDisplay  - 1;
    pScrn1->frameY1   = pScrn1->frameY0   + info->CurrentLayout.mode->VDisplay  - 1;

    if(SDMPTR(pScrn1)->CRT2Position != radeonClone) {
       pScrn1->frameX1 += CRT1XOffs + CRT2XOffs;
       pScrn1->frameY1 += CRT1YOffs + CRT2YOffs;
    }

/*
    RADEONDoAdjustFrame(pScrn1, info->CRT1frameX0, info->CRT1frameY0, FALSE);
    RADEONDoAdjustFrame(pScrn1, pScrn2->frameX0, pScrn2->frameY0, TRUE);
*/
}

void
RADEONAdjustFrameMerged(int scrnIndex, int x, int y, int flags)
{
    ScrnInfoPtr pScrn1 = xf86Screens[scrnIndex];
    RADEONInfoPtr info = RADEONPTR(pScrn1);
    ScrnInfoPtr pScrn2 = info->CRT2pScrn;

    RADEONAdjustFrameMergedHelper(scrnIndex, x, y, flags);
    RADEONDoAdjustFrame(pScrn1, info->CRT1frameX0, info->CRT1frameY0, FALSE);
    RADEONDoAdjustFrame(pScrn1, pScrn2->frameX0, pScrn2->frameY0, TRUE);
}

static void
RADEONMergedFBCalcDPI(ScrnInfoPtr pScrn1, ScrnInfoPtr pScrn2, RADEONScrn2Rel srel, Bool quiet)
{
    RADEONInfoPtr info = RADEONPTR(pScrn1);
    MessageType from = X_DEFAULT;
    xf86MonPtr DDC1 = (xf86MonPtr)(pScrn1->monitor->DDC);
    xf86MonPtr DDC2 = (xf86MonPtr)(pScrn2->monitor->DDC);
    int ddcWidthmm = 0, ddcHeightmm = 0;
    const char *dsstr = "MergedFB: Display dimensions: %dx%d mm\n";

    /* This sets the DPI for MergedFB mode. The problem is that
     * this can never be exact, because the output devices may
     * have different dimensions. This function tries to compromise
     * through a few assumptions, and it just calculates an average
     * DPI value for both monitors.
     */

    /* Copy user-given DisplaySize (which should regard BOTH monitors!) */
    pScrn1->widthmm = pScrn1->monitor->widthmm;
    pScrn1->heightmm = pScrn1->monitor->heightmm;

    if(monitorResolution > 0) {

       /* Set command line given values (overrules given options) */
       pScrn1->xDpi = monitorResolution;
       pScrn1->yDpi = monitorResolution;
       from = X_CMDLINE;

    } else if(info->MergedFBXDPI) {

       /* Set option-wise given values (overrules DisplaySize config option) */
       pScrn1->xDpi = info->MergedFBXDPI;
       pScrn1->yDpi = info->MergedFBYDPI;
       from = X_CONFIG;

    } else if(pScrn1->widthmm > 0 || pScrn1->heightmm > 0) {

       /* Set values calculated from given DisplaySize */
       from = X_CONFIG;
       if(pScrn1->widthmm > 0) {
	  pScrn1->xDpi = (int)((double)pScrn1->virtualX * 25.4 / pScrn1->widthmm);
       }
       if(pScrn1->heightmm > 0) {
	  pScrn1->yDpi = (int)((double)pScrn1->virtualY * 25.4 / pScrn1->heightmm);
       }
       if(!quiet) {
          xf86DrvMsg(pScrn1->scrnIndex, from, dsstr, pScrn1->widthmm, pScrn1->heightmm);
       }

    } else if(ddcWidthmm && ddcHeightmm) {

       /* Set values from DDC-provided display size */

       /* Get DDC display size; if only either CRT1 or CRT2 provided these,
	* assume equal dimensions for both, otherwise add dimensions
	*/
       if( (DDC1 && (DDC1->features.hsize > 0 && DDC1->features.vsize > 0)) &&
	   (DDC2 && (DDC2->features.hsize > 0 && DDC2->features.vsize > 0)) ) {
	  ddcWidthmm = max(DDC1->features.hsize, DDC2->features.hsize) * 10;
	  ddcHeightmm = max(DDC1->features.vsize, DDC2->features.vsize) * 10;
	  switch(srel) {
	  case radeonLeftOf:
	  case radeonRightOf:
	     ddcWidthmm = (DDC1->features.hsize + DDC2->features.hsize) * 10;
	     break;
	  case radeonAbove:
	  case radeonBelow:
	     ddcHeightmm = (DDC1->features.vsize + DDC2->features.vsize) * 10;
	  default:
	     break;
	  }
       } else if(DDC1 && (DDC1->features.hsize > 0 && DDC1->features.vsize > 0)) {
	  ddcWidthmm = DDC1->features.hsize * 10;
	  ddcHeightmm = DDC1->features.vsize * 10;
	  switch(srel) {
	  case radeonLeftOf:
	  case radeonRightOf:
	     ddcWidthmm *= 2;
	     break;
	  case radeonAbove:
	  case radeonBelow:
	     ddcHeightmm *= 2;
	  default:
	     break;
          }
       } else if(DDC2 && (DDC2->features.hsize > 0 && DDC2->features.vsize > 0) ) {
	  ddcWidthmm = DDC2->features.hsize * 10;
	  ddcHeightmm = DDC2->features.vsize * 10;
	  switch(srel) {
	  case radeonLeftOf:
	  case radeonRightOf:
	     ddcWidthmm *= 2;
	     break;
	  case radeonAbove:
	  case radeonBelow:
	     ddcHeightmm *= 2;
	  default:
	     break;
	  }
       }

       from = X_PROBED;

       if(!quiet) {
          xf86DrvMsg(pScrn1->scrnIndex, from, dsstr, ddcWidthmm, ddcHeightmm);
       }

       pScrn1->widthmm = ddcWidthmm;
       pScrn1->heightmm = ddcHeightmm;
       if(pScrn1->widthmm > 0) {
	  pScrn1->xDpi = (int)((double)pScrn1->virtualX * 25.4 / pScrn1->widthmm);
       }
       if(pScrn1->heightmm > 0) {
	  pScrn1->yDpi = (int)((double)pScrn1->virtualY * 25.4 / pScrn1->heightmm);
       }

    } else {

       pScrn1->xDpi = pScrn1->yDpi = DEFAULT_DPI;

    }

    /* Sanity check */
    if(pScrn1->xDpi > 0 && pScrn1->yDpi <= 0)
       pScrn1->yDpi = pScrn1->xDpi;
    if(pScrn1->yDpi > 0 && pScrn1->xDpi <= 0)
       pScrn1->xDpi = pScrn1->yDpi;

    pScrn2->xDpi = pScrn1->xDpi;
    pScrn2->yDpi = pScrn1->yDpi;

    if(!quiet) {
       xf86DrvMsg(pScrn1->scrnIndex, from, "MergedFB: DPI set to (%d, %d)\n",
		pScrn1->xDpi, pScrn1->yDpi);
    }
}


void
RADEONMergedFBSetDpi(ScrnInfoPtr pScrn1, ScrnInfoPtr pScrn2, RADEONScrn2Rel srel)
{
    RADEONInfoPtr      info       = RADEONPTR(pScrn1);

    RADEONMergedFBCalcDPI(pScrn1, pScrn2, srel, FALSE);

    info->MergedDPISRel = srel;
    info->RADEONMergedDPIVX = pScrn1->virtualX;
    info->RADEONMergedDPIVY = pScrn1->virtualY;

}

void
RADEONMergedFBResetDpi(ScrnInfoPtr pScrn, Bool force)
{
    RADEONInfoPtr info = RADEONPTR(pScrn);
    ScreenPtr pScreen = screenInfo.screens[pScrn->scrnIndex];
    RADEONScrn2Rel srel = ((RADEONMergedDisplayModePtr)info->CurrentLayout.mode->Private)->CRT2Position;

    /* This does the same calculation for the DPI as
     * the initial run. This means that an eventually
     * given -dpi command line switch will lead to
     * constant dpi values, regardless of the virtual
     * screen size.
     * I consider this consequent. If this is undesired,
     * one should use the DisplaySize parameter in the
     * config file instead of the command line switch.
     * The DPI will be calculated then.
     */

    if(force						||
       (info->MergedDPISRel != srel)			||
       (info->RADEONMergedDPIVX != pScrn->virtualX)	||
       (info->RADEONMergedDPIVY != pScrn->virtualY)
						) {

       RADEONMergedFBCalcDPI(pScrn, info->CRT2pScrn, srel, TRUE);

       pScreen->mmWidth = (pScrn->virtualX * 254 + pScrn->xDpi * 5) / (pScrn->xDpi * 10);
       pScreen->mmHeight = (pScrn->virtualY * 254 + pScrn->yDpi * 5) / (pScrn->yDpi * 10);

       info->MergedDPISRel = srel;
       info->RADEONMergedDPIVX = pScrn->virtualX;
       info->RADEONMergedDPIVY = pScrn->virtualY;

    }
}

/* radeon cursor helpers */
static void
RADEONChooseCursorCRTC(ScrnInfoPtr pScrn1, int x, int y)
{
    RADEONInfoPtr      info       = RADEONPTR(pScrn1);
    unsigned char     *RADEONMMIO = info->MMIO;
    RADEONScrn2Rel srel = 
        ((RADEONMergedDisplayModePtr)info->CurrentLayout.mode->Private)->CRT2Position;
    ScrnInfoPtr pScrn2 = info->CRT2pScrn;
    
    if (srel == radeonClone) {
	/* show cursor 2 */
	OUTREGP(RADEON_CRTC2_GEN_CNTL, RADEON_CRTC2_CUR_EN,
		~RADEON_CRTC2_CUR_EN);
	/* show cursor 1 */
	OUTREGP(RADEON_CRTC_GEN_CNTL, RADEON_CRTC_CUR_EN,
		~RADEON_CRTC_CUR_EN);
    }
    else {
	if (((x >= pScrn1->frameX0) && (x <= pScrn1->frameX1)) &&
       	    ((y >= pScrn1->frameY0) && (y <= pScrn1->frameY1))) {
		/* hide cursor 2 */
		OUTREGP(RADEON_CRTC2_GEN_CNTL, 0, ~RADEON_CRTC2_CUR_EN);
		/* show cursor 1 */
		OUTREGP(RADEON_CRTC_GEN_CNTL, RADEON_CRTC_CUR_EN,
			~RADEON_CRTC_CUR_EN);
	}
	if (((x >= pScrn2->frameX0) && (x <= pScrn2->frameX1)) &&
       	    ((y >= pScrn2->frameY0) && (y <= pScrn2->frameY1))) {
		/* hide cursor 1 */
		OUTREGP(RADEON_CRTC_GEN_CNTL, 0, ~RADEON_CRTC_CUR_EN);
		/* show cursor 2 */
		OUTREGP(RADEON_CRTC2_GEN_CNTL, RADEON_CRTC2_CUR_EN,
			~RADEON_CRTC2_CUR_EN);
	}
    }
}

void
RADEONSetCursorPositionMerged(ScrnInfoPtr pScrn, int x, int y)
{
    RADEONInfoPtr      info       = RADEONPTR(pScrn);
    unsigned char     *RADEONMMIO = info->MMIO;
    xf86CursorInfoPtr  cursor     = info->cursor;
    int                xorigin    = 0;
    int                yorigin    = 0;
    int		       stride     = 256;
    ScrnInfoPtr    pScrn2 = info->CRT2pScrn;
    DisplayModePtr mode1 = CDMPTR->CRT1;
    DisplayModePtr mode2 = CDMPTR->CRT2;
    int            x1, y1, x2, y2;
    int                total_y1    = pScrn->frameY1 - pScrn->frameY0;
    int                total_y2    = pScrn2->frameY1 - pScrn2->frameY0;

    if (x < 0)                        xorigin = -x+1;
    if (y < 0)                        yorigin = -y+1;
    /* if (y > total_y)                  y       = total_y; */
    if (xorigin >= cursor->MaxWidth)  xorigin = cursor->MaxWidth - 1;
    if (yorigin >= cursor->MaxHeight) yorigin = cursor->MaxHeight - 1;

    x += pScrn->frameX0;
    y += pScrn->frameY0;

    x1 = x - info->CRT1frameX0;
    y1 = y - info->CRT1frameY0;

    x2 = x - pScrn2->frameX0;
    y2 = y - pScrn2->frameY0;

    if (y1 > total_y1)
	y1       = total_y1;
    if (y2 > total_y2)                  
	y2       = total_y2;

    if(mode1->Flags & V_INTERLACE)
	y1 /= 2;
    else if(mode1->Flags & V_DBLSCAN)
	y1 *= 2;

    if(mode2->Flags & V_INTERLACE)
	y2 /= 2;
    else if(mode2->Flags & V_DBLSCAN)
	y2 *= 2;

    if (x < 0)
	x = 0;
    if (y < 0)
	y = 0;

    RADEONChooseCursorCRTC(pScrn, x, y);

		/* cursor1 */
    OUTREG(RADEON_CUR_HORZ_VERT_OFF,  (RADEON_CUR_LOCK
				   | (xorigin << 16)
				   | yorigin));
    OUTREG(RADEON_CUR_HORZ_VERT_POSN, (RADEON_CUR_LOCK
				   | ((xorigin ? 0 : x1) << 16)
				   | (yorigin ? 0 : y1)));
    OUTREG(RADEON_CUR_OFFSET, info->cursor_offset + yorigin * stride);
		/* cursor2 */
    OUTREG(RADEON_CUR2_HORZ_VERT_OFF,  (RADEON_CUR2_LOCK
				    | (xorigin << 16)
				    | yorigin));
    OUTREG(RADEON_CUR2_HORZ_VERT_POSN, (RADEON_CUR2_LOCK
				    | ((xorigin ? 0 : x2) << 16)
				    | (yorigin ? 0 : y2)));
    OUTREG(RADEON_CUR2_OFFSET, info->cursor_offset + yorigin * stride);
}

/* radeon Xv helpers */

/* choose the crtc for the overlay for mergedfb based on the location
   of the output window and the orientation of the crtcs */

void
RADEONChooseOverlayCRTC(
    ScrnInfoPtr pScrn,
    BoxPtr dstBox
) {
    RADEONInfoPtr info = RADEONPTR(pScrn);
    RADEONScrn2Rel srel = 
        ((RADEONMergedDisplayModePtr)info->CurrentLayout.mode->Private)->CRT2Position;

    if (srel == radeonLeftOf) {
        if (dstBox->x1 >= info->CRT2pScrn->frameX1)
            info->OverlayOnCRTC2 = FALSE;
        else
            info->OverlayOnCRTC2 = TRUE;
    }
    if (srel == radeonRightOf) {
        if (dstBox->x2 <= info->CRT2pScrn->frameX0)
            info->OverlayOnCRTC2 = FALSE;
        else
            info->OverlayOnCRTC2 = TRUE;
    }
    if (srel == radeonAbove) {
        if (dstBox->y1 >= info->CRT2pScrn->frameY1)
            info->OverlayOnCRTC2 = FALSE;
        else
            info->OverlayOnCRTC2 = TRUE;
    }
    if (srel == radeonBelow) {
	if (dstBox->y2 <= info->CRT2pScrn->frameY0)
            info->OverlayOnCRTC2 = FALSE;
        else
            info->OverlayOnCRTC2 = TRUE;
    }
}