fbdevhw.c revision d566a54b
1/* all drivers 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 "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    "fbdevhw",
26    MODULEVENDORSTRING,
27    MODINFOSTRING1,
28    MODINFOSTRING2,
29    XORG_VERSION_CURRENT,
30    0, 0, 2,
31    ABI_CLASS_VIDEODRV,
32    ABI_VIDEODRV_VERSION,
33    MOD_CLASS_NONE,
34    {0, 0, 0, 0}
35};
36
37_X_EXPORT XF86ModuleData fbdevhwModuleData = {
38    &fbdevHWVersRec,
39    NULL,
40    NULL
41};
42
43#include <fcntl.h>
44#include <errno.h>
45#include <sys/mman.h>
46#include <sys/ioctl.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <unistd.h>
50
51/* -------------------------------------------------------------------- */
52/* our private data, and two functions to allocate/free this            */
53
54#define FBDEVHWPTRLVAL(p) (p)->privates[fbdevHWPrivateIndex].ptr
55#define FBDEVHWPTR(p) ((fbdevHWPtr)(FBDEVHWPTRLVAL(p)))
56
57static int fbdevHWPrivateIndex = -1;
58
59typedef struct {
60    /* framebuffer device: filename (/dev/fb*), handle, more */
61    char *device;
62    int fd;
63    void *fbmem;
64    unsigned int fbmem_len;
65    unsigned int fboff;
66    char *mmio;
67    unsigned int mmio_len;
68
69    /* current hardware state */
70    struct fb_fix_screeninfo fix;
71    struct fb_var_screeninfo var;
72
73    /* saved video mode */
74    struct fb_var_screeninfo saved_var;
75
76    /* buildin video mode */
77    DisplayModeRec buildin;
78
79    /* disable non-fatal unsupported ioctls */
80    CARD32 unsupported_ioctls;
81} fbdevHWRec, *fbdevHWPtr;
82
83enum {
84    FBIOBLANK_UNSUPPORTED = 0,
85};
86
87Bool
88fbdevHWGetRec(ScrnInfoPtr pScrn)
89{
90    if (fbdevHWPrivateIndex < 0)
91        fbdevHWPrivateIndex = xf86AllocateScrnInfoPrivateIndex();
92
93    if (FBDEVHWPTR(pScrn) != NULL)
94        return TRUE;
95
96    FBDEVHWPTRLVAL(pScrn) = xnfcalloc(sizeof(fbdevHWRec), 1);
97    return TRUE;
98}
99
100void
101fbdevHWFreeRec(ScrnInfoPtr pScrn)
102{
103    if (fbdevHWPrivateIndex < 0)
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 information                          */
122
123#ifdef DEBUG
124static void
125print_fbdev_mode(const 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(const 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    }
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/* 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        snprintf(filename, sizeof(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            snprintf(filename, sizeof(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            snprintf(filename, sizeof(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, const 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    }
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, "open %s: %s\n", dev, strerror(errno));
329        return -1;
330    }
331
332    /* only touch non-PCI devices on this path */
333    {
334        char buf[PATH_MAX] = {0};
335        char *sysfs_path = NULL;
336        char *node = strrchr(dev, '/') + 1;
337
338        if (asprintf(&sysfs_path, "/sys/class/graphics/%s/device/subsystem", node) < 0 ||
339            readlink(sysfs_path, buf, sizeof(buf) - 1) < 0 ||
340            strstr(buf, "bus/pci")) {
341            free(sysfs_path);
342            close(fd);
343            return -1;
344        }
345        free(sysfs_path);
346    }
347
348    if (namep) {
349        if (-1 == ioctl(fd, FBIOGET_FSCREENINFO, (void *) (&fix))) {
350            *namep = NULL;
351            xf86DrvMsg(scrnIndex, X_ERROR,
352                       "FBIOGET_FSCREENINFO: %s\n", strerror(errno));
353            return -1;
354        }
355        else {
356            *namep = xnfalloc(16);
357            strncpy(*namep, fix.id, 16);
358        }
359    }
360    return fd;
361}
362
363/* -------------------------------------------------------------------- */
364
365Bool
366fbdevHWProbe(struct pci_device *pPci, char *device, char **namep)
367{
368    int fd;
369
370    if (pPci)
371        fd = fbdev_open_pci(pPci, namep);
372    else
373        fd = fbdev_open(-1, device, namep);
374
375    if (-1 == fd)
376        return FALSE;
377    close(fd);
378    return TRUE;
379}
380
381Bool
382fbdevHWInit(ScrnInfoPtr pScrn, struct pci_device *pPci, char *device)
383{
384    fbdevHWPtr fPtr;
385
386    fbdevHWGetRec(pScrn);
387    fPtr = FBDEVHWPTR(pScrn);
388
389    /* open device */
390    if (pPci)
391        fPtr->fd = fbdev_open_pci(pPci, NULL);
392    else
393        fPtr->fd = fbdev_open(pScrn->scrnIndex, device, NULL);
394    if (-1 == fPtr->fd) {
395        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
396                   "Failed to open framebuffer device, consult warnings"
397                   " and/or errors above for possible reasons\n"
398                   "\t(you may have to look at the server log to see"
399                   " warnings)\n");
400        return FALSE;
401    }
402
403    /* get current fb device settings */
404    if (-1 == ioctl(fPtr->fd, FBIOGET_FSCREENINFO, (void *) (&fPtr->fix))) {
405        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
406                   "ioctl FBIOGET_FSCREENINFO: %s\n", strerror(errno));
407        return FALSE;
408    }
409    if (-1 == ioctl(fPtr->fd, FBIOGET_VSCREENINFO, (void *) (&fPtr->var))) {
410        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
411                   "ioctl FBIOGET_VSCREENINFO: %s\n", strerror(errno));
412        return FALSE;
413    }
414
415    /* we can use the current settings as "buildin mode" */
416    fbdev2xfree_timing(&fPtr->var, &fPtr->buildin);
417    fPtr->buildin.name = "current";
418    fPtr->buildin.next = &fPtr->buildin;
419    fPtr->buildin.prev = &fPtr->buildin;
420    fPtr->buildin.type |= M_T_BUILTIN;
421
422    return TRUE;
423}
424
425char *
426fbdevHWGetName(ScrnInfoPtr pScrn)
427{
428    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
429
430    return fPtr->fix.id;
431}
432
433int
434fbdevHWGetDepth(ScrnInfoPtr pScrn, int *fbbpp)
435{
436    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
437
438    if (fbbpp)
439        *fbbpp = fPtr->var.bits_per_pixel;
440
441    if (fPtr->fix.visual == FB_VISUAL_TRUECOLOR ||
442        fPtr->fix.visual == FB_VISUAL_DIRECTCOLOR)
443        return fPtr->var.red.length + fPtr->var.green.length +
444            fPtr->var.blue.length;
445    else
446        return fPtr->var.bits_per_pixel;
447}
448
449int
450fbdevHWGetLineLength(ScrnInfoPtr pScrn)
451{
452    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
453
454    if (fPtr->fix.line_length)
455        return fPtr->fix.line_length;
456    else
457        return fPtr->var.xres_virtual * fPtr->var.bits_per_pixel / 8;
458}
459
460int
461fbdevHWGetType(ScrnInfoPtr pScrn)
462{
463    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
464
465    return fPtr->fix.type;
466}
467
468int
469fbdevHWGetVidmem(ScrnInfoPtr pScrn)
470{
471    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
472
473    return fPtr->fix.smem_len;
474}
475
476static Bool
477fbdevHWSetMode(ScrnInfoPtr pScrn, DisplayModePtr mode, Bool check)
478{
479    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
480    struct fb_var_screeninfo req_var = fPtr->var, set_var;
481
482    xfree2fbdev_fblayout(pScrn, &req_var);
483    xfree2fbdev_timing(mode, &req_var);
484
485#ifdef DEBUG
486    print_xfree_mode("init", mode);
487    print_fbdev_mode("init", &req_var);
488#endif
489
490    set_var = req_var;
491
492    if (check)
493        set_var.activate = FB_ACTIVATE_TEST;
494
495    if (0 != ioctl(fPtr->fd, FBIOPUT_VSCREENINFO, (void *) (&set_var))) {
496        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
497                   "FBIOPUT_VSCREENINFO: %s\n", strerror(errno));
498        return FALSE;
499    }
500
501    if (!fbdev_modes_equal(&set_var, &req_var)) {
502        if (!check)
503            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
504                       "FBIOPUT_VSCREENINFO succeeded but modified " "mode\n");
505#ifdef DEBUG
506        print_fbdev_mode("returned", &set_var);
507#endif
508        return FALSE;
509    }
510
511    if (!check)
512        fPtr->var = set_var;
513
514    return TRUE;
515}
516
517void
518fbdevHWSetVideoModes(ScrnInfoPtr pScrn)
519{
520    const char **modename;
521    DisplayModePtr mode, this, last = pScrn->modes;
522
523    if (NULL == pScrn->display->modes)
524        return;
525
526    pScrn->virtualX = pScrn->display->virtualX;
527    pScrn->virtualY = pScrn->display->virtualY;
528
529    for (modename = pScrn->display->modes; *modename != NULL; modename++) {
530        for (mode = pScrn->monitor->Modes; mode != NULL; mode = mode->next) {
531            if (0 == strcmp(mode->name, *modename)) {
532                if (fbdevHWSetMode(pScrn, mode, TRUE))
533                    break;
534
535                xf86DrvMsg(pScrn->scrnIndex, X_INFO,
536                           "\tmode \"%s\" test failed\n", *modename);
537            }
538        }
539
540        if (NULL == mode) {
541            xf86DrvMsg(pScrn->scrnIndex, X_INFO,
542                       "\tmode \"%s\" not found\n", *modename);
543            continue;
544        }
545
546        xf86DrvMsg(pScrn->scrnIndex, X_INFO, "\tmode \"%s\" ok\n", *modename);
547
548        if (pScrn->virtualX < mode->HDisplay)
549            pScrn->virtualX = mode->HDisplay;
550        if (pScrn->virtualY < mode->VDisplay)
551            pScrn->virtualY = mode->VDisplay;
552
553        if (NULL == pScrn->modes) {
554            this = pScrn->modes = xf86DuplicateMode(mode);
555            this->next = this;
556            this->prev = this;
557        }
558        else {
559            this = xf86DuplicateMode(mode);
560            this->next = pScrn->modes;
561            this->prev = last;
562            last->next = this;
563            pScrn->modes->prev = this;
564        }
565        last = this;
566    }
567}
568
569DisplayModePtr
570fbdevHWGetBuildinMode(ScrnInfoPtr pScrn)
571{
572    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
573
574    return &fPtr->buildin;
575}
576
577void
578fbdevHWUseBuildinMode(ScrnInfoPtr pScrn)
579{
580    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
581
582    pScrn->modes = &fPtr->buildin;
583    pScrn->virtualX = pScrn->display->virtualX;
584    pScrn->virtualY = pScrn->display->virtualY;
585    if (pScrn->virtualX < fPtr->buildin.HDisplay)
586        pScrn->virtualX = fPtr->buildin.HDisplay;
587    if (pScrn->virtualY < fPtr->buildin.VDisplay)
588        pScrn->virtualY = fPtr->buildin.VDisplay;
589}
590
591/* -------------------------------------------------------------------- */
592
593static void
594calculateFbmem_len(fbdevHWPtr fPtr)
595{
596    fPtr->fboff = (unsigned long) fPtr->fix.smem_start & ~PAGE_MASK;
597    fPtr->fbmem_len = (fPtr->fboff + fPtr->fix.smem_len + ~PAGE_MASK) &
598        PAGE_MASK;
599}
600
601void *
602fbdevHWMapVidmem(ScrnInfoPtr pScrn)
603{
604    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
605
606    if (NULL == fPtr->fbmem) {
607        calculateFbmem_len(fPtr);
608        fPtr->fbmem = mmap(NULL, fPtr->fbmem_len, PROT_READ | PROT_WRITE,
609                           MAP_SHARED, fPtr->fd, 0);
610        if (-1 == (long) fPtr->fbmem) {
611            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
612                       "mmap fbmem: %s\n", strerror(errno));
613            fPtr->fbmem = NULL;
614        }
615        else {
616            /* Perhaps we'd better add fboff to fbmem and return 0 in
617               fbdevHWLinearOffset()? Of course we then need to mask
618               fPtr->fbmem with PAGE_MASK in fbdevHWUnmapVidmem() as
619               well. [geert] */
620        }
621    }
622    pScrn->memPhysBase =
623        (unsigned long) fPtr->fix.smem_start & (unsigned long) (PAGE_MASK);
624    pScrn->fbOffset =
625        (unsigned long) fPtr->fix.smem_start & (unsigned long) (~PAGE_MASK);
626    return fPtr->fbmem;
627}
628
629int
630fbdevHWLinearOffset(ScrnInfoPtr pScrn)
631{
632    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
633
634    return fPtr->fboff;
635}
636
637Bool
638fbdevHWUnmapVidmem(ScrnInfoPtr pScrn)
639{
640    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
641
642    if (NULL != fPtr->fbmem) {
643        if (-1 == munmap(fPtr->fbmem, fPtr->fbmem_len))
644            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
645                       "munmap fbmem: %s\n", strerror(errno));
646        fPtr->fbmem = NULL;
647    }
648    return TRUE;
649}
650
651void *
652fbdevHWMapMMIO(ScrnInfoPtr pScrn)
653{
654    unsigned int mmio_off;
655
656    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
657
658    if (NULL == fPtr->mmio) {
659        /* tell the kernel not to use accels to speed up console scrolling */
660        fPtr->var.accel_flags = 0;
661        if (0 != ioctl(fPtr->fd, FBIOPUT_VSCREENINFO, (void *) (&fPtr->var))) {
662            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
663                       "FBIOPUT_VSCREENINFO: %s\n", strerror(errno));
664            return FALSE;
665        }
666        mmio_off = (unsigned long) fPtr->fix.mmio_start & ~PAGE_MASK;
667        fPtr->mmio_len = (mmio_off + fPtr->fix.mmio_len + ~PAGE_MASK) &
668            PAGE_MASK;
669        if (NULL == fPtr->fbmem)
670            calculateFbmem_len(fPtr);
671        fPtr->mmio = mmap(NULL, fPtr->mmio_len, PROT_READ | PROT_WRITE,
672                          MAP_SHARED, fPtr->fd, fPtr->fbmem_len);
673        if (-1 == (long) fPtr->mmio) {
674            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
675                       "mmap mmio: %s\n", strerror(errno));
676            fPtr->mmio = NULL;
677        }
678        else
679            fPtr->mmio += mmio_off;
680    }
681    return fPtr->mmio;
682}
683
684Bool
685fbdevHWUnmapMMIO(ScrnInfoPtr pScrn)
686{
687    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
688
689    if (NULL != fPtr->mmio) {
690        if (-1 ==
691            munmap((void *) ((unsigned long) fPtr->mmio & PAGE_MASK),
692                   fPtr->mmio_len))
693            xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "munmap mmio: %s\n",
694                       strerror(errno));
695        fPtr->mmio = NULL;
696        /* FIXME: restore var.accel_flags [geert] */
697    }
698    return TRUE;
699}
700
701/* -------------------------------------------------------------------- */
702
703Bool
704fbdevHWModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode)
705{
706    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
707
708    pScrn->vtSema = TRUE;
709
710    /* set */
711    if (!fbdevHWSetMode(pScrn, mode, FALSE))
712        return FALSE;
713
714    /* read back */
715    if (0 != ioctl(fPtr->fd, FBIOGET_FSCREENINFO, (void *) (&fPtr->fix))) {
716        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
717                   "FBIOGET_FSCREENINFO: %s\n", strerror(errno));
718        return FALSE;
719    }
720    if (0 != ioctl(fPtr->fd, FBIOGET_VSCREENINFO, (void *) (&fPtr->var))) {
721        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
722                   "FBIOGET_VSCREENINFO: %s\n", strerror(errno));
723        return FALSE;
724    }
725
726    if (pScrn->defaultVisual == TrueColor ||
727        pScrn->defaultVisual == DirectColor) {
728        /* XXX: This is a hack, but it should be a NOP for all the setups that
729         * worked before and actually seems to fix some others...
730         */
731        pScrn->offset.red = fPtr->var.red.offset;
732        pScrn->offset.green = fPtr->var.green.offset;
733        pScrn->offset.blue = fPtr->var.blue.offset;
734        pScrn->mask.red =
735            ((1 << fPtr->var.red.length) - 1) << fPtr->var.red.offset;
736        pScrn->mask.green =
737            ((1 << fPtr->var.green.length) - 1) << fPtr->var.green.offset;
738        pScrn->mask.blue =
739            ((1 << fPtr->var.blue.length) - 1) << fPtr->var.blue.offset;
740    }
741
742    return TRUE;
743}
744
745/* -------------------------------------------------------------------- */
746/* video mode save/restore                                              */
747void
748fbdevHWSave(ScrnInfoPtr pScrn)
749{
750    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
751
752    if (0 != ioctl(fPtr->fd, FBIOGET_VSCREENINFO, (void *) (&fPtr->saved_var)))
753        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
754                   "FBIOGET_VSCREENINFO: %s\n", strerror(errno));
755}
756
757void
758fbdevHWRestore(ScrnInfoPtr pScrn)
759{
760    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
761
762    if (0 != ioctl(fPtr->fd, FBIOPUT_VSCREENINFO, (void *) (&fPtr->saved_var)))
763        xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
764                   "FBIOPUT_VSCREENINFO: %s\n", strerror(errno));
765}
766
767/* -------------------------------------------------------------------- */
768/* callback for xf86HandleColormaps                                     */
769
770void
771fbdevHWLoadPalette(ScrnInfoPtr pScrn, int numColors, int *indices,
772                   LOCO * colors, VisualPtr pVisual)
773{
774    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
775    struct fb_cmap cmap;
776    unsigned short red, green, blue;
777    int i;
778
779    cmap.len = 1;
780    cmap.red = &red;
781    cmap.green = &green;
782    cmap.blue = &blue;
783    cmap.transp = NULL;
784    for (i = 0; i < numColors; i++) {
785        cmap.start = indices[i];
786        red = (colors[indices[i]].red << 8) | colors[indices[i]].red;
787        green = (colors[indices[i]].green << 8) | colors[indices[i]].green;
788        blue = (colors[indices[i]].blue << 8) | colors[indices[i]].blue;
789        if (-1 == ioctl(fPtr->fd, FBIOPUTCMAP, (void *) &cmap))
790            xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
791                       "FBIOPUTCMAP: %s\n", strerror(errno));
792    }
793}
794
795/* -------------------------------------------------------------------- */
796/* these can be hooked directly into ScrnInfoRec                        */
797
798ModeStatus
799fbdevHWValidMode(ScrnInfoPtr pScrn, DisplayModePtr mode, Bool verbose, int flags)
800{
801    if (!fbdevHWSetMode(pScrn, mode, TRUE))
802        return MODE_BAD;
803
804    return MODE_OK;
805}
806
807Bool
808fbdevHWSwitchMode(ScrnInfoPtr pScrn, DisplayModePtr mode)
809{
810    if (!fbdevHWSetMode(pScrn, mode, FALSE))
811        return FALSE;
812
813    return TRUE;
814}
815
816void
817fbdevHWAdjustFrame(ScrnInfoPtr pScrn, int x, int y)
818{
819    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
820
821    if (x < 0 || x + fPtr->var.xres > fPtr->var.xres_virtual ||
822        y < 0 || y + fPtr->var.yres > fPtr->var.yres_virtual)
823        return;
824
825    fPtr->var.xoffset = x;
826    fPtr->var.yoffset = y;
827    if (-1 == ioctl(fPtr->fd, FBIOPAN_DISPLAY, (void *) &fPtr->var))
828        xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, 5,
829                       "FBIOPAN_DISPLAY: %s\n", strerror(errno));
830}
831
832Bool
833fbdevHWEnterVT(ScrnInfoPtr pScrn)
834{
835    if (!fbdevHWModeInit(pScrn, pScrn->currentMode))
836        return FALSE;
837    fbdevHWAdjustFrame(pScrn, pScrn->frameX0, pScrn->frameY0);
838    return TRUE;
839}
840
841void
842fbdevHWLeaveVT(ScrnInfoPtr pScrn)
843{
844    fbdevHWRestore(pScrn);
845}
846
847void
848fbdevHWDPMSSet(ScrnInfoPtr pScrn, int mode, int flags)
849{
850    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
851    unsigned long fbmode;
852
853    if (!pScrn->vtSema)
854        return;
855
856    if (fPtr->unsupported_ioctls & (1 << FBIOBLANK_UNSUPPORTED))
857        return;
858
859    switch (mode) {
860    case DPMSModeOn:
861        fbmode = 0;
862        break;
863    case DPMSModeStandby:
864        fbmode = 2;
865        break;
866    case DPMSModeSuspend:
867        fbmode = 3;
868        break;
869    case DPMSModeOff:
870        fbmode = 4;
871        break;
872    default:
873        return;
874    }
875
876RETRY:
877    if (-1 == ioctl(fPtr->fd, FBIOBLANK, (void *) fbmode)) {
878        switch (errno) {
879        case EAGAIN:
880            xf86DrvMsg(pScrn->scrnIndex, X_INFO,
881                       "FBIOBLANK: %s\n", strerror(errno));
882	    break;
883        case EINTR:
884        case ERESTART:
885            goto RETRY;
886        default:
887            fPtr->unsupported_ioctls |= (1 << FBIOBLANK_UNSUPPORTED);
888            xf86DrvMsg(pScrn->scrnIndex, X_INFO,
889                       "FBIOBLANK: %s (Screen blanking not supported "
890                       "by kernel - disabling)\n", strerror(errno));
891        }
892    }
893}
894
895Bool
896fbdevHWSaveScreen(ScreenPtr pScreen, int mode)
897{
898    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
899    fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
900    unsigned long unblank;
901
902    if (!pScrn->vtSema)
903        return TRUE;
904
905    if (fPtr->unsupported_ioctls & (1 << FBIOBLANK_UNSUPPORTED))
906        return FALSE;
907
908    unblank = xf86IsUnblank(mode);
909
910RETRY:
911    if (-1 == ioctl(fPtr->fd, FBIOBLANK, (void *) (1 - unblank))) {
912        switch (errno) {
913        case EAGAIN:
914            xf86DrvMsg(pScrn->scrnIndex, X_INFO,
915                       "FBIOBLANK: %s\n", strerror(errno));
916            break;
917        case EINTR:
918        case ERESTART:
919            goto RETRY;
920        default:
921            fPtr->unsupported_ioctls |= (1 << FBIOBLANK_UNSUPPORTED);
922            xf86DrvMsg(pScrn->scrnIndex, X_INFO,
923                       "FBIOBLANK: %s (Screen blanking not supported "
924                       "by kernel - disabling)\n", strerror(errno));
925        }
926        return FALSE;
927    }
928
929    return TRUE;
930}
931
932xf86SwitchModeProc *
933fbdevHWSwitchModeWeak(void)
934{
935    return fbdevHWSwitchMode;
936}
937
938xf86AdjustFrameProc *
939fbdevHWAdjustFrameWeak(void)
940{
941    return fbdevHWAdjustFrame;
942}
943
944xf86EnterVTProc *
945fbdevHWEnterVTWeak(void)
946{
947    return fbdevHWEnterVT;
948}
949
950xf86LeaveVTProc *
951fbdevHWLeaveVTWeak(void)
952{
953    return fbdevHWLeaveVT;
954}
955
956xf86ValidModeProc *
957fbdevHWValidModeWeak(void)
958{
959    return fbdevHWValidMode;
960}
961
962xf86DPMSSetProc *
963fbdevHWDPMSSetWeak(void)
964{
965    return fbdevHWDPMSSet;
966}
967
968xf86LoadPaletteProc *
969fbdevHWLoadPaletteWeak(void)
970{
971    return fbdevHWLoadPalette;
972}
973
974SaveScreenProcPtr
975fbdevHWSaveScreenWeak(void)
976{
977    return fbdevHWSaveScreen;
978}
979