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