mirror of
https://github.com/X11Libre/xserver.git
synced 2026-03-25 08:29:23 +00:00
it's always present here, so no need to ifdef on it. Signed-off-by: Enrico Weigelt, metux IT consult <info@metux.net>
1040 lines
30 KiB
C
1040 lines
30 KiB
C
#include <xorg-config.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <glob.h>
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#ifdef HAVE_SYS_SYSMACROS_H
|
|
#include <sys/sysmacros.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_MKDEV_H
|
|
#include <sys/mkdev.h> /* 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 <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
|
|
};
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* 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;
|
|
}
|