/* all drivers need this */ #ifdef HAVE_XORG_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_SYSMACROS_H #include #endif #ifdef HAVE_SYS_MKDEV_H #include /* for minor() on Solaris */ #endif #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 #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 }; /* -------------------------------------------------------------------- */ /* 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; uint32_t saved_accel; /* buildin video mode */ DisplayModeRec buildin; /* disable non-fatal unsupported ioctls */ CARD32 unsupported_ioctls; } fbdevHWRec, *fbdevHWPtr; enum { FBIOBLANK_UNSUPPORTED = 0, }; static Bool fbdevHWGetRec(ScrnInfoPtr pScrn) { if (fbdevHWPrivateIndex < 0) fbdevHWPrivateIndex = xf86AllocateScrnInfoPrivateIndex(); if (FBDEVHWPTR(pScrn) != NULL) return TRUE; FBDEVHWPTRLVAL(pScrn) = XNFcallocarray(1, sizeof(fbdevHWRec)); return TRUE; } /* -------------------------------------------------------------------- */ /* 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 */ /* Wrapper around open() that also get the framebuffer name */ static int fbdev_open_device(int scrnIndex, const char *dev, char **namep) { int fd = dev ? open(dev, O_RDWR) : -1; if (!namep) { return fd; } if (fd == -1) { return -1; } struct fb_fix_screeninfo fix; if (ioctl(fd, FBIOGET_FSCREENINFO, (void *) (&fix)) == -1) { *namep = NULL; xf86DrvMsg(scrnIndex, X_ERROR, "Not using framebuffer device %s: FBIOGET_FSCREENINFO: %s\n", dev, strerror(errno)); close(fd); return -1; } *namep = malloc(16); if (*namep) { strncpy(*namep, fix.id, 16); } return fd; } static int fbdev_check_user_devices(int scrnIndex, const char* dev, char **namep) { int fd; /* try argument (from XF86Config) first */ if (dev) { fd = fbdev_open_device(scrnIndex, dev, namep); } else { /* second: environment variable */ dev = getenv("FRAMEBUFFER"); fd = fbdev_open_device(scrnIndex, dev, namep); } if (dev && fd == -1) { xf86DrvMsg(scrnIndex, X_ERROR, "Could not use the explicitly provided framebuffer: %s\n", dev); } return fd; } /** * Try to find the framebuffer device for a given PCI device * This probe works in the following way: * * 1. If we have device passed by the user, we store it's minor number. * We then look through the framebuffers associated to the pPci pci device. * If we find one that has the same minor as the one passed by the user, we * open the filename passed by the user and return an fd to it. * Otherwise, we return -1; * * 2. If we don't have a device passed by the user, * we look through the framebuffers associated to the pPci pci device. * If we find one that is valid, we return an fd to it. * Otherwise, we return -1; */ static int fbdev_open_pci(int scrnIndex, struct pci_device *pPci, const char *device, char **namep) { /* * We really don't care what pci slot we claim when using the fbdev driver * However, due to how the probe interface is designed, * we have to be careful to not claim the wrong pci slot. */ char pattern[PATH_MAX]; int fd; int fbdev_minor = -1; fd = fbdev_check_user_devices(scrnIndex, device, namep); int tfd; snprintf(pattern, sizeof(pattern), "/sys/bus/pci/devices/%04x:%02x:%02x.%d", pPci->domain, pPci->bus, pPci->dev, pPci->func); tfd = open(pattern, O_RDONLY); if (tfd == -1) { xf86DrvMsg(scrnIndex, X_WARNING, "Sysfs interface cannot be used." "Pci probe for framebuffer devices cannot function properly.\n"); if (fd != -1) { xf86DrvMsg(scrnIndex, X_WARNING, "Using device: %s without further checks\n", device); return fd; } xf86DrvMsg(scrnIndex, X_ERROR, "Unable to find a valid framebuffer device\n"); return -1; } close(tfd); if (fd != -1) { struct stat res; if (fstat(fd, &res) == 0) { fbdev_minor = minor(res.st_rdev); } close(fd); fd = -1; if (namep) { free(*namep); *namep = NULL; } } #define FBDEV_CHECK_PCI_GLOB(glob_pattern) \ do { \ glob_t res; \ snprintf(pattern, sizeof(pattern), \ "/sys/bus/pci/devices/%04x:%02x:%02x.%d/" glob_pattern "/dev", \ pPci->domain, pPci->bus, pPci->dev, pPci->func); \ if (!glob(pattern, GLOB_NOSORT | GLOB_NOESCAPE, NULL, &res)) { \ char filename[PATH_MAX] = "/dev/"; \ for (int i = 0; i < res.gl_pathc; i++) { \ int maj, min = -1; \ FILE *f = fopen(res.gl_pathv[i], "r"); \ if (f) { \ (void)!fscanf(f, "%d:%d", &maj, &min); \ fclose(f); \ } \ if (fbdev_minor != -1) { \ if (fbdev_minor != min) { \ continue; \ } \ /* We have determined the the device the user gave us matches this pci device */ \ /* However, the name could be different than /dev/fb* */ \ /* Since we already have a filename from the user, use that instead of guessing */ \ return fbdev_check_user_devices(scrnIndex, device, namep); \ } \ char *src = strstr(res.gl_pathv[i], "graphics") + sizeof("graphics/") - 1; /* Has to match */ \ char *dst = filename + sizeof("/dev/") - 1; \ while (*src != '/') { \ *dst++ = *src++; \ } \ *dst = '\0'; \ fd = fbdev_open_device(scrnIndex, filename, namep); \ if (fd != -1) { \ return fd; \ } \ } \ } \ globfree(&res); \ } while(0) FBDEV_CHECK_PCI_GLOB("graphics/fb*"); FBDEV_CHECK_PCI_GLOB("graphics:fb*"); FBDEV_CHECK_PCI_GLOB("*/graphics/fb*"); FBDEV_CHECK_PCI_GLOB("*/graphics:fb*"); #undef FBDEV_CHECK_PCI_GLOB xf86DrvMsg(scrnIndex, X_ERROR, "Unable to find a valid framebuffer device\n"); return -1; } static int fbdev_open(int scrnIndex, const char *dev, char **namep) { int fd; fd = fbdev_check_user_devices(scrnIndex, dev, namep); if (fd != -1) { /* fbdev was provided by the user and not guessed, just return it */ return fd; } /* try the default device symlink */ dev = "/dev/fb"; fd = fbdev_open_device(scrnIndex, dev, namep); /* last tries, framebuffers 0 through 31 */ char devbuf[] = "/dev/fbxx"; for (int i = 0; i <= 31 && fd == -1; i++) { snprintf(devbuf, sizeof(devbuf), "/dev/fb%d", i); fd = fbdev_open_device(scrnIndex, devbuf, namep); } if (fd == -1) { xf86DrvMsg(scrnIndex, X_ERROR, "Unable to find a valid framebuffer device\n"); } return fd; } /* -------------------------------------------------------------------- */ Bool fbdevHWProbe(struct pci_device *pPci, const char *device, char **namep) { int fd; if (pPci) fd = fbdev_open_pci(-1, pPci, device, 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, const char *device) { fbdevHWPtr fPtr; fbdevHWGetRec(pScrn); fPtr = FBDEVHWPTR(pScrn); /* open device */ if (pPci) fPtr->fd = fbdev_open_pci(pScrn->scrnIndex, pPci, device, 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; } } 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->saved_accel = fPtr->var.accel_flags; 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; fPtr->var.accel_flags = fPtr->saved_accel; if (0 != ioctl(fPtr->fd, FBIOPUT_VSCREENINFO, (void *) (&fPtr->var))) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "FBIOPUT_VSCREENINFO: %s\n", strerror(errno)); return FALSE; } } 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; } xf86LeaveVTProc * fbdevHWLeaveVTWeak(void) { return fbdevHWLeaveVT; } xf86ValidModeProc * fbdevHWValidModeWeak(void) { return fbdevHWValidMode; } xf86DPMSSetProc * fbdevHWDPMSSetWeak(void) { return fbdevHWDPMSSet; } xf86LoadPaletteProc * fbdevHWLoadPaletteWeak(void) { return fbdevHWLoadPalette; }