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