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