1020 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1020 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
	
| /* all drivers need this */
 | |
| #ifdef HAVE_XORG_CONFIG_H
 | |
| #include <xorg-config.h>
 | |
| #endif
 | |
| 
 | |
| #include <string.h>
 | |
| #include <sys/mman.h>
 | |
| 
 | |
| #include "xf86.h"
 | |
| #include "xf86Modes.h"
 | |
| #include "xf86_OSproc.h"
 | |
| 
 | |
| /* pci stuff */
 | |
| #include "xf86Pci.h"
 | |
| 
 | |
| #include "xf86cmap.h"
 | |
| 
 | |
| #include "fbdevhw.h"
 | |
| #include "fbpriv.h"
 | |
| #include "globals.h"
 | |
| #include <X11/extensions/dpmsconst.h>
 | |
| 
 | |
| #define PAGE_MASK               (~(getpagesize() - 1))
 | |
| 
 | |
| static XF86ModuleVersionInfo fbdevHWVersRec = {
 | |
|     .modname      = "fbdevhw",
 | |
|     .vendor       = MODULEVENDORSTRING,
 | |
|     ._modinfo1_   = MODINFOSTRING1,
 | |
|     ._modinfo2_   = MODINFOSTRING2,
 | |
|     .xf86version  = XORG_VERSION_CURRENT,
 | |
|     .majorversion = 0,
 | |
|     .minorversion = 0,
 | |
|     .patchlevel   = 2,
 | |
|     .abiclass     = ABI_CLASS_VIDEODRV,
 | |
|     .abiversion   = ABI_VIDEODRV_VERSION,
 | |
| };
 | |
| 
 | |
| _X_EXPORT XF86ModuleData fbdevhwModuleData = {
 | |
|     .vers = &fbdevHWVersRec
 | |
| };
 | |
| 
 | |
| #include <fcntl.h>
 | |
| #include <errno.h>
 | |
| #include <sys/mman.h>
 | |
| #include <sys/ioctl.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* our private data, and two functions to allocate/free this            */
 | |
| 
 | |
| #define FBDEVHWPTRLVAL(p) (p)->privates[fbdevHWPrivateIndex].ptr
 | |
| #define FBDEVHWPTR(p) ((fbdevHWPtr)(FBDEVHWPTRLVAL(p)))
 | |
| 
 | |
| static int fbdevHWPrivateIndex = -1;
 | |
| 
 | |
| typedef struct {
 | |
|     /* framebuffer device: filename (/dev/fb*), handle, more */
 | |
|     char *device;
 | |
|     int fd;
 | |
|     void *fbmem;
 | |
|     unsigned int fbmem_len;
 | |
|     unsigned int fboff;
 | |
|     char *mmio;
 | |
|     unsigned int mmio_len;
 | |
| 
 | |
|     /* current hardware state */
 | |
|     struct fb_fix_screeninfo fix;
 | |
|     struct fb_var_screeninfo var;
 | |
| 
 | |
|     /* saved video mode */
 | |
|     struct fb_var_screeninfo saved_var;
 | |
| 
 | |
|     /* buildin video mode */
 | |
|     DisplayModeRec buildin;
 | |
| 
 | |
|     /* disable non-fatal unsupported ioctls */
 | |
|     CARD32 unsupported_ioctls;
 | |
| } fbdevHWRec, *fbdevHWPtr;
 | |
| 
 | |
| enum {
 | |
|     FBIOBLANK_UNSUPPORTED = 0,
 | |
| };
 | |
| 
 | |
| Bool
 | |
