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