1/*
2 * SBus Weitek P9100 driver
3 *
4 * Copyright (C) 2005, 2006 Michael Lorenz
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * MICHAEL LORENZ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23/* $NetBSD: pnozz_driver.c,v 1.7 2021/05/27 04:48:10 jdc Exp $ */
24
25/*
26 * this driver has been tested on SPARCbook 3GX and 3TX, it supports full
27 * acceleration in 8, 16 and 24 bit colour
28 */
29
30#ifdef HAVE_CONFIG_H
31#include "config.h"
32#endif
33
34#include <sys/ioctl.h>
35
36#include "pnozz.h"
37#include "xf86.h"
38#include "xf86_OSproc.h"
39#include "mipointer.h"
40#include "micmap.h"
41
42#define DEBUG 1
43
44#include "fb.h"
45#include "xf86cmap.h"
46
47#include "compat-api.h"
48
49static const OptionInfoRec * PnozzAvailableOptions(int chipid, int busid);
50static void	PnozzIdentify(int flags);
51static Bool	PnozzProbe(DriverPtr drv, int flags);
52static Bool	PnozzPreInit(ScrnInfoPtr pScrn, int flags);
53static Bool	PnozzScreenInit(SCREEN_INIT_ARGS_DECL);
54static Bool	PnozzEnterVT(VT_FUNC_ARGS_DECL);
55static void	PnozzLeaveVT(VT_FUNC_ARGS_DECL);
56static Bool	PnozzCloseScreen(CLOSE_SCREEN_ARGS_DECL);
57static Bool	PnozzSaveScreen(ScreenPtr pScreen, int mode);
58
59/* Required if the driver supports mode switching */
60static Bool	PnozzSwitchMode(SWITCH_MODE_ARGS_DECL);
61/* Required if the driver supports moving the viewport */
62static void	PnozzAdjustFrame(ADJUST_FRAME_ARGS_DECL);
63
64/* Optional functions */
65static void	PnozzFreeScreen(FREE_SCREEN_ARGS_DECL);
66static ModeStatus PnozzValidMode(SCRN_ARG_TYPE arg, DisplayModePtr mode,
67			       Bool verbose, int flags);
68
69void PnozzSync(ScrnInfoPtr);
70void PnozzSave(PnozzPtr);
71void PnozzRestore(PnozzPtr);
72int PnozzSetDepth(PnozzPtr, int);	/* return true or false */
73void DumpSCR(unsigned int);
74
75static void PnozzLoadPalette(ScrnInfoPtr, int, int *, LOCO *, VisualPtr);
76
77#define VERSION 4000
78#define PNOZZ_NAME "p9100"
79#define PNOZZ_DRIVER_NAME "pnozz"
80#define PNOZZ_MAJOR_VERSION 2
81#define PNOZZ_MINOR_VERSION 0
82#define PNOZZ_PATCHLEVEL 0
83
84/*
85 * This contains the functions needed by the server after loading the driver
86 * module.  It must be supplied, and gets passed back by the SetupProc
87 * function in the dynamic case.  In the static case, a reference to this
88 * is compiled in, and this requires that the name of this DriverRec be
89 * an upper-case version of the driver name.
90 */
91
92DriverRec PNOZZ = {
93    VERSION,
94    PNOZZ_DRIVER_NAME,
95    PnozzIdentify,
96    PnozzProbe,
97    PnozzAvailableOptions,
98    NULL,
99    0
100};
101
102typedef enum {
103    OPTION_SW_CURSOR,
104    OPTION_HW_CURSOR,
105    OPTION_NOACCEL,
106    OPTION_ACCELMETHOD
107} PnozzOpts;
108
109static const OptionInfoRec PnozzOptions[] = {
110    { OPTION_SW_CURSOR,		"SWcursor",	OPTV_BOOLEAN,	{0}, FALSE },
111    { OPTION_HW_CURSOR,		"HWcursor",	OPTV_BOOLEAN,	{0}, FALSE },
112    { OPTION_NOACCEL,		"NoAccel",	OPTV_BOOLEAN,	{0}, FALSE },
113    { OPTION_ACCELMETHOD,	"AccelMethod",	OPTV_STRING,	{0}, FALSE },
114    { -1,			NULL,		OPTV_NONE,	{0}, FALSE }
115};
116
117static const char *ramdacSymbols[] = {
118    "xf86CreateCursorInfoRec",
119    "xf86DestroyCursorInfoRec",
120    "xf86InitCursor",
121    NULL
122};
123
124static const char *fbSymbols[] = {
125    "fbScreenInit",
126    "fbPictureInit",
127    NULL
128};
129
130static MODULESETUPPROTO(PnozzSetup);
131
132static XF86ModuleVersionInfo PnozzVersRec =
133{
134	"pnozz",
135	MODULEVENDORSTRING,
136	MODINFOSTRING1,
137	MODINFOSTRING2,
138	XORG_VERSION_CURRENT,
139	PNOZZ_MAJOR_VERSION, PNOZZ_MINOR_VERSION, PNOZZ_PATCHLEVEL,
140	ABI_CLASS_VIDEODRV,
141	ABI_VIDEODRV_VERSION,
142	MOD_CLASS_VIDEODRV,
143	{0,0,0,0}
144};
145
146XF86ModuleData pnozzModuleData = { &PnozzVersRec, PnozzSetup, NULL };
147
148pointer
149PnozzSetup(pointer module, pointer opts, int *errmaj, int *errmin)
150{
151    static Bool setupDone = FALSE;
152
153    if (!setupDone) {
154	setupDone = TRUE;
155	xf86AddDriver(&PNOZZ, module, 0);
156
157	/*
158	 * Modules that this driver always requires can be loaded here
159	 * by calling LoadSubModule().
160	 */
161
162	/*
163	 * The return value must be non-NULL on success even though there
164	 * is no TearDownProc.
165	 */
166	return (pointer)TRUE;
167    } else {
168	if (errmaj) *errmaj = LDR_ONCEONLY;
169	return NULL;
170    }
171}
172
173static volatile unsigned int scratch32;
174
175void pnozz_write_4(PnozzPtr p, int offset, unsigned int value)
176{
177	if ((offset & 0xffffff80) != p->offset_mask) {
178		p->offset_mask = offset & 0xffffff80;
179		scratch32 = *(volatile unsigned int *)(p->fb + offset);
180	}
181	*((volatile unsigned int *)(p->fbc + offset)) = value;
182}
183
184unsigned int pnozz_read_4(PnozzPtr p, int offset)
185{
186	if ((offset & 0xffffff80) != p->offset_mask) {
187		p->offset_mask = offset & 0xffffff80;
188		scratch32 = *(volatile unsigned int *)(p->fb + offset);
189	}
190	return *(volatile unsigned int *)(p->fbc + offset);
191}
192
193void pnozz_write_dac(PnozzPtr p, int offset, unsigned char value)
194{
195	CARD32 val = ((CARD32)value) << 16;
196
197	scratch32 = pnozz_read_4(p, PWRUP_CNFG);
198	if ((offset != DAC_INDX_DATA) && (offset != DAC_CMAP_DATA)) {
199		do {
200			pnozz_write_4(p, offset, val);
201		} while (pnozz_read_4(p, offset) != val);
202	} else {
203		pnozz_write_4(p, offset, val);
204	}
205}
206
207unsigned char pnozz_read_dac(PnozzPtr p, int offset)
208{
209	scratch32 = pnozz_read_4(p, PWRUP_CNFG);
210	return ((pnozz_read_4(p, offset) >> 16) & 0xff);
211}
212
213void pnozz_write_dac_ctl_reg(PnozzPtr p, int offset, unsigned char val)
214{
215
216	pnozz_write_dac(p, DAC_INDX_HI, (offset & 0xff00) >> 8);
217	pnozz_write_dac(p, DAC_INDX_LO, (offset & 0xff));
218	pnozz_write_dac(p, DAC_INDX_DATA, val);
219}
220
221void pnozz_write_dac_ctl_reg_2(PnozzPtr p, int offset, unsigned short val)
222{
223
224	pnozz_write_dac(p, DAC_INDX_HI, (offset & 0xff00) >> 8);
225	pnozz_write_dac(p, DAC_INDX_LO, (offset & 0xff));
226	pnozz_write_dac(p, DAC_INDX_CTL, DAC_INDX_AUTOINCR);
227	pnozz_write_dac(p, DAC_INDX_DATA, val & 0xff);
228	pnozz_write_dac(p, DAC_INDX_DATA, (val & 0xff00) >> 8);
229}
230
231unsigned char pnozz_read_dac_ctl_reg(PnozzPtr p, int offset)
232{
233	pnozz_write_dac(p, DAC_INDX_HI, (offset & 0xff00) >> 8);
234	pnozz_write_dac(p, DAC_INDX_LO, (offset & 0xff));
235	return pnozz_read_dac(p, DAC_INDX_DATA);
236}
237
238void pnozz_write_dac_cmap_reg(PnozzPtr p, int offset, unsigned int val)
239{
240	pnozz_write_dac(p, DAC_CMAP_WRIDX,(offset & 0xff));
241	pnozz_write_dac(p, DAC_CMAP_DATA,(val & 0xff));
242	pnozz_write_dac(p, DAC_CMAP_DATA,(val & 0xff00) >> 8);
243	pnozz_write_dac(p, DAC_CMAP_DATA,(val & 0xff0000) >> 16);
244}
245
246static void
247PnozzLoadPalette(ScrnInfoPtr pScrn, int numColors, int *indices, LOCO *colors,
248    VisualPtr pVisual)
249{
250    PnozzPtr pPnozz = GET_PNOZZ_FROM_SCRN(pScrn);
251    int i, index;
252
253    PnozzSync(pScrn);
254    pnozz_write_dac(pPnozz, DAC_INDX_CTL, DAC_INDX_AUTOINCR);
255
256    for (i = 0; i < numColors; i++)
257    {
258    	index = indices[i];
259	if (index >= 0) {
260    	    pnozz_write_dac(pPnozz, DAC_CMAP_WRIDX, index);
261	    pnozz_write_dac(pPnozz, DAC_CMAP_DATA, colors[index].red);
262	    pnozz_write_dac(pPnozz, DAC_CMAP_DATA, colors[index].green);
263	    pnozz_write_dac(pPnozz, DAC_CMAP_DATA, colors[index].blue);
264	}
265    }
266    PnozzSync(pScrn);
267}
268
269static Bool
270PnozzGetRec(ScrnInfoPtr pScrn)
271{
272    /*
273     * Allocate an PnozzRec, and hook it into pScrn->driverPrivate.
274     * pScrn->driverPrivate is initialised to NULL, so we can check if
275     * the allocation has already been done.
276     */
277    if (pScrn->driverPrivate != NULL)
278	return TRUE;
279
280    pScrn->driverPrivate = xnfcalloc(sizeof(PnozzRec), 1);
281    return TRUE;
282}
283
284static void
285PnozzFreeRec(ScrnInfoPtr pScrn)
286{
287    PnozzPtr pPnozz;
288
289    if (pScrn->driverPrivate == NULL)
290	return;
291
292    pPnozz = GET_PNOZZ_FROM_SCRN(pScrn);
293
294    free(pScrn->driverPrivate);
295    pScrn->driverPrivate = NULL;
296
297    return;
298}
299
300static const OptionInfoRec *
301PnozzAvailableOptions(int chipid, int busid)
302{
303    return PnozzOptions;
304}
305
306/* Mandatory */
307static void
308PnozzIdentify(int flags)
309{
310    xf86Msg(X_INFO, "%s: driver for Weitek P9100 found in Tadpole SPARCbook 3GX and others\n", PNOZZ_NAME);
311}
312
313
314/* Mandatory */
315static Bool
316PnozzProbe(DriverPtr drv, int flags)
317{
318    int i;
319    GDevPtr *devSections;
320    int *usedChips;
321    int numDevSections;
322    int numUsed;
323    Bool foundScreen = FALSE;
324    EntityInfoPtr pEnt;
325
326    /*
327     * The aim here is to find all cards that this driver can handle,
328     * and for the ones not already claimed by another driver, claim the
329     * slot, and allocate a ScrnInfoRec.
330     *
331     * This should be a minimal probe, and it should under no circumstances
332     * change the state of the hardware.  Because a device is found, don't
333     * assume that it will be used.  Don't do any initialisations other than
334     * the required ScrnInfoRec initialisations.  Don't allocate any new
335     * data structures.
336     */
337
338    /*
339     * Next we check, if there has been a chipset override in the config file.
340     * For this we must find out if there is an active device section which
341     * is relevant, i.e., which has no driver specified or has THIS driver
342     * specified.
343     */
344
345    if ((numDevSections = xf86MatchDevice(PNOZZ_DRIVER_NAME,
346					  &devSections)) <= 0) {
347	/*
348	 * There's no matching device section in the config file, so quit
349	 * now.
350	 */
351	return FALSE;
352    }
353
354    /*
355     * We need to probe the hardware first.  We then need to see how this
356     * fits in with what is given in the config file, and allow the config
357     * file info to override any contradictions.
358     */
359
360    numUsed = xf86MatchSbusInstances(PNOZZ_NAME, SBUS_DEVICE_P9100,
361		   devSections, numDevSections,
362		   drv, &usedChips);
363
364    free(devSections);
365    if (numUsed <= 0)
366	return FALSE;
367
368    if (flags & PROBE_DETECT)
369	foundScreen = TRUE;
370    else
371	for (i = 0; i < numUsed; i++) {
372	    pEnt = xf86GetEntityInfo(usedChips[i]);
373
374	    /*
375	     * Check that nothing else has claimed the slots.
376	     */
377	    if(pEnt->active) {
378		ScrnInfoPtr pScrn;
379
380		/* Allocate a ScrnInfoRec and claim the slot */
381		pScrn = xf86AllocateScreen(drv, 0);
382
383		/* Fill in what we can of the ScrnInfoRec */
384		pScrn->driverVersion = VERSION;
385		pScrn->driverName	 = PNOZZ_DRIVER_NAME;
386		pScrn->name		 = PNOZZ_NAME;
387		pScrn->Probe	 	 = PnozzProbe;
388		pScrn->PreInit	 	 = PnozzPreInit;
389		pScrn->ScreenInit	 = PnozzScreenInit;
390  		pScrn->SwitchMode	 = PnozzSwitchMode;
391  		pScrn->AdjustFrame	 = PnozzAdjustFrame;
392		pScrn->EnterVT		 = PnozzEnterVT;
393		pScrn->LeaveVT		 = PnozzLeaveVT;
394		pScrn->FreeScreen	 = PnozzFreeScreen;
395		pScrn->ValidMode	 = PnozzValidMode;
396		xf86AddEntityToScreen(pScrn, pEnt->index);
397		foundScreen = TRUE;
398	    }
399	    free(pEnt);
400    	}
401    free(usedChips);
402    return foundScreen;
403}
404
405/* Mandatory */
406static Bool
407PnozzPreInit(ScrnInfoPtr pScrn, int flags)
408{
409    PnozzPtr pPnozz;
410    sbusDevicePtr psdp;
411    MessageType from;
412    rgb defaultWeight = {0, 0, 0};
413    int i;
414
415    if (flags & PROBE_DETECT) return FALSE;
416
417    /*
418     * Note: This function is only called once at server startup, and
419     * not at the start of each server generation.  This means that
420     * only things that are persistent across server generations can
421     * be initialised here.  xf86Screens[] is (pScrn is a pointer to one
422     * of these).  Privates allocated using xf86AllocateScrnInfoPrivateIndex()
423     * are too, and should be used for data that must persist across
424     * server generations.
425     *
426     * Per-generation data should be allocated with
427     * AllocateScreenPrivateIndex() from the ScreenInit() function.
428     */
429
430    /* Allocate the PnozzRec driverPrivate */
431    if (!PnozzGetRec(pScrn)) {
432	return FALSE;
433    }
434    pPnozz = GET_PNOZZ_FROM_SCRN(pScrn);
435
436    /* always mismatch on first access */
437    pPnozz->offset_mask = 0xffffffff;
438
439    /* Set pScrn->monitor */
440    pScrn->monitor = pScrn->confScreen->monitor;
441
442    /* This driver doesn't expect more than one entity per screen */
443    if (pScrn->numEntities > 1)
444	return FALSE;
445    /* This is the general case */
446    for (i = 0; i < pScrn->numEntities; i++) {
447	EntityInfoPtr pEnt = xf86GetEntityInfo(pScrn->entityList[i]);
448
449	/* PNOZZ is purely SBUS */
450	if (pEnt->location.type == BUS_SBUS) {
451	    psdp = xf86GetSbusInfoForEntity(pEnt->index);
452	    pPnozz->psdp = psdp;
453	} else
454	    return FALSE;
455    }
456
457    /*********************
458    deal with depth
459    *********************/
460    if (!xf86SetDepthBpp(pScrn, 0, 0, 0, Support32bppFb)) {
461	return FALSE;
462    } else {
463	/* Check that the returned depth is one we support */
464#ifdef DEBUG
465	xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
466	    "Depth requested: %d\n", pScrn->depth);
467#endif
468	switch (pScrn->depth) {
469	case 8:
470	case 16:
471	case 24:
472	    /* OK */
473	    break;
474	default:
475	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
476		       "Given depth (%d) is not supported by this driver\n",
477		       pScrn->depth);
478	    return FALSE;
479	}
480    }
481    xf86PrintDepthBpp(pScrn);
482
483    /* We use a programmable clock */
484    pScrn->progClock = TRUE;
485
486    /* Set the bits per RGB for 8bpp mode */
487    if (pScrn->depth == 8)
488	pScrn->rgbBits = 8;
489
490    if (pScrn->depth > 8) {
491      if (!xf86SetWeight(pScrn, defaultWeight, defaultWeight))
492        return FALSE;
493    }
494
495    if (!xf86SetDefaultVisual(pScrn, -1)) {
496      return FALSE;
497    } else {
498      /* We don't currently support DirectColor at > 8bpp */
499      if (pScrn->depth > 8 && pScrn->defaultVisual != TrueColor) {
500        xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Given default visual"
501		 " (%s) is not supported at depth %d\n",
502		 xf86GetVisualName(pScrn->defaultVisual), pScrn->depth);
503        return FALSE;
504      }
505    }
506
507    /* Collect all of the relevant option flags (fill in pScrn->options) */
508    xf86CollectOptions(pScrn, NULL);
509
510    /* Process the options */
511    if (!(pPnozz->Options = malloc(sizeof(PnozzOptions))))
512	return FALSE;
513
514    memcpy(pPnozz->Options, PnozzOptions, sizeof(PnozzOptions));
515    xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, pPnozz->Options);
516
517    /*
518     * The new cmap code requires this to be initialised.
519     */
520
521    {
522	Gamma zeros = {0.0, 0.0, 0.0};
523
524	if (!xf86SetGamma(pScrn, zeros)) {
525	    return FALSE;
526	}
527    }
528
529    /* Set the bits per RGB for 8bpp mode */
530    from = X_DEFAULT;
531
532    /* determine whether we use hardware or software cursor */
533    pPnozz->HWCursor = TRUE;
534    if (xf86GetOptValBool(pPnozz->Options, OPTION_HW_CURSOR, &pPnozz->HWCursor))
535	from = X_CONFIG;
536    if (xf86ReturnOptValBool(pPnozz->Options, OPTION_SW_CURSOR, FALSE)) {
537	from = X_CONFIG;
538	pPnozz->HWCursor = FALSE;
539    }
540
541    xf86DrvMsg(pScrn->scrnIndex, from, "Using %s cursor\n",
542		pPnozz->HWCursor ? "HW" : "SW");
543
544    if (xf86ReturnOptValBool(pPnozz->Options, OPTION_NOACCEL, FALSE)) {
545	pPnozz->NoAccel = TRUE;
546	xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "Acceleration disabled\n");
547    }
548
549    pPnozz->useXAA = FALSE;
550
551    char *optstr;
552    optstr = (char *)xf86GetOptValString(pPnozz->Options, OPTION_ACCELMETHOD);
553    if (optstr == NULL) optstr = "exa";
554    if (xf86NameCmp(optstr, "xaa") == 0)
555	pPnozz->useXAA = TRUE;
556
557    if (xf86LoadSubModule(pScrn, "fb") == NULL) {
558	PnozzFreeRec(pScrn);
559	return FALSE;
560    }
561
562    if (xf86LoadSubModule(pScrn, "ramdac") == NULL) {
563	PnozzFreeRec(pScrn);
564	return FALSE;
565    }
566
567    /*********************
568    set up clock and mode stuff
569    *********************/
570
571    pScrn->progClock = TRUE;
572
573    if(pScrn->display->virtualX || pScrn->display->virtualY) {
574	xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
575		   "Pnozz does not support a virtual desktop\n");
576	pScrn->display->virtualX = 0;
577	pScrn->display->virtualY = 0;
578    }
579
580    xf86SbusUseBuiltinMode(pScrn, pPnozz->psdp);
581    pScrn->currentMode = pScrn->modes;
582    pScrn->displayWidth = pScrn->virtualX;
583
584    /* Set display resolution */
585    xf86SetDpi(pScrn, 0, 0);
586
587    return TRUE;
588}
589
590/* Mandatory */
591
592/* This gets called at the start of each server generation */
593
594static Bool
595PnozzScreenInit(SCREEN_INIT_ARGS_DECL)
596{
597    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
598    PnozzPtr pPnozz;
599    VisualPtr visual;
600    int ret,len=0,i;
601    unsigned int *regs, pctl, pfb, *fb;
602
603    /*
604     * First get the ScrnInfoRec
605     */
606    pScrn = xf86Screens[pScreen->myNum];
607
608    pPnozz = GET_PNOZZ_FROM_SCRN(pScrn);
609
610    /*
611     * XXX
612     * figure out how much video RAM we really have - 2MB is just by far the
613     * most common size
614     */
615    pPnozz->vidmem = 0x200000;	/* map 2MB */
616    pPnozz->fb =
617	xf86MapSbusMem (pPnozz->psdp, 0, pPnozz->vidmem);
618    fb=(unsigned int *)pPnozz->fb;
619
620    pPnozz->fbc =
621	xf86MapSbusMem (pPnozz->psdp, pPnozz->vidmem,0x8000);	/* map registers */
622
623    if (! pPnozz->fbc) {
624	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
625		"xf86MapSbusMem failed fbc:%p fb:%p\n",
626		pPnozz->fbc, pPnozz->fb);
627
628	if (pPnozz->fb) {
629            xf86UnmapSbusMem(pPnozz->psdp, pPnozz->fb, pPnozz->vidmem);
630            pPnozz->fb = NULL;
631	}
632
633	return FALSE;
634    }
635
636    /*
637     * The next step is to setup the screen's visuals, and initialise the
638     * framebuffer code.  In cases where the framebuffer's default
639     * choices for things like visual layouts and bits per RGB are OK,
640     * this may be as simple as calling the framebuffer's ScreenInit()
641     * function.  If not, the visuals will need to be setup before calling
642     * a fb ScreenInit() function and fixed up after.
643     */
644
645    /*
646     * Reset visual list.
647     */
648    miClearVisualTypes();
649
650#ifdef DEBUG
651    xf86Msg(X_ERROR, "depth: %d, bpp: %d\n", pScrn->depth, pScrn->bitsPerPixel);
652#endif
653    switch (pScrn->bitsPerPixel) {
654    	case 8:
655	    pPnozz->depthshift = 0;
656	    break;
657    	case 16:
658	    pPnozz->depthshift = 1;
659	    break;
660    	case 32:
661	    pPnozz->depthshift = 2;
662	    break;
663	default:
664	    return FALSE;
665    }
666    pPnozz->width = pScrn->virtualX;
667    pPnozz->height = pScrn->virtualY;
668    pPnozz->scanlinesize = pScrn->virtualX << pPnozz->depthshift;
669
670    PnozzSave(pPnozz);
671
672    /*
673     * ok, let's switch to whatever depth That Guy Out There wants.
674     * We won't switch video mode, only colour depth -
675     */
676    if(!PnozzSetDepth(pPnozz, pScrn->bitsPerPixel))
677    	return FALSE;
678
679    /* Setup the visuals we support. */
680
681    if (!miSetVisualTypes(pScrn->depth, miGetDefaultVisualMask(pScrn->depth),
682			  pScrn->rgbBits, pScrn->defaultVisual))
683	return FALSE;
684
685    miSetPixmapDepths();
686
687    /*
688     * Call the framebuffer layer's ScreenInit function, and fill in other
689     * pScreen fields.
690     */
691#if DEBUG
692    xf86Msg(X_ERROR, "sls: %d\n", pPnozz->scanlinesize);
693#endif
694    ret = fbScreenInit(pScreen, pPnozz->fb, pScrn->virtualX,
695		       pScrn->virtualY, pScrn->xDpi, pScrn->yDpi,
696		       pScrn->displayWidth, pScrn->bitsPerPixel);
697
698    /* should be set by PnozzSetDepth() */
699    pPnozz->maxheight = (pPnozz->vidmem / pPnozz->scanlinesize) & 0xffff;
700#if DEBUG
701    xf86Msg(X_ERROR, "max scanlines: %d\n", pPnozz->maxheight);
702#endif
703    if (!ret)
704	return FALSE;
705
706    if (pScrn->bitsPerPixel > 8) {
707      visual = pScreen->visuals + pScreen->numVisuals;
708      while (--visual >= pScreen->visuals) {
709        if ((visual->class | DynamicClass) == DirectColor) {
710	  visual->offsetRed = pScrn->offset.red;
711	  visual->offsetGreen = pScrn->offset.green;
712	  visual->offsetBlue = pScrn->offset.blue;
713	  visual->redMask = pScrn->mask.red;
714	  visual->greenMask = pScrn->mask.green;
715	  visual->blueMask = pScrn->mask.blue;
716        }
717      }
718    }
719
720    fbPictureInit(pScreen, 0, 0);
721
722    xf86SetBackingStore(pScreen);
723    xf86SetSilkenMouse(pScreen);
724    xf86SetBlackWhitePixels(pScreen);
725
726    if (!pPnozz->NoAccel) {
727#ifdef HAVE_XAA_H
728	if (pPnozz->useXAA) {
729            BoxRec bx;
730            pPnozz->pXAA = XAACreateInfoRec();
731            PnozzAccelInit(pScrn);
732            bx.x1 = bx.y1 = 0;
733            bx.x2 = pPnozz->width;
734            bx.y2 = pPnozz->maxheight;
735            xf86InitFBManager(pScreen, &bx);
736            if(!XAAInit(pScreen, pPnozz->pXAA))
737                return FALSE;
738            xf86Msg(X_INFO, "%s: Using XAA acceleration\n", pPnozz->psdp->device);
739	} else
740#endif /* HAVE_XAA_H */
741	{
742            /* EXA */
743            XF86ModReqInfo req;
744            int errmaj, errmin;
745
746            memset(&req, 0, sizeof(XF86ModReqInfo));
747            req.majorversion = EXA_VERSION_MAJOR;
748            req.minorversion = EXA_VERSION_MINOR;
749            if (!LoadSubModule(pScrn->module, "exa", NULL, NULL, NULL, &req,
750		&errmaj, &errmin)) {
751		LoaderErrorMsg(NULL, "exa", errmaj, errmin);
752		return FALSE;
753            }
754            if (!PnozzEXAInit(pScreen))
755		return FALSE;
756
757            xf86Msg(X_INFO, "%s: Using EXA acceleration\n", pPnozz->psdp->device);
758	}
759    }
760
761    /* Initialise cursor functions */
762    miDCInitialize (pScreen, xf86GetPointerScreenFuncs());
763
764    /*
765     * Initialize HW cursor layer.
766     * Must follow software cursor initialization
767     */
768    xf86SbusHideOsHwCursor(pPnozz->psdp);
769    if (pPnozz->HWCursor) {
770	extern Bool PnozzHWCursorInit(ScreenPtr pScreen);
771
772	if(!PnozzHWCursorInit(pScreen)) {
773	    xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
774		       "Hardware cursor initialization failed\n");
775	    return(FALSE);
776	}
777    }
778
779    /* Initialise default colourmap */
780    if (!miCreateDefColormap(pScreen))
781	return FALSE;
782#if 1
783    if(!xf86SbusHandleColormaps(pScreen, pPnozz->psdp))
784#else
785    if(!xf86HandleColormaps(pScreen, 256, pScrn->rgbBits, PnozzLoadPalette, NULL,
786        /*CMAP_PALETTED_TRUECOLOR|*/CMAP_RELOAD_ON_MODE_SWITCH))
787#endif
788	return FALSE;
789    pPnozz->CloseScreen = pScreen->CloseScreen;
790    pScreen->CloseScreen = PnozzCloseScreen;
791    pScreen->SaveScreen = PnozzSaveScreen;
792
793    /* Report any unused options (only for the first generation) */
794    if (serverGeneration == 1) {
795	xf86ShowUnusedOptions(pScrn->scrnIndex, pScrn->options);
796    }
797
798    /* unblank the screen */
799    PnozzSaveScreen(pScreen, SCREEN_SAVER_OFF);
800
801    /* Done */
802    return TRUE;
803}
804
805
806/* Usually mandatory */
807static Bool
808PnozzSwitchMode(SWITCH_MODE_ARGS_DECL)
809{
810    xf86Msg(X_ERROR, "SwitchMode: %d %d %d %d\n", mode->CrtcHTotal,
811        mode->CrtcHSyncStart, mode->CrtcHSyncEnd, mode->CrtcHDisplay);
812    return TRUE;
813}
814
815
816/*
817 * This function is used to initialize the Start Address - the first
818 * displayed location in the video memory.
819 */
820/* Usually mandatory */
821static void
822PnozzAdjustFrame(ADJUST_FRAME_ARGS_DECL)
823{
824    /* we don't support virtual desktops for now */
825    return;
826}
827
828/*
829 * This is called when VT switching back to the X server.  Its job is
830 * to reinitialise the video mode.
831 */
832
833/* Mandatory */
834static Bool
835PnozzEnterVT(VT_FUNC_ARGS_DECL)
836{
837    SCRN_INFO_PTR(arg);
838    PnozzPtr pPnozz = GET_PNOZZ_FROM_SCRN(pScrn);
839
840    xf86SbusHideOsHwCursor (pPnozz->psdp);
841    return TRUE;
842}
843
844
845/*
846 * This is called when VT switching away from the X server.
847 */
848
849/* Mandatory */
850static void
851PnozzLeaveVT(VT_FUNC_ARGS_DECL)
852{
853    return;
854}
855
856
857/*
858 * This is called at the end of each server generation.  It restores the
859 * original (text) mode.  It should really also unmap the video memory too.
860 */
861
862/* Mandatory */
863static Bool
864PnozzCloseScreen(CLOSE_SCREEN_ARGS_DECL)
865{
866    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
867    PnozzPtr pPnozz = GET_PNOZZ_FROM_SCRN(pScrn);
868    int state = 1;
869
870    pScrn->vtSema = FALSE;
871
872    if (pPnozz->HWCursor)
873    	PnozzHideCursor(pScrn);
874
875    PnozzRestore(pPnozz);	/* restore colour depth */
876
877    xf86UnmapSbusMem(pPnozz->psdp, pPnozz->fb,pPnozz->vidmem);
878    xf86UnmapSbusMem(pPnozz->psdp, pPnozz->fbc,0x8000);
879
880    /* make sure video is turned on */
881    ioctl(pPnozz->psdp->fd, FBIOSVIDEO, &state);
882
883    pScreen->CloseScreen = pPnozz->CloseScreen;
884    return (*pScreen->CloseScreen)(CLOSE_SCREEN_ARGS);
885}
886
887
888/* Free up any per-generation data structures */
889
890/* Optional */
891static void
892PnozzFreeScreen(FREE_SCREEN_ARGS_DECL)
893{
894    SCRN_INFO_PTR(arg);
895    PnozzFreeRec(pScrn);
896}
897
898
899/* Checks if a mode is suitable for the selected chipset. */
900
901/* Optional */
902static ModeStatus
903PnozzValidMode(SCRN_ARG_TYPE arg, DisplayModePtr mode, Bool verbose, int flags)
904{
905    if (mode->Flags & V_INTERLACE)
906	return(MODE_BAD);
907
908    return(MODE_OK);
909}
910
911/* Do screen blanking */
912
913/* Mandatory */
914static Bool
915PnozzSaveScreen(ScreenPtr pScreen, int mode)
916{
917    ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
918    PnozzPtr pPnozz = GET_PNOZZ_FROM_SCRN(pScrn);
919    int fd = pPnozz->psdp->fd, state;
920
921    /*
922     * we're using ioctl() instead of just whacking the DAC because the
923     * underlying driver will also turn off the backlight which we couldn't do
924     * from here without adding lots more hardware dependencies
925     */
926    switch(mode)
927    {
928	case SCREEN_SAVER_ON:
929	case SCREEN_SAVER_CYCLE:
930    		state = 0;
931		if(ioctl(fd, FBIOSVIDEO, &state) == -1)
932		{
933			/* complain */
934		}
935		break;
936	case SCREEN_SAVER_OFF:
937	case SCREEN_SAVER_FORCER:
938    		state = 1;
939		if(ioctl(fd, FBIOSVIDEO, &state) == -1)
940		{
941			/* complain */
942		}
943		break;
944	default:
945		return FALSE;
946    }
947    return TRUE;
948}
949
950int shift_1(int b)
951{
952    if (b > 0)
953	return (16 << b);
954    return 0;
955}
956
957int shift_2(int b)
958{
959    if (b > 0)
960	return (512 << b);
961    return 0;
962}
963
964void
965PnozzSave(PnozzPtr pPnozz)
966{
967	int i;
968	pPnozz->SvSysConf = pnozz_read_4(pPnozz, SYS_CONF);
969	pPnozz->SvDAC_MC3 = pnozz_read_dac_ctl_reg(pPnozz, DAC_MISC_3);
970	pPnozz->SvDAC_MCCR = pnozz_read_dac_ctl_reg(pPnozz, DAC_MISC_CLK);
971	pPnozz->SvDAC_PF = pnozz_read_dac_ctl_reg(pPnozz, DAC_PIXEL_FMT);
972	pPnozz->SvPLL = pnozz_read_dac_ctl_reg(pPnozz, DAC_PLL0);
973	pPnozz->SvVCO = pnozz_read_dac_ctl_reg(pPnozz, DAC_VCO_DIV);
974	pPnozz->SvMemCtl = pnozz_read_4(pPnozz, VID_MEM_CONFIG);
975	for (i = 0; i < 4; i++)
976		pPnozz->CRTC[i] = pnozz_read_4(pPnozz, VID_HTOTAL + (i << 2));
977	pPnozz->DidSave = 1;
978#if DEBUG
979	xf86Msg(X_ERROR, "Saved: %x %x %x %x\n", pPnozz->SvSysConf,
980	    pPnozz->SvDAC_MCCR, pPnozz->SvDAC_PF, pPnozz->SvDAC_MC3);
981	DumpSCR(pPnozz->SvSysConf);
982#endif
983}
984
985void DumpSCR(unsigned int scr)
986{
987#if DEBUG
988	int s0, s1, s2, s3, ps;
989	int width;
990	ps = (scr >> PIXEL_SHIFT) & 7;
991	s0 = (scr >> SHIFT_0) & 7;
992	s1 = (scr >> SHIFT_1) & 7;
993	s2 = (scr >> SHIFT_2) & 7;
994	s3 = (scr >> SHIFT_3) & 3;
995	width = shift_1(s0) + shift_1(s1) + shift_1(s2) + shift_2(s3);
996	xf86Msg(X_ERROR, "ps: %d wi: %d\n", ps, width);
997#endif
998}
999
1000void DumpDAC(PnozzPtr pPnozz)
1001{
1002#if DEBUG
1003    int addr, i, val;
1004    char line[256], buffer[16];
1005    pnozz_write_dac(pPnozz, DAC_INDX_LO, 0);
1006    pnozz_write_dac(pPnozz, DAC_INDX_HI, 0);
1007    for (addr = 0; addr < 0x100; addr += 16) {
1008    	snprintf(line, 16, "%02x:", addr);
1009	for (i=0;i<16;i++) {
1010	    val = pnozz_read_dac(pPnozz, DAC_INDX_DATA);
1011	    snprintf(buffer, 16, " %02x", val);
1012	    strcat(line, buffer);
1013	}
1014	xf86Msg(X_ERROR, "%s\n", line);
1015    }
1016#endif
1017}
1018
1019void DumpCRTC(PnozzPtr pPnozz)
1020{
1021#if DEBUG
1022    int i;
1023    unsigned int reg;
1024    for (i = 0x108; i<0x140; i += 4) {
1025        reg = pnozz_read_4(pPnozz, i);
1026	xf86Msg(X_ERROR, "%x / %d ", reg, reg);
1027    }
1028    reg = pnozz_read_4(pPnozz, VID_MEM_CONFIG);
1029    xf86Msg(X_ERROR, "memcfg: %08x\n", reg);
1030    xf86Msg(X_ERROR, "shiftclk:  %x\n", (reg >> 10) & 7);
1031    xf86Msg(X_ERROR, "shiftmode: %x\n", (reg >> 22) & 3);
1032    xf86Msg(X_ERROR, "crtc_clk:  %x\n", (reg >> 13) & 7);
1033#endif
1034}
1035
1036void
1037PnozzRestore(PnozzPtr pPnozz)
1038{
1039    int i;
1040    if(pPnozz->DidSave == 1) {
1041	pnozz_write_4(pPnozz, SYS_CONF, pPnozz->SvSysConf);
1042	pnozz_write_4(pPnozz, VID_MEM_CONFIG, pPnozz->SvMemCtl);
1043	for (i = 0; i < 4; i++)
1044	    pnozz_write_4(pPnozz, VID_HTOTAL + (i << 2), pPnozz->CRTC[i]);
1045
1046        pnozz_write_dac_ctl_reg(pPnozz, DAC_PLL0, pPnozz->SvPLL);
1047        pnozz_write_dac_ctl_reg(pPnozz, DAC_MISC_3, pPnozz->SvDAC_MC3);
1048        pnozz_write_dac_ctl_reg(pPnozz, DAC_MISC_CLK, pPnozz->SvDAC_MCCR);
1049	pnozz_write_dac_ctl_reg(pPnozz, DAC_PIXEL_FMT, pPnozz->SvDAC_PF);
1050	pnozz_write_dac_ctl_reg(pPnozz, DAC_VCO_DIV, pPnozz->SvVCO);
1051    }
1052}
1053
1054unsigned int upper_bit(unsigned int b)
1055{
1056	unsigned int mask=0x80000000;
1057	int cnt = 31;
1058	if (b == 0)
1059		return -1;
1060	while ((mask != 0) && ((b & mask) == 0)) {
1061		mask = mask >> 1;
1062		cnt--;
1063	}
1064	return cnt;
1065}
1066
1067/*
1068 * To switch colour depth we need to:
1069 * - double or quadruple both crtc and shift clock ( for 16 or 32 bit )
1070 * - double or quadruple scanline length
1071 * - switch the DAC to the appropriate pixel format
1072 * - tell the drawing engine about new line length / pixel size
1073 */
1074
1075int
1076PnozzSetDepth(PnozzPtr pPnozz, int depth)
1077{
1078    int new_sls;
1079    unsigned int bits, scr, sscr, memctl, mem;
1080    int s0, s1, s2, s3, ps, crtcline;
1081    unsigned char pf, mc3, es;
1082
1083#ifdef DEBUG
1084    xf86Msg(X_ERROR, "SetDepth: %d\n", depth);
1085    DumpDAC(pPnozz);
1086    DumpCRTC(pPnozz);
1087#endif
1088
1089    switch (depth) {
1090	case 8:
1091	    pPnozz->depthshift = 0;
1092	    ps = 2;
1093	    pf = 3;
1094	    mc3 = 0;
1095	    es = 0;	/* no swapping */
1096	    memctl = 3;
1097	    break;
1098	case 16:
1099	    pPnozz->depthshift = 1;
1100	    ps = 3;
1101	    pf = 4;
1102	    mc3 = 0;
1103	    es = 2;	/* swap bytes in 16bit words */
1104	    memctl = 2;
1105	    break;
1106	case 24:
1107	    /* boo */
1108	    xf86Msg(X_ERROR, "We don't DO 24bit pixels dammit!\n");
1109	    return 0;
1110	case 32:
1111	    pPnozz->depthshift = 2;
1112	    ps = 5;
1113	    pf = 6;
1114	    mc3 = 0;
1115	    es = 6;	/* swap both half-words and bytes */
1116	    memctl = 1;	/* 0 */
1117	    break;
1118    }
1119    /*
1120     * this could be done a lot shorter and faster but then nobody would
1121     * understand what the hell we're doing here without getting a major
1122     * headache. Scanline size is encoded as 4 shift values, 3 of them 3 bits
1123     * wide, 16 << n for n>0, one 2 bits, 512 << n for n>0. n==0 means 0
1124     */
1125    new_sls = pPnozz->width << pPnozz->depthshift;
1126    pPnozz->scanlinesize = new_sls;
1127    bits = new_sls;
1128    s3 = upper_bit(bits);
1129    if (s3 > 9) {
1130	bits &= ~(1 << s3);
1131	s3 -= 9;
1132    } else s3 = 0;
1133    s2 = upper_bit(bits);
1134    if (s2 > 0) {
1135	bits &= ~(1 << s2);
1136	s2 -= 4;
1137    } else s2 = 0;
1138    s1 = upper_bit(bits);
1139    if (s1 > 0) {
1140        bits &= ~(1 << s1);
1141        s1 -= 4;
1142    } else s1 = 0;
1143    s0 = upper_bit(bits);
1144    if (s0 > 0) {
1145        bits &= ~(1 << s0);
1146        s0 -= 4;
1147    } else s0 = 0;
1148
1149#if DEBUG
1150    xf86Msg(X_ERROR, "sls: %x sh: %d %d %d %d leftover: %x\n", new_sls, s0, s1,
1151        s2, s3, bits);
1152#endif
1153
1154    /*
1155     * now let's put these values into the System Config Register. No need to
1156     * read it here since we (hopefully) just saved the content
1157     */
1158    scr = pnozz_read_4(pPnozz, SYS_CONF);
1159    scr = (s0 << SHIFT_0) | (s1 << SHIFT_1) | (s2 << SHIFT_2) |
1160        (s3 << SHIFT_3) | (ps << PIXEL_SHIFT) | (es << SWAP_SHIFT);
1161#if DEBUG
1162    xf86Msg(X_ERROR, "new scr: %x DAC %x %x\n", scr, pf, mc3);
1163    DumpSCR(scr);
1164#endif
1165
1166    mem = pnozz_read_4(pPnozz, VID_MEM_CONFIG);
1167#if DEBUG
1168    xf86Msg(X_ERROR, "old memctl: %08x\n", mem);
1169#endif
1170    /* set shift and crtc clock */
1171    mem &= ~(0x0000fc00);
1172    mem |= (memctl << 10) | (memctl << 13);
1173    pnozz_write_4(pPnozz, VID_MEM_CONFIG, mem);
1174#if DEBUG
1175    xf86Msg(X_ERROR, "new memctl: %08x\n", mem);
1176#endif
1177    /* whack the engine... */
1178    pnozz_write_4(pPnozz, SYS_CONF, scr);
1179
1180    /* ok, whack the DAC */
1181    pnozz_write_dac_ctl_reg(pPnozz, DAC_MISC_1, 0x11);
1182    pnozz_write_dac_ctl_reg(pPnozz, DAC_MISC_2, 0x45);
1183    pnozz_write_dac_ctl_reg(pPnozz, DAC_MISC_3, mc3);
1184    /*
1185     * despite the 3GX manual saying otherwise we don't need to mess with any
1186     * clock dividers here
1187     */
1188    pnozz_write_dac_ctl_reg(pPnozz, DAC_MISC_CLK, 1);
1189    pnozz_write_dac_ctl_reg(pPnozz, 3, 0);
1190    pnozz_write_dac_ctl_reg(pPnozz, 4, 0);
1191
1192    pnozz_write_dac_ctl_reg(pPnozz, DAC_POWER_MGT, 0);
1193    pnozz_write_dac_ctl_reg(pPnozz, DAC_OPERATION, 0);
1194    pnozz_write_dac_ctl_reg(pPnozz, DAC_PALETTE_CTRL, 0);
1195
1196    pnozz_write_dac_ctl_reg(pPnozz, DAC_PIXEL_FMT, pf);
1197
1198    /* TODO: distinguish between 15 and 16 bit */
1199    pnozz_write_dac_ctl_reg(pPnozz, DAC_8BIT_CTRL, 0);
1200    /* direct colour, linear, 565 */
1201    pnozz_write_dac_ctl_reg(pPnozz, DAC_16BIT_CTRL, 0xc6);
1202    /* direct colour */
1203    pnozz_write_dac_ctl_reg(pPnozz, DAC_32BIT_CTRL, 3);
1204
1205    pnozz_write_dac_ctl_reg(pPnozz, 0x10, 2);
1206    pnozz_write_dac_ctl_reg(pPnozz, 0x11, 0);
1207    pnozz_write_dac_ctl_reg(pPnozz, 0x14, 5);
1208    pnozz_write_dac_ctl_reg(pPnozz, 0x08, 1);
1209    pnozz_write_dac_ctl_reg(pPnozz, 0x15, 5);
1210    pnozz_write_dac_ctl_reg(pPnozz, 0x16, 0x63);
1211
1212    /* whack the CRTC */
1213    /* we always transfer 64bit in one go */
1214    crtcline = pPnozz->scanlinesize >> 3;
1215#if DEBUG
1216    xf86Msg(X_ERROR, "crtcline: %d\n", crtcline);
1217#endif
1218    pnozz_write_4(pPnozz, VID_HTOTAL, (24 << pPnozz->depthshift) + crtcline);
1219    pnozz_write_4(pPnozz, VID_HSRE, 8 << pPnozz->depthshift);
1220    pnozz_write_4(pPnozz, VID_HBRE, 18 << pPnozz->depthshift);
1221    pnozz_write_4(pPnozz, VID_HBFE, (18 << pPnozz->depthshift) + crtcline);
1222
1223#if DEBUG
1224    sscr = pnozz_read_4(pPnozz, SYS_CONF);
1225    xf86Msg(X_ERROR, "scr: %x\n", sscr);
1226#endif
1227    return TRUE;
1228}
1229