| fbdevHWGetRec(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     if (fbdevHWPrivateIndex < 0)
 | |
|         fbdevHWPrivateIndex = xf86AllocateScrnInfoPrivateIndex();
 | |
| 
 | |
|     if (FBDEVHWPTR(pScrn) != NULL)
 | |
|         return TRUE;
 | |
| 
 | |
|     FBDEVHWPTRLVAL(pScrn) = XNFcallocarray(1, sizeof(fbdevHWRec));
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| void
 | |
| fbdevHWFreeRec(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     if (fbdevHWPrivateIndex < 0)
 | |
|         return;
 | |
|     free(FBDEVHWPTR(pScrn));
 | |
|     FBDEVHWPTRLVAL(pScrn) = NULL;
 | |
| }
 | |
| 
 | |
| int
 | |
| fbdevHWGetFD(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     fbdevHWPtr fPtr;
 | |
| 
 | |
|     fbdevHWGetRec(pScrn);
 | |
|     fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     return fPtr->fd;
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* some helpers for printing debug information                          */
 | |
| 
 | |
| #ifdef DEBUG
 | |
| static void
 | |
| print_fbdev_mode(const char *txt, struct fb_var_screeninfo *var)
 | |
| {
 | |
|     ErrorF("fbdev %s mode:\t%d   %d %d %d %d   %d %d %d %d   %d %d:%d:%d\n",
 | |
|            txt, var->pixclock,
 | |
|            var->xres, var->right_margin, var->hsync_len, var->left_margin,
 | |
|            var->yres, var->lower_margin, var->vsync_len, var->upper_margin,
 | |
|            var->bits_per_pixel,
 | |
|            var->red.length, var->green.length, var->blue.length);
 | |
| }
 | |
| 
 | |
| static void
 | |
| print_xfree_mode(const char *txt, DisplayModePtr mode)
 | |
| {
 | |
|     ErrorF("xfree %s mode:\t%d   %d %d %d %d   %d %d %d %d\n",
 | |
|            txt, mode->Clock,
 | |
|            mode->HDisplay, mode->HSyncStart, mode->HSyncEnd, mode->HTotal,
 | |
|            mode->VDisplay, mode->VSyncStart, mode->VSyncEnd, mode->VTotal);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* Convert timings between the XFree and the Frame Buffer Device        */
 | |
| 
 | |
| static void
 | |
| xfree2fbdev_fblayout(ScrnInfoPtr pScrn, struct fb_var_screeninfo *var)
 | |
| {
 | |
|     var->xres_virtual = pScrn->displayWidth ? pScrn->displayWidth :
 | |
|         pScrn->virtualX;
 | |
|     var->yres_virtual = pScrn->virtualY;
 | |
|     var->bits_per_pixel = pScrn->bitsPerPixel;
 | |
|     if (pScrn->defaultVisual == TrueColor ||
 | |
|         pScrn->defaultVisual == DirectColor) {
 | |
|         var->red.length = pScrn->weight.red;
 | |
|         var->green.length = pScrn->weight.green;
 | |
|         var->blue.length = pScrn->weight.blue;
 | |
|     }
 | |
|     else {
 | |
|         var->red.length = 8;
 | |
|         var->green.length = 8;
 | |
|         var->blue.length = 8;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| xfree2fbdev_timing(DisplayModePtr mode, struct fb_var_screeninfo *var)
 | |
| {
 | |
|     var->xres = mode->HDisplay;
 | |
|     var->yres = mode->VDisplay;
 | |
|     if (var->xres_virtual < var->xres)
 | |
|         var->xres_virtual = var->xres;
 | |
|     if (var->yres_virtual < var->yres)
 | |
|         var->yres_virtual = var->yres;
 | |
|     var->xoffset = var->yoffset = 0;
 | |
|     var->pixclock = mode->Clock ? 1000000000 / mode->Clock : 0;
 | |
|     var->right_margin = mode->HSyncStart - mode->HDisplay;
 | |
|     var->hsync_len = mode->HSyncEnd - mode->HSyncStart;
 | |
|     var->left_margin = mode->HTotal - mode->HSyncEnd;
 | |
|     var->lower_margin = mode->VSyncStart - mode->VDisplay;
 | |
|     var->vsync_len = mode->VSyncEnd - mode->VSyncStart;
 | |
|     var->upper_margin = mode->VTotal - mode->VSyncEnd;
 | |
|     var->sync = 0;
 | |
|     if (mode->Flags & V_PHSYNC)
 | |
|         var->sync |= FB_SYNC_HOR_HIGH_ACT;
 | |
|     if (mode->Flags & V_PVSYNC)
 | |
|         var->sync |= FB_SYNC_VERT_HIGH_ACT;
 | |
|     if (mode->Flags & V_PCSYNC)
 | |
|         var->sync |= FB_SYNC_COMP_HIGH_ACT;
 | |
|     if (mode->Flags & V_BCAST)
 | |
|         var->sync |= FB_SYNC_BROADCAST;
 | |
|     if (mode->Flags & V_INTERLACE)
 | |
|         var->vmode = FB_VMODE_INTERLACED;
 | |
|     else if (mode->Flags & V_DBLSCAN)
 | |
|         var->vmode = FB_VMODE_DOUBLE;
 | |
|     else
 | |
|         var->vmode = FB_VMODE_NONINTERLACED;
 | |
| }
 | |
| 
 | |
| static Bool
 | |
| fbdev_modes_equal(struct fb_var_screeninfo *set, struct fb_var_screeninfo *req)
 | |
| {
 | |
|     return (set->xres_virtual >= req->xres_virtual &&
 | |
|             set->yres_virtual >= req->yres_virtual &&
 | |
|             set->bits_per_pixel == req->bits_per_pixel &&
 | |
|             set->red.length == req->red.length &&
 | |
|             set->green.length == req->green.length &&
 | |
|             set->blue.length == req->blue.length &&
 | |
|             set->xres == req->xres && set->yres == req->yres &&
 | |
|             set->right_margin == req->right_margin &&
 | |
|             set->hsync_len == req->hsync_len &&
 | |
|             set->left_margin == req->left_margin &&
 | |
|             set->lower_margin == req->lower_margin &&
 | |
|             set->vsync_len == req->vsync_len &&
 | |
|             set->upper_margin == req->upper_margin &&
 | |
|             set->sync == req->sync && set->vmode == req->vmode);
 | |
| }
 | |
| 
 | |
| static void
 | |
| fbdev2xfree_timing(struct fb_var_screeninfo *var, DisplayModePtr mode)
 | |
| {
 | |
|     mode->Clock = var->pixclock ? 1000000000 / var->pixclock : 0;
 | |
|     mode->HDisplay = var->xres;
 | |
|     mode->HSyncStart = mode->HDisplay + var->right_margin;
 | |
|     mode->HSyncEnd = mode->HSyncStart + var->hsync_len;
 | |
|     mode->HTotal = mode->HSyncEnd + var->left_margin;
 | |
|     mode->VDisplay = var->yres;
 | |
|     mode->VSyncStart = mode->VDisplay + var->lower_margin;
 | |
|     mode->VSyncEnd = mode->VSyncStart + var->vsync_len;
 | |
|     mode->VTotal = mode->VSyncEnd + var->upper_margin;
 | |
|     mode->Flags = 0;
 | |
|     mode->Flags |= var->sync & FB_SYNC_HOR_HIGH_ACT ? V_PHSYNC : V_NHSYNC;
 | |
|     mode->Flags |= var->sync & FB_SYNC_VERT_HIGH_ACT ? V_PVSYNC : V_NVSYNC;
 | |
|     mode->Flags |= var->sync & FB_SYNC_COMP_HIGH_ACT ? V_PCSYNC : V_NCSYNC;
 | |
|     if (var->sync & FB_SYNC_BROADCAST)
 | |
|         mode->Flags |= V_BCAST;
 | |
|     if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED)
 | |
|         mode->Flags |= V_INTERLACE;
 | |
|     else if ((var->vmode & FB_VMODE_MASK) == FB_VMODE_DOUBLE)
 | |
|         mode->Flags |= V_DBLSCAN;
 | |
|     mode->SynthClock = mode->Clock;
 | |
|     mode->CrtcHDisplay = mode->HDisplay;
 | |
|     mode->CrtcHSyncStart = mode->HSyncStart;
 | |
|     mode->CrtcHSyncEnd = mode->HSyncEnd;
 | |
|     mode->CrtcHTotal = mode->HTotal;
 | |
|     mode->CrtcVDisplay = mode->VDisplay;
 | |
|     mode->CrtcVSyncStart = mode->VSyncStart;
 | |
|     mode->CrtcVSyncEnd = mode->VSyncEnd;
 | |
|     mode->CrtcVTotal = mode->VTotal;
 | |
|     mode->CrtcHAdjusted = FALSE;
 | |
|     mode->CrtcVAdjusted = FALSE;
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* open correct framebuffer device                                      */
 | |
| 
 | |
| /**
 | |
|  * Try to find the framebuffer device for a given PCI device
 | |
|  */
 | |
| static int
 | |
| fbdev_open_pci(struct pci_device *pPci, char **namep)
 | |
| {
 | |
|     struct fb_fix_screeninfo fix;
 | |
|     char filename[256];
 | |
|     int fd, i;
 | |
| 
 | |
|     for (i = 0; i < 8; i++) {
 | |
|         snprintf(filename, sizeof(filename),
 | |
|                  "/sys/bus/pci/devices/%04x:%02x:%02x.%d/graphics/fb%d",
 | |
|                  pPci->domain, pPci->bus, pPci->dev, pPci->func, i);
 | |
| 
 | |
|         fd = open(filename, O_RDONLY, 0);
 | |
|         if (fd < 0) {
 | |
|             snprintf(filename, sizeof(filename),
 | |
|                      "/sys/bus/pci/devices/%04x:%02x:%02x.%d/graphics:fb%d",
 | |
|                      pPci->domain, pPci->bus, pPci->dev, pPci->func, i);
 | |
|             fd = open(filename, O_RDONLY, 0);
 | |
|         }
 | |
|         if (fd >= 0) {
 | |
|             close(fd);
 | |
|             snprintf(filename, sizeof(filename), "/dev/fb%d", i);
 | |
| 
 | |
|             fd = open(filename, O_RDWR, 0);
 | |
|             if (fd != -1) {
 | |
|                 if (ioctl(fd, FBIOGET_FSCREENINFO, (void *) &fix) != -1) {
 | |
|                     if (namep) {
 | |
|                         *namep = XNFalloc(16);
 | |
|                         strncpy(*namep, fix.id, 16);
 | |
|                     }
 | |
| 
 | |
|                     return fd;
 | |
|                 }
 | |
|                 close(fd);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (namep)
 | |
|         *namep = NULL;
 | |
| 
 | |
|     xf86DrvMsg(-1, X_ERROR, "Unable to find a valid framebuffer device\n");
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| /* *
 | |
|  * Try to resolve a filename as symbolic link.  If the file is not a link, the
 | |
|  * original filename is returned.  NULL is returned if readlink raised an
 | |
|  * error.
 | |
|  */
 | |
| static const char *
 | |
| resolve_link(const char *filename, char *resolve_buf, size_t resolve_buf_size)
 | |
| {
 | |
|     ssize_t len = readlink(filename, resolve_buf, resolve_buf_size - 1);
 | |
|     /* if it is a link resolve it */
 | |
|     if (len >= 0) {
 | |
|         resolve_buf[len] = '\0';
 | |
|         return resolve_buf;
 | |
|     }
 | |
|     else {
 | |
|         if (errno == EINVAL) {
 | |
|             return filename;
 | |
|         }
 | |
|         else {
 | |
|             // Have caller handle error condition.
 | |
|             return NULL;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static int
 | |
| fbdev_open(int scrnIndex, const char *dev, char **namep)
 | |
| {
 | |
|     struct fb_fix_screeninfo fix;
 | |
|     int fd;
 | |
| 
 | |
|     /* try argument (from XF86Config) first */
 | |
|     if (dev) {
 | |
|         fd = open(dev, O_RDWR, 0);
 | |
|     }
 | |
|     else {
 | |
|         /* second: environment variable */
 | |
|         dev = getenv("FRAMEBUFFER");
 | |
|         if ((NULL == dev) || ((fd = open(dev, O_RDWR, 0)) == -1)) {
 | |
|             /* last try: default device */
 | |
|             dev = "/dev/fb0";
 | |
|             fd = open(dev, O_RDWR, 0);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (fd == -1) {
 | |
|         xf86DrvMsg(scrnIndex, X_ERROR, "open %s: %s\n", dev, strerror(errno));
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* only touch non-PCI devices on this path */
 | |
|     {
 | |
|         char device_path_buf[PATH_MAX];
 | |
|         char buf[PATH_MAX] = {0};
 | |
|         char *sysfs_path = NULL;
 | |
|         char const *real_dev = resolve_link(dev, device_path_buf,
 | |
|                                             sizeof(device_path_buf));
 | |
|         if (real_dev == NULL) {
 | |
|             xf86DrvMsg(scrnIndex, X_ERROR,
 | |
|                        "Failed resolving symbolic link for device '%s': %s",
 | |
|                        dev, strerror(errno));
 | |
|             return -1;
 | |
|         }
 | |
| 
 | |
|         const char *node = strrchr(real_dev, '/');
 | |
| 
 | |
|         if (node == NULL) {
 | |
|             node = real_dev;
 | |
|         }
 | |
|         else {
 | |
|             node++;
 | |
|         }
 | |
| 
 | |
|         if (asprintf(&sysfs_path, "/sys/class/graphics/%s/device/subsystem", node) < 0 ||
 | |
|             readlink(sysfs_path, buf, sizeof(buf) - 1) < 0 ||
 | |
|             strstr(buf, "bus/pci")) {
 | |
|             free(sysfs_path);
 | |
|             close(fd);
 | |
|             return -1;
 | |
|         }
 | |
|         free(sysfs_path);
 | |
|     }
 | |
| 
 | |
|     if (namep) {
 | |
|         if (-1 == ioctl(fd, FBIOGET_FSCREENINFO, (void *) (&fix))) {
 | |
|             *namep = NULL;
 | |
|             xf86DrvMsg(scrnIndex, X_ERROR,
 | |
|                        "FBIOGET_FSCREENINFO: %s\n", strerror(errno));
 | |
|             return -1;
 | |
|         }
 | |
|         else {
 | |
|             *namep = XNFalloc(16);
 | |
|             strncpy(*namep, fix.id, 16);
 | |
|         }
 | |
|     }
 | |
|     return fd;
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| 
 | |
| Bool
 | |
| fbdevHWProbe(struct pci_device *pPci, char *device, char **namep)
 | |
| {
 | |
|     int fd;
 | |
| 
 | |
|     if (pPci)
 | |
|         fd = fbdev_open_pci(pPci, namep);
 | |
|     else
 | |
|         fd = fbdev_open(-1, device, namep);
 | |
| 
 | |
|     if (-1 == fd)
 | |
|         return FALSE;
 | |
|     close(fd);
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| Bool
 | |
| fbdevHWInit(ScrnInfoPtr pScrn, struct pci_device *pPci, char *device)
 | |
| {
 | |
|     fbdevHWPtr fPtr;
 | |
| 
 | |
|     fbdevHWGetRec(pScrn);
 | |
|     fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     /* open device */
 | |
|     if (pPci)
 | |
|         fPtr->fd = fbdev_open_pci(pPci, NULL);
 | |
|     else
 | |
|         fPtr->fd = fbdev_open(pScrn->scrnIndex, device, NULL);
 | |
|     if (-1 == fPtr->fd) {
 | |
|         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
 | |
|                    "Failed to open framebuffer device, consult warnings"
 | |
|                    " and/or errors above for possible reasons\n"
 | |
|                    "\t(you may have to look at the server log to see"
 | |
|                    " warnings)\n");
 | |
|         return FALSE;
 | |
|     }
 | |
| 
 | |
|     /* get current fb device settings */
 | |
|     if (-1 == ioctl(fPtr->fd, FBIOGET_FSCREENINFO, (void *) (&fPtr->fix))) {
 | |
|         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
 | |
|                    "ioctl FBIOGET_FSCREENINFO: %s\n", strerror(errno));
 | |
|         return FALSE;
 | |
|     }
 | |
|     if (-1 == ioctl(fPtr->fd, FBIOGET_VSCREENINFO, (void *) (&fPtr->var))) {
 | |
|         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
 | |
|                    "ioctl FBIOGET_VSCREENINFO: %s\n", strerror(errno));
 | |
|         return FALSE;
 | |
|     }
 | |
| 
 | |
|     /* we can use the current settings as "buildin mode" */
 | |
|     fbdev2xfree_timing(&fPtr->var, &fPtr->buildin);
 | |
|     fPtr->buildin.name = "current";
 | |
|     fPtr->buildin.next = &fPtr->buildin;
 | |
|     fPtr->buildin.prev = &fPtr->buildin;
 | |
|     fPtr->buildin.type |= M_T_BUILTIN;
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| char *
 | |
| fbdevHWGetName(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     return fPtr->fix.id;
 | |
| }
 | |
| 
 | |
| int
 | |
| fbdevHWGetDepth(ScrnInfoPtr pScrn, int *fbbpp)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     if (fbbpp)
 | |
|         *fbbpp = fPtr->var.bits_per_pixel;
 | |
| 
 | |
|     if (fPtr->fix.visual == FB_VISUAL_TRUECOLOR ||
 | |
|         fPtr->fix.visual == FB_VISUAL_DIRECTCOLOR)
 | |
|         return fPtr->var.red.length + fPtr->var.green.length +
 | |
|             fPtr->var.blue.length;
 | |
|     else
 | |
|         return fPtr->var.bits_per_pixel;
 | |
| }
 | |
| 
 | |
| int
 | |
| fbdevHWGetLineLength(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     if (fPtr->fix.line_length)
 | |
|         return fPtr->fix.line_length;
 | |
|     else
 | |
|         return fPtr->var.xres_virtual * fPtr->var.bits_per_pixel / 8;
 | |
| }
 | |
| 
 | |
| int
 | |
| fbdevHWGetType(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     return fPtr->fix.type;
 | |
| }
 | |
| 
 | |
| int
 | |
| fbdevHWGetVidmem(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     return fPtr->fix.smem_len;
 | |
| }
 | |
| 
 | |
| static Bool
 | |
| fbdevHWSetMode(ScrnInfoPtr pScrn, DisplayModePtr mode, Bool check)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
|     struct fb_var_screeninfo req_var = fPtr->var, set_var;
 | |
| 
 | |
|     xfree2fbdev_fblayout(pScrn, &req_var);
 | |
|     xfree2fbdev_timing(mode, &req_var);
 | |
| 
 | |
| #ifdef DEBUG
 | |
|     print_xfree_mode("init", mode);
 | |
|     print_fbdev_mode("init", &req_var);
 | |
| #endif
 | |
| 
 | |
|     set_var = req_var;
 | |
| 
 | |
|     if (check)
 | |
|         set_var.activate = FB_ACTIVATE_TEST;
 | |
| 
 | |
|     if (0 != ioctl(fPtr->fd, FBIOPUT_VSCREENINFO, (void *) (&set_var))) {
 | |
|         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
 | |
|                    "FBIOPUT_VSCREENINFO: %s\n", strerror(errno));
 | |
|         return FALSE;
 | |
|     }
 | |
| 
 | |
|     if (!fbdev_modes_equal(&set_var, &req_var)) {
 | |
|         if (!check)
 | |
|             xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
 | |
|                        "FBIOPUT_VSCREENINFO succeeded but modified " "mode\n");
 | |
| #ifdef DEBUG
 | |
|         print_fbdev_mode("returned", &set_var);
 | |
| #endif
 | |
|         return FALSE;
 | |
|     }
 | |
| 
 | |
|     if (!check)
 | |
|         fPtr->var = set_var;
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| void
 | |
| fbdevHWSetVideoModes(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     const char **modename;
 | |
|     DisplayModePtr mode, this, last = pScrn->modes;
 | |
| 
 | |
|     if (NULL == pScrn->display->modes)
 | |
|         return;
 | |
| 
 | |
|     pScrn->virtualX = pScrn->display->virtualX;
 | |
|     pScrn->virtualY = pScrn->display->virtualY;
 | |
| 
 | |
|     for (modename = pScrn->display->modes; *modename != NULL; modename++) {
 | |
|         for (mode = pScrn->monitor->Modes; mode != NULL; mode = mode->next) {
 | |
|             if (0 == strcmp(mode->name, *modename)) {
 | |
|                 if (fbdevHWSetMode(pScrn, mode, TRUE))
 | |
|                     break;
 | |
| 
 | |
|                 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
 | |
|                            "\tmode \"%s\" test failed\n", *modename);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (NULL == mode) {
 | |
|             xf86DrvMsg(pScrn->scrnIndex, X_INFO,
 | |
|                        "\tmode \"%s\" not found\n", *modename);
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         xf86DrvMsg(pScrn->scrnIndex, X_INFO, "\tmode \"%s\" ok\n", *modename);
 | |
| 
 | |
|         if (pScrn->virtualX < mode->HDisplay)
 | |
|             pScrn->virtualX = mode->HDisplay;
 | |
|         if (pScrn->virtualY < mode->VDisplay)
 | |
|             pScrn->virtualY = mode->VDisplay;
 | |
| 
 | |
|         if (NULL == pScrn->modes) {
 | |
|             this = pScrn->modes = xf86DuplicateMode(mode);
 | |
|             this->next = this;
 | |
|             this->prev = this;
 | |
|         }
 | |
|         else {
 | |
|             this = xf86DuplicateMode(mode);
 | |
|             this->next = pScrn->modes;
 | |
|             this->prev = last;
 | |
|             last->next = this;
 | |
|             pScrn->modes->prev = this;
 | |
|         }
 | |
|         last = this;
 | |
|     }
 | |
| }
 | |
| 
 | |
| DisplayModePtr
 | |
| fbdevHWGetBuildinMode(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     return &fPtr->buildin;
 | |
| }
 | |
| 
 | |
| void
 | |
| fbdevHWUseBuildinMode(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     pScrn->modes = &fPtr->buildin;
 | |
|     pScrn->virtualX = pScrn->display->virtualX;
 | |
|     pScrn->virtualY = pScrn->display->virtualY;
 | |
|     if (pScrn->virtualX < fPtr->buildin.HDisplay)
 | |
|         pScrn->virtualX = fPtr->buildin.HDisplay;
 | |
|     if (pScrn->virtualY < fPtr->buildin.VDisplay)
 | |
|         pScrn->virtualY = fPtr->buildin.VDisplay;
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| 
 | |
| static void
 | |
| calculateFbmem_len(fbdevHWPtr fPtr)
 | |
| {
 | |
|     fPtr->fboff = (unsigned long) fPtr->fix.smem_start & ~PAGE_MASK;
 | |
|     fPtr->fbmem_len = (fPtr->fboff + fPtr->fix.smem_len + ~PAGE_MASK) &
 | |
|         PAGE_MASK;
 | |
| }
 | |
| 
 | |
| void *
 | |
| fbdevHWMapVidmem(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     if (NULL == fPtr->fbmem) {
 | |
|         calculateFbmem_len(fPtr);
 | |
|         fPtr->fbmem = mmap(NULL, fPtr->fbmem_len, PROT_READ | PROT_WRITE,
 | |
|                            MAP_SHARED, fPtr->fd, 0);
 | |
|         if (-1 == (long) fPtr->fbmem) {
 | |
|             xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
 | |
|                        "mmap fbmem: %s\n", strerror(errno));
 | |
|             fPtr->fbmem = NULL;
 | |
|         }
 | |
|         else {
 | |
|             /* Perhaps we'd better add fboff to fbmem and return 0 in
 | |
|                fbdevHWLinearOffset()? Of course we then need to mask
 | |
|                fPtr->fbmem with PAGE_MASK in fbdevHWUnmapVidmem() as
 | |
|                well. [geert] */
 | |
|         }
 | |
|     }
 | |
|     pScrn->memPhysBase =
 | |
|         (unsigned long) fPtr->fix.smem_start & (unsigned long) (PAGE_MASK);
 | |
|     pScrn->fbOffset =
 | |
|         (unsigned long) fPtr->fix.smem_start & (unsigned long) (~PAGE_MASK);
 | |
|     return fPtr->fbmem;
 | |
| }
 | |
| 
 | |
| int
 | |
| fbdevHWLinearOffset(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     return fPtr->fboff;
 | |
| }
 | |
| 
 | |
| Bool
 | |
| fbdevHWUnmapVidmem(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     if (NULL != fPtr->fbmem) {
 | |
|         if (-1 == munmap(fPtr->fbmem, fPtr->fbmem_len))
 | |
|             xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
 | |
|                        "munmap fbmem: %s\n", strerror(errno));
 | |
|         fPtr->fbmem = NULL;
 | |
|     }
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| void *
 | |
| fbdevHWMapMMIO(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     unsigned int mmio_off;
 | |
| 
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     if (NULL == fPtr->mmio) {
 | |
|         /* tell the kernel not to use accels to speed up console scrolling */
 | |
|         fPtr->var.accel_flags = 0;
 | |
|         if (0 != ioctl(fPtr->fd, FBIOPUT_VSCREENINFO, (void *) (&fPtr->var))) {
 | |
|             xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
 | |
|                        "FBIOPUT_VSCREENINFO: %s\n", strerror(errno));
 | |
|             return FALSE;
 | |
|         }
 | |
|         mmio_off = (unsigned long) fPtr->fix.mmio_start & ~PAGE_MASK;
 | |
|         fPtr->mmio_len = (mmio_off + fPtr->fix.mmio_len + ~PAGE_MASK) &
 | |
|             PAGE_MASK;
 | |
|         if (NULL == fPtr->fbmem)
 | |
|             calculateFbmem_len(fPtr);
 | |
|         fPtr->mmio = mmap(NULL, fPtr->mmio_len, PROT_READ | PROT_WRITE,
 | |
|                           MAP_SHARED, fPtr->fd, fPtr->fbmem_len);
 | |
|         if (-1 == (long) fPtr->mmio) {
 | |
|             xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
 | |
|                        "mmap mmio: %s\n", strerror(errno));
 | |
|             fPtr->mmio = NULL;
 | |
|         }
 | |
|         else
 | |
|             fPtr->mmio += mmio_off;
 | |
|     }
 | |
|     return fPtr->mmio;
 | |
| }
 | |
| 
 | |
| Bool
 | |
| fbdevHWUnmapMMIO(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     if (NULL != fPtr->mmio) {
 | |
|         if (-1 ==
 | |
|             munmap((void *) ((unsigned long) fPtr->mmio & PAGE_MASK),
 | |
|                    fPtr->mmio_len))
 | |
|             xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "munmap mmio: %s\n",
 | |
|                        strerror(errno));
 | |
|         fPtr->mmio = NULL;
 | |
|         /* FIXME: restore var.accel_flags [geert] */
 | |
|     }
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| 
 | |
| Bool
 | |
| fbdevHWModeInit(ScrnInfoPtr pScrn, DisplayModePtr mode)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     pScrn->vtSema = TRUE;
 | |
| 
 | |
|     /* set */
 | |
|     if (!fbdevHWSetMode(pScrn, mode, FALSE))
 | |
|         return FALSE;
 | |
| 
 | |
|     /* read back */
 | |
|     if (0 != ioctl(fPtr->fd, FBIOGET_FSCREENINFO, (void *) (&fPtr->fix))) {
 | |
|         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
 | |
|                    "FBIOGET_FSCREENINFO: %s\n", strerror(errno));
 | |
|         return FALSE;
 | |
|     }
 | |
|     if (0 != ioctl(fPtr->fd, FBIOGET_VSCREENINFO, (void *) (&fPtr->var))) {
 | |
|         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
 | |
|                    "FBIOGET_VSCREENINFO: %s\n", strerror(errno));
 | |
|         return FALSE;
 | |
|     }
 | |
| 
 | |
|     if (pScrn->defaultVisual == TrueColor ||
 | |
|         pScrn->defaultVisual == DirectColor) {
 | |
|         /* XXX: This is a hack, but it should be a NOP for all the setups that
 | |
|          * worked before and actually seems to fix some others...
 | |
|          */
 | |
|         pScrn->offset.red = fPtr->var.red.offset;
 | |
|         pScrn->offset.green = fPtr->var.green.offset;
 | |
|         pScrn->offset.blue = fPtr->var.blue.offset;
 | |
|         pScrn->mask.red =
 | |
|             ((1 << fPtr->var.red.length) - 1) << fPtr->var.red.offset;
 | |
|         pScrn->mask.green =
 | |
|             ((1 << fPtr->var.green.length) - 1) << fPtr->var.green.offset;
 | |
|         pScrn->mask.blue =
 | |
|             ((1 << fPtr->var.blue.length) - 1) << fPtr->var.blue.offset;
 | |
|     }
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* video mode save/restore                                              */
 | |
| void
 | |
| fbdevHWSave(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     if (0 != ioctl(fPtr->fd, FBIOGET_VSCREENINFO, (void *) (&fPtr->saved_var)))
 | |
|         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
 | |
|                    "FBIOGET_VSCREENINFO: %s\n", strerror(errno));
 | |
| }
 | |
| 
 | |
| void
 | |
| fbdevHWRestore(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     if (0 != ioctl(fPtr->fd, FBIOPUT_VSCREENINFO, (void *) (&fPtr->saved_var)))
 | |
|         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
 | |
|                    "FBIOPUT_VSCREENINFO: %s\n", strerror(errno));
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* callback for xf86HandleColormaps                                     */
 | |
| 
 | |
| void
 | |
| fbdevHWLoadPalette(ScrnInfoPtr pScrn, int numColors, int *indices,
 | |
|                    LOCO * colors, VisualPtr pVisual)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
|     struct fb_cmap cmap;
 | |
|     unsigned short red, green, blue;
 | |
|     int i;
 | |
| 
 | |
|     cmap.len = 1;
 | |
|     cmap.red = &red;
 | |
|     cmap.green = &green;
 | |
|     cmap.blue = &blue;
 | |
|     cmap.transp = NULL;
 | |
|     for (i = 0; i < numColors; i++) {
 | |
|         cmap.start = indices[i];
 | |
|         red = (colors[indices[i]].red << 8) | colors[indices[i]].red;
 | |
|         green = (colors[indices[i]].green << 8) | colors[indices[i]].green;
 | |
|         blue = (colors[indices[i]].blue << 8) | colors[indices[i]].blue;
 | |
|         if (-1 == ioctl(fPtr->fd, FBIOPUTCMAP, (void *) &cmap))
 | |
|             xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
 | |
|                        "FBIOPUTCMAP: %s\n", strerror(errno));
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* these can be hooked directly into ScrnInfoRec                        */
 | |
| 
 | |
| ModeStatus
 | |
| fbdevHWValidMode(ScrnInfoPtr pScrn, DisplayModePtr mode, Bool verbose, int flags)
 | |
| {
 | |
|     if (!fbdevHWSetMode(pScrn, mode, TRUE))
 | |
|         return MODE_BAD;
 | |
| 
 | |
|     return MODE_OK;
 | |
| }
 | |
| 
 | |
| Bool
 | |
| fbdevHWSwitchMode(ScrnInfoPtr pScrn, DisplayModePtr mode)
 | |
| {
 | |
|     if (!fbdevHWSetMode(pScrn, mode, FALSE))
 | |
|         return FALSE;
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| void
 | |
| fbdevHWAdjustFrame(ScrnInfoPtr pScrn, int x, int y)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
| 
 | |
|     if (x < 0 || x + fPtr->var.xres > fPtr->var.xres_virtual ||
 | |
|         y < 0 || y + fPtr->var.yres > fPtr->var.yres_virtual)
 | |
|         return;
 | |
| 
 | |
|     fPtr->var.xoffset = x;
 | |
|     fPtr->var.yoffset = y;
 | |
|     if (-1 == ioctl(fPtr->fd, FBIOPAN_DISPLAY, (void *) &fPtr->var))
 | |
|         xf86DrvMsgVerb(pScrn->scrnIndex, X_WARNING, 5,
 | |
|                        "FBIOPAN_DISPLAY: %s\n", strerror(errno));
 | |
| }
 | |
| 
 | |
| Bool
 | |
| fbdevHWEnterVT(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     if (!fbdevHWModeInit(pScrn, pScrn->currentMode))
 | |
|         return FALSE;
 | |
|     fbdevHWAdjustFrame(pScrn, pScrn->frameX0, pScrn->frameY0);
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| void
 | |
| fbdevHWLeaveVT(ScrnInfoPtr pScrn)
 | |
| {
 | |
|     fbdevHWRestore(pScrn);
 | |
| }
 | |
| 
 | |
| void
 | |
| fbdevHWDPMSSet(ScrnInfoPtr pScrn, int mode, int flags)
 | |
| {
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
|     unsigned long fbmode;
 | |
| 
 | |
|     if (!pScrn->vtSema)
 | |
|         return;
 | |
| 
 | |
|     if (fPtr->unsupported_ioctls & (1 << FBIOBLANK_UNSUPPORTED))
 | |
|         return;
 | |
| 
 | |
|     switch (mode) {
 | |
|     case DPMSModeOn:
 | |
|         fbmode = 0;
 | |
|         break;
 | |
|     case DPMSModeStandby:
 | |
|         fbmode = 2;
 | |
|         break;
 | |
|     case DPMSModeSuspend:
 | |
|         fbmode = 3;
 | |
|         break;
 | |
|     case DPMSModeOff:
 | |
|         fbmode = 4;
 | |
|         break;
 | |
|     default:
 | |
|         return;
 | |
|     }
 | |
| 
 | |
| RETRY:
 | |
|     if (-1 == ioctl(fPtr->fd, FBIOBLANK, (void *) fbmode)) {
 | |
|         switch (errno) {
 | |
|         case EAGAIN:
 | |
|             xf86DrvMsg(pScrn->scrnIndex, X_INFO,
 | |
|                        "FBIOBLANK: %s\n", strerror(errno));
 | |
| 	    break;
 | |
|         case EINTR:
 | |
|         case ERESTART:
 | |
|             goto RETRY;
 | |
|         default:
 | |
|             fPtr->unsupported_ioctls |= (1 << FBIOBLANK_UNSUPPORTED);
 | |
|             xf86DrvMsg(pScrn->scrnIndex, X_INFO,
 | |
|                        "FBIOBLANK: %s (Screen blanking not supported "
 | |
|                        "by kernel - disabling)\n", strerror(errno));
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| Bool
 | |
| fbdevHWSaveScreen(ScreenPtr pScreen, int mode)
 | |
| {
 | |
|     ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
 | |
|     fbdevHWPtr fPtr = FBDEVHWPTR(pScrn);
 | |
|     unsigned long unblank;
 | |
| 
 | |
|     if (!pScrn->vtSema)
 | |
|         return TRUE;
 | |
| 
 | |
|     if (fPtr->unsupported_ioctls & (1 << FBIOBLANK_UNSUPPORTED))
 | |
|         return FALSE;
 | |
| 
 | |
|     unblank = xf86IsUnblank(mode);
 | |
| 
 | |
| RETRY:
 | |
|     if (-1 == ioctl(fPtr->fd, FBIOBLANK, (void *) (1 - unblank))) {
 | |
|         switch (errno) {
 | |
|         case EAGAIN:
 | |
|             xf86DrvMsg(pScrn->scrnIndex, X_INFO,
 | |
|                        "FBIOBLANK: %s\n", strerror(errno));
 | |
|             break;
 | |
|         case EINTR:
 | |
|         case ERESTART:
 | |
|             goto RETRY;
 | |
|         default:
 | |
|             fPtr->unsupported_ioctls |= (1 << FBIOBLANK_UNSUPPORTED);
 | |
|             xf86DrvMsg(pScrn->scrnIndex, X_INFO,
 | |
|                        "FBIOBLANK: %s (Screen blanking not supported "
 | |
|                        "by kernel - disabling)\n", strerror(errno));
 | |
|         }
 | |
|         return FALSE;
 | |
|     }
 | |
| 
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| xf86SwitchModeProc *
 | |
| fbdevHWSwitchModeWeak(void)
 | |
| {
 | |
|     return fbdevHWSwitchMode;
 | |
| }
 | |
| 
 | |
| xf86AdjustFrameProc *
 | |
| fbdevHWAdjustFrameWeak(void)
 | |
| {
 | |
|     return fbdevHWAdjustFrame;
 | |
| }
 | |
| 
 | |
| xf86EnterVTProc *
 | |
| fbdevHWEnterVTWeak(void)
 | |
| {
 | |
|     return fbdevHWEnterVT;
 | |
| }
 | |
| 
 | |
| xf86LeaveVTProc *
 | |
| fbdevHWLeaveVTWeak(void)
 | |
| {
 | |
|     return fbdevHWLeaveVT;
 | |
| }
 | |
| 
 | |
| xf86ValidModeProc *
 | |
| fbdevHWValidModeWeak(void)
 | |
| {
 | |
|     return fbdevHWValidMode;
 | |
| }
 | |
| 
 | |
| xf86DPMSSetProc *
 | |
| fbdevHWDPMSSetWeak(void)
 | |
| {
 | |
|     return fbdevHWDPMSSet;
 | |
| }
 | |
| 
 | |
| xf86LoadPaletteProc *
 | |
| fbdevHWLoadPaletteWeak(void)
 | |
| {
 | |
|     return fbdevHWLoadPalette;
 | |
| }
 | |
| 
 | |
| SaveScreenProcPtr
 | |
| fbdevHWSaveScreenWeak(void)
 | |
| {
 | |
|     return fbdevHWSaveScreen;
 | |
| }
 |