fbdevhw.c revision 9ace9065
1/* all driver need this */
2#ifdef HAVE_XORG_CONFIG_H
3#include <xorg-config.h>
4#endif
5
6#include <string.h>
7
8#include "xf86.h"
9#include "xf86_OSproc.h"
10
11/* pci stuff */
12#include "xf86PciInfo.h"
13#include "xf86Pci.h"
14
15#include "xf86cmap.h"
16
17#include "fbdevhw.h"
18#include "fbpriv.h"
19#include "globals.h"
20#include <X11/extensions/dpmsconst.h>
21
22#define PAGE_MASK               (~(getpagesize() - 1))
23
24static XF86ModuleVersionInfo fbdevHWVersRec =
25{
26	"fbdevhw",
27	MODULEVENDORSTRING,
28	MODINFOSTRING1,
29	MODINFOSTRING2,
30	XORG_VERSION_CURRENT,
31	0, 0, 2,
32	ABI_CLASS_VIDEODRV,
33	ABI_VIDEODRV_VERSION,
34	MOD_CLASS_NONE,
35	{0,0,0,0}
36};
37
38_X_EXPORT XF86ModuleData fbdevhwModuleData = {
39    &fbdevHWVersRec,
40    NULL,
41    NULL
42};
43
44#include <fcntl.h>
45#include <errno.h>
46#include <sys/mman.h>
47#include <sys/ioctl.h>
48#include <stdio.h>
49#include <stdlib.h>
50#include <unistd.h>
51#include <string.h>
52
53/* -------------------------------------------------------------------- */
54/* our private data, and two functions to allocate/free this            */
55
56#define FBDEVHWPTRLVAL(p) (p)->privates[fbdevHWPrivateIndex].ptr
57#define FBDEVHWPTR(p) ((fbdevHWPtr)(FBDEVHWPTRLVAL(p)))
58
59static int fbdevHWPrivateIndex = -1;
60
61typedef struct {
62	/* framebuffer device: filename (/dev/fb*), handle, more */
63	char*				device;
64	int				fd;
65	void*				fbmem;
66	unsigned int			fbmem_len;
67	unsigned int			fboff;
68	char*				mmio;
69	unsigned int			mmio_len;
70
71	/* current hardware state */
72	struct fb_fix_screeninfo	fix;
73	struct fb_var_screeninfo	var;
74
75	/* saved video mode */
76	struct fb_var_screeninfo	saved_var;
77
78	/* buildin video mode */
79	DisplayModeRec			buildin;
80
81} fbdevHWRec, *fbdevHWPtr;
82
83Bool
84fbdevHWGetRec(ScrnInfoPtr pScrn)
85{
86	fbdevHWPtr fPtr;
87
88	if (fbdevHWPrivateIndex < 0)
89		fbdevHWPrivateIndex = xf86AllocateScrnInfoPrivateIndex();
90
91	if (FBDEVHWPTR(pScrn) != NULL)
92		return TRUE;
93
94	fPtr = FBDEVHWPTRLVAL(pScrn) = xnfcalloc(sizeof(fbdevHWRec), 1);
95	return TRUE;
96}
97
98void
99fbdevHWFreeRec(ScrnInfoPtr pScrn)
100{
101	if (fbdevHWPrivateIndex < 0)
102		return;
103	if (FBDEVHWPTR(pScrn) == NULL)
104		return;
105	free(FBDEVHWPTR(pScrn));
106	FBDEVHWPTRLVAL(pScrn) = NULL;
107}
108
109int
110fbdevHWGetFD(ScrnInfoPtr pScrn)
111{
112    fbdevHWPtr fPtr;
113
114    fbdevHWGetRec(pScrn);
115    fPtr = FBDEVHWPTR(pScrn);
116
117    return fPtr->fd;
118}
119
120/* -------------------------------------------------------------------- */
121/* some helpers for printing debug informations                         */
122
123#if DEBUG
124static void
125print_fbdev_mode(char *txt, struct fb_var_screeninfo *var)
126{
127	ErrorF( "fbdev %s mode:\t%d   %d %d %d %d   %d %d %d %d   %d %d:%d:%d\n",
128		txt,var->pixclock,
129		var->xres, var->right_margin, var->hsync_len, var->left_margin,
130		var->yres, var->lower_margin, var->vsync_len, var->upper_margin,
131		var->bits_per_pixel,
132		var->red.length, var->green.length, var->blue.length);
133}
134
135static void
136print_xfree_mode(char *txt, DisplayModePtr mode)
137{
138	ErrorF( "xfree %s mode:\t%d   %d %d %d %d   %d %d %d %d\n",
139		txt,mode->Clock,
140		mode->HDisplay, mode->HSyncStart, mode->HSyncEnd, mode->HTotal,
141		mode->VDisplay, mode->VSyncStart, mode->VSyncEnd, mode->VTotal);
142}
143#endif
144
145/* -------------------------------------------------------------------- */
146/* Convert timings between the XFree and the Frame Buffer Device        */
147
148static void
149xfree2fbdev_fblayout(ScrnInfoPtr pScrn, struct fb_var_screeninfo *var)
150{
151	var->xres_virtual   = pScrn->displayWidth ? pScrn->displayWidth :
152			      pScrn->virtualX;
153	var->yres_virtual   = pScrn->virtualY;
154	var->bits_per_pixel = pScrn->bitsPerPixel;
155	if (pScrn->defaultVisual == TrueColor ||
156	    pScrn->defaultVisual == DirectColor) {
157	    var->red.length     = pScrn->weight.red;
158	    var->green.length   = pScrn->weight.green;
159	    var->blue.length    = pScrn->weight.blue;
160	} else {
161	    var->red.length     = 8;
162	    var->green.length   = 8;
163	    var->blue.length    = 8;
164	}
165}
166
167static void
168xfree2fbdev_timing(DisplayModePtr mode, struct fb_var_screeninfo *var)
169{
170	var->xres = mode->HDisplay;
171	var->yres = mode->VDisplay;
172	if (var->xres_virtual < var->xres)
173		var->xres_virtual = var->xres;
174	if (var->yres_virtual < var->yres)
175		var->yres_virtual = var->yres;
176	var->xoffset = var->yoffset = 0;
177	var->pixclock = mode->Clock ? 1000000000/mode->Clock : 0;
178	var->right_margin = mode->HSyncStart-mode->HDisplay;
179	var->hsync_len = mode->HSyncEnd-mode->HSyncStart;
180	var->left_margin = mode->HTotal-mode->HSyncEnd;
181	var->lower_margin = mode->VSyncStart-mode->VDisplay;
182	var->vsync_len = mode->VSyncEnd-mode->VSyncStart;
183	var->upper_margin = mode->VTotal-mode->VSyncEnd;
184	var->sync = 0;
185	if (mode->Flags & V_PHSYNC)
186		var->sync |= FB_SYNC_HOR_HIGH_ACT;
187	if (mode->Flags & V_PVSYNC)
188		var->sync |= FB_SYNC_VERT_HIGH_ACT;
189	if (mode->Flags & V_PCSYNC)
190		var->sync |= FB_SYNC_COMP_HIGH_ACT;
191	if (mode->Flags & V_BCAST)
192		var->sync |= FB_SYNC_BROADCAST;
193	if (mode->Flags & V_INTERLACE)
194		var->vmode = FB_VMODE_INTERLACED;
195	else if (mode->Flags & V_DBLSCAN)
196		var->vmode = FB_VMODE_DOUBLE;
197	else
198		var->vmode = FB_VMODE_NONINTERLACED;
199}
200
201static Bool
202fbdev_modes_equal(struct fb_var_screeninfo *set, struct fb_var_screeninfo *req)
203{
204	return (set->xres_virtual >= req->xres_virtual &&
205		set->yres_virtual >= req->yres_virtual &&
206		set->bits_per_pixel == req->bits_per_pixel &&
207		set->red.length == req->red.length &&
208		set->green.length == req->green.length &&
209		set->blue.length == req->blue.length &&
210		set->xres == req->xres && set->yres == req->yres &&
211		set->right_margin == req->right_margin &&
212		set->hsync_len == req->hsync_len &&
213		set->left_margin == req->left_margin &&
214		set->lower_margin == req->lower_margin &&
215		set->vsync_len == req->vsync_len &&
216		set->upper_margin == req->upper_margin &&
217		set->sync == req->sync && set->vmode == req->vmode);
218}
219
220static void
221fbdev2xfree_timing(struct fb_var_screeninfo *var, DisplayModePtr mode)
222{
223	mode->Clock = var->pixclock ? 1000000000/var->pixclock : 0;
224	mode->HDisplay = var->xres;
225	mode->HSyncStart = mode->HDisplay+var->right_margin;
226	mode->HSyncEnd = mode->HSyncStart+var->hsync_len;
227	mode->HTotal = mode->HSyncEnd+var->left_margin;
228	mode->VDisplay = var->yres;
229	mode->VSyncStart = mode->VDisplay+var->lower_margin;
230	mode->VSyncEnd = mode->VSyncStart+var->vsync_len;
231	mode->VTotal = mode->VSyncEnd+var->upper_margin;
232	mode->Flags = 0;
233	mode->Flags |= var->sync & FB_SYNC_HOR_HIGH_ACT ? V_PHSYNC : V_NHSYNC;
234	mode->Flags |= var->sync & FB_SYNC_VERT_HIGH_ACT ? V_PVSYNC : V_NVSYNC;
235	mode->Flags |= var->sync & FB_SYNC_COMP_HIGH_ACT ? V_PCSYNC : V_NCSYNC;
236	if (var->sync & FB_SYNC_BROADCAST)
237		mode->Flags |= V_BCAST;
238	if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED)
239		mode->Flags |= V_INTERLACE;
240	else if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE)
241		mode->Flags |= V_DBLSCAN;
242	mode->SynthClock = mode->Clock;
243	mode->CrtcHDisplay = mode->HDisplay;
244	mode->CrtcHSyncStart = mode->HSyncStart;
245	mode->CrtcHSyncEnd = mode->HSyncEnd;
246	mode->CrtcHTotal = mode->HTotal;
247	mode->CrtcVDisplay = mode->VDisplay;
248	mode->CrtcVSyncStart = mode->VSyncStart;
249	mode->CrtcVSyncEnd = mode->VSyncEnd;
250	mode->CrtcVTotal = mode->VTotal;
251	mode->CrtcHAdjusted = FALSE;
252	mode->CrtcVAdjusted = FALSE;
253}
254
255
256/* -------------------------------------------------------------------- */
257/* open correct framebuffer device                                      */
258
259/**
260 * Try to find the framebuffer device for a given PCI device
261 */
262static int
263fbdev_open_pci(struct pci_device * pPci, char **namep)
264{
265    struct	fb_fix_screeninfo fix;
266    char	filename[256];
267    int	fd, i;
268
269    for (i = 0; i < 8; i++) {
270	sprintf(filename,
271		"/sys/bus/pci/devices/%04x:%02x:%02x.%d/graphics/fb%d",
272		pPci->domain, pPci->bus, pPci->dev, pPci->func, i);
273
274	fd = open(filename, O_RDONLY, 0);
275        if (fd < 0) {
276            sprintf(filename,
277                    "/sys/bus/pci/devices/%04x:%02x:%02x.%d/graphics:fb%d",
278                    pPci->domain, pPci->bus, pPci->dev, pPci->func, i);
279            fd = open(filename, O_RDONLY, 0);
280        }
281	if (fd >= 0) {
282	    close(fd);
283	    sprintf(filename, "/dev/fb%d", i);
284
285	    fd = open(filename, O_RDWR, 0);
286	    if (fd != -1) {
287		if (ioctl(fd, FBIOGET_FSCREENINFO, (void*) & fix) != -1) {
288		    if (namep) {
289			*namep = xnfalloc(16);
290			strncpy(*namep,fix.id,16);
291		    }
292
293		    return fd;
294		}
295		close(fd);
296	    }
297	}
298    }
299
300    if (namep)
301      *namep = NULL;
302
303    xf86DrvMsg(-1, X_ERROR, "Unable to find a valid framebuffer device\n");
304    return -1;
305}
306
307static int
308fbdev_open(int scrnIndex, char *dev, char** namep)
309{
310	struct	fb_fix_screeninfo fix;
311	int    fd;
312
313	/* try argument (from XF86Config) first */
314	if (dev) {
315	    fd = open(dev,O_RDWR,0);
316	} else {
317	    /* second: environment variable */
318	    dev = getenv("FRAMEBUFFER");
319	    if ((NULL == dev) || ((fd = open(dev,O_RDWR,0)) == -1)) {
320		/* last try: default device */
321		dev = "/dev/fb0";
322		fd = open(dev,O_RDWR,0);
323	    }
324	}
325
326	if (fd == -1) {
327	    xf86DrvMsg(scrnIndex, X_ERROR,
328		       "open %s: %s\n", dev, strerror(errno));
329	    return -1;
330	}
331
332	if (namep) {
333	    if (-1 == ioctl(fd,FBIOGET_FSCREENINFO,(void*)(&fix))) {
334		*namep = NULL;
335		xf86DrvMsg(scrnIndex, X_ERROR,
336			   "FBIOGET_FSCREENINFO: %s\n", strerror(errno));
337		return -1;
338	    } else {
339		*namep = xnfalloc(16);
340		strncpy(*namep,fix.id,16);
341	    }
342	}
343	return fd;
344}
345
346/* -------------------------------------------------------------------- */
347
348Bool
349fbdevHWProbe(struct pci_device * pPci, char *device,char **namep)
350{
351	int fd;
352
353	if (pPci)
354		fd = fbdev_open_pci(pPci,namep);
355	else
356		fd = fbdev_open(-1,device,namep);
357
358	if (-1 == fd)
359		return FALSE;
360	close(fd);
361	return TRUE;
362}
363
364Bool
365fbdevHWInit(ScrnInfoPtr pScrn, struct pci_device * pPci, char *device)
366{
367	fbdevHWPtr fPtr;
368
369	fbdevHWGetRec(pScrn);
370	fPtr = FBDEVHWPTR(pScrn);
371
372	/* open device */
373	if (pPci)
374		fPtr->fd = fbdev_open_pci(pPci,NULL);
375	else
376		fPtr->fd = fbdev_open(pScrn->scrnIndex,device,NULL);
377	if (-1 == fPtr->fd) {
378		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
379			   "Failed to open framebuffer device, consult warnings"
380			   " and/or errors above for possible reasons\n"
381			   "\t(you may have to look at the server log to see"
382			   " warnings)\n");
383		return FALSE;
384	}
385
386	/* get current fb device settings */
387	if (-1 == ioctl(fPtr->fd,FBIOGET_FSCREENINFO,(void*)(&fPtr->fix))) {
388		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
389			   "ioctl FBIOGET_FSCREENINFO: %s\n",
390			   strerror(errno));
391		return FALSE;
392	}
393	if (-1 == ioctl(fPtr->fd,FBIOGET_VSCREENINFO,(void*)(&fPtr->var))) {
394		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
395			   "ioctl FBIOGET_VSCREENINFO: %s\n",
396			   strerror(errno));
397		return FALSE;
398	}
399
400	/* we can use the current settings as "buildin mode" */
401	fbdev2xfree_timing(&fPtr->var, &fPtr->buildin);
402	fPtr->buildin.name  = "current";
403	fPtr->buildin.next  = &fPtr->buildin;
404	fPtr->buildin.prev  = &fPtr->buildin;
405	fPtr->buildin.type |= M_T_BUILTIN;
406
407	return TRUE;
408}
409
410char*
411fbdevHWGetName(ScrnInfoPtr pScrn)
412{
413	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
414	return fPtr->fix.id;
415}
416
417int
418fbdevHWGetDepth(ScrnInfoPtr pScrn, int *fbbpp)
419{
420	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
421
422	if (fbbpp)
423	    *fbbpp = fPtr->var.bits_per_pixel;
424
425	if (fPtr->fix.visual == FB_VISUAL_TRUECOLOR ||
426	    fPtr->fix.visual == FB_VISUAL_DIRECTCOLOR)
427		return fPtr->var.red.length+fPtr->var.green.length+
428			fPtr->var.blue.length;
429	else
430		return fPtr->var.bits_per_pixel;
431}
432
433int
434fbdevHWGetLineLength(ScrnInfoPtr pScrn)
435{
436	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
437
438	if (fPtr->fix.line_length)
439		return fPtr->fix.line_length;
440	else
441		return fPtr->var.xres_virtual*fPtr->var.bits_per_pixel/8;
442}
443
444int
445fbdevHWGetType(ScrnInfoPtr pScrn)
446{
447	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
448	return fPtr->fix.type;
449}
450
451int
452fbdevHWGetVidmem(ScrnInfoPtr pScrn)
453{
454	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
455	return fPtr->fix.smem_len;
456}
457
458static Bool
459fbdevHWSetMode(ScrnInfoPtr pScrn, DisplayModePtr mode, Bool check)
460{
461	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
462	struct fb_var_screeninfo req_var = fPtr->var, set_var;
463
464	xfree2fbdev_fblayout(pScrn, &req_var);
465	xfree2fbdev_timing(mode, &req_var);
466
467#if DEBUG
468	print_xfree_mode("init", mode);
469	print_fbdev_mode("init", &req_var);
470#endif
471
472	set_var = req_var;
473
474	if (check)
475		set_var.activate = FB_ACTIVATE_TEST;
476
477	if (0 != ioctl(fPtr->fd, FBIOPUT_VSCREENINFO, (void*)(&set_var))) {
478		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
479			   "FBIOPUT_VSCREENINFO: %s\n", strerror(errno));
480		return FALSE;
481	}
482
483	if (!fbdev_modes_equal(&set_var, &req_var)) {
484		if (!check)
485			xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
486				   "FBIOPUT_VSCREENINFO succeeded but modified "
487				   "mode\n");
488#if DEBUG
489		print_fbdev_mode("returned", &set_var);
490#endif
491		return FALSE;
492	}
493
494	if (!check)
495		fPtr->var = set_var;
496
497	return TRUE;
498}
499
500void
501fbdevHWSetVideoModes(ScrnInfoPtr pScrn)
502{
503	char **modename;
504	DisplayModePtr mode,this,last = pScrn->modes;
505
506	if (NULL == pScrn->display->modes)
507		return;
508
509	pScrn->virtualX = pScrn->display->virtualX;
510	pScrn->virtualY = pScrn->display->virtualY;
511
512	for (modename = pScrn->display->modes; *modename != NULL; modename++) {
513		for (mode = pScrn->monitor->Modes; mode != NULL; mode = mode->next)
514			if (0 == strcmp(mode->name,*modename))
515				break;
516		if (NULL == mode) {
517			xf86DrvMsg(pScrn->scrnIndex, X_INFO,
518				   "\tmode \"%s\" not found\n", *modename);
519			continue;
520		}
521
522		if (!fbdevHWSetMode(pScrn, mode, TRUE)) {
523			xf86DrvMsg(pScrn->scrnIndex, X_INFO,
524				   "\tmode \"%s\" test failed\n", *modename);
525			continue;
526		}
527		xf86DrvMsg(pScrn->scrnIndex, X_INFO,
528			   "\tmode \"%s\" ok\n", *modename);
529
530		if (pScrn->virtualX < mode->HDisplay)
531			pScrn->virtualX = mode->HDisplay;
532		if (pScrn->virtualY < mode->VDisplay)
533			pScrn->virtualY = mode->VDisplay;
534
535		if (NULL == pScrn->modes) {
536			this = pScrn->modes = xf86DuplicateMode(mode);
537			this->next = this;
538			this->prev = this;
539		} else {
540			this = xf86DuplicateMode(mode);
541			this->next = pScrn->modes;
542			this->prev = last;
543			last->next = this;
544			pScrn->modes->prev = this;
545		}
546		last = this;
547	}
548}
549
550DisplayModePtr
551fbdevHWGetBuildinMode(ScrnInfoPtr pScrn)
552{
553	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
554	return &fPtr->buildin;
555}
556
557void
558fbdevHWUseBuildinMode(ScrnInfoPtr pScrn)
559{
560	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
561
562	pScrn->modes    = &fPtr->buildin;
563	pScrn->virtualX = pScrn->display->virtualX;
564	pScrn->virtualY = pScrn->display->virtualY;
565	if (pScrn->virtualX < fPtr->buildin.HDisplay)
566		pScrn->virtualX = fPtr->buildin.HDisplay;
567	if (pScrn->virtualY < fPtr->buildin.VDisplay)
568		pScrn->virtualY = fPtr->buildin.VDisplay;
569}
570
571/* -------------------------------------------------------------------- */
572
573static void
574calculateFbmem_len(fbdevHWPtr fPtr)
575{
576	fPtr->fboff = (unsigned long) fPtr->fix.smem_start & ~PAGE_MASK;
577	fPtr->fbmem_len = (fPtr->fboff+fPtr->fix.smem_len+~PAGE_MASK) &
578			  PAGE_MASK;
579}
580
581
582void*
583fbdevHWMapVidmem(ScrnInfoPtr pScrn)
584{
585	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
586
587	if (NULL == fPtr->fbmem) {
588		calculateFbmem_len(fPtr);
589		fPtr->fbmem = mmap(NULL, fPtr->fbmem_len, PROT_READ | PROT_WRITE,
590				   MAP_SHARED, fPtr->fd, 0);
591		if (-1 == (long)fPtr->fbmem) {
592			xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
593				   "mmap fbmem: %s\n", strerror(errno));
594			fPtr->fbmem = NULL;
595		} else {
596		    /* Perhaps we'd better add fboff to fbmem and return 0 in
597		       fbdevHWLinearOffset()? Of course we then need to mask
598		       fPtr->fbmem with PAGE_MASK in fbdevHWUnmapVidmem() as
599		       well. [geert] */
600		}
601	}
602	pScrn->memPhysBase = (unsigned long)fPtr->fix.smem_start & (unsigned long)(PAGE_MASK);
603	pScrn->fbOffset = (unsigned long)fPtr->fix.smem_start & (unsigned long)(~PAGE_MASK);
604	return fPtr->fbmem;
605}
606
607int
608fbdevHWLinearOffset(ScrnInfoPtr pScrn)
609{
610	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
611
612	return fPtr->fboff;
613}
614
615Bool
616fbdevHWUnmapVidmem(ScrnInfoPtr pScrn)
617{
618	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
619
620	if (NULL != fPtr->fbmem) {
621		if (-1 == munmap(fPtr->fbmem, fPtr->fbmem_len))
622			xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
623				   "munmap fbmem: %s\n", strerror(errno));
624		fPtr->fbmem = NULL;
625	}
626	return TRUE;
627}
628
629void*
630fbdevHWMapMMIO(ScrnInfoPtr pScrn)
631{
632	unsigned int mmio_off;
633
634	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
635
636	if (NULL == fPtr->mmio) {
637		/* tell the kernel not to use accels to speed up console scrolling */
638		fPtr->var.accel_flags = 0;
639		if (0 != ioctl(fPtr->fd,FBIOPUT_VSCREENINFO,(void*)(&fPtr->var))) {
640			xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
641				   "FBIOPUT_VSCREENINFO: %s\n", strerror(errno));
642			return FALSE;
643		}
644		mmio_off = (unsigned long) fPtr->fix.mmio_start & ~PAGE_MASK;
645		fPtr->mmio_len = (mmio_off+fPtr->fix.mmio_len+~PAGE_MASK) &
646				  PAGE_MASK;
647		if (NULL == fPtr->fbmem)
648			calculateFbmem_len(fPtr);
649		fPtr->mmio = mmap(NULL, fPtr->mmio_len, PROT_READ | PROT_WRITE,
650				  MAP_SHARED, fPtr->fd, fPtr->fbmem_len);
651		if (-1 == (long)fPtr->mmio) {
652			xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
653				   "mmap mmio: %s\n", strerror(errno));
654			fPtr->mmio = NULL;
655		} else
656			fPtr->mmio += mmio_off;
657	}
658	return fPtr->mmio;
659}
660
661Bool
662fbdevHWUnmapMMIO(ScrnInfoPtr pScrn)
663{
664	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
665
666	if (NULL != fPtr->mmio) {
667		if (-1 == munmap((void *)((unsigned long)fPtr->mmio & PAGE_MASK), fPtr->mmio_len))
668			xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
669				   "munmap mmio: %s\n", strerror(errno));
670		fPtr->mmio = NULL;
671		/* FIXME: restore var.accel_flags [geert] */
672	}
673	return TRUE;
674}
675
676/* -------------------------------------------------------------------- */
677
678Bool
679fbdevHWModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode)
680{
681	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
682
683	pScrn->vtSema = TRUE;
684
685	/* set */
686	if (!fbdevHWSetMode(pScrn, mode, FALSE))
687		return FALSE;
688
689	/* read back */
690	if (0 != ioctl(fPtr->fd,FBIOGET_FSCREENINFO,(void*)(&fPtr->fix))) {
691		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
692			   "FBIOGET_FSCREENINFO: %s\n", strerror(errno));
693		return FALSE;
694	}
695	if (0 != ioctl(fPtr->fd,FBIOGET_VSCREENINFO,(void*)(&fPtr->var))) {
696		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
697			   "FBIOGET_VSCREENINFO: %s\n", strerror(errno));
698		return FALSE;
699	}
700
701	if (pScrn->defaultVisual == TrueColor ||
702	    pScrn->defaultVisual == DirectColor) {
703	    /* XXX: This is a hack, but it should be a NOP for all the setups that
704	     * worked before and actually seems to fix some others...
705	     */
706	    pScrn->offset.red   = fPtr->var.red.offset;
707	    pScrn->offset.green = fPtr->var.green.offset;
708	    pScrn->offset.blue  = fPtr->var.blue.offset;
709	    pScrn->mask.red     = ((1 << fPtr->var.red.length) - 1) << fPtr->var.red.offset;
710	    pScrn->mask.green   = ((1 << fPtr->var.green.length) - 1) << fPtr->var.green.offset;
711	    pScrn->mask.blue    = ((1 << fPtr->var.blue.length) - 1) << fPtr->var.blue.offset;
712	}
713
714	return TRUE;
715}
716
717/* -------------------------------------------------------------------- */
718/* video mode save/restore                                              */
719void
720fbdevHWSave(ScrnInfoPtr pScrn)
721{
722	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
723
724	if (0 != ioctl(fPtr->fd,FBIOGET_VSCREENINFO,(void*)(&fPtr->saved_var)))
725		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
726			   "FBIOGET_VSCREENINFO: %s\n", strerror(errno));
727}
728
729void
730fbdevHWRestore(ScrnInfoPtr pScrn)
731{
732	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
733
734	if (0 != ioctl(fPtr->fd,FBIOPUT_VSCREENINFO,(void*)(&fPtr->saved_var)))
735		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
736			   "FBIOPUT_VSCREENINFO: %s\n", strerror(errno));
737}
738
739/* -------------------------------------------------------------------- */
740/* callback for xf86HandleColormaps                                     */
741
742void
743fbdevHWLoadPalette(ScrnInfoPtr pScrn, int numColors, int *indices,
744		 LOCO *colors, VisualPtr pVisual)
745{
746	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
747	struct fb_cmap cmap;
748	unsigned short red,green,blue;
749	int i;
750
751	cmap.len   = 1;
752	cmap.red   = &red;
753	cmap.green = &green;
754	cmap.blue  = &blue;
755	cmap.transp = NULL;
756	for (i = 0; i < numColors; i++) {
757		cmap.start = indices[i];
758		red   = (colors[indices[i]].red   << 8) |
759			colors[indices[i]].red;
760		green = (colors[indices[i]].green << 8) |
761			colors[indices[i]].green;
762		blue  = (colors[indices[i]].blue  << 8) |
763			colors[indices[i]].blue;
764		if (-1 == ioctl(fPtr->fd,FBIOPUTCMAP,(void*)&cmap))
765			xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
766				   "FBIOPUTCMAP: %s\n", strerror(errno));
767	}
768}
769
770/* -------------------------------------------------------------------- */
771/* these can be hooked directly into ScrnInfoRec                        */
772
773ModeStatus
774fbdevHWValidMode(int scrnIndex, DisplayModePtr mode, Bool verbose, int flags)
775{
776	ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
777
778	if (!fbdevHWSetMode(pScrn, mode, TRUE))
779		return MODE_BAD;
780
781	return MODE_OK;
782}
783
784Bool
785fbdevHWSwitchMode(int scrnIndex, DisplayModePtr mode, int flags)
786{
787	ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
788
789
790	if (!fbdevHWSetMode(pScrn, mode, FALSE))
791		return FALSE;
792
793	return TRUE;
794}
795
796void
797fbdevHWAdjustFrame(int scrnIndex, int x, int y, int flags)
798{
799	ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
800	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
801
802	if ( x < 0 || x + fPtr->var.xres > fPtr->var.xres_virtual ||
803	     y < 0 || y + fPtr->var.yres > fPtr->var.yres_virtual )
804		return;
805
806	fPtr->var.xoffset = x;
807	fPtr->var.yoffset = y;
808	if (-1 == ioctl(fPtr->fd,FBIOPAN_DISPLAY,(void*)&fPtr->var))
809		xf86DrvMsgVerb(scrnIndex, X_WARNING, 5,
810			   "FBIOPAN_DISPLAY: %s\n", strerror(errno));
811}
812
813Bool
814fbdevHWEnterVT(int scrnIndex, int flags)
815{
816	ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
817
818	if (!fbdevHWModeInit(pScrn, pScrn->currentMode))
819		return FALSE;
820	fbdevHWAdjustFrame(scrnIndex, pScrn->frameX0, pScrn->frameY0, 0);
821	return TRUE;
822}
823
824void
825fbdevHWLeaveVT(int scrnIndex, int flags)
826{
827	ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
828
829	fbdevHWRestore(pScrn);
830}
831
832void
833fbdevHWDPMSSet(ScrnInfoPtr pScrn, int mode, int flags)
834{
835	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
836	unsigned long fbmode;
837
838	if (!pScrn->vtSema)
839		return;
840
841	switch (mode) {
842		case DPMSModeOn:
843			fbmode = 0;
844			break;
845		case DPMSModeStandby:
846			fbmode = 2;
847			break;
848		case DPMSModeSuspend:
849			fbmode = 3;
850			break;
851		case DPMSModeOff:
852			fbmode = 4;
853			break;
854		default:
855			return;
856	}
857
858	if (-1 == ioctl(fPtr->fd, FBIOBLANK, (void *)fbmode))
859		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
860			   "FBIOBLANK: %s\n", strerror(errno));
861}
862
863Bool
864fbdevHWSaveScreen(ScreenPtr pScreen, int mode)
865{
866	ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
867	fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
868	unsigned long unblank;
869
870	if (!pScrn->vtSema)
871		return TRUE;
872
873	unblank = xf86IsUnblank(mode);
874
875	if (-1 == ioctl(fPtr->fd, FBIOBLANK, (void *)(1-unblank))) {
876		xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
877			   "FBIOBLANK: %s\n", strerror(errno));
878		return FALSE;
879	}
880
881	return TRUE;
882}
883
884xf86SwitchModeProc *
885fbdevHWSwitchModeWeak(void) { return fbdevHWSwitchMode; }
886
887xf86AdjustFrameProc *
888fbdevHWAdjustFrameWeak(void) { return fbdevHWAdjustFrame; }
889
890xf86EnterVTProc *
891fbdevHWEnterVTWeak(void) { return fbdevHWEnterVT; }
892
893xf86LeaveVTProc *
894fbdevHWLeaveVTWeak(void) { return fbdevHWLeaveVT; }
895
896xf86ValidModeProc *
897fbdevHWValidModeWeak(void) { return fbdevHWValidMode; }
898
899xf86DPMSSetProc *
900fbdevHWDPMSSetWeak(void) { return fbdevHWDPMSSet; }
901
902xf86LoadPaletteProc *
903fbdevHWLoadPaletteWeak(void) { return fbdevHWLoadPalette; }
904
905SaveScreenProcPtr
906fbdevHWSaveScreenWeak(void) { return fbdevHWSaveScreen; }
907