/* * Copyright © 1999 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include "fbdev.h" #include #include extern int KdTsPhyScreen; char *fbdevDevicePath = NULL; static Bool fbdevInitialize (KdCardInfo *card, FbdevPriv *priv) { unsigned long off; if (fbdevDevicePath == NULL) fbdevDevicePath = "/dev/fb0"; if ((priv->fd = open(fbdevDevicePath, O_RDWR)) < 0) { ErrorF("Error opening framebuffer %s: %s\n", fbdevDevicePath, strerror(errno)); return FALSE; } /* quiet valgrind */ memset (&priv->fix, '\0', sizeof (priv->fix)); if (ioctl(priv->fd, FBIOGET_FSCREENINFO, &priv->fix) < 0) { perror("Error with /dev/fb ioctl FIOGET_FSCREENINFO"); close (priv->fd); return FALSE; } /* quiet valgrind */ memset (&priv->var, '\0', sizeof (priv->var)); if (ioctl(priv->fd, FBIOGET_VSCREENINFO, &priv->var) < 0) { perror("Error with /dev/fb ioctl FIOGET_VSCREENINFO"); close (priv->fd); return FALSE; } priv->fb_base = (char *) mmap ((caddr_t) NULL, priv->fix.smem_len, PROT_READ|PROT_WRITE, MAP_SHARED, priv->fd, 0); if (priv->fb_base == (char *)-1) { perror("ERROR: mmap framebuffer fails!"); close (priv->fd); return FALSE; } off = (unsigned long) priv->fix.smem_start % (unsigned long) getpagesize(); priv->fb = priv->fb_base + off; return TRUE; } Bool fbdevCardInit (KdCardInfo *card) { FbdevPriv *priv; priv = (FbdevPriv *) malloc(sizeof (FbdevPriv)); if (!priv) return FALSE; if (!fbdevInitialize (card, priv)) { free(priv); return FALSE; } card->driver = priv; return TRUE; } static Pixel fbdevMakeContig (Pixel orig, Pixel others) { Pixel low; low = lowbit (orig) >> 1; while (low && (others & low) == 0) { orig |= low; low >>= 1; } return orig; } static Bool fbdevModeSupported (KdScreenInfo *screen, const KdMonitorTiming *t) { return TRUE; } static void fbdevConvertMonitorTiming (const KdMonitorTiming *t, struct fb_var_screeninfo *var) { memset (var, 0, sizeof (struct fb_var_screeninfo)); var->xres = t->horizontal; var->yres = t->vertical; var->xres_virtual = t->horizontal; var->yres_virtual = t->vertical; var->xoffset = 0; var->yoffset = 0; var->pixclock = t->clock ? 1000000000 / t->clock : 0; var->left_margin = t->hbp; var->right_margin = t->hfp; var->upper_margin = t->vbp; var->lower_margin = t->vfp; var->hsync_len = t->hblank - t->hfp - t->hbp; var->vsync_len = t->vblank - t->vfp - t->vbp; var->sync = 0; var->vmode = 0; if (t->hpol == KdSyncPositive) var->sync |= FB_SYNC_HOR_HIGH_ACT; if (t->vpol == KdSyncPositive) var->sync |= FB_SYNC_VERT_HIGH_ACT; } static Bool fbdevScreenInitialize (KdScreenInfo *screen, FbdevScrPriv *scrpriv) { FbdevPriv *priv = screen->card->driver; Pixel allbits; int depth; Bool gray; struct fb_var_screeninfo var; const KdMonitorTiming *t; int k; k = ioctl (priv->fd, FBIOGET_VSCREENINFO, &var); if (!screen->width || !screen->height) { if (k >= 0) { screen->width = var.xres; screen->height = var.yres; } else { screen->width = 1024; screen->height = 768; } screen->rate = 103; /* FIXME: should get proper value from fb driver */ } if (!screen->fb.depth) { if (k >= 0) screen->fb.depth = var.bits_per_pixel; else screen->fb.depth = 16; } if ((screen->width != var.xres) || (screen->height != var.yres)) { t = KdFindMode (screen, fbdevModeSupported); screen->rate = t->rate; screen->width = t->horizontal; screen->height = t->vertical; /* Now try setting the mode */ if (k < 0 || (t->horizontal != var.xres || t->vertical != var.yres)) fbdevConvertMonitorTiming (t, &var); } var.activate = FB_ACTIVATE_NOW; var.bits_per_pixel = screen->fb.depth; var.nonstd = 0; var.grayscale = 0; k = ioctl (priv->fd, FBIOPUT_VSCREENINFO, &var); if (k < 0) { fprintf (stderr, "error: %s\n", strerror (errno)); return FALSE; } /* Re-get the "fixed" parameters since they might have changed */ k = ioctl (priv->fd, FBIOGET_FSCREENINFO, &priv->fix); if (k < 0) perror ("FBIOGET_FSCREENINFO"); /* Now get the new screeninfo */ ioctl (priv->fd, FBIOGET_VSCREENINFO, &priv->var); depth = priv->var.bits_per_pixel; gray = priv->var.grayscale; switch (priv->fix.visual) { case FB_VISUAL_PSEUDOCOLOR: if (gray) { screen->fb.visuals = (1 << StaticGray); /* could also support GrayScale, but what's the point? */ } else { screen->fb.visuals = ((1 << StaticGray) | (1 << GrayScale) | (1 << StaticColor) | (1 << PseudoColor) | (1 << TrueColor) | (1 << DirectColor)); } screen->fb.blueMask = 0x00; screen->fb.greenMask = 0x00; screen->fb.redMask = 0x00; break; case FB_VISUAL_STATIC_PSEUDOCOLOR: if (gray) { screen->fb.visuals = (1 << StaticGray); } else { screen->fb.visuals = (1 << StaticColor); } screen->fb.blueMask = 0x00; screen->fb.greenMask = 0x00; screen->fb.redMask = 0x00; break; case FB_VISUAL_TRUECOLOR: case FB_VISUAL_DIRECTCOLOR: screen->fb.visuals = (1 << TrueColor); #define Mask(o,l) (((1 << l) - 1) << o) screen->fb.redMask = Mask (priv->var.red.offset, priv->var.red.length); screen->fb.greenMask = Mask (priv->var.green.offset, priv->var.green.length); screen->fb.blueMask = Mask (priv->var.blue.offset, priv->var.blue.length); /* * This is a kludge so that Render will work -- fill in the gaps * in the pixel */ screen->fb.redMask = fbdevMakeContig (screen->fb.redMask, screen->fb.greenMask| screen->fb.blueMask); screen->fb.greenMask = fbdevMakeContig (screen->fb.greenMask, screen->fb.redMask| screen->fb.blueMask); screen->fb.blueMask = fbdevMakeContig (screen->fb.blueMask, screen->fb.redMask| screen->fb.greenMask); allbits = screen->fb.redMask | screen->fb.greenMask | screen->fb.blueMask; depth = 32; while (depth && !(allbits & (1 << (depth - 1)))) depth--; break; default: return FALSE; break; } screen->fb.depth = depth; screen->fb.bitsPerPixel = priv->var.bits_per_pixel; scrpriv->randr = screen->randr; return fbdevMapFramebuffer (screen); } Bool fbdevScreenInit (KdScreenInfo *screen) { FbdevScrPriv *scrpriv; scrpriv = calloc(1, sizeof (FbdevScrPriv)); if (!scrpriv) return FALSE; screen->driver = scrpriv; if (!fbdevScreenInitialize (screen, scrpriv)) { screen->driver = 0; free(scrpriv); return FALSE; } return TRUE; } static void * fbdevWindowLinear (ScreenPtr pScreen, CARD32 row, CARD32 offset, int mode, CARD32 *size, void *closure) { KdScreenPriv(pScreen); FbdevPriv *priv = pScreenPriv->card->driver; if (!pScreenPriv->enabled) return 0; *size = priv->fix.line_length; return (CARD8 *) priv->fb + row * priv->fix.line_length + offset; } Bool fbdevMapFramebuffer (KdScreenInfo *screen) { FbdevScrPriv *scrpriv = screen->driver; KdPointerMatrix m; FbdevPriv *priv = screen->card->driver; if (scrpriv->randr != RR_Rotate_0) scrpriv->shadow = TRUE; else scrpriv->shadow = FALSE; KdComputePointerMatrix (&m, scrpriv->randr, screen->width, screen->height); KdSetPointerMatrix (&m); screen->width = priv->var.xres; screen->height = priv->var.yres; if (scrpriv->shadow) { if (!KdShadowFbAlloc (screen, scrpriv->randr & (RR_Rotate_90|RR_Rotate_270))) return FALSE; } else { screen->fb.byteStride = priv->fix.line_length; screen->fb.pixelStride = (priv->fix.line_length * 8 / priv->var.bits_per_pixel); screen->fb.frameBuffer = (CARD8 *) (priv->fb); } return TRUE; } static void fbdevSetScreenSizes (ScreenPtr pScreen) { KdScreenPriv(pScreen); KdScreenInfo *screen = pScreenPriv->screen; FbdevScrPriv *scrpriv = screen->driver; FbdevPriv *priv = screen->card->driver; if (scrpriv->randr & (RR_Rotate_0|RR_Rotate_180)) { pScreen->width = priv->var.xres; pScreen->height = priv->var.yres; pScreen->mmWidth = screen->width_mm; pScreen->mmHeight = screen->height_mm; } else { pScreen->width = priv->var.yres; pScreen->height = priv->var.xres; pScreen->mmWidth = screen->height_mm; pScreen->mmHeight = screen->width_mm; } } static Bool fbdevUnmapFramebuffer (KdScreenInfo *screen) { KdShadowFbFree (screen); return TRUE; } static Bool fbdevSetShadow (ScreenPtr pScreen) { KdScreenPriv(pScreen); KdScreenInfo *screen = pScreenPriv->screen; FbdevScrPriv *scrpriv = screen->driver; FbdevPriv *priv = screen->card->driver; ShadowUpdateProc update; ShadowWindowProc window; int useYX = 0; #ifdef __arm__ /* Use variant copy routines that always read left to right in the shadow framebuffer. Reading vertical strips is exceptionally slow on XScale due to cache effects. */ useYX = 1; #endif window = fbdevWindowLinear; update = 0; if (scrpriv->randr) if (priv->var.bits_per_pixel == 16) { switch (scrpriv->randr) { case RR_Rotate_90: if (useYX) update = shadowUpdateRotate16_90YX; else update = shadowUpdateRotate16_90; break; case RR_Rotate_180: update = shadowUpdateRotate16_180; break; case RR_Rotate_270: if (useYX) update = shadowUpdateRotate16_270YX; else update = shadowUpdateRotate16_270; break; default: update = shadowUpdateRotate16; break; } } else update = shadowUpdateRotatePacked; else update = shadowUpdatePacked; return KdShadowSet (pScreen, scrpriv->randr, update, window); } #ifdef RANDR static Bool fbdevRandRGetInfo (ScreenPtr pScreen, Rotation *rotations) { KdScreenPriv(pScreen); KdScreenInfo *screen = pScreenPriv->screen; FbdevScrPriv *scrpriv = screen->driver; RRScreenSizePtr pSize; Rotation randr; int n; *rotations = RR_Rotate_All|RR_Reflect_All; for (n = 0; n < pScreen->numDepths; n++) if (pScreen->allowedDepths[n].numVids) break; if (n == pScreen->numDepths) return FALSE; pSize = RRRegisterSize (pScreen, screen->width, screen->height, screen->width_mm, screen->height_mm); randr = KdSubRotation (scrpriv->randr, screen->randr); RRSetCurrentConfig (pScreen, randr, 0, pSize); return TRUE; } static Bool fbdevRandRSetConfig (ScreenPtr pScreen, Rotation randr, int rate, RRScreenSizePtr pSize) { KdScreenPriv(pScreen); KdScreenInfo *screen = pScreenPriv->screen; FbdevScrPriv *scrpriv = screen->driver; Bool wasEnabled = pScreenPriv->enabled; FbdevScrPriv oldscr; int oldwidth; int oldheight; int oldmmwidth; int oldmmheight; int newwidth, newheight; if (screen->randr & (RR_Rotate_0|RR_Rotate_180)) { newwidth = pSize->width; newheight = pSize->height; } else { newwidth = pSize->height; newheight = pSize->width; } if (wasEnabled) KdDisableScreen (pScreen); oldscr = *scrpriv; oldwidth = screen->width; oldheight = screen->height; oldmmwidth = pScreen->mmWidth; oldmmheight = pScreen->mmHeight; /* * Set new configuration */ scrpriv->randr = KdAddRotation (screen->randr, randr); fbdevUnmapFramebuffer (screen); if (!fbdevMapFramebuffer (screen)) goto bail4; KdShadowUnset (screen->pScreen); if (!fbdevSetShadow (screen->pScreen)) goto bail4; fbdevSetScreenSizes (screen->pScreen); /* * Set frame buffer mapping */ (*pScreen->ModifyPixmapHeader) (fbGetScreenPixmap (pScreen), pScreen->width, pScreen->height, screen->fb.depth, screen->fb.bitsPerPixel, screen->fb.byteStride, screen->fb.frameBuffer); /* set the subpixel order */ KdSetSubpixelOrder (pScreen, scrpriv->randr); if (wasEnabled) KdEnableScreen (pScreen); return TRUE; bail4: fbdevUnmapFramebuffer (screen); *scrpriv = oldscr; (void) fbdevMapFramebuffer (screen); pScreen->width = oldwidth; pScreen->height = oldheight; pScreen->mmWidth = oldmmwidth; pScreen->mmHeight = oldmmheight; if (wasEnabled) KdEnableScreen (pScreen); return FALSE; } static Bool fbdevRandRInit (ScreenPtr pScreen) { rrScrPrivPtr pScrPriv; if (!RRScreenInit (pScreen)) return FALSE; pScrPriv = rrGetScrPriv(pScreen); pScrPriv->rrGetInfo = fbdevRandRGetInfo; pScrPriv->rrSetConfig = fbdevRandRSetConfig; return TRUE; } #endif static Bool fbdevCreateColormap (ColormapPtr pmap) { ScreenPtr pScreen = pmap->pScreen; KdScreenPriv(pScreen); FbdevPriv *priv = pScreenPriv->card->driver; VisualPtr pVisual; int i; int nent; xColorItem *pdefs; switch (priv->fix.visual) { case FB_VISUAL_STATIC_PSEUDOCOLOR: pVisual = pmap->pVisual; nent = pVisual->ColormapEntries; pdefs = malloc(nent * sizeof (xColorItem)); if (!pdefs) return FALSE; for (i = 0; i < nent; i++) pdefs[i].pixel = i; fbdevGetColors (pScreen, nent, pdefs); for (i = 0; i < nent; i++) { pmap->red[i].co.local.red = pdefs[i].red; pmap->red[i].co.local.green = pdefs[i].green; pmap->red[i].co.local.blue = pdefs[i].blue; } free(pdefs); return TRUE; default: return fbInitializeColormap (pmap); } } Bool fbdevInitScreen (ScreenPtr pScreen) { #ifdef TOUCHSCREEN KdTsPhyScreen = pScreen->myNum; #endif pScreen->CreateColormap = fbdevCreateColormap; return TRUE; } Bool fbdevFinishInitScreen (ScreenPtr pScreen) { if (!shadowSetup (pScreen)) return FALSE; #ifdef RANDR if (!fbdevRandRInit (pScreen)) return FALSE; #endif return TRUE; } Bool fbdevCreateResources (ScreenPtr pScreen) { return fbdevSetShadow (pScreen); } void fbdevPreserve (KdCardInfo *card) { } static int fbdevUpdateFbColormap(FbdevPriv *priv, int minidx, int maxidx) { struct fb_cmap cmap; cmap.start = minidx; cmap.len = maxidx - minidx + 1; cmap.red = &priv->red[minidx]; cmap.green = &priv->green[minidx]; cmap.blue = &priv->blue[minidx]; cmap.transp = 0; return ioctl(priv->fd, FBIOPUTCMAP, &cmap); } Bool fbdevEnable (ScreenPtr pScreen) { KdScreenPriv(pScreen); FbdevPriv *priv = pScreenPriv->card->driver; int k; priv->var.activate = FB_ACTIVATE_NOW|FB_CHANGE_CMAP_VBL; /* display it on the LCD */ k = ioctl (priv->fd, FBIOPUT_VSCREENINFO, &priv->var); if (k < 0) { perror ("FBIOPUT_VSCREENINFO"); return FALSE; } if (priv->fix.visual == FB_VISUAL_DIRECTCOLOR) { int i; for (i = 0; i < (1 << priv->var.red.length) || i < (1 << priv->var.green.length) || i < (1 << priv->var.blue.length); i++) { priv->red[i] = i * 65535 / ((1 << priv->var.red.length) - 1); priv->green[i] = i * 65535 / ((1 << priv->var.green.length) - 1); priv->blue[i] = i * 65535 / ((1 << priv->var.blue.length) - 1); } fbdevUpdateFbColormap(priv, 0, i); } return TRUE; } Bool fbdevDPMS (ScreenPtr pScreen, int mode) { KdScreenPriv(pScreen); FbdevPriv *priv = pScreenPriv->card->driver; static int oldmode = -1; if (mode == oldmode) return TRUE; #ifdef FBIOPUT_POWERMODE if (ioctl (priv->fd, FBIOPUT_POWERMODE, &mode) >= 0) { oldmode = mode; return TRUE; } #endif #ifdef FBIOBLANK if (ioctl (priv->fd, FBIOBLANK, mode ? mode + 1 : 0) >= 0) { oldmode = mode; return TRUE; } #endif return FALSE; } void fbdevDisable (ScreenPtr pScreen) { } void fbdevRestore (KdCardInfo *card) { } void fbdevScreenFini (KdScreenInfo *screen) { } void fbdevCardFini (KdCardInfo *card) { FbdevPriv *priv = card->driver; munmap (priv->fb_base, priv->fix.smem_len); close (priv->fd); free(priv); } /* * Retrieve actual colormap and return selected n entries in pdefs. */ void fbdevGetColors (ScreenPtr pScreen, int n, xColorItem *pdefs) { KdScreenPriv(pScreen); FbdevPriv *priv = pScreenPriv->card->driver; struct fb_cmap cmap; int p; int k; int min, max; min = 256; max = 0; for (k = 0; k < n; k++) { if (pdefs[k].pixel < min) min = pdefs[k].pixel; if (pdefs[k].pixel > max) max = pdefs[k].pixel; } cmap.start = min; cmap.len = max - min + 1; cmap.red = &priv->red[min]; cmap.green = &priv->green[min]; cmap.blue = &priv->blue[min]; cmap.transp = 0; k = ioctl (priv->fd, FBIOGETCMAP, &cmap); if (k < 0) { perror ("can't get colormap"); return; } while (n--) { p = pdefs->pixel; pdefs->red = priv->red[p]; pdefs->green = priv->green[p]; pdefs->blue = priv->blue[p]; pdefs++; } } /* * Change colormap by updating n entries described in pdefs. */ void fbdevPutColors (ScreenPtr pScreen, int n, xColorItem *pdefs) { KdScreenPriv(pScreen); FbdevPriv *priv = pScreenPriv->card->driver; int p; int min, max; min = 256; max = 0; while (n--) { p = pdefs->pixel; priv->red[p] = pdefs->red; priv->green[p] = pdefs->green; priv->blue[p] = pdefs->blue; if (p < min) min = p; if (p > max) max = p; pdefs++; } fbdevUpdateFbColormap(priv, min, max); }