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 "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 
24 static 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 
57 static int fbdevHWPrivateIndex = -1;
58 
59 typedef 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 
83 enum {
84     FBIOBLANK_UNSUPPORTED = 0,
85 };
86 
87 Bool
fbdevHWGetRec(ScrnInfoPtr pScrn)88 fbdevHWGetRec(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 
100 void
fbdevHWFreeRec(ScrnInfoPtr pScrn)101 fbdevHWFreeRec(ScrnInfoPtr pScrn)
102 {
103     if (fbdevHWPrivateIndex < 0)
104         return;
105     free(FBDEVHWPTR(pScrn));
106     FBDEVHWPTRLVAL(pScrn) = NULL;
107 }
108 
109 int
fbdevHWGetFD(ScrnInfoPtr pScrn)110 fbdevHWGetFD(ScrnInfoPtr pScrn)
111 {
112     fbdevHWPtr fPtr;
113 
114     fbdevHWGetRec(pScrn);
115     fPtr = FBDEVHWPTR(pScrn);
116 
117     return fPtr->fd;
118 }
119 
120 /* -------------------------------------------------------------------- */
121 /* some helpers for printing debug informations                         */
122 
123 #ifdef DEBUG
124 static void
print_fbdev_mode(const char * txt,struct fb_var_screeninfo * var)125 print_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 
135 static void
print_xfree_mode(const char * txt,DisplayModePtr mode)136 print_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 
148 static void
xfree2fbdev_fblayout(ScrnInfoPtr pScrn,struct fb_var_screeninfo * var)149 xfree2fbdev_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 
168 static void
xfree2fbdev_timing(DisplayModePtr mode,struct fb_var_screeninfo * var)169 xfree2fbdev_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 
202 static Bool
fbdev_modes_equal(struct fb_var_screeninfo * set,struct fb_var_screeninfo * req)203 fbdev_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 
221 static void
fbdev2xfree_timing(struct fb_var_screeninfo * var,DisplayModePtr mode)222 fbdev2xfree_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  */
262 static int
fbdev_open_pci(struct pci_device * pPci,char ** namep)263 fbdev_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 
307 static int
fbdev_open(int scrnIndex,const char * dev,char ** namep)308 fbdev_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];
335         char *sysfs_path = NULL;
336         char *node = strrchr(dev, '/') + 1;
337 
338         if (asprintf(&sysfs_path, "/sys/class/graphics/%s", node) < 0 ||
339             readlink(sysfs_path, buf, sizeof(buf)) < 0 ||
340             strstr(buf, "devices/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 
365 Bool
fbdevHWProbe(struct pci_device * pPci,char * device,char ** namep)366 fbdevHWProbe(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 
381 Bool
fbdevHWInit(ScrnInfoPtr pScrn,struct pci_device * pPci,char * device)382 fbdevHWInit(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 
425 char *
fbdevHWGetName(ScrnInfoPtr pScrn)426 fbdevHWGetName(ScrnInfoPtr pScrn)
427 {
428     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
429 
430     return fPtr->fix.id;
431 }
432 
433 int
fbdevHWGetDepth(ScrnInfoPtr pScrn,int * fbbpp)434 fbdevHWGetDepth(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 
449 int
fbdevHWGetLineLength(ScrnInfoPtr pScrn)450 fbdevHWGetLineLength(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 
460 int
fbdevHWGetType(ScrnInfoPtr pScrn)461 fbdevHWGetType(ScrnInfoPtr pScrn)
462 {
463     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
464 
465     return fPtr->fix.type;
466 }
467 
468 int
fbdevHWGetVidmem(ScrnInfoPtr pScrn)469 fbdevHWGetVidmem(ScrnInfoPtr pScrn)
470 {
471     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
472 
473     return fPtr->fix.smem_len;
474 }
475 
476 static Bool
fbdevHWSetMode(ScrnInfoPtr pScrn,DisplayModePtr mode,Bool check)477 fbdevHWSetMode(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 
517 void
fbdevHWSetVideoModes(ScrnInfoPtr pScrn)518 fbdevHWSetVideoModes(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 
569 DisplayModePtr
fbdevHWGetBuildinMode(ScrnInfoPtr pScrn)570 fbdevHWGetBuildinMode(ScrnInfoPtr pScrn)
571 {
572     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
573 
574     return &fPtr->buildin;
575 }
576 
577 void
fbdevHWUseBuildinMode(ScrnInfoPtr pScrn)578 fbdevHWUseBuildinMode(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 
593 static void
calculateFbmem_len(fbdevHWPtr fPtr)594 calculateFbmem_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 
601 void *
fbdevHWMapVidmem(ScrnInfoPtr pScrn)602 fbdevHWMapVidmem(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 
629 int
fbdevHWLinearOffset(ScrnInfoPtr pScrn)630 fbdevHWLinearOffset(ScrnInfoPtr pScrn)
631 {
632     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
633 
634     return fPtr->fboff;
635 }
636 
637 Bool
fbdevHWUnmapVidmem(ScrnInfoPtr pScrn)638 fbdevHWUnmapVidmem(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 
651 void *
fbdevHWMapMMIO(ScrnInfoPtr pScrn)652 fbdevHWMapMMIO(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 
684 Bool
fbdevHWUnmapMMIO(ScrnInfoPtr pScrn)685 fbdevHWUnmapMMIO(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 
703 Bool
fbdevHWModeInit(ScrnInfoPtr pScrn,DisplayModePtr mode)704 fbdevHWModeInit(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                                              */
747 void
fbdevHWSave(ScrnInfoPtr pScrn)748 fbdevHWSave(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 
757 void
fbdevHWRestore(ScrnInfoPtr pScrn)758 fbdevHWRestore(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 
770 void
fbdevHWLoadPalette(ScrnInfoPtr pScrn,int numColors,int * indices,LOCO * colors,VisualPtr pVisual)771 fbdevHWLoadPalette(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 
798 ModeStatus
fbdevHWValidMode(ScrnInfoPtr pScrn,DisplayModePtr mode,Bool verbose,int flags)799 fbdevHWValidMode(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 
807 Bool
fbdevHWSwitchMode(ScrnInfoPtr pScrn,DisplayModePtr mode)808 fbdevHWSwitchMode(ScrnInfoPtr pScrn, DisplayModePtr mode)
809 {
810     if (!fbdevHWSetMode(pScrn, mode, FALSE))
811         return FALSE;
812 
813     return TRUE;
814 }
815 
816 void
fbdevHWAdjustFrame(ScrnInfoPtr pScrn,int x,int y)817 fbdevHWAdjustFrame(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 
832 Bool
fbdevHWEnterVT(ScrnInfoPtr pScrn)833 fbdevHWEnterVT(ScrnInfoPtr pScrn)
834 {
835     if (!fbdevHWModeInit(pScrn, pScrn->currentMode))
836         return FALSE;
837     fbdevHWAdjustFrame(pScrn, pScrn->frameX0, pScrn->frameY0);
838     return TRUE;
839 }
840 
841 void
fbdevHWLeaveVT(ScrnInfoPtr pScrn)842 fbdevHWLeaveVT(ScrnInfoPtr pScrn)
843 {
844     fbdevHWRestore(pScrn);
845 }
846 
847 void
fbdevHWDPMSSet(ScrnInfoPtr pScrn,int mode,int flags)848 fbdevHWDPMSSet(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 
876 RETRY:
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 
895 Bool
fbdevHWSaveScreen(ScreenPtr pScreen,int mode)896 fbdevHWSaveScreen(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 
910 RETRY:
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 
932 xf86SwitchModeProc *
fbdevHWSwitchModeWeak(void)933 fbdevHWSwitchModeWeak(void)
934 {
935     return fbdevHWSwitchMode;
936 }
937 
938 xf86AdjustFrameProc *
fbdevHWAdjustFrameWeak(void)939 fbdevHWAdjustFrameWeak(void)
940 {
941     return fbdevHWAdjustFrame;
942 }
943 
944 xf86EnterVTProc *
fbdevHWEnterVTWeak(void)945 fbdevHWEnterVTWeak(void)
946 {
947     return fbdevHWEnterVT;
948 }
949 
950 xf86LeaveVTProc *
fbdevHWLeaveVTWeak(void)951 fbdevHWLeaveVTWeak(void)
952 {
953     return fbdevHWLeaveVT;
954 }
955 
956 xf86ValidModeProc *
fbdevHWValidModeWeak(void)957 fbdevHWValidModeWeak(void)
958 {
959     return fbdevHWValidMode;
960 }
961 
962 xf86DPMSSetProc *
fbdevHWDPMSSetWeak(void)963 fbdevHWDPMSSetWeak(void)
964 {
965     return fbdevHWDPMSSet;
966 }
967 
968 xf86LoadPaletteProc *
fbdevHWLoadPaletteWeak(void)969 fbdevHWLoadPaletteWeak(void)
970 {
971     return fbdevHWLoadPalette;
972 }
973 
974 SaveScreenProcPtr
fbdevHWSaveScreenWeak(void)975 fbdevHWSaveScreenWeak(void)
976 {
977     return fbdevHWSaveScreen;
978 }
979