diff options
Diffstat (limited to 'hw/xfree86/os-support/lynxos/lynx_video.c')
-rw-r--r-- | hw/xfree86/os-support/lynxos/lynx_video.c | 597 |
1 files changed, 568 insertions, 29 deletions
diff --git a/hw/xfree86/os-support/lynxos/lynx_video.c b/hw/xfree86/os-support/lynxos/lynx_video.c index 94b2f0ecd..c667994b3 100644 --- a/hw/xfree86/os-support/lynxos/lynx_video.c +++ b/hw/xfree86/os-support/lynxos/lynx_video.c @@ -21,7 +21,7 @@ * */ -/* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/lynxos/lynx_video.c,v 3.2.4.1 1997/05/09 07:15:24 hohndel Exp $ */ +/* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/lynxos/lynx_video.c,v 3.18 2002/12/14 04:41:14 dawes Exp $ */ #include "X.h" #include "input.h" @@ -30,6 +30,81 @@ #include "xf86.h" #include "xf86Priv.h" #include "xf86_OSlib.h" +#include "xf86OSpriv.h" + +#if defined(__powerpc__) + +# if defined(USE_MACHINE_ABSOLUTE) +# include <machine/absolute.h> +# else +# define __USER_SPACE_INCLUDE +# include <hw_absolute.h> +# endif + +void ppcPciIoMap(int bus); +#endif + +#if 0 +#define DEBUG +#endif + +#ifdef HAS_MTRR_SUPPORT +#include <sys/memrange.h> +#define X_MTRR_ID "XFree86" + +static pointer setWC(int, unsigned long, unsigned long, Bool, MessageType); +static void undoWC(int, pointer); +static Bool cleanMTRR(void); +static int devMemFd = -1; +#define MTRR_DEVICE "/dev/mtrr" +#endif + + +#if !defined(NO_MMAP) +#include <sys/mman.h> + +int smem_remove(char *name) +{ + return(0); +} + +char *smem_create(char *name, char *arg_addr, long size, int mode) +{ + int fd; + void *addr = 0; + char *retval; + size_t len = size; + int prot = PROT_READ|PROT_WRITE|PROT_UNCACHE; + int flags = MAP_SHARED; + off_t off = (off_t)arg_addr; + + if ((fd = open("/dev/mem" , O_RDWR)) < 0) + { + retval = (char *)-1; + } + else + { + if (mode == SM_DETACH) + { + munmap(arg_addr, len); + retval = 0; + } + else + { + if ((retval = mmap (addr, len, prot, flags, fd, off) ) == MAP_FAILED) + { + retval = (char *)-1; + } + } + + close(fd); + } + + return(retval); +} + +#endif + /***************************************************************************/ /* Video Memory Mapping section */ @@ -38,19 +113,24 @@ typedef struct { char name[16]; - pointer Base; - long Size; + unsigned long Base; + unsigned long Size; char *ptr; int RefCnt; } _SMEMS; -#define MAX_SMEMS 8 +#define MAX_SMEMS 16 static _SMEMS smems[MAX_SMEMS]; + +#ifndef MAP_FAILED +#define MAP_FAILED ((void *)-1) +#endif + static void -smemCleanup() +smemCleanup(void) { int i; @@ -60,18 +140,15 @@ smemCleanup() (void)smem_remove(smems[i].name); *smems[i].name = '\0'; smems[i].ptr = NULL; - smems[i].Base = NULL; + smems[i].Base = 0; smems[i].Size = 0; smems[i].RefCnt = 0; } } } -pointer xf86MapVidMem(ScreenNum, Region, Base, Size) -int ScreenNum; -int Region; -pointer Base; -unsigned long Size; +static pointer +MapVidMem(int ScreenNum, unsigned long Base, unsigned long Size, int flags) { static int once; int free_slot = -1; @@ -86,14 +163,15 @@ unsigned long Size; { if (!*smems[i].name && free_slot == -1) free_slot = i; - if (smems[i].Base == Base && smems[i].Size == Size && *smems[i].name) { + if (smems[i].Base == Base && smems[i].Size == Size + && *smems[i].name) { smems[i].RefCnt++; return smems[i].ptr; } } if (i == MAX_SMEMS && free_slot == -1) { - FatalError("xf86MapVidMem: failed to smem_create Base %x Size %x (out of SMEMS entries)\n", + FatalError("MapVidMem: failed to smem_create Base %x Size %x (out of SMEMS entries)\n", Base, Size); } @@ -101,53 +179,85 @@ unsigned long Size; sprintf(smems[i].name, "Video-%d", i); smems[i].Base = Base; smems[i].Size = Size; - smems[i].ptr = smem_create(smems[i].name, Base, Size, SM_READ|SM_WRITE); + + xf86MsgVerb(X_INFO, 3, "MapVidMem: Base=0x%x Size=0x%x\n", + Base, Size); + +#if defined(__powerpc__) + if (((unsigned long)Base & PHYS_IO_MEM_START) != PHYS_IO_MEM_START) { + Base = Base | PHYS_IO_MEM_START; + } +#endif + + smems[i].ptr = smem_create(smems[i].name, (char *)Base, Size, SM_READ|SM_WRITE); smems[i].RefCnt = 1; if (smems[i].ptr == NULL) { /* check if there is a stale segment around */ if (smem_remove(smems[i].name) == 0) { - ErrorF("xf86MapVidMem: removed stale smem_ segment %s\n", - smems[i].name); + xf86Msg(X_INFO, + "MapVidMem: removed stale smem_ segment %s\n", + smems[i].name); smems[i].ptr = smem_create(smems[i].name, - Base, Size, SM_READ|SM_WRITE); + (char *)Base, Size, SM_READ|SM_WRITE); } if (smems[i].ptr == NULL) { *smems[i].name = '\0'; - FatalError("xf86MapVidMem: failed to smem_create Base %x Size %x (%s)\n", + FatalError("MapVidMem: failed to smem_create Base %x Size %x (%s)\n", Base, Size, strerror(errno)); } } + xf86MsgVerb(X_INFO, 3, "MapVidMem: Base=0x%x Size=0x%x Ptr=0x%x\n", + Base, Size, smems[i].ptr); return smems[i].ptr; } -void xf86UnMapVidMem(ScreenNum, Region, Base, Size) -int ScreenNum; -int Region; -pointer Base; -unsigned long Size; +static void +UnMapVidMem(int ScreenNum, pointer Base, unsigned long Size) { int i; + xf86MsgVerb(X_INFO, 3, "UnMapVidMem: Base/Ptr=0x%x Size=0x%x\n", + Base, Size); for (i = 0; i < MAX_SMEMS; i++) { - if (*smems[i].name && smems[i].ptr == Base && smems[i].Size == Size) + if (*smems[i].name && smems[i].ptr == Base + && smems[i].Size == Size) { if (--smems[i].RefCnt > 0) return; + (void)smem_create(NULL, smems[i].ptr, 0, SM_DETACH); + xf86MsgVerb(X_INFO, 3, + "UnMapVidMem: smem_create(%s, 0x%08x, ... " + "SM_DETACH)\n", smems[i].name, smems[i].ptr); (void)smem_remove(smems[i].name); *smems[i].name = '\0'; smems[i].RefCnt = 0; return; } } - ErrorF("xf86UnMapVidMem: no SMEM found for Base = %lx Size = %lx\n", Base, Size); + xf86MsgVerb(X_WARNING, 2, + "UnMapVidMem: no SMEM found for Base = %lx Size = %lx\n", + Base, Size); } -Bool xf86LinearVidMem() + +void +xf86OSInitVidMem(VidMemInfoPtr pVidMem) { - return(TRUE); + pVidMem->linearSupported = TRUE; + pVidMem->mapMem = MapVidMem; + pVidMem->unmapMem = UnMapVidMem; + pVidMem->setWC = 0; + pVidMem->undoWC = 0; +#ifdef HAS_MTRR_SUPPORT + if (cleanMTRR()) { + pVidMem->setWC = setWC; + pVidMem->undoWC = undoWC; + } +#endif + pVidMem->initialised = TRUE; } @@ -155,13 +265,442 @@ Bool xf86LinearVidMem() /* Interrupt Handling section */ /***************************************************************************/ -Bool xf86DisableInterrupts() +Bool +xf86DisableInterrupts() { return(TRUE); } -void xf86EnableInterrupts() +void +xf86EnableInterrupts() +{ + return; +} + +/***************************************************************************/ +/* I/O Permissions section for PowerPC */ +/***************************************************************************/ + +#if defined(__powerpc__) + +volatile unsigned char *ioBase = MAP_FAILED; +volatile unsigned char *pciConfBase = MAP_FAILED; + +static int IOEnabled; + + +static void +removeIOSmem(void) +{ + smem_create(NULL, (char *) ioBase, 0, SM_DETACH); + smem_remove("IOBASE"); + ioBase = MAP_FAILED; +} + +void +xf86EnableIO() { + if (IOEnabled++ == 0) { + ioBase = (unsigned char *) smem_create("IOBASE", + (char *)PHYS_ISA_IO_SPACE, 64*1024, SM_READ|SM_WRITE); + if (ioBase == MAP_FAILED) { + --IOEnabled; + FatalError("xf86EnableIO: Failed to map I/O\n"); + } else { +#ifdef DEBUG + ErrorF("xf86EnableIO: mapped I/O at vaddr 0x%08x\n", + ioBase); +#endif + atexit(removeIOSmem); + } + } return; } +void +xf86DisableIO() +{ + if (!IOEnabled) + return; + + if (--IOEnabled == 0) + removeIOSmem(); + return; +} + +#if 0 +void +xf86DisableIOPrivs(void) +{ + return; +} +#endif +void +ppcPciIoMap(int bus) +{ + xf86EnableIO(); +} + +#endif + + +#ifdef HAS_MTRR_SUPPORT +/* memory range (MTRR) support for LynxOS (taken from BSD MTRR support) */ + +/* + * This code is experimental. Some parts may be overkill, and other parts + * may be incomplete. + */ + +/* + * getAllRanges returns the full list of memory ranges with attributes set. + */ + +static struct mem_range_desc * +getAllRanges(int *nmr) +{ + struct mem_range_desc *mrd; + struct mem_range_op mro; + + /* + * Find how many ranges there are. If this fails, then the kernel + * probably doesn't have MTRR support. + */ + mro.mo_arg[0] = 0; + if (ioctl(devMemFd, MEMRANGE_GET, &mro)) + return NULL; + *nmr = mro.mo_arg[0]; + mrd = xnfalloc(*nmr * sizeof(struct mem_range_desc)); + mro.mo_arg[0] = *nmr; + mro.mo_desc = mrd; + if (ioctl(devMemFd, MEMRANGE_GET, &mro)) { + xfree(mrd); + return NULL; + } + return mrd; +} + +/* + * cleanMTRR removes any memory attribute that may be left by a previous + * X server. Normally there won't be any, but this takes care of the + * case where a server crashed without being able finish cleaning up. + */ + +static Bool +cleanMTRR() +{ + struct mem_range_desc *mrd; + struct mem_range_op mro; + int nmr, i; + + /* This shouldn't happen */ + if (devMemFd < 0) { + if ((devMemFd = open(MTRR_DEVICE, O_RDONLY)) < 0) { +perror("open MTRR"); + return FALSE; + } + } + + if (!(mrd = getAllRanges(&nmr))) + return FALSE; + + for (i = 0; i < nmr; i++) { + if (strcmp(mrd[i].mr_owner, X_MTRR_ID) == 0 && + (mrd[i].mr_flags & MDF_ACTIVE)) { +#ifdef DEBUG + ErrorF("Clean for (0x%lx,0x%lx)\n", + (unsigned long)mrd[i].mr_base, + (unsigned long)mrd[i].mr_len); +#endif + if (mrd[i].mr_flags & MDF_FIXACTIVE) { + mro.mo_arg[0] = MEMRANGE_SET_UPDATE; + mrd[i].mr_flags = MDF_UNCACHEABLE; + } else { + mro.mo_arg[0] = MEMRANGE_SET_REMOVE; + } + mro.mo_desc = mrd + i; + ioctl(devMemFd, MEMRANGE_SET, &mro); + } + } +#ifdef DEBUG + sleep(10); +#endif + xfree(mrd); + return TRUE; +} + +typedef struct x_RangeRec { + struct mem_range_desc mrd; + Bool wasWC; + struct x_RangeRec * next; +} RangeRec, *RangePtr; + +static void +freeRangeList(RangePtr range) +{ + RangePtr rp; + + while (range) { + rp = range; + range = rp->next; + xfree(rp); + } +} + +static RangePtr +dupRangeList(RangePtr list) +{ + RangePtr new = NULL, rp, p; + + rp = list; + while (rp) { + p = xnfalloc(sizeof(RangeRec)); + *p = *rp; + p->next = new; + new = p; + rp = rp->next; + } + return new; +} + +static RangePtr +sortRangeList(RangePtr list) +{ + RangePtr rp1, rp2, copy, sorted = NULL, minp, prev, minprev; + unsigned long minBase; + + /* Sort by base address */ + rp1 = copy = dupRangeList(list); + while (rp1) { + minBase = rp1->mrd.mr_base; + minp = rp1; + minprev = NULL; + prev = rp1; + rp2 = rp1->next; + while (rp2) { + if (rp2->mrd.mr_base < minBase) { + minBase = rp2->mrd.mr_base; + minp = rp2; + minprev = prev; + } + prev = rp2; + rp2 = rp2->next; + } + if (minprev) { + minprev->next = minp->next; + rp1 = copy; + } else { + rp1 = minp->next; + } + minp->next = sorted; + sorted = minp; + } + return sorted; +} + +/* + * findRanges returns a list of ranges that overlap the specified range. + */ + +static void +findRanges(unsigned long base, unsigned long size, RangePtr *ucp, RangePtr *wcp) +{ + struct mem_range_desc *mrd; + int nmr, i; + RangePtr rp, *p; + + if (!(mrd = getAllRanges(&nmr))) + return; + + for (i = 0; i < nmr; i++) { + if ((mrd[i].mr_flags & MDF_ACTIVE) && + mrd[i].mr_base < base + size && + mrd[i].mr_base + mrd[i].mr_len > base) { + if (mrd[i].mr_flags & MDF_WRITECOMBINE) + p = wcp; + else if (mrd[i].mr_flags & MDF_UNCACHEABLE) + p = ucp; + else + continue; + rp = xnfalloc(sizeof(RangeRec)); + rp->mrd = mrd[i]; + rp->next = *p; + *p = rp; + } + } + xfree(mrd); +} + +/* + * This checks if the existing overlapping ranges fully cover the requested + * range. Is this overkill? + */ + +static Bool +fullCoverage(unsigned long base, unsigned long size, RangePtr overlap) +{ + RangePtr rp1, sorted = NULL; + unsigned long end; + + sorted = sortRangeList(overlap); + /* Look for gaps */ + rp1 = sorted; + end = base + size; + while (rp1) { + if (rp1->mrd.mr_base > base) { + freeRangeList(sorted); + return FALSE; + } else { + base = rp1->mrd.mr_base + rp1->mrd.mr_len; + } + if (base >= end) { + freeRangeList(sorted); + return TRUE; + } + rp1 = rp1->next; + } + freeRangeList(sorted); + return FALSE; +} + +static pointer +addWC(int screenNum, unsigned long base, unsigned long size, MessageType from) +{ + RangePtr uc = NULL, wc = NULL, retlist = NULL; + struct mem_range_desc mrd; + struct mem_range_op mro; + + findRanges(base, size, &uc, &wc); + + /* See of the full range is already WC */ + if (!uc && fullCoverage(base, size, wc)) { + xf86DrvMsg(screenNum, from, + "Write-combining range (0x%lx,0x%lx) was already set\n", + base, size); + return NULL; + } + + /* Otherwise, try to add the new range */ + mrd.mr_base = base; + mrd.mr_len = size; + strcpy(mrd.mr_owner, X_MTRR_ID); + mrd.mr_flags = MDF_WRITECOMBINE; + mro.mo_desc = &mrd; + mro.mo_arg[0] = MEMRANGE_SET_UPDATE; + if (ioctl(devMemFd, MEMRANGE_SET, &mro)) { + xf86DrvMsg(screenNum, X_WARNING, + "Failed to set write-combining range " + "(0x%lx,0x%lx)\n", base, size); + return NULL; + } else { + xf86DrvMsg(screenNum, from, + "Write-combining range (0x%lx,0x%lx)\n", base, size); + retlist = xnfalloc(sizeof(RangeRec)); + retlist->mrd = mrd; + retlist->wasWC = FALSE; + retlist->next = NULL; + return retlist; + } +} + +static pointer +delWC(int screenNum, unsigned long base, unsigned long size, MessageType from) +{ + RangePtr uc = NULL, wc = NULL, retlist = NULL; + struct mem_range_desc mrd; + struct mem_range_op mro; + + findRanges(base, size, &uc, &wc); + + /* + * See of the full range is already not WC, or if there is full + * coverage from UC ranges. + */ + if (!wc || fullCoverage(base, size, uc)) { + xf86DrvMsg(screenNum, from, + "Write-combining range (0x%lx,0x%lx) was already clear\n", + base, size); + return NULL; + } + + /* Otherwise, try to add the new range */ + mrd.mr_base = base; + mrd.mr_len = size; + strcpy(mrd.mr_owner, X_MTRR_ID); + mrd.mr_flags = MDF_UNCACHEABLE; + mro.mo_desc = &mrd; + mro.mo_arg[0] = MEMRANGE_SET_UPDATE; + if (ioctl(devMemFd, MEMRANGE_SET, &mro)) { + xf86DrvMsg(screenNum, X_WARNING, + "Failed to remove write-combining range " + "(0x%lx,0x%lx)\n", base, size); + /* XXX Should then remove all of the overlapping WC ranges */ + return NULL; + } else { + xf86DrvMsg(screenNum, from, + "Removed Write-combining range (0x%lx,0x%lx)\n", + base, size); + retlist = xnfalloc(sizeof(RangeRec)); + retlist->mrd = mrd; + retlist->wasWC = TRUE; + retlist->next = NULL; + return retlist; + } +} + +static pointer +setWC(int screenNum, unsigned long base, unsigned long size, Bool enable, + MessageType from) +{ + if (enable) + return addWC(screenNum, base, size, from); + else + return delWC(screenNum, base, size, from); +} + +static void +undoWC(int screenNum, pointer list) +{ + RangePtr rp; + struct mem_range_op mro; + Bool failed; + + rp = list; + while (rp) { +#ifdef DEBUG + ErrorF("Undo for (0x%lx,0x%lx), %d\n", + (unsigned long)rp->mrd.mr_base, + (unsigned long)rp->mrd.mr_len, rp->wasWC); +#endif + failed = FALSE; + if (rp->wasWC) { + mro.mo_arg[0] = MEMRANGE_SET_UPDATE; + rp->mrd.mr_flags = MDF_WRITECOMBINE; + strcpy(rp->mrd.mr_owner, "unknown"); + } else { + mro.mo_arg[0] = MEMRANGE_SET_REMOVE; + } + mro.mo_desc = &rp->mrd; + + if (ioctl(devMemFd, MEMRANGE_SET, &mro)) { + if (!rp->wasWC) { + mro.mo_arg[0] = MEMRANGE_SET_UPDATE; + rp->mrd.mr_flags = MDF_UNCACHEABLE; + strcpy(rp->mrd.mr_owner, "unknown"); + if (ioctl(devMemFd, MEMRANGE_SET, &mro)) + failed = TRUE; + } else + failed = TRUE; + } + if (failed) { + xf86DrvMsg(screenNum, X_WARNING, + "Failed to restore MTRR range (0x%lx,0x%lx)\n", + (unsigned long)rp->mrd.mr_base, + (unsigned long)rp->mrd.mr_len); + } + rp = rp->next; + } +} + +#endif /* HAS_MTRR_SUPPORT */ + |