mga_merge.c revision 4a32b415
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
4
5/* All drivers should typically include these */
6#include "xorg-server.h"
7#include "xf86.h"
8#include "xf86_OSproc.h"
9
10/* All drivers need this */
11
12#include "compiler.h"
13
14#include "mga.h"
15#include "mga_macros.h"
16#include "mga_reg.h"
17#include "mga_merge.h"
18
19#include "fbdevhw.h"
20
21static int
22StrToRanges(range* r, const char* s) {
23    float num=0.0;
24    int rangenum=0;
25    Bool gotdash = FALSE;
26    Bool nextdash = FALSE;
27    const char* strnum=NULL;
28    do {
29        switch(*s) {
30            case '0': case '1': case '2': case '3': case '4': case '5':
31            case '6': case '7': case '8': case '9': case '.':
32                if(strnum == NULL) {
33                    strnum = s;
34                    gotdash = nextdash;
35                    nextdash = FALSE;
36                 }
37
38                break;
39            case '-':
40            case ' ': case 0:
41                if(strnum == NULL) break; /*is extra seperator */
42                if(strnum != NULL) sscanf(strnum,"%f",&num);
43                if(gotdash) /*if wasn't singlet: correct. */
44                    r[rangenum-1].hi = num;
45                else { /*first, assume singlet */
46                    r[rangenum].lo = num;
47                    r[rangenum].hi = num;
48                    rangenum++;
49                }
50                strnum = NULL;
51                if(*s == '-')
52                    nextdash = (rangenum != 0); /*ignore dash if before any number.*/
53                break;
54            default :
55                return 0;
56        }
57    } while(*(s++) != 0); /* run loop for every char including null terminator.*/
58
59    return rangenum;
60}
61
62
63/* Copys mode i, links the result to dest, and returns it.
64 * Links i and j in Private record.
65 * if dest is NULL, return value is copy of i linked to itself.
66 */
67static DisplayModePtr
68CopyModeNLink(ScrnInfoPtr pScrn, DisplayModePtr dest, DisplayModePtr i, DisplayModePtr j, MgaScrn2Rel srel) {
69    DisplayModePtr mode;
70    int dx = 0,dy = 0;
71    /* start with first node */
72    mode = malloc(sizeof(DisplayModeRec));
73    memcpy(mode,i, sizeof(DisplayModeRec));
74    mode->Private = malloc(sizeof(MergedDisplayModeRec));
75    ((MergedDisplayModePtr)mode->Private)->Monitor1 = i;
76    ((MergedDisplayModePtr)mode->Private)->Monitor2 = j;
77    ((MergedDisplayModePtr)mode->Private)->Monitor2Pos = srel;
78    mode->PrivSize = 0;
79
80        switch(srel) {
81            case mgaLeftOf:
82            case mgaRightOf:
83                dx = min(pScrn->virtualX,i->HDisplay + j->HDisplay) -  mode->HDisplay;
84                dy = min(pScrn->virtualY,  max(i->VDisplay,j->VDisplay)) - mode->VDisplay;
85                break;
86            case mgaAbove:
87            case mgaBelow:
88                dy = min(pScrn->virtualY,i->VDisplay + j->VDisplay) - mode->VDisplay;
89                dx = min(pScrn->virtualX, max(i->HDisplay,j->HDisplay)) - mode->HDisplay;
90                break;
91            case mgaClone:
92                dx = min(pScrn->virtualX, max(i->HDisplay,j->HDisplay)) - mode->HDisplay;
93                dy = min(pScrn->virtualY, max(i->VDisplay,j->VDisplay)) - mode->VDisplay;
94                break;
95        }
96    mode->HDisplay += dx;
97    mode->HSyncStart += dx;
98    mode->HSyncEnd += dx;
99    mode->HTotal += dx;
100    mode->VDisplay += dy;
101    mode->VSyncStart += dy;
102    mode->VSyncEnd += dy;
103    mode->VTotal += dy;
104    mode->Clock = 0; /* Shows we're in Merge mode. */
105
106    mode->next = mode;
107    mode->prev = mode;
108
109    if(dest) {
110        /* Insert node after "dest" */
111        mode->next = dest->next;
112        dest->next->prev = mode;
113        mode->prev = dest;
114        dest->next = mode;
115    }
116
117    return mode;
118}
119
120static DisplayModePtr
121GetModeFromName(const char* str, DisplayModePtr i)
122{
123    DisplayModePtr c = i;
124    if(!i) return NULL;
125    do {
126        if(strcmp(str,c->name) == 0) return c;
127        c = c->next;
128    } while(c != i);
129    return NULL;
130}
131
132/* takes a config file string of MetaModes and generates a MetaModeList */
133static DisplayModePtr
134GenerateModeList(ScrnInfoPtr pScrn, const char* str,
135		 DisplayModePtr i, DisplayModePtr j, MgaScrn2Rel srel) {
136    const char* strmode = str;
137    char modename[256];
138    Bool gotdash = FALSE;
139    MgaScrn2Rel sr;
140
141    DisplayModePtr mode1 = NULL;
142    DisplayModePtr mode2 = NULL;
143    DisplayModePtr result = NULL;
144    do {
145        switch(*str) {
146            case 0:
147            case '-':
148            case ' ':
149	    case ',':
150	    case ';':
151                if((strmode != str)) {/*we got a mode */
152                    /* read new entry */
153                    strncpy(modename,strmode,str - strmode);
154                    modename[str - strmode] = 0;
155
156                    if(gotdash) {
157                        if(mode1 == NULL) return NULL;
158                        mode2 = GetModeFromName(modename,j);
159                        if(!mode2) {
160                            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
161                                "Mode: \"%s\" is not a supported mode for monitor 2\n",modename);
162                            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
163                                "Skipping metamode \"%s-%s\".\n",mode1->name,modename);
164                            mode1 = NULL;
165                        }
166                    } else {
167                        mode1 = GetModeFromName(modename,i);
168                        if(!mode1) {
169                            const char* tmps = str;
170                            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
171                                "Mode: \"%s\" is not a supported mode for monitor 1\n",modename);
172                            /* find if a monitor2 mode follows */
173                            gotdash = FALSE;
174                            while(*tmps == ' ' || *tmps == ';') tmps++;
175                            if(*tmps == '-' || *tmps == ',') { /* skip the next mode */
176                                tmps++;
177                                while(*tmps == ' ' || *tmps == ';') tmps++; /*skip spaces */
178                                while(*tmps && *tmps != ' ' && *tmps != ';' && *tmps != '-' && *tmps != ',') tmps++; /*skip modename */
179                                /* for error message */
180                                strncpy(modename,strmode,tmps - strmode);
181                                modename[tmps - strmode] = 0;
182                                str = tmps-1;
183                            }
184                            xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
185                                "Skipping metamode \"%s\".\n",modename);
186                            mode1 = NULL;
187                        }
188                    }
189                    gotdash = FALSE;
190                }
191                strmode = str+1; /* number starts on next char */
192                gotdash |= (*str == '-' || *str == ',');
193
194                if(*str != 0) break; /* if end of string, we wont get a chance to catch a char and run the
195                                        default case. do it now */
196
197            default:
198                if(!gotdash && mode1) { /* complete previous pair */
199                    sr = srel;
200                    if(!mode2) {
201                        mode2 = GetModeFromName(mode1->name,j);
202                        sr = mgaClone;
203                    }
204                    if(!mode2) {
205                        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
206                            "Mode: \"%s\" is not a supported mode for monitor 2\n",mode1->name);
207                        xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
208                            "Skipping clone mode \"%s\".\n", mode1->name);
209                        mode1 = NULL;
210                    } else {
211                        result = CopyModeNLink(pScrn,result,mode1,mode2,sr);
212                        mode1 = NULL;
213                        mode2 = NULL;
214                    }
215                }
216                break;
217
218        }
219    } while(*(str++) != 0);
220    return result;
221}
222
223
224/* second CRTC init funcitons. Used to check monitor timings and refreshes.
225 * this function looses lots of maintainability points due to redundancy,
226 * but it still was the cleanest and least-intrusive way I found. */
227
228Bool
229MGAPreInitMergedFB(ScrnInfoPtr pScrn1, int flags)
230{
231    ScrnInfoPtr pScrn;
232    MGAPtr pMga;
233    MGAPtr pMga1;
234    MessageType from;
235    int i;
236    const char* s;
237    ClockRangePtr clockRanges;
238    MgaScrn2Rel Monitor2Pos;
239
240    xf86DrvMsg(pScrn1->scrnIndex, X_INFO, "==== Start of second screen initialization ====\n");
241    pScrn = malloc(sizeof(ScrnInfoRec));
242    memcpy(pScrn,pScrn1,sizeof(ScrnInfoRec));
243
244    pScrn->driverPrivate = NULL;
245    /* Allocate the MGARec driverPrivate */
246    if (!MGAGetRec(pScrn)) {
247	return FALSE;
248    }
249
250    pMga = MGAPTR(pScrn);
251    pMga1 = MGAPTR(pScrn1);
252    pMga1->pScrn2 = pScrn;
253
254    /* Get the entity, and make sure it is PCI. */
255    pMga->pEnt = pMga1->pEnt;
256
257    /* Set pMga->device to the relevant Device section */
258    pMga->device = pMga1->device;
259
260    if (flags & PROBE_DETECT) {
261	MGAProbeDDC(pScrn, pMga->pEnt->index); /*FIXME make shure this probes second monitor */
262	return TRUE;
263    }
264
265#ifndef XSERVER_LIBPCIACCESS
266    pMga->PciTag = pMga1->PciTag;
267#endif
268    pMga->Primary = pMga1->Primary;
269
270    /* Set pScrn->monitor */
271    {
272        pScrn->monitor = malloc(sizeof(MonRec));
273        /* copy everything we don't care about */
274        memcpy(pScrn->monitor,pScrn1->monitor,sizeof(MonRec));
275        pScrn->monitor->DDC = NULL;   /*FIXME:have to try this */
276        if ((s = xf86GetOptValString(pMga1->Options, OPTION_HSYNC2))) {
277            pScrn->monitor->nHsync = StrToRanges(pScrn->monitor->hsync,s);
278        }
279        if ((s = xf86GetOptValString(pMga1->Options, OPTION_VREFRESH2))) {
280            pScrn->monitor->nVrefresh = StrToRanges(pScrn->monitor->vrefresh,s);
281        }
282
283
284
285   }
286
287    pMga->SecondCrtc = TRUE;
288    pMga->HWCursor = FALSE;
289    pScrn->AdjustFrame = MGAAdjustMergeFrames;
290    pScrn1->AdjustFrame = MGAAdjustMergeFrames;
291
292/*    if (!xf86SetDepthBpp(pScrn, 0, 0, 0, flags24))   FIXME:have to copy result form scrn1
293  if (!xf86SetWeight(pScrn, zeros, zeros)) {
294*/
295
296    /* We use a programamble clock */
297    pScrn->progClock = TRUE;
298
299    /* Collect all of the relevant option flags (fill in pScrn->options) */
300    pScrn->options = pScrn1->options;
301
302/*   xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, pMga->Options);*/
303    pMga->Options = pMga1->Options;
304
305
306    /* Set the bits per RGB for 8bpp mode */
307    if (pScrn->depth == 8)
308	pScrn->rgbBits = 8;
309
310    /*
311     * Set the Chipset and ChipRev, allowing config file entries to
312     * override.
313     */
314    pScrn->chipset = pScrn1->chipset;
315    pMga->Chipset = pMga1->Chipset;
316    pMga->ChipRev = pMga1->ChipRev;
317
318#ifdef MGADRI
319    pMga->agpMode = pMga1->agpMode;
320#endif
321
322    pMga->NoAccel = pMga1->NoAccel;
323    pMga->UsePCIRetry = pMga1->UsePCIRetry;
324    pMga->SyncOnGreen = pMga1->SyncOnGreen;
325    pMga->ShowCache = pMga1->ShowCache;
326    pMga->HasSDRAM = pMga1->HasSDRAM;
327    pMga->MemClk = pMga1->MemClk;
328    pMga->colorKey = pMga1->colorKey;
329    pScrn->colorKey = pScrn1->colorKey;
330    pScrn->overlayFlags = pScrn1->overlayFlags;
331    pMga->videoKey = pMga1->videoKey;
332    /* unsupported options */
333    pMga->HWCursor = FALSE;
334    pMga->ShadowFB = FALSE;
335    pMga->FBDev = FALSE;
336
337    pMga->OverclockMem = pMga1->OverclockMem;
338    pMga->TexturedVideo = pMga1->TexturedVideo;
339    pMga->MergedFB = TRUE;
340
341    pMga->Rotate = 0;
342
343    switch (pMga->Chipset) {
344    case PCI_CHIP_MGA2064:
345    case PCI_CHIP_MGA2164:
346    case PCI_CHIP_MGA2164_AGP:
347	MGA2064SetupFuncs(pScrn);
348	break;
349    case PCI_CHIP_MGA1064:
350    case PCI_CHIP_MGAG100:
351    case PCI_CHIP_MGAG100_PCI:
352    case PCI_CHIP_MGAG200:
353    case PCI_CHIP_MGAG200_PCI:
354    case PCI_CHIP_MGAG200_SE_A_PCI:
355    case PCI_CHIP_MGAG200_SE_B_PCI:
356    case PCI_CHIP_MGAG200_WINBOND_PCI:
357    case PCI_CHIP_MGAG200_EV_PCI:
358    case PCI_CHIP_MGAG200_EH_PCI:
359	case PCI_CHIP_MGAG200_ER_PCI:
360    case PCI_CHIP_MGAG400:
361    case PCI_CHIP_MGAG550:
362	MGAGSetupFuncs(pScrn);
363	break;
364    }
365
366    pMga->FbAddress = pMga1->FbAddress;
367    pMga->PciInfo = pMga1->PciInfo;
368#ifndef XSERVER_LIBPCIACCESS
369    pMga->IOAddress = pMga1->IOAddress;
370    pMga->ILOADAddress = pMga1->ILOADAddress;
371    pMga->BiosFrom = pMga1->BiosFrom;
372    pMga->BiosAddress = pMga1->BiosAddress;
373#endif
374
375    /*
376     * Read the BIOS data struct
377     */
378
379    mga_read_and_process_bios( pScrn );
380
381    /* HW bpp matches reported bpp */
382    pMga->HwBpp = pMga1->HwBpp;
383
384    /*
385     * Reset card if it isn't primary one
386     */
387    if ( (!pMga->Primary && !pMga->FBDev) )
388        MGASoftReset(pScrn);
389
390
391    pScrn->videoRam = pScrn1->videoRam;
392    pMga->FbMapSize = pMga1->FbMapSize;
393    pMga->SrcOrg = pMga1->SrcOrg;
394    pMga->DstOrg = pMga1->DstOrg;
395
396   /* Set the bpp shift value */
397    pMga->BppShifts[0] = 0;
398    pMga->BppShifts[1] = 1;
399    pMga->BppShifts[2] = 0;
400    pMga->BppShifts[3] = 2;
401
402    /*
403     * fill MGAdac struct
404     * Warning: currently, it should be after RAM counting
405     */
406    (*pMga->PreInit)(pScrn);
407
408#if !defined(__powerpc__)
409
410    /* Read and print the Monitor DDC info */
411/*    pScrn->monitor->DDC = MGAdoDDC(pScrn);*/ /*FIXME: have to try this*/
412#endif /* !__powerpc__ */
413
414    /*
415     * If the driver can do gamma correction, it should call xf86SetGamma()
416     * here.
417     */
418    {
419	Gamma zeros = {0.0, 0.0, 0.0};
420
421	if (!xf86SetGamma(pScrn, zeros)) {
422	    return FALSE;
423	}
424    }
425
426
427    /* Set the min pixel clock */
428    pMga->MinClock = pMga1->MinClock;	/* XXX Guess, need to check this */
429    xf86DrvMsg(pScrn->scrnIndex, X_DEFAULT, "CRTC2: Min pixel clock is %d MHz\n",
430	       pMga->MinClock / 1000);
431   /* Override on 2nd crtc */
432
433    if (pMga->ChipRev >= 0x80 || (pMga->Chipset == PCI_CHIP_MGAG550)) {
434	/* G450, G550 */
435        pMga->MaxClock = 234000;
436    } else {
437        pMga->MaxClock = 135000;
438    }
439    xf86DrvMsg(pScrn->scrnIndex, X_DEFAULT, "CRTC2: Max pixel clock is %d MHz\n",
440	       pMga->MaxClock / 1000);
441    /*
442     * Setup the ClockRanges, which describe what clock ranges are available,
443     * and what sort of modes they can be used for.
444     */
445    clockRanges = xnfcalloc(sizeof(ClockRange), 1);
446    clockRanges->next = NULL;
447    clockRanges->minClock = pMga->MinClock;
448    clockRanges->maxClock = pMga->MaxClock;
449    clockRanges->clockIndex = -1;		/* programmable */
450    clockRanges->interlaceAllowed = TRUE;
451    clockRanges->doubleScanAllowed = TRUE;
452    clockRanges->interlaceAllowed = FALSE; /*no interlace on CRTC2 */
453
454    clockRanges->ClockMulFactor = 1;
455    clockRanges->ClockDivFactor = 1;
456    /* Only set MemClk if appropriate for the ramdac */
457    if (pMga->Dac.SetMemClk) {
458	if (pMga->MemClk == 0) {
459	    pMga->MemClk = pMga->Dac.MemoryClock;
460	    from = pMga->Dac.MemClkFrom;
461	} else
462	    from = X_CONFIG;
463	xf86DrvMsg(pScrn->scrnIndex, from, "CRTC2: MCLK used is %.1f MHz\n",
464		   pMga->MemClk / 1000.0);
465    }
466
467    /*
468     * xf86ValidateModes will check that the mode HTotal and VTotal values
469     * don't exceed the chipset's limit if pScrn->maxHValue and
470     * pScrn->maxVValue are set.  Since our MGAValidMode() already takes
471     * care of this, we don't worry about setting them here.
472     */
473    {
474	int Pitches1[] =
475	  {640, 768, 800, 960, 1024, 1152, 1280, 1600, 1920, 2048, 0};
476	int Pitches2[] =
477	  {512, 640, 768, 800, 832, 960, 1024, 1152, 1280, 1600, 1664,
478		1920, 2048, 0};
479	int *linePitches = NULL;
480	int minPitch = 256;
481	int maxPitch = 2048;
482
483        switch(pMga->Chipset) {
484	case PCI_CHIP_MGA2064:
485	   if (!pMga->NoAccel) {
486		linePitches = malloc(sizeof(Pitches1));
487		memcpy(linePitches, Pitches1, sizeof(Pitches1));
488		minPitch = maxPitch = 0;
489	   }
490	   break;
491	case PCI_CHIP_MGA2164:
492	case PCI_CHIP_MGA2164_AGP:
493	case PCI_CHIP_MGA1064:
494	   if (!pMga->NoAccel) {
495		linePitches = malloc(sizeof(Pitches2));
496		memcpy(linePitches, Pitches2, sizeof(Pitches2));
497		minPitch = maxPitch = 0;
498	   }
499	   break;
500	case PCI_CHIP_MGAG100:
501	case PCI_CHIP_MGAG100_PCI:
502	   maxPitch = 2048;
503	   break;
504	case PCI_CHIP_MGAG200:
505	case PCI_CHIP_MGAG200_PCI:
506	case PCI_CHIP_MGAG200_SE_A_PCI:
507	case PCI_CHIP_MGAG200_SE_B_PCI:
508        case PCI_CHIP_MGAG200_WINBOND_PCI:
509        case PCI_CHIP_MGAG200_EV_PCI:
510        case PCI_CHIP_MGAG200_EH_PCI:
511	case PCI_CHIP_MGAG200_ER_PCI:
512	case PCI_CHIP_MGAG400:
513	case PCI_CHIP_MGAG550:
514	   maxPitch = 4096;
515	   break;
516	}
517
518        pScrn->modePool=NULL;
519        pScrn->modes = NULL;
520	i = xf86ValidateModes(pScrn, pScrn->monitor->Modes,
521			      pScrn->display->modes, clockRanges,
522			      linePitches, minPitch, maxPitch,
523			      pMga->Roundings[(pScrn->bitsPerPixel >> 3) - 1] *
524					pScrn->bitsPerPixel, 128, 2048,
525			      pScrn->display->virtualX,
526			      pScrn->display->virtualY,
527			      pMga->FbMapSize,
528			      LOOKUP_BEST_REFRESH);
529
530	free(linePitches);
531    }
532
533
534    if (i < 1 && pMga->FBDev) {
535	fbdevHWUseBuildinMode(pScrn);
536	pScrn->displayWidth = pScrn->virtualX; /* FIXME: might be wrong */
537	i = 1;
538    }
539    if (i == -1) {
540	xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "CRTC2: Validate Modes Failed\n");
541	MGAFreeRec(pScrn);
542	return FALSE;
543    }
544
545    /* Prune the modes marked as invalid */
546    xf86PruneDriverModes(pScrn);
547
548    if (i == 0 || pScrn->modes == NULL) {
549	xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "CRTC2: No valid modes found\n");
550	MGAFreeRec(pScrn);
551	return FALSE;
552    }
553
554    /*
555     * Set the CRTC parameters for all of the modes based on the type
556     * of mode, and the chipset's interlace requirements.
557     *
558     * Calling this is required if the mode->Crtc* values are used by the
559     * driver and if the driver doesn't provide code to set them.  They
560     * are not pre-initialised at all.
561     */
562    MGA_NOT_HAL(xf86SetCrtcForModes(pScrn, INTERLACE_HALVE_V));
563
564    /* Set the current mode to the first in the list */
565    pScrn->currentMode = pScrn->modes;
566
567    /* Print the list of modes being used */
568    xf86PrintModes(pScrn);
569
570    /* Set display resolution */
571    xf86SetDpi(pScrn, 0, 0);
572
573    /*
574     * Compute the byte offset into the linear frame buffer where the
575     * frame buffer data should actually begin.  According to DDK misc.c
576     * line 1023, if more than 4MB is to be displayed, YDSTORG must be set
577     * appropriately to align memory bank switching, and this requires a
578     * corresponding offset on linear frame buffer access.
579     * This is only needed for WRAM.
580     */
581
582    pMga->YDstOrg = pMga1->YDstOrg;
583    xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 2, "CRTC2: YDstOrg is set to %d\n",
584		   pMga->YDstOrg);
585    pMga->FbUsableSize = pMga1->FbUsableSize;
586    pMga->FbCursorOffset = pMga1->FbCursorOffset;
587
588    pMga->CurrentLayout.bitsPerPixel = pScrn->bitsPerPixel;
589    pMga->CurrentLayout.depth = pScrn->depth;
590    pMga->CurrentLayout.displayWidth = pScrn->displayWidth;
591    pMga->CurrentLayout.weight.red = pScrn->weight.red;
592    pMga->CurrentLayout.weight.green = pScrn->weight.green;
593    pMga->CurrentLayout.weight.blue = pScrn->weight.blue;
594    pMga->CurrentLayout.mode = pScrn->currentMode;
595
596
597    Monitor2Pos = mgaRightOf;
598    if ((s = xf86GetOptValString(pMga1->Options, OPTION_MONITOR2POS))) {
599        switch(s[0]) {
600            case 'L': case 'l': case 'G': case 'g':
601                Monitor2Pos = mgaLeftOf;
602                break;
603            case 'R': case 'r': case 'D': case 'd':
604                Monitor2Pos = mgaRightOf;
605                break;
606
607            case 'A': case 'a': case 'H': case 'h':
608                Monitor2Pos = mgaAbove;
609                break;
610
611            case 'B': case 'b':
612                Monitor2Pos = mgaBelow;
613                break;
614
615            case 'C': case 'c':
616                Monitor2Pos = mgaClone;
617                break;
618            default:
619                Monitor2Pos = mgaRightOf;
620                break;
621        }
622    }
623
624    /* Fool xf86 into thinking we have huge modes */
625    /* Keep the original values somewhere */
626    pMga1->M1modes = pScrn1->modes;
627    pMga1->M1currentMode = pScrn1->currentMode;
628    /* make a copy of the mode list, so we can modify it. */
629    if ((s = xf86GetOptValString(pMga1->Options, OPTION_METAMODES))) {
630        pScrn1->modes = GenerateModeList(pScrn,s,pMga1->M1modes,pScrn->modes,Monitor2Pos); /*FIXME: free this list*/
631        if(!pScrn1->modes) {
632	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Parse Error reading MetaModes, or No modes left.\n");
633            return FALSE;
634        }
635
636        pScrn1->modes = pScrn1->modes->next;
637        pScrn1->currentMode = pScrn1->modes;
638    } else {
639        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "MetaModes option missing.\n");
640        return FALSE;
641    }
642    xf86DrvMsg(pScrn1->scrnIndex, X_INFO, "==== End of second screen initialization ====\n");
643    return TRUE;
644}
645
646void
647MGADisplayPowerManagementSetMerged(ScrnInfoPtr pScrn, int PowerManagementMode,
648				  int flags)
649{
650    MGADisplayPowerManagementSet(pScrn,PowerManagementMode,flags);
651    MGADisplayPowerManagementSetCrtc2(pScrn,PowerManagementMode,flags);
652}
653
654typedef struct _region {
655    int x0,x1,y0,y1;
656    } region;
657
658static Bool
659InRegion(int x, int y, region r) {
660    return (r.x0 <= x) && (x < r.x1) && (r.y0 <= y) && (y < r.y1);
661}
662
663
664#define BOUND(test,low,hi) { \
665    if(test < low) test = low; \
666    if(test > hi) test = hi; }
667#define REBOUND(low,hi,test) { \
668    if(test < low) { \
669        hi += test-low; \
670        low = test; } \
671    if(test > hi) { \
672        low += test-hi; \
673        hi = test; } }
674 void
675MGAMergePointerMoved(SCRN_ARG_TYPE arg, int x, int y)
676{
677  SCRN_INFO_PTR(arg);
678  MGAPtr        pMga = MGAPTR(pScrn);
679  ScrnInfoPtr   pScr2 = pMga->pScrn2;
680
681  region out,in1,in2,f2,f1;
682
683  int deltax,deltay;
684
685  /* for ease. */
686  f1.x0 = pMga->M1frameX0;
687  f1.x1 = pMga->M1frameX1+1;
688  f1.y0 = pMga->M1frameY0;
689  f1.y1 = pMga->M1frameY1+1;
690  f2.x0 = pScr2->frameX0;
691  f2.x1 = pScr2->frameX1+1;
692  f2.y0 = pScr2->frameY0;
693  f2.y1 = pScr2->frameY1+1;
694
695
696  /*specify outer clipping region. crossing this causes all frames to move*/
697  out.x0 = pScrn->frameX0;
698  out.x1 = pScrn->frameX1+1;
699  out.y0 = pScrn->frameY0;
700  out.y1 = pScrn->frameY1+1;
701
702  /*
703   * specify inner sliding window. beeing outsize both frames, and inside
704   * the outer cliping window, causes corresponding frame to slide
705   */
706  in1 = out;
707  in2 = out;
708  switch(((MergedDisplayModePtr)pScrn->currentMode->Private)->Monitor2Pos) {
709      case mgaLeftOf :
710          in1.x0 = f1.x0;
711          in2.x1 = f2.x1;
712          break;
713      case mgaRightOf :
714          in1.x1 = f1.x1;
715          in2.x0 = f2.x0;
716          break;
717      case mgaBelow :
718          in1.y1 = f1.y1;
719          in2.y0 = f2.y0;
720          break;
721      case mgaAbove :
722          in1.y0 = f1.y0;
723          in2.y1 = f2.y1;
724          break;
725      case mgaClone :
726          break;
727      }
728
729
730    deltay = 0;
731    deltax = 0;
732
733    if(InRegion(x,y,out)) {
734        if( InRegion(x,y, in1) && !InRegion(x,y, f1) ) {
735            REBOUND(f1.x0,f1.x1,x);
736            REBOUND(f1.y0,f1.y1,y);
737            deltax = 1; /*force frame update */
738        }
739        if( InRegion(x,y, in2) && !InRegion(x,y, f2) ) {
740            REBOUND(f2.x0,f2.x1,x);
741            REBOUND(f2.y0,f2.y1,y);
742            deltax = 1; /*force frame update */
743        }
744    }
745    else {  /*outside outer clipping region*/
746        if ( out.x0 > x) {
747            deltax = x - out.x0;
748        }
749        if ( out.x1 < x) {
750            deltax = x - out.x1;
751        }
752        f1.x0 += deltax;
753        f1.x1 += deltax;
754        f2.x0 += deltax;
755        f2.x1 += deltax;
756        pScrn->frameX0 += deltax;
757        pScrn->frameX1 += deltax;
758
759
760        if ( out.y0 > y) {
761            deltay = y - out.y0;
762        }
763        if ( out.y1 < y) {
764            deltay = y - out.y1;
765        }
766        f1.y0 += deltay;
767        f1.y1 += deltay;
768        f2.y0 += deltay;
769        f2.y1 += deltay;
770        pScrn->frameY0 += deltay;
771        pScrn->frameY1 += deltay;
772    }
773
774
775    if (deltax != 0 || deltay != 0) {
776        /* back to reality. */
777        pMga->M1frameX0 = f1.x0;
778        pMga->M1frameY0 = f1.y0;
779        pScr2->frameX0 = f2.x0;
780        pScr2->frameY0 = f2.y0;
781
782        /*Adjust Granularity */
783        MGAAdjustGranularity(pScrn,&pMga->M1frameX0,&pMga->M1frameY0);
784        MGAAdjustGranularity(pScrn,&pScr2->frameX0,&pScr2->frameY0);
785        MGAAdjustGranularity(pScrn,&pScrn->frameX0,&pScrn->frameY0);
786
787        pMga->M1frameX1 = pMga->M1frameX0 + MDMPTR(pScrn)->Monitor1->HDisplay -1;
788        pMga->M1frameY1 = pMga->M1frameY0 + MDMPTR(pScrn)->Monitor1->VDisplay -1;
789        pScr2->frameX1 = pScr2->frameX0 + MDMPTR(pScrn)->Monitor2->HDisplay -1;
790        pScr2->frameY1 = pScr2->frameY0 + MDMPTR(pScrn)->Monitor2->VDisplay -1;
791        pScrn->frameX1 = pScrn->frameX0 + pScrn->currentMode->HDisplay -1;
792        pScrn->frameY1 = pScrn->frameY0 + pScrn->currentMode->VDisplay -1;
793
794        MGAAdjustFrame(ADJUST_FRAME_ARGS(pScrn, pMga->M1frameX0, pMga->M1frameY0));
795        MGAAdjustFrameCrtc2(ADJUST_FRAME_ARGS(pScrn, pScr2->frameX0, pScr2->frameY0));
796    }
797
798/*  if(pMga->PointerMoved)
799      (*pMga->PointerMoved)(scrnIndex, x, y);  FIXME: do I need to call old func?*/
800
801}
802
803
804void
805MGAAdjustMergeFrames(ADJUST_FRAME_ARGS_DECL) {
806    SCRN_INFO_PTR(arg);
807    ScrnInfoPtr pScrn1 = pScrn;
808    MGAPtr pMga = MGAPTR(pScrn1);
809    ScrnInfoPtr pScrn2 = pMga->pScrn2;
810    int VTotal = pScrn1->currentMode->VDisplay;
811    int HTotal = pScrn1->currentMode->HDisplay;
812    int VMax = VTotal;
813    int HMax = HTotal;
814
815    BOUND(x,0,pScrn1->virtualX-HTotal);
816    BOUND(y,0,pScrn1->virtualY-VTotal);
817    switch(MDMPTR(pScrn1)->Monitor2Pos) {
818        case mgaLeftOf:
819            pScrn2->frameX0 = x;
820            BOUND(pScrn2->frameY0,y,y + VMax - MDMPTR(pScrn1)->Monitor2->VDisplay);
821            pMga->M1frameX0 = x+MDMPTR(pScrn1)->Monitor2->HDisplay;
822            BOUND(pMga->M1frameY0,y,y + VMax - MDMPTR(pScrn1)->Monitor1->VDisplay);
823            break;
824        case mgaRightOf:
825            pMga->M1frameX0 = x;
826            BOUND(pMga->M1frameY0,y,y + VMax - MDMPTR(pScrn1)->Monitor1->VDisplay);
827            pScrn2->frameX0 = x+MDMPTR(pScrn1)->Monitor1->HDisplay;
828            BOUND(pScrn2->frameY0,y,y + VMax - MDMPTR(pScrn1)->Monitor2->VDisplay);
829            break;
830        case mgaAbove:
831            BOUND(pScrn2->frameX0,x,x + HMax - MDMPTR(pScrn1)->Monitor2->HDisplay);
832            pScrn2->frameY0 = y;
833            BOUND(pMga->M1frameX0,x,x + HMax - MDMPTR(pScrn1)->Monitor1->HDisplay);
834            pMga->M1frameY0 = y+MDMPTR(pScrn1)->Monitor2->VDisplay;
835            break;
836        case mgaBelow:
837            BOUND(pMga->M1frameX0,x,x + HMax - MDMPTR(pScrn1)->Monitor1->HDisplay);
838            pMga->M1frameY0 = y;
839            BOUND(pScrn2->frameX0,x,x + HMax - MDMPTR(pScrn1)->Monitor2->HDisplay);
840            pScrn2->frameY0 = y+MDMPTR(pScrn1)->Monitor1->VDisplay;
841            break;
842        case mgaClone:
843            BOUND(pMga->M1frameX0,x,x + HMax - MDMPTR(pScrn1)->Monitor1->HDisplay);
844            BOUND(pMga->M1frameY0,y,y + VMax - MDMPTR(pScrn1)->Monitor1->VDisplay);
845            BOUND(pScrn2->frameX0,x,x + HMax - MDMPTR(pScrn1)->Monitor2->HDisplay);
846            BOUND(pScrn2->frameY0,y,y + VMax - MDMPTR(pScrn1)->Monitor2->VDisplay);
847            break;
848    }
849    /* sanity checks. Make shure were not out of bounds */
850    BOUND(pMga->M1frameX0,0,pScrn1->virtualX -MDMPTR(pScrn1)->Monitor1->HDisplay);
851    BOUND(pMga->M1frameY0,0,pScrn1->virtualY -MDMPTR(pScrn1)->Monitor1->VDisplay);
852    BOUND(pScrn2->frameX0,0,pScrn2->virtualX -MDMPTR(pScrn1)->Monitor2->HDisplay);
853    BOUND(pScrn2->frameY0,0,pScrn2->virtualY -MDMPTR(pScrn1)->Monitor2->VDisplay);
854
855    pScrn1->frameX0 = x;
856    pScrn1->frameY0 = y;
857
858    /* check granularity */
859    MGAAdjustGranularity(pScrn1,&pMga->M1frameX0,&pMga->M1frameY0);
860    MGAAdjustGranularity(pScrn1,&pScrn2->frameX0,&pScrn2->frameY0);
861    MGAAdjustGranularity(pScrn1,&pScrn1->frameX0,&pScrn1->frameY0);
862
863    /* complete shitty redundant info */
864    pMga->M1frameX1 = pMga->M1frameX0 + MDMPTR(pScrn1)->Monitor1->HDisplay -1;
865    pMga->M1frameY1 = pMga->M1frameY0 + MDMPTR(pScrn1)->Monitor1->VDisplay -1;
866    pScrn2->frameX1 = pScrn2->frameX0 + MDMPTR(pScrn1)->Monitor2->HDisplay -1;
867    pScrn2->frameY1 = pScrn2->frameY0 + MDMPTR(pScrn1)->Monitor2->VDisplay -1;
868    pScrn1->frameX1 = pScrn1->frameX0 + pScrn1->currentMode->HDisplay -1;
869    pScrn1->frameY1 = pScrn1->frameY0 + pScrn1->currentMode->VDisplay -1;
870
871    MGAAdjustFrame(ADJUST_FRAME_ARGS(pScrn, pMga->M1frameX0, pMga->M1frameY0));
872    MGAAdjustFrameCrtc2(ADJUST_FRAME_ARGS(pScrn, pScrn2->frameX0, pScrn2->frameY0));
873    return;
874}
875
876Bool
877MGACloseScreenMerged(ScreenPtr pScreen) {
878    ScrnInfoPtr pScrn1 = xf86ScreenToScrn(pScreen);
879    MGAPtr pMga = MGAPTR(pScrn1);
880    ScrnInfoPtr pScrn2 = pMga->pScrn2;
881
882    if(pScrn2) {
883        free(pScrn2->monitor);
884        pScrn2->monitor = NULL;
885
886        free(pScrn2);
887        pMga->pScrn2 = NULL;
888    }
889
890    if(pScrn1->modes) {
891        pScrn1->currentMode = pScrn1->modes;
892        do {
893            DisplayModePtr p = pScrn1->currentMode->next;
894            free(pScrn1->currentMode->Private);
895            free(pScrn1->currentMode);
896            pScrn1->currentMode = p;
897        }while( pScrn1->currentMode != pScrn1->modes);
898    }
899
900    pScrn1->currentMode = pMga->M1currentMode;
901    pScrn1->modes = pMga->M1modes;
902
903    return TRUE;
904}
905
906Bool
907MGASaveScreenMerged(ScreenPtr pScreen, int mode)
908{
909    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
910    MGAPtr pMga = MGAPTR(pScrn);
911    BOOL on = xf86IsUnblank(mode);
912    CARD8 reg;
913
914    if (on) {
915/*        SetTimdeSinceLastInputEvent();*/
916
917        /* power on Dac1 */
918        reg = inMGAdac(MGA1064_MISC_CTL);
919        reg |= MGA1064_MISC_CTL_DAC_EN;
920        outMGAdac(MGA1064_MISC_CTL, reg);
921
922        /* power on Dac2 */
923        reg = inMGAdac(MGA1064_PWR_CTL);
924        reg |= MGA1064_PWR_CTL_DAC2_EN;
925        outMGAdac(MGA1064_PWR_CTL, reg);
926    } else {
927        /* power off Dac1 */
928        reg = inMGAdac(MGA1064_MISC_CTL);
929        reg &= ~MGA1064_MISC_CTL_DAC_EN;
930        outMGAdac(MGA1064_MISC_CTL, reg);
931
932        /* power off Dac2 */
933        reg = inMGAdac(MGA1064_PWR_CTL);
934        reg &= ~MGA1064_PWR_CTL_DAC2_EN;
935        outMGAdac(MGA1064_PWR_CTL, reg);
936    }
937
938    return TRUE;
939}
940
941
942