diff options
author | Søren Sandmann Pedersen <ssp@redhat.com> | 2011-02-03 05:53:01 -0500 |
---|---|---|
committer | Søren Sandmann Pedersen <ssp@redhat.com> | 2011-07-15 11:05:33 -0400 |
commit | 526b6abaa0a27a7ae158bb4d39b903c48ae9f421 (patch) | |
tree | 9132d5bf079dc8a7da7c27a08d3cd0144be87c91 | |
parent | ce5507756e87a73bc6275ac275073932cd122583 (diff) |
Add old driver in as a compatibility layer
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/compat/Makefile.am | 41 | ||||
-rw-r--r-- | src/compat/compat-lookup3.c | 769 | ||||
-rw-r--r-- | src/compat/compat-lookup3.h | 26 | ||||
-rw-r--r-- | src/compat/compat-qxl.h | 610 | ||||
-rw-r--r-- | src/compat/compat-qxl_cursor.c | 196 | ||||
-rw-r--r-- | src/compat/compat-qxl_driver.c | 1454 | ||||
-rw-r--r-- | src/compat/compat-qxl_image.c | 255 | ||||
-rw-r--r-- | src/compat/compat-qxl_mem.c | 321 | ||||
-rw-r--r-- | src/compat/compat-qxl_ring.c | 97 |
11 files changed, 3771 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac index eca91e9..751496d 100644 --- a/configure.ac +++ b/configure.ac @@ -85,5 +85,6 @@ AC_CONFIG_FILES([ Makefile src/Makefile src/uxa/Makefile + src/compat/Makefile ]) AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am index ff4fa14..f3a65bf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,7 +25,7 @@ # _ladir passes a dummy rpath to libtool so the thing will actually link # TODO: -nostdlib/-Bstatic/-lgcc platform magic, not installing the .a, etc. -SUBDIRS=uxa +SUBDIRS=uxa compat AM_CFLAGS = $(SPICE_PROTOCOL_CFLAGS) $(XORG_CFLAGS) $(PCIACCESS_CFLAGS) $(CWARNFLAGS) diff --git a/src/compat/Makefile.am b/src/compat/Makefile.am new file mode 100644 index 0000000..84a30f2 --- /dev/null +++ b/src/compat/Makefile.am @@ -0,0 +1,41 @@ +# Copyright 2008 Red Hat, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# on the rights to use, copy, modify, merge, publish, distribute, sub +# license, and/or sell copies of the Software, and to permit persons to whom +# the Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice (including the next +# paragraph) shall be included in all copies or substantial portions of the +# Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +# this is obnoxious: +# -module lets us name the module exactly how we want +# -avoid-version prevents gratuitous .0.0.0 version numbers on the end +# _ladir passes a dummy rpath to libtool so the thing will actually link +# TODO: -nostdlib/-Bstatic/-lgcc platform magic, not installing the .a, etc. +qxl_drv_la_LTLIBRARIES = qxl_drv.la +qxl_drv_la_LDFLAGS = -module -avoid-version +qxl_drv_ladir = @moduledir@/drivers +AM_CFLAGS = -g +AM_CFLAGS = $(XORG_CFLAGS) $(PCIACCESS_CFLAGS) $(CWARNFLAGS) + +qxl_drv_la_SOURCES = \ + compat-qxl.h \ + compat-qxl_driver.c \ + compat-qxl_image.c \ + compat-qxl_ring.c \ + compat-qxl_mem.c \ + compat-lookup3.c \ + compat-lookup3.h \ + compat-qxl_cursor.c diff --git a/src/compat/compat-lookup3.c b/src/compat/compat-lookup3.c new file mode 100644 index 0000000..c301d85 --- /dev/null +++ b/src/compat/compat-lookup3.c @@ -0,0 +1,769 @@ +/* +------------------------------------------------------------------------------- +lookup3.c, by Bob Jenkins, May 2006, Public Domain. + +These are functions for producing 32-bit hashes for hash table lookup. +hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() +are externally useful functions. Routines to test the hash are included +if SELF_TEST is defined. You can use this free for any purpose. It's in +the public domain. It has no warranty. + +You probably want to use hashlittle(). hashlittle() and hashbig() +hash byte arrays. hashlittle() is is faster than hashbig() on +little-endian machines. Intel and AMD are little-endian machines. +On second thought, you probably want hashlittle2(), which is identical to +hashlittle() except it returns two 32-bit hashes for the price of one. +You could implement hashbig2() if you wanted but I haven't bothered here. + +If you want to find a hash of, say, exactly 7 integers, do + a = i1; b = i2; c = i3; + mix(a,b,c); + a += i4; b += i5; c += i6; + mix(a,b,c); + a += i7; + final(a,b,c); +then use c as the hash value. If you have a variable length array of +4-byte integers to hash, use hashword(). If you have a byte array (like +a character string), use hashlittle(). If you have several byte arrays, or +a mix of things, see the comments above hashlittle(). + +Why is this so big? I read 12 bytes at a time into 3 4-byte integers, +then mix those integers. This is fast (you can do a lot more thorough +mixing with 12*3 instructions on 3 integers than you can with 3 instructions +on 1 byte), but shoehorning those bytes into integers efficiently is messy. +------------------------------------------------------------------------------- +*/ + +#include <stdio.h> /* defines printf for tests */ +#include <time.h> /* defines time_t for timings in the test */ +#include "compat-lookup3.h" +#ifdef linux +# include <endian.h> /* attempt to define endianness */ +#endif + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* +------------------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. + +This is reversible, so any information in (a,b,c) before mix() is +still in (a,b,c) after mix(). + +If four pairs of (a,b,c) inputs are run through mix(), or through +mix() in reverse, there are at least 32 bits of the output that +are sometimes the same for one pair and different for another pair. +This was tested for: +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that +satisfy this are + 4 6 8 16 19 4 + 9 15 3 18 27 15 + 14 9 3 7 17 3 +Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing +for "differ" defined as + with a one-bit base and a two-bit delta. I +used http://burtleburtle.net/bob/hash/avalanche.html to choose +the operations, constants, and arrangements of the variables. + +This does not achieve avalanche. There are input bits of (a,b,c) +that fail to affect some output bits of (a,b,c), especially of a. The +most thoroughly mixed value is c, but it doesn't really even achieve +avalanche in c. + +This allows some parallelism. Read-after-writes are good at doubling +the number of bits affected, so the goal of mixing pulls in the opposite +direction as the goal of parallelism. I did what I could. Rotates +seem to cost as much as shifts on every machine I could lay my hands +on, and rotates are much kinder to the top and bottom bits, so I used +rotates. +------------------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* +------------------------------------------------------------------------------- +final -- final mixing of 3 32-bit values (a,b,c) into c + +Pairs of (a,b,c) values differing in only a few bits will usually +produce values of c that look totally different. This was tested for +* pairs that differed by one bit, by two bits, in any combination + of top bits of (a,b,c), or in any combination of bottom bits of + (a,b,c). +* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + is commonly produced by subtraction) look like a single 1-bit + difference. +* the base values were pseudorandom, all zero but one bit set, or + all zero plus a counter that starts at zero. + +These constants passed: + 14 11 25 16 4 14 24 + 12 14 25 16 4 14 24 +and these came close: + 4 8 15 26 3 22 24 + 10 8 15 26 3 22 24 + 11 8 15 26 3 22 24 +------------------------------------------------------------------------------- +*/ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* +-------------------------------------------------------------------- + This works on all machines. To be useful, it requires + -- that the key be an array of uint32_t's, and + -- that the length be the number of uint32_t's in the key + + The function hashword() is identical to hashlittle() on little-endian + machines, and identical to hashbig() on big-endian machines, + except that the length has to be measured in uint32_ts rather than in + bytes. hashlittle() is more complicated than hashword() only because + hashlittle() has to dance around fitting the key bytes into registers. +-------------------------------------------------------------------- +*/ +uint32_t hashword( + const uint32_t *k, /* the key, an array of uint32_t values */ + size_t length, /* the length of the key, in uint32_ts */ + uint32_t initval) /* the previous hash, or an arbitrary value */ +{ + uint32_t a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + (((uint32_t)length)<<2) + initval; + + /*------------------------------------------------- handle most of the key */ + while (length > 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 3; + k += 3; + } + + /*------------------------------------------- handle the last 3 uint32_t's */ + switch(length) /* all the case statements fall through */ + { + case 3 : c+=k[2]; + case 2 : b+=k[1]; + case 1 : a+=k[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /*------------------------------------------------------ report the result */ + return c; +} + + +/* +-------------------------------------------------------------------- +hashword2() -- same as hashword(), but take two seeds and return two +32-bit values. pc and pb must both be nonnull, and *pc and *pb must +both be initialized with seeds. If you pass in (*pb)==0, the output +(*pc) will be the same as the return value from hashword(). +-------------------------------------------------------------------- +*/ +void hashword2 ( +const uint32_t *k, /* the key, an array of uint32_t values */ +size_t length, /* the length of the key, in uint32_ts */ +uint32_t *pc, /* IN: seed OUT: primary hash value */ +uint32_t *pb) /* IN: more seed OUT: secondary hash value */ +{ + uint32_t a,b,c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)(length<<2)) + *pc; + c += *pb; + + /*------------------------------------------------- handle most of the key */ + while (length > 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 3; + k += 3; + } + + /*------------------------------------------- handle the last 3 uint32_t's */ + switch(length) /* all the case statements fall through */ + { + case 3 : c+=k[2]; + case 2 : b+=k[1]; + case 1 : a+=k[0]; + final(a,b,c); + case 0: /* case 0: nothing left to add */ + break; + } + /*------------------------------------------------------ report the result */ + *pc=c; *pb=b; +} + + +/* +------------------------------------------------------------------------------- +hashlittle() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + length : the length of the key, counting by bytes + initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Two keys differing by one or two bits will have +totally different hash values. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (uint8_t **)k, do it like this: + for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h); + +By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this +code any way you wish, private, educational, or commercial. It's free. + +Use for hash table lookup, or anything where one collision in 2^^32 is +acceptable. Do NOT use for cryptographic purposes. +------------------------------------------------------------------------------- +*/ + +uint32_t hashlittle( const void *key, size_t length, uint32_t initval) +{ + uint32_t a,b,c; /* internal state */ + union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ +#ifdef VALGRIND + const uint8_t *k8; +#endif + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + + +/* + * hashlittle2: return 2 32-bit hash values + * + * This is identical to hashlittle(), except it returns two 32-bit hash + * values instead of just one. This is good enough for hash table + * lookup with 2^^64 buckets, or if you want a second hash if you're not + * happy with the first, or if you want a probably-unique 64-bit ID for + * the key. *pc is better mixed than *pb, so use *pc first. If you want + * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)". + */ +void hashlittle2( + const void *key, /* the key to hash */ + size_t length, /* length of the key */ + uint32_t *pc, /* IN: primary initval, OUT: primary hash */ + uint32_t *pb) /* IN: secondary initval, OUT: secondary hash */ +{ + uint32_t a,b,c; /* internal state */ + union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + *pc; + c += *pb; + + u.ptr = key; + if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ +#ifdef VALGRIND + const uint8_t *k8; +#endif + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + +#endif /* !valgrind */ + + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) + { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + /*----------------------------- handle the last (probably partial) block */ + k8 = (const uint8_t *)k; + switch(length) + { + case 12: c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=k[0]; + break; + case 1 : a+=k8[0]; + break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9 : c+=k[8]; + case 8 : b+=((uint32_t)k[7])<<24; + case 7 : b+=((uint32_t)k[6])<<16; + case 6 : b+=((uint32_t)k[5])<<8; + case 5 : b+=k[4]; + case 4 : a+=((uint32_t)k[3])<<24; + case 3 : a+=((uint32_t)k[2])<<16; + case 2 : a+=((uint32_t)k[1])<<8; + case 1 : a+=k[0]; + break; + case 0 : *pc=c; *pb=b; return; /* zero length strings require no mixing */ + } + } + + final(a,b,c); + *pc=c; *pb=b; +} + + + +/* + * hashbig(): + * This is the same as hashword() on big-endian machines. It is different + * from hashlittle() on all machines. hashbig() takes advantage of + * big-endian byte ordering. + */ +uint32_t hashbig( const void *key, size_t length, uint32_t initval) +{ + uint32_t a,b,c; + union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */ + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; + + u.ptr = key; + if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) { + const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */ +#ifdef VALGRIND + const uint8_t *k8; +#endif + + /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */ + while (length > 12) + { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /*----------------------------- handle the last (probably partial) block */ + /* + * "k[2]<<8" actually reads beyond the end of the string, but + * then shifts out the part it's not allowed to read. Because the + * string is aligned, the illegal read is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch(length) + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff00; a+=k[0]; break; + case 6 : b+=k[1]&0xffff0000; a+=k[0]; break; + case 5 : b+=k[1]&0xff000000; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff00; break; + case 2 : a+=k[0]&0xffff0000; break; + case 1 : a+=k[0]&0xff000000; break; + case 0 : return c; /* zero length strings require no mixing */ + } + +#else /* make valgrind happy */ + + k8 = (const uint8_t *)k; + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<8; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<16; /* fall through */ + case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */ + case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */ + case 1 : a+=((uint32_t)k8[0])<<24; break; + case 0 : return c; + } + +#endif /* !VALGRIND */ + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + /*--------------- all but the last block: affect some 32 bits of (a,b,c) */ + while (length > 12) + { + a += ((uint32_t)k[0])<<24; + a += ((uint32_t)k[1])<<16; + a += ((uint32_t)k[2])<<8; + a += ((uint32_t)k[3]); + b += ((uint32_t)k[4])<<24; + b += ((uint32_t)k[5])<<16; + b += ((uint32_t)k[6])<<8; + b += ((uint32_t)k[7]); + c += ((uint32_t)k[8])<<24; + c += ((uint32_t)k[9])<<16; + c += ((uint32_t)k[10])<<8; + c += ((uint32_t)k[11]); + mix(a,b,c); + length -= 12; + k += 12; + } + + /*-------------------------------- last block: affect all 32 bits of (c) */ + switch(length) /* all the case statements fall through */ + { + case 12: c+=k[11]; + case 11: c+=((uint32_t)k[10])<<8; + case 10: c+=((uint32_t)k[9])<<16; + case 9 : c+=((uint32_t)k[8])<<24; + case 8 : b+=k[7]; + case 7 : b+=((uint32_t)k[6])<<8; + case 6 : b+=((uint32_t)k[5])<<16; + case 5 : b+=((uint32_t)k[4])<<24; + case 4 : a+=k[3]; + case 3 : a+=((uint32_t)k[2])<<8; + case 2 : a+=((uint32_t)k[1])<<16; + case 1 : a+=((uint32_t)k[0])<<24; + break; + case 0 : return c; + } + } + + final(a,b,c); + return c; +} + diff --git a/src/compat/compat-lookup3.h b/src/compat/compat-lookup3.h new file mode 100644 index 0000000..50c1cf4 --- /dev/null +++ b/src/compat/compat-lookup3.h @@ -0,0 +1,26 @@ +#ifndef __LOOKUP3_H +#define __LOOKUP3_H + +#if defined(__GNUC__) || defined(__sun) + +#include <stdint.h> + +#else + +#ifdef QXLDD +#include <windef.h> +#include "os_dep.h" +#else +#include <stddef.h> +#include <basetsd.h> +#endif + +typedef UINT32 uint32_t; +typedef UINT16 uint16_t; +typedef UINT8 uint8_t; + +#endif + +uint32_t hashlittle( const void *key, size_t length, uint32_t initval); + +#endif diff --git a/src/compat/compat-qxl.h b/src/compat/compat-qxl.h new file mode 100644 index 0000000..bec43b3 --- /dev/null +++ b/src/compat/compat-qxl.h @@ -0,0 +1,610 @@ +/* + * Copyright 2008 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include <stdint.h> + +#include "compiler.h" +#include "xf86.h" +#if GET_ABI_MAJOR(ABI_VIDEODRV_VERSION) < 6 +#include "xf86Resources.h" +#endif +#include "xf86PciInfo.h" +#include "xf86Cursor.h" +#include "xf86_OSproc.h" +#include "xf86xv.h" +#include "shadow.h" +#include "micmap.h" +#ifdef XSERVER_PCIACCESS +#include "pciaccess.h" +#endif +#include "fb.h" +#include "vgaHW.h" + +#define hidden _X_HIDDEN + +#define QXL_NAME "qxl" +#define QXL_DRIVER_NAME "qxl" +#define PCI_VENDOR_RED_HAT 0x1b36 + +#define PCI_CHIP_QXL_0100 0x0100 + +#pragma pack(push,1) + +/* I/O port definitions */ +enum { + QXL_IO_NOTIFY_CMD, + QXL_IO_NOTIFY_CURSOR, + QXL_IO_UPDATE_AREA, + QXL_IO_UPDATE_IRQ, + QXL_IO_NOTIFY_OOM, + QXL_IO_RESET, + QXL_IO_SET_MODE, + QXL_IO_LOG, +}; + +struct qxl_mode { + uint32_t id; + uint32_t x_res; + uint32_t y_res; + uint32_t bits; + uint32_t stride; + uint32_t x_mili; + uint32_t y_mili; + uint32_t orientation; +}; + +typedef enum +{ + QXL_CMD_NOP, + QXL_CMD_DRAW, + QXL_CMD_UPDATE, + QXL_CMD_CURSOR, + QXL_CMD_MESSAGE +} qxl_command_type; + +struct qxl_command { + uint64_t data; + uint32_t type; + uint32_t pad; +}; + +struct qxl_rect { + uint32_t top; + uint32_t left; + uint32_t bottom; + uint32_t right; +}; + +union qxl_release_info { + uint64_t id; + uint64_t next; +}; + +struct qxl_clip { + uint32_t type; + uint64_t address; +}; + +struct qxl_point { + int x; + int y; +}; + +struct qxl_pattern { + uint64_t pat; + struct qxl_point pos; +}; + +typedef enum +{ + QXL_BRUSH_TYPE_NONE, + QXL_BRUSH_TYPE_SOLID, + QXL_BRUSH_TYPE_PATTERN +} qxl_brush_type; + +struct qxl_brush { + uint32_t type; + union { + uint32_t color; + struct qxl_pattern pattern; + } u; +}; + +struct qxl_mask { + unsigned char flags; + struct qxl_point pos; + uint64_t bitmap; +}; + +typedef enum { + QXL_IMAGE_TYPE_BITMAP, + QXL_IMAGE_TYPE_QUIC, + QXL_IMAGE_TYPE_PNG, + QXL_IMAGE_TYPE_LZ_PLT = 100, + QXL_IMAGE_TYPE_LZ_RGB, + QXL_IMAGE_TYPE_GLZ_RGB, + QXL_IMAGE_TYPE_FROM_CACHE, +} qxl_image_type; + +typedef enum { + QXL_IMAGE_CACHE = (1 << 0) +} qxl_image_flags; + +struct qxl_image_descriptor +{ + uint64_t id; + uint8_t type; + uint8_t flags; + uint32_t width; + uint32_t height; +}; + +struct qxl_data_chunk { + uint32_t data_size; + uint64_t prev_chunk; + uint64_t next_chunk; + uint8_t data[0]; +}; + +typedef enum +{ + QXL_BITMAP_FMT_INVALID, + QXL_BITMAP_FMT_1BIT_LE, + QXL_BITMAP_FMT_1BIT_BE, + QXL_BITMAP_FMT_4BIT_LE, + QXL_BITMAP_FMT_4BIT_BE, + QXL_BITMAP_FMT_8BIT, + QXL_BITMAP_FMT_16BIT, + QXL_BITMAP_FMT_24BIT, + QXL_BITMAP_FMT_32BIT, + QXL_BITMAP_FMT_RGBA, +} qxl_bitmap_format; + +typedef enum { + QXL_BITMAP_PAL_CACHE_ME = (1 << 0), + QXL_BITMAP_PAL_FROM_CACHE = (1 << 1), + QXL_BITMAP_TOP_DOWN = (1 << 2), +} qxl_bitmap_flags; + +struct qxl_bitmap { + uint8_t format; + uint8_t flags; + uint32_t x; /* actually width */ + uint32_t y; /* actually height */ + uint32_t stride; /* in bytes */ + uint64_t palette; /* Can be NULL */ + uint64_t data; /* A qxl_data_chunk that actually contains the data */ +}; + +struct qxl_image { + struct qxl_image_descriptor descriptor; + union + { + struct qxl_bitmap bitmap; + } u; +}; + +struct qxl_fill { + struct qxl_brush brush; + unsigned short rop_descriptor; + struct qxl_mask mask; +}; + +struct qxl_opaque { + uint64_t src_bitmap; + struct qxl_rect src_area; + struct qxl_brush brush; + unsigned short rop_descriptor; + unsigned char scale_mode; + struct qxl_mask mask; +}; + +struct qxl_copy { + uint64_t src_bitmap; + struct qxl_rect src_area; + unsigned short rop_descriptor; + unsigned char scale_mode; + struct qxl_mask mask; +}; + +struct qxl_transparent { + uint64_t src_bitmap; + struct qxl_rect src_area; + uint32_t src_color; + uint32_t true_color; +}; + +struct qxl_alpha_blend { + unsigned char alpha; + uint64_t src_bitmap; + struct qxl_rect src_area; +}; + +struct qxl_copy_bits { + struct qxl_point src_pos; +}; + +struct qxl_blend { /* same as copy */ + uint64_t src_bitmap; + struct qxl_rect src_area; + unsigned short rop_descriptor; + unsigned char scale_mode; + struct qxl_mask mask; +}; + +struct qxl_rop3 { + uint64_t src_bitmap; + struct qxl_rect src_area; + struct qxl_brush brush; + unsigned char rop3; + unsigned char scale_mode; + struct qxl_mask mask; +}; + +struct qxl_line_attr { + unsigned char flags; + unsigned char join_style; + unsigned char end_style; + unsigned char style_nseg; + int width; + int miter_limit; + uint64_t style; +}; + +struct qxl_stroke { + uint64_t path; + struct qxl_line_attr attr; + struct qxl_brush brush; + unsigned short fore_mode; + unsigned short back_mode; +}; + +struct qxl_text { + uint64_t str; + struct qxl_rect back_area; + struct qxl_brush fore_brush; + struct qxl_brush back_brush; + unsigned short fore_mode; + unsigned short back_mode; +}; + +struct qxl_blackness { + struct qxl_mask mask; +}; + +struct qxl_inverse { + struct qxl_mask mask; +}; + +struct qxl_whiteness { + struct qxl_mask mask; +}; + +/* Effects */ +typedef enum +{ + QXL_EFFECT_BLEND, + QXL_EFFECT_OPAQUE, + QXL_EFFECT_REVERT_ON_DUP, + QXL_EFFECT_BLACKNESS_ON_DUP, + QXL_EFFECT_WHITENESS_ON_DUP, + QXL_EFFECT_NOP_ON_DUP, + QXL_EFFECT_NOP, + QXL_EFFECT_OPAQUE_BRUSH +} qxl_effect_type; + +typedef enum +{ + QXL_CLIP_TYPE_NONE, + QXL_CLIP_TYPE_RECTS, + QXL_CLIP_TYPE_PATH, +} qxl_clip_type; + +typedef enum { + QXL_DRAW_NOP, + QXL_DRAW_FILL, + QXL_DRAW_OPAQUE, + QXL_DRAW_COPY, + QXL_COPY_BITS, + QXL_DRAW_BLEND, + QXL_DRAW_BLACKNESS, + QXL_DRAW_WHITENESS, + QXL_DRAW_INVERS, + QXL_DRAW_ROP3, + QXL_DRAW_STROKE, + QXL_DRAW_TEXT, + QXL_DRAW_TRANSPARENT, + QXL_DRAW_ALPHA_BLEND, +} qxl_draw_type; + +struct qxl_drawable { + union qxl_release_info release_info; + unsigned char effect; + unsigned char type; + unsigned short bitmap_offset; + struct qxl_rect bitmap_area; + struct qxl_rect bbox; + struct qxl_clip clip; + uint32_t mm_time; + union { + struct qxl_fill fill; + struct qxl_opaque opaque; + struct qxl_copy copy; + struct qxl_transparent transparent; + struct qxl_alpha_blend alpha_blend; + struct qxl_copy_bits copy_bits; + struct qxl_blend blend; + struct qxl_rop3 rop3; + struct qxl_stroke stroke; + struct qxl_text text; + struct qxl_blackness blackness; + struct qxl_inverse inverse; + struct qxl_whiteness whiteness; + } u; +}; + +struct qxl_update_cmd { + union qxl_release_info release_info; + struct qxl_rect area; + uint32_t update_id; +}; + +struct qxl_point16 { + int16_t x; + int16_t y; +}; + +enum { + QXL_CURSOR_SET, + QXL_CURSOR_MOVE, + QXL_CURSOR_HIDE, + QXL_CURSOR_TRAIL, +}; + +#define QXL_CURSOR_DEVICE_DATA_SIZE 128 + +enum { + CURSOR_TYPE_ALPHA, + CURSOR_TYPE_MONO, + CURSOR_TYPE_COLOR4, + CURSOR_TYPE_COLOR8, + CURSOR_TYPE_COLOR16, + CURSOR_TYPE_COLOR24, + CURSOR_TYPE_COLOR32, +}; + +struct qxl_cursor_header { + uint64_t unique; + uint16_t type; + uint16_t width; + uint16_t height; + uint16_t hot_spot_x; + uint16_t hot_spot_y; +}; + +struct qxl_cursor +{ + struct qxl_cursor_header header; + uint32_t data_size; + struct qxl_data_chunk chunk; +}; + +struct qxl_cursor_cmd { + union qxl_release_info release_info; + uint8_t type; + union { + struct { + struct qxl_point16 position; + unsigned char visible; + uint64_t shape; + } set; + struct { + uint16_t length; + uint16_t frequency; + } trail; + struct qxl_point16 position; + } u; + uint8_t device_data[QXL_CURSOR_DEVICE_DATA_SIZE]; +}; + +struct qxl_rom { + uint32_t magic; + uint32_t id; + uint32_t update_id; + uint32_t compression_level; + uint32_t log_level; + uint32_t mode; + uint32_t modes_offset; + uint32_t num_io_pages; + uint32_t pages_offset; + uint32_t draw_area_offset; + uint32_t draw_area_size; + uint32_t ram_header_offset; + uint32_t mm_clock; +}; + +struct qxl_ring_header { + uint32_t num_items; + uint32_t prod; + uint32_t notify_on_prod; + uint32_t cons; + uint32_t notify_on_cons; +}; + +#define QXL_LOG_BUF_SIZE 4096 + +struct qxl_ram_header { + uint32_t magic; + uint32_t int_pending; + uint32_t int_mask; + unsigned char log_buf[QXL_LOG_BUF_SIZE]; + struct qxl_ring_header cmd_ring_hdr; + struct qxl_command cmd_ring[32]; + struct qxl_ring_header cursor_ring_hdr; + struct qxl_command cursor_ring[32]; + struct qxl_ring_header release_ring_hdr; + uint64_t release_ring[8]; + struct qxl_rect update_area; +}; + +#pragma pack(pop) + +typedef struct _qxl_screen_t qxl_screen_t; + +struct _qxl_screen_t +{ + /* These are the names QXL uses */ + void * ram; /* Video RAM */ + void * ram_physical; + void * vram; /* Command RAM */ + struct qxl_rom * rom; /* Parameter RAM */ + + struct qxl_ring * command_ring; + struct qxl_ring * cursor_ring; + struct qxl_ring * release_ring; + + int num_modes; + struct qxl_mode * modes; + int io_base; + int draw_area_offset; + int draw_area_size; + + void * fb; + int bytes_per_pixel; + + struct qxl_mem * mem; /* Context for qxl_alloc/free */ + + EntityInfoPtr entity; + + void * io_pages; + void * io_pages_physical; + +#ifdef XSERVER_LIBPCIACCESS + struct pci_device * pci; +#else + pciVideoPtr pci; + PCITAG pci_tag; +#endif + vgaRegRec vgaRegs; + + CreateScreenResourcesProcPtr create_screen_resources; + CloseScreenProcPtr close_screen; + CreateGCProcPtr create_gc; +#if 0 + PaintWindowProcPtr paint_window_background; + PaintWindowProcPtr paint_window_border; +#endif + CopyWindowProcPtr copy_window; + + DamagePtr damage; + RegionRec pending_copy; + RegionRec to_be_sent; + + int16_t cur_x; + int16_t cur_y; + int16_t hot_x; + int16_t hot_y; + + ScrnInfoPtr pScrn; +}; + +static inline uint64_t +physical_address (qxl_screen_t *qxl, void *virtual) +{ + return (uint64_t) ((unsigned long)virtual + (((unsigned long)qxl->ram_physical - (unsigned long)qxl->ram))); +} + +static inline void * +virtual_address (qxl_screen_t *qxl, void *physical) +{ + return (void *) ((unsigned long)physical + ((unsigned long)qxl->ram - (unsigned long)qxl->ram_physical)); +} + +static inline void * +u64_to_pointer (uint64_t u) +{ + return (void *)(unsigned long)u; +} + +static inline uint64_t +pointer_to_u64 (void *p) +{ + return (uint64_t)(unsigned long)p; +} + +struct qxl_ring; + +/* + * HW cursor + */ +void qxl_cursor_init (ScreenPtr pScreen); + + + +/* + * Rings + */ +struct qxl_ring * qxl_ring_create (struct qxl_ring_header *header, + int element_size, + int n_elements, + int prod_notify); +void qxl_ring_push (struct qxl_ring *ring, + const void *element); +Bool qxl_ring_pop (struct qxl_ring *ring, + void *element); +void qxl_ring_wait_idle (struct qxl_ring *ring); + + + +/* + * Images + */ +struct qxl_image *qxl_image_create (qxl_screen_t *qxl, + const uint8_t *data, + int x, + int y, + int width, + int height, + int stride); +void qxl_image_destroy (qxl_screen_t *qxl, + struct qxl_image *image); +void qxl_drop_image_cache (qxl_screen_t *qxl); + + +/* + * Malloc + */ +struct qxl_mem * qxl_mem_create (void *base, + unsigned long n_bytes); +void qxl_mem_dump_stats (struct qxl_mem *mem, + const char *header); +void * qxl_alloc (struct qxl_mem *mem, + unsigned long n_bytes); +void qxl_free (struct qxl_mem *mem, + void *d); +void qxl_mem_free_all (struct qxl_mem *mem); +void * qxl_allocnf (qxl_screen_t *qxl, + unsigned long size); + + diff --git a/src/compat/compat-qxl_cursor.c b/src/compat/compat-qxl_cursor.c new file mode 100644 index 0000000..bb5387e --- /dev/null +++ b/src/compat/compat-qxl_cursor.c @@ -0,0 +1,196 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <string.h> +#include "compat-qxl.h" +#include <cursorstr.h> + +static void +push_cursor (qxl_screen_t *qxl, struct qxl_cursor_cmd *cursor) +{ + struct qxl_command cmd; + + /* See comment on push_command() in qxl_driver.c */ + if (qxl->rom->mode != ~0) + { + cmd.type = QXL_CMD_CURSOR; + cmd.data = physical_address (qxl, cursor); + + qxl_ring_push (qxl->cursor_ring, &cmd); + } +} + +static struct qxl_cursor_cmd * +qxl_alloc_cursor_cmd(qxl_screen_t *qxl) +{ + struct qxl_cursor_cmd *cmd = + qxl_allocnf (qxl, sizeof(struct qxl_cursor_cmd)); + + cmd->release_info.id = pointer_to_u64 (cmd) | 1; + + return cmd; +} + +static void +qxl_set_cursor_position(ScrnInfoPtr pScrn, int x, int y) +{ + qxl_screen_t *qxl = pScrn->driverPrivate; + struct qxl_cursor_cmd *cmd = qxl_alloc_cursor_cmd(qxl); + + qxl->cur_x = x; + qxl->cur_y = y; + + cmd->type = QXL_CURSOR_MOVE; + cmd->u.position.x = qxl->cur_x + qxl->hot_x; + cmd->u.position.y = qxl->cur_y + qxl->hot_y; + + push_cursor(qxl, cmd); +} + +static void +qxl_load_cursor_image(ScrnInfoPtr pScrn, unsigned char *bits) +{ +} + +static void +qxl_set_cursor_colors(ScrnInfoPtr pScrn, int bg, int fg) +{ + /* Should not be called since UseHWCursor returned FALSE */ +} + +static void +qxl_load_cursor_argb (ScrnInfoPtr pScrn, CursorPtr pCurs) +{ + qxl_screen_t *qxl = pScrn->driverPrivate; + int w = pCurs->bits->width; + int h = pCurs->bits->height; + int size = w * h * sizeof (CARD32); + + struct qxl_cursor_cmd *cmd = qxl_alloc_cursor_cmd (qxl); + struct qxl_cursor *cursor = + qxl_allocnf(qxl, sizeof(struct qxl_cursor) + size); + + cursor->header.unique = 0; + cursor->header.type = CURSOR_TYPE_ALPHA; + cursor->header.width = w; + cursor->header.height = h; + /* I wonder if we can just tell the client that the hotspot is 0, 0 + * always? The coordinates we are getting from X are for 0, 0 anyway, + * so the question is if the client uses the hotspot for anything else? + */ + cursor->header.hot_spot_x = pCurs->bits->xhot; + cursor->header.hot_spot_y = pCurs->bits->yhot; + + cursor->data_size = size; + + cursor->chunk.next_chunk = 0; + cursor->chunk.prev_chunk = 0; + cursor->chunk.data_size = size; + + memcpy (cursor->chunk.data, pCurs->bits->argb, size); + +#if 0 + int i, j; + for (j = 0; j < h; ++j) + { + for (i = 0; i < w; ++i) + { + ErrorF ("%c", (pCurs->bits->argb[j * w + i] & 0xff000000) == 0xff000000? '#' : '.'); + } + + ErrorF ("\n"); + } +#endif + + qxl->hot_x = pCurs->bits->xhot; + qxl->hot_y = pCurs->bits->yhot; + + cmd->type = QXL_CURSOR_SET; + cmd->u.set.position.x = qxl->cur_x + qxl->hot_x; + cmd->u.set.position.y = qxl->cur_y + qxl->hot_y; + cmd->u.set.shape = physical_address (qxl, cursor); + cmd->u.set.visible = TRUE; + + push_cursor(qxl, cmd); +} + +static Bool +qxl_use_hw_cursor (ScreenPtr pScrn, CursorPtr pCurs) +{ + /* Old-school bitmap cursors are not + * hardware accelerated for now. + */ + return FALSE; +} + +static Bool +qxl_use_hw_cursorARGB (ScreenPtr pScrn, CursorPtr pCurs) +{ + return TRUE; +} + +static void +qxl_hide_cursor(ScrnInfoPtr pScrn) +{ + qxl_screen_t *qxl = pScrn->driverPrivate; + struct qxl_cursor_cmd *cursor = qxl_alloc_cursor_cmd(qxl); + + cursor->type = QXL_CURSOR_HIDE; + + push_cursor(qxl, cursor); +} + +static void +qxl_show_cursor(ScrnInfoPtr pScrn) +{ + /* + * slightly hacky, but there's no QXL_CURSOR_SHOW. Could maybe do + * QXL_CURSOR_SET? + */ + qxl_screen_t *qxl = pScrn->driverPrivate; + + qxl_set_cursor_position(pScrn, qxl->cur_x, qxl->cur_y); +} + +hidden void +qxl_cursor_init(ScreenPtr pScreen) +{ + xf86CursorInfoPtr cursor; + + cursor = xcalloc(1, sizeof(xf86CursorInfoRec)); + if (!cursor) + return; + + cursor->MaxWidth = cursor->MaxHeight = 64; + /* cursor->Flags; */ + cursor->SetCursorPosition = qxl_set_cursor_position; + cursor->LoadCursorARGB = qxl_load_cursor_argb; + cursor->UseHWCursor = qxl_use_hw_cursor; + cursor->UseHWCursorARGB = qxl_use_hw_cursorARGB; + cursor->LoadCursorImage = qxl_load_cursor_image; + cursor->SetCursorColors = qxl_set_cursor_colors; + cursor->HideCursor = qxl_hide_cursor; + cursor->ShowCursor = qxl_show_cursor; + + if (!xf86InitCursor(pScreen, cursor)) + xfree(cursor); +} diff --git a/src/compat/compat-qxl_driver.c b/src/compat/compat-qxl_driver.c new file mode 100644 index 0000000..7cd5f40 --- /dev/null +++ b/src/compat/compat-qxl_driver.c @@ -0,0 +1,1454 @@ +/* + * Copyright 2008 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** \file qxl_driver.c + * \author Adam Jackson <ajax@redhat.com> + * + * This is qxl, a driver for the Qumranet paravirtualized graphics device + * in qemu. + */ + +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <time.h> +#include <stdlib.h> +#include "compat-qxl.h" +#include "assert.h" + +#define CHECK_POINT() + +static int +garbage_collect (qxl_screen_t *qxl) +{ + uint64_t id; + int i = 0; + + while (qxl_ring_pop (qxl->release_ring, &id)) + { + while (id) + { + /* We assume that there the two low bits of a pointer are + * available. If the low one is set, then the command in + * question is a cursor command + */ +#define POINTER_MASK ((1 << 2) - 1) + + union qxl_release_info *info = u64_to_pointer (id & ~POINTER_MASK); + struct qxl_cursor_cmd *cmd = (struct qxl_cursor_cmd *)info; + struct qxl_drawable *drawable = (struct qxl_drawable *)info; + int is_cursor = FALSE; + + if ((id & POINTER_MASK) == 1) + is_cursor = TRUE; + + if (is_cursor && cmd->type == QXL_CURSOR_SET) + { + struct qxl_cursor *cursor = (void *)virtual_address ( + qxl, u64_to_pointer (cmd->u.set.shape)); + + qxl_free (qxl->mem, cursor); + } + else if (!is_cursor && drawable->type == QXL_DRAW_COPY) + { + struct qxl_image *image = virtual_address ( + qxl, u64_to_pointer (drawable->u.copy.src_bitmap)); + + qxl_image_destroy (qxl, image); + } + + id = info->next; + + qxl_free (qxl->mem, info); + } + } + + return i > 0; +} + +static void +qxl_usleep (int useconds) +{ + struct timespec t; + + t.tv_sec = useconds / 1000000; + t.tv_nsec = (useconds - (t.tv_sec * 1000000)) * 1000; + + errno = 0; + while (nanosleep (&t, &t) == -1 && errno == EINTR) + ; + +} + +#if 0 +static void +push_update_area (qxl_screen_t *qxl, const struct qxl_rect *area) +{ + struct qxl_update_cmd *update = qxl_allocnf (qxl, sizeof *update); + struct qxl_command cmd; + + update->release_info.id = (uint64_t)update; + update->area = *area; + update->update_id = 0; + + cmd.type = QXL_CMD_UDPATE; + cmd.data = physical_address (qxl, update); + + qxl_ring_push (qxl->command_ring, &cmd); +} +#endif + +void * +qxl_allocnf (qxl_screen_t *qxl, unsigned long size) +{ + void *result; + int n_attempts = 0; + static int nth_oom = 1; + + garbage_collect (qxl); + + while (!(result = qxl_alloc (qxl->mem, size))) + { + struct qxl_ram_header *ram_header = (void *)((unsigned long)qxl->ram + + qxl->rom->ram_header_offset); + + /* Rather than go out of memory, we simply tell the + * device to dump everything + */ + ram_header->update_area.top = 0; + ram_header->update_area.bottom = 1280; + ram_header->update_area.left = 0; + ram_header->update_area.right = 800; + + outb (qxl->io_base + QXL_IO_UPDATE_AREA, 0); + + ErrorF ("eliminated memory (%d)\n", nth_oom++); + + outb (qxl->io_base + QXL_IO_NOTIFY_OOM, 0); + + qxl_usleep (10000); + + if (garbage_collect (qxl)) + { + n_attempts = 0; + } + else if (++n_attempts == 1000) + { + qxl_mem_dump_stats (qxl->mem, "Out of mem - stats\n"); + + fprintf (stderr, "Out of memory\n"); + exit (1); + } + } + + return result; +} + +static Bool +qxl_blank_screen(ScreenPtr pScreen, int mode) +{ + return TRUE; +} + +static void +qxl_unmap_memory(qxl_screen_t *qxl, int scrnIndex) +{ +#ifdef XSERVER_LIBPCIACCESS + if (qxl->ram) + pci_device_unmap_range(qxl->pci, qxl->ram, qxl->pci->regions[0].size); + if (qxl->vram) + pci_device_unmap_range(qxl->pci, qxl->vram, qxl->pci->regions[1].size); + if (qxl->rom) + pci_device_unmap_range(qxl->pci, qxl->rom, qxl->pci->regions[2].size); +#else + if (qxl->ram) + xf86UnMapVidMem(scrnIndex, qxl->ram, (1 << qxl->pci->size[0])); + if (qxl->vram) + xf86UnMapVidMem(scrnIndex, qxl->vram, (1 << qxl->pci->size[1])); + if (qxl->rom) + xf86UnMapVidMem(scrnIndex, qxl->rom, (1 << qxl->pci->size[2])); +#endif + + qxl->ram = qxl->ram_physical = qxl->vram = qxl->rom = NULL; + + qxl->num_modes = 0; + qxl->modes = NULL; +} + +static Bool +qxl_map_memory(qxl_screen_t *qxl, int scrnIndex) +{ +#ifdef XSERVER_LIBPCIACCESS + pci_device_map_range(qxl->pci, qxl->pci->regions[0].base_addr, + qxl->pci->regions[0].size, + PCI_DEV_MAP_FLAG_WRITABLE | PCI_DEV_MAP_FLAG_WRITE_COMBINE, + &qxl->ram); + qxl->ram_physical = u64_to_pointer (qxl->pci->regions[0].base_addr); + + pci_device_map_range(qxl->pci, qxl->pci->regions[1].base_addr, + qxl->pci->regions[1].size, + PCI_DEV_MAP_FLAG_WRITABLE, + &qxl->vram); + + pci_device_map_range(qxl->pci, qxl->pci->regions[2].base_addr, + qxl->pci->regions[2].size, 0, + (void **)&qxl->rom); + + qxl->io_base = qxl->pci->regions[3].base_addr; +#else + qxl->ram = xf86MapPciMem(scrnIndex, VIDMEM_FRAMEBUFFER, + qxl->pci_tag, qxl->pci->memBase[0], + (1 << qxl->pci->size[0])); + qxl->ram_physical = (void *)qxl->pci->memBase[0]; + + qxl->vram = xf86MapPciMem(scrnIndex, VIDMEM_MMIO | VIDMEM_MMIO_32BIT, + qxl->pci_tag, qxl->pci->memBase[1], + (1 << qxl->pci->size[1])); + + qxl->rom = xf86MapPciMem(scrnIndex, VIDMEM_MMIO | VIDMEM_MMIO_32BIT, + qxl->pci_tag, qxl->pci->memBase[2], + (1 << qxl->pci->size[2])); + + qxl->io_base = qxl->pci->ioBase[3]; +#endif + if (!qxl->ram || !qxl->vram || !qxl->rom) + return FALSE; + + xf86DrvMsg(scrnIndex, X_INFO, "ram at %p; vram at %p; rom at %p\n", + qxl->ram, qxl->vram, qxl->rom); + + qxl->num_modes = *(uint32_t *)((uint8_t *)qxl->rom + qxl->rom->modes_offset); + qxl->modes = (struct qxl_mode *)(((uint8_t *)qxl->rom) + qxl->rom->modes_offset + 4); + + return TRUE; +} + +static void +qxl_save_state(ScrnInfoPtr pScrn) +{ + qxl_screen_t *qxl = pScrn->driverPrivate; + + vgaHWSaveFonts(pScrn, &qxl->vgaRegs); +} + +static void +qxl_restore_state(ScrnInfoPtr pScrn) +{ + qxl_screen_t *qxl = pScrn->driverPrivate; + + vgaHWRestoreFonts(pScrn, &qxl->vgaRegs); +} + +static Bool +qxl_close_screen(int scrnIndex, ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; + qxl_screen_t *qxl = pScrn->driverPrivate; + + if (pScrn->vtSema) { + qxl_restore_state(pScrn); + qxl_unmap_memory(qxl, scrnIndex); + } + pScrn->vtSema = FALSE; + + xfree(qxl->fb); + + pScreen->CreateScreenResources = qxl->create_screen_resources; + pScreen->CloseScreen = qxl->close_screen; + + return pScreen->CloseScreen(scrnIndex, pScreen); +} + +static Bool +qxl_switch_mode(int scrnIndex, DisplayModePtr p, int flags) +{ + qxl_screen_t *qxl = xf86Screens[scrnIndex]->driverPrivate; + int mode_index = (int)(unsigned long)p->Private; + struct qxl_mode *m = qxl->modes + mode_index; + ScreenPtr pScreen = qxl->pScrn->pScreen; + + if (!m) + return FALSE; + + /* if (debug) */ + xf86DrvMsg (scrnIndex, X_INFO, "Setting mode %d (%d x %d) (%d x %d) %p\n", + m->id, m->x_res, m->y_res, p->HDisplay, p->VDisplay, p); + + outb(qxl->io_base + QXL_IO_RESET, 0); + + outb(qxl->io_base + QXL_IO_SET_MODE, m->id); + + qxl->bytes_per_pixel = (qxl->pScrn->bitsPerPixel + 7) / 8; + + /* If this happens out of ScreenInit, we won't have a screen yet. In that + * case createScreenResources will make things right. + */ + if (pScreen) + { + PixmapPtr pPixmap = pScreen->GetScreenPixmap(pScreen); + + if (pPixmap) + { + pScreen->ModifyPixmapHeader( + pPixmap, + m->x_res, m->y_res, + -1, -1, + qxl->pScrn->displayWidth * qxl->bytes_per_pixel, + NULL); + } + } + + if (qxl->mem) + { + qxl_mem_free_all (qxl->mem); + qxl_drop_image_cache (qxl); + } + + + return TRUE; +} + +static void +push_drawable (qxl_screen_t *qxl, struct qxl_drawable *drawable) +{ + struct qxl_command cmd; + + /* When someone runs "init 3", the device will be + * switched into VGA mode and there is nothing we + * can do about it. We get no notification. + * + * However, if commands are submitted when the device + * is in VGA mode, they will be queued up, and then + * the next time a mode set set, an assertion in the + * device will take down the entire virtual machine. + * + * The author of the QXL device is opposed to this + * for reasons I don't understand. + */ + if (qxl->rom->mode != ~0) + { + cmd.type = QXL_CMD_DRAW; + cmd.data = physical_address (qxl, drawable); + + qxl_ring_push (qxl->command_ring, &cmd); + } +} + +static struct qxl_drawable * +make_drawable (qxl_screen_t *qxl, uint8_t type, + const struct qxl_rect *rect + /* , pRegion clip */) +{ + struct qxl_drawable *drawable; + + CHECK_POINT(); + + drawable = qxl_allocnf (qxl, sizeof *drawable); + + CHECK_POINT(); + + drawable->release_info.id = pointer_to_u64 (drawable); + + drawable->type = type; + + drawable->effect = QXL_EFFECT_OPAQUE; + drawable->bitmap_offset = 0; + drawable->bitmap_area.top = 0; + drawable->bitmap_area.left = 0; + drawable->bitmap_area.bottom = 0; + drawable->bitmap_area.right = 0; + /* FIXME: add clipping */ + drawable->clip.type = QXL_CLIP_TYPE_NONE; + + if (rect) + drawable->bbox = *rect; + + drawable->mm_time = qxl->rom->mm_clock; + + CHECK_POINT(); + + return drawable; +} + +enum ROPDescriptor { + ROPD_INVERS_SRC = (1 << 0), + ROPD_INVERS_BRUSH = (1 << 1), + ROPD_INVERS_DEST = (1 << 2), + ROPD_OP_PUT = (1 << 3), + ROPD_OP_OR = (1 << 4), + ROPD_OP_AND = (1 << 5), + ROPD_OP_XOR = (1 << 6), + ROPD_OP_BLACKNESS = (1 << 7), + ROPD_OP_WHITENESS = (1 << 8), + ROPD_OP_INVERS = (1 << 9), + ROPD_INVERS_RES = (1 <<10), +}; + +static void +undamage_box (qxl_screen_t *qxl, const struct qxl_rect *rect) +{ + RegionRec region; + BoxRec box; + + box.x1 = rect->left; + box.y1 = rect->top; + box.x2 = rect->right; + box.y2 = rect->bottom; + + REGION_INIT (qxl->pScrn->pScreen, ®ion, &box, 0); + + REGION_SUBTRACT (qxl->pScrn->pScreen, &(qxl->pending_copy), &(qxl->pending_copy), ®ion); + + REGION_EMPTY (qxl->pScrn->pScreen, &(qxl->pending_copy)); +} + +static void +clear_pending_damage (qxl_screen_t *qxl) +{ + REGION_EMPTY (qxl->pScrn->pScreen, &(qxl->pending_copy)); +} + +static void +submit_fill (qxl_screen_t *qxl, const struct qxl_rect *rect, uint32_t color) +{ + struct qxl_drawable *drawable; + + CHECK_POINT(); + + drawable = make_drawable (qxl, QXL_DRAW_FILL, rect); + + CHECK_POINT(); + + drawable->u.fill.brush.type = QXL_BRUSH_TYPE_SOLID; + drawable->u.fill.brush.u.color = color; + drawable->u.fill.rop_descriptor = ROPD_OP_PUT; + drawable->u.fill.mask.flags = 0; + drawable->u.fill.mask.pos.x = 0; + drawable->u.fill.mask.pos.y = 0; + drawable->u.fill.mask.bitmap = 0; + + push_drawable (qxl, drawable); + + undamage_box (qxl, rect); +} + +static void +translate_rect (struct qxl_rect *rect) +{ + rect->right -= rect->left; + rect->bottom -= rect->top; + rect->left = rect->top = 0; +} + +static void +submit_copy (qxl_screen_t *qxl, const struct qxl_rect *rect) +{ + struct qxl_drawable *drawable; + ScrnInfoPtr pScrn = qxl->pScrn; + + if (rect->left == rect->right || + rect->top == rect->bottom) + { + /* Empty rectangle */ + return ; + } + + drawable = make_drawable (qxl, QXL_DRAW_COPY, rect); + + drawable->u.copy.src_bitmap = physical_address ( + qxl, qxl_image_create (qxl, qxl->fb, rect->left, rect->top, + rect->right - rect->left, + rect->bottom - rect->top, + pScrn->displayWidth * qxl->bytes_per_pixel)); + drawable->u.copy.src_area = *rect; + translate_rect (&drawable->u.copy.src_area); + drawable->u.copy.rop_descriptor = ROPD_OP_PUT; + drawable->u.copy.scale_mode = 0; + drawable->u.copy.mask.flags = 0; + drawable->u.copy.mask.pos.x = 0; + drawable->u.copy.mask.pos.y = 0; + drawable->u.copy.mask.bitmap = 0; + + push_drawable (qxl, drawable); +} + +static void +print_region (const char *header, RegionPtr pRegion) +{ + int nbox = REGION_NUM_RECTS (pRegion); + BoxPtr pbox = REGION_RECTS (pRegion); + + ErrorF ("%s \n", header); + + while (nbox--) + { + ErrorF (" %d %d %d %d (size: %d %d)\n", + pbox->x1, pbox->y1, pbox->x2, pbox->y2, + pbox->x2 - pbox->x1, pbox->y2 - pbox->y1); + + pbox++; + } +} + +static void +accept_damage (qxl_screen_t *qxl) +{ + REGION_UNION (qxl->pScrn->pScreen, &(qxl->to_be_sent), &(qxl->to_be_sent), + &(qxl->pending_copy)); + + REGION_EMPTY (qxl->pScrn->pScreen, &(qxl->pending_copy)); +} + +static void +qxl_send_copies (qxl_screen_t *qxl) +{ + BoxPtr pBox; + int nbox; + + nbox = REGION_NUM_RECTS (&qxl->to_be_sent); + pBox = REGION_RECTS (&qxl->to_be_sent); + +/* if (REGION_NUM_RECTS (&qxl->to_be_sent) > 0) */ +/* print_region ("send bits", &qxl->to_be_sent); */ + + while (nbox--) + { + struct qxl_rect qrect; + + qrect.top = pBox->y1; + qrect.left = pBox->x1; + qrect.bottom = pBox->y2; + qrect.right = pBox->x2; + + submit_copy (qxl, &qrect); + + pBox++; + } + + REGION_EMPTY(qxl->pScrn->pScreen, &qxl->to_be_sent); +} + +static void +paint_shadow (qxl_screen_t *qxl) +{ + struct qxl_rect qrect; + + qrect.top = 0; + qrect.bottom = 1200; + qrect.left = 0; + qrect.right = 1600; + + submit_copy (qxl, &qrect); +} + +static void +qxl_sanity_check (qxl_screen_t *qxl) +{ + /* read the mode back from the rom */ + if (!qxl->rom || !qxl->pScrn) + return; + + if (qxl->rom->mode == ~0) + { + ErrorF("QXL device jumped back to VGA mode - resetting mode\n"); + qxl_switch_mode(qxl->pScrn->scrnIndex, qxl->pScrn->currentMode, 0); + } +} + +static void +qxl_block_handler (pointer data, OSTimePtr pTimeout, pointer pRead) +{ + qxl_screen_t *qxl = (qxl_screen_t *) data; + + if (!qxl->pScrn->vtSema) + return; + + qxl_sanity_check(qxl); + + accept_damage (qxl); + + qxl_send_copies (qxl); +} + +static void +qxl_wakeup_handler (pointer data, int i, pointer LastSelectMask) +{ +} + +/* Damage Handling + * + * When something is drawn, X first generates a damage callback, then + * it calls the GC function to actually draw it. In most cases, we want + * to simply draw into the shadow framebuffer, then submit a copy to the + * device, but when the operation is hardware accelerated, we don't want + * to submit the copy. So, damage is first accumulated into 'pending_copy', + * then if we accelerated the operation, that damage is deleted. + * + * If we _didn't_ accelerate, we need to union the pending_copy damage + * onto the to_be_sent damage, and then submit a copy command in the block + * handler. + * + * This means that when new damage happens, if there is already pending + * damage, that must first be unioned onto to_be_sent, and then the new + * damage must be stored in pending_copy. + * + * The qxl_screen_t struct contains two regions, "pending_copy" and + * "to_be_sent". + * + * Pending copy is + * + */ +static void +qxl_on_damage (DamagePtr pDamage, RegionPtr pRegion, pointer closure) +{ + qxl_screen_t *qxl = closure; + +/* print_region ("damage", pRegion); */ + +/* print_region ("on_damage ", pRegion); */ + + accept_damage (qxl); + +/* print_region ("accepting, qxl->to_be_sent is now", &qxl->to_be_sent); */ + + REGION_COPY (qxl->pScrn->pScreen, &(qxl->pending_copy), pRegion); +} + + +static Bool +qxl_create_screen_resources(ScreenPtr pScreen) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + qxl_screen_t *qxl = pScrn->driverPrivate; + Bool ret; + PixmapPtr pPixmap; + + pScreen->CreateScreenResources = qxl->create_screen_resources; + ret = pScreen->CreateScreenResources (pScreen); + pScreen->CreateScreenResources = qxl_create_screen_resources; + + if (!ret) + return FALSE; + + qxl->damage = DamageCreate (qxl_on_damage, NULL, + DamageReportRawRegion, + TRUE, pScreen, qxl); + + + pPixmap = pScreen->GetScreenPixmap(pScreen); + + if (!RegisterBlockAndWakeupHandlers(qxl_block_handler, qxl_wakeup_handler, qxl)) + return FALSE; + + REGION_INIT (pScreen, &(qxl->pending_copy), NullBox, 0); + + REGION_INIT (pScreen, &(qxl->to_be_sent), NullBox, 0); + + DamageRegister (&pPixmap->drawable, qxl->damage); + return TRUE; +} + +static PixmapPtr +get_window_pixmap (DrawablePtr pDrawable, int *xoff, int *yoff) +{ + ScreenPtr pScreen = pDrawable->pScreen; + PixmapPtr result; + + if (pDrawable->type != DRAWABLE_WINDOW) + return NULL; + + result = pScreen->GetWindowPixmap ((WindowPtr)pDrawable); + + *xoff = pDrawable->x; + *yoff = pDrawable->y; + + return result; +} + +static void +qxl_poly_fill_rect (DrawablePtr pDrawable, + GCPtr pGC, + int nrect, + xRectangle *prect) +{ + ScrnInfoPtr pScrn = xf86Screens[pDrawable->pScreen->myNum]; + qxl_screen_t *qxl = pScrn->driverPrivate; + PixmapPtr pPixmap; + int xoff, yoff; + + if ((pPixmap = get_window_pixmap (pDrawable, &xoff, &yoff)) && + pGC->fillStyle == FillSolid && + pGC->alu == GXcopy && + (unsigned int)pGC->planemask == FB_ALLONES) + { + RegionPtr pReg = RECTS_TO_REGION (pScreen, nrect, prect, CT_UNSORTED); + RegionPtr pClip = fbGetCompositeClip (pGC); + BoxPtr pBox; + int nbox; + + REGION_TRANSLATE(pScreen, pReg, xoff, yoff); + REGION_INTERSECT(pScreen, pReg, pClip, pReg); + + pBox = REGION_RECTS (pReg); + nbox = REGION_NUM_RECTS (pReg); + + while (nbox--) + { + struct qxl_rect qrect; + + qrect.left = pBox->x1; + qrect.right = pBox->x2; + qrect.top = pBox->y1; + qrect.bottom = pBox->y2; + + submit_fill (qxl, &qrect, pGC->fgPixel); + + pBox++; + } + + REGION_DESTROY (pScreen, pReg); + } + + fbPolyFillRect (pDrawable, pGC, nrect, prect); +} + +static void +qxl_copy_n_to_n (DrawablePtr pSrcDrawable, + DrawablePtr pDstDrawable, + GCPtr pGC, + BoxPtr pbox, + int nbox, + int dx, + int dy, + Bool reverse, + Bool upsidedown, + Pixel bitplane, + void *closure) +{ + ScreenPtr pScreen = pSrcDrawable->pScreen; + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + qxl_screen_t *qxl = pScrn->driverPrivate; + int src_xoff, src_yoff; + int dst_xoff, dst_yoff; + PixmapPtr pSrcPixmap, pDstPixmap; + + if ((pSrcPixmap = get_window_pixmap (pSrcDrawable, &src_xoff, &src_yoff)) && + (pDstPixmap = get_window_pixmap (pDstDrawable, &dst_xoff, &dst_yoff))) + { + int n = nbox; + BoxPtr b = pbox; + + assert (pSrcPixmap == pDstPixmap); + +/* ErrorF ("Accelerated copy: %d boxes\n", n); */ + + /* At this point we know that any pending damage must + * have been caused by whatever copy operation triggered us. + * + * Therefore we can clear it. + * + * We couldn't clear it at the toplevel function because + * the copy might end up being empty, in which case no + * damage would have been generated. Which means the + * pending damage would have been caused by some + * earlier operation. + */ + if (n) + { +/* ErrorF ("Clearing pending damage\n"); */ + clear_pending_damage (qxl); + + /* We have to do this because the copy will cause the damage + * to be sent to move. + * + * Instead of just sending the bits, we could also move + * the existing damage around; however that's a bit more + * complex, and the performance win is unlikely to be + * very big. + */ + qxl_send_copies (qxl); + } + + while (n--) + { + struct qxl_drawable *drawable; + struct qxl_rect qrect; + + qrect.top = b->y1; + qrect.bottom = b->y2; + qrect.left = b->x1; + qrect.right = b->x2; + +/* ErrorF (" Translate %d %d %d %d by %d %d (offsets %d %d)\n", */ +/* b->x1, b->y1, b->x2, b->y2, */ +/* dx, dy, dst_xoff, dst_yoff); */ + + drawable = make_drawable (qxl, QXL_COPY_BITS, &qrect); + drawable->u.copy_bits.src_pos.x = b->x1 + dx; + drawable->u.copy_bits.src_pos.y = b->y1 + dy; + + push_drawable (qxl, drawable); + +#if 0 + if (closure) + qxl_usleep (1000000); +#endif + +#if 0 + submit_fill (qxl, &qrect, rand()); +#endif + + b++; + } + } +/* else */ +/* ErrorF ("Unaccelerated copy\n"); */ + + fbCopyNtoN (pSrcDrawable, pDstDrawable, pGC, pbox, nbox, dx, dy, reverse, upsidedown, bitplane, closure); +} + +static RegionPtr +qxl_copy_area(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable, GCPtr pGC, + int srcx, int srcy, int width, int height, int dstx, int dsty) +{ + if (pSrcDrawable->type == DRAWABLE_WINDOW && + pDstDrawable->type == DRAWABLE_WINDOW) + { + RegionPtr res; + +/* ErrorF ("accelerated copy %d %d %d %d %d %d\n", */ +/* srcx, srcy, width, height, dstx, dsty); */ + + res = fbDoCopy (pSrcDrawable, pDstDrawable, pGC, + srcx, srcy, width, height, dstx, dsty, + qxl_copy_n_to_n, 0, NULL); + + return res; + } + else + { +/* ErrorF ("Falling back %d %d %d %d %d %d\n", */ +/* srcx, srcy, width, height, dstx, dsty); */ + + return fbCopyArea (pSrcDrawable, pDstDrawable, pGC, + srcx, srcy, width, height, dstx, dsty); + } +} + +static void +qxl_fill_region_solid (DrawablePtr pDrawable, RegionPtr pRegion, Pixel pixel) +{ + ScreenPtr pScreen = pDrawable->pScreen; + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + qxl_screen_t *qxl = pScrn->driverPrivate; + PixmapPtr pPixmap; + int xoff, yoff; + + if ((pPixmap = get_window_pixmap (pDrawable, &xoff, &yoff))) + { + int nbox = REGION_NUM_RECTS (pRegion); + BoxPtr pBox = REGION_RECTS (pRegion); + + while (nbox--) + { + struct qxl_rect qrect; + + qrect.left = pBox->x1; + qrect.right = pBox->x2; + qrect.top = pBox->y1; + qrect.bottom = pBox->y2; + + submit_fill (qxl, &qrect, pixel); + + pBox++; + } + } + + fbFillRegionSolid (pDrawable, pRegion, 0, + fbReplicatePixel (pixel, pDrawable->bitsPerPixel)); +} + +static void +qxl_copy_window (WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc) +{ + RegionRec rgnDst; + int dx, dy; + + dx = ptOldOrg.x - pWin->drawable.x; + dy = ptOldOrg.y - pWin->drawable.y; + + REGION_TRANSLATE (pScreen, prgnSrc, -dx, -dy); + + REGION_INIT (pScreen, &rgnDst, NullBox, 0); + + REGION_INTERSECT(pScreen, &rgnDst, &pWin->borderClip, prgnSrc); + + fbCopyRegion (&pWin->drawable, &pWin->drawable, + NULL, + &rgnDst, dx, dy, qxl_copy_n_to_n, 0, NULL); + + REGION_UNINIT (pScreen, &rgnDst); + +/* REGION_TRANSLATE (pScreen, prgnSrc, dx, dy); */ + +/* fbCopyWindow (pWin, ptOldOrg, prgnSrc); */ +} + +static int +qxl_create_gc (GCPtr pGC) +{ + static GCOps ops; + static int initialized; + + if (!fbCreateGC (pGC)) + return FALSE; + + if (!initialized) + { + ops = *pGC->ops; + ops.PolyFillRect = qxl_poly_fill_rect; + ops.CopyArea = qxl_copy_area; + + initialized = TRUE; + } + + pGC->ops = &ops; + return TRUE; +} + +static Bool +qxl_screen_init(int scrnIndex, ScreenPtr pScreen, int argc, char **argv) +{ + ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; + qxl_screen_t *qxl = pScrn->driverPrivate; + struct qxl_rom *rom; + struct qxl_ram_header *ram_header; + VisualPtr visual; + + CHECK_POINT(); + + qxl->pScrn = pScrn; + + if (!qxl_map_memory(qxl, scrnIndex)) + return FALSE; + + rom = qxl->rom; + ram_header = (void *)((unsigned long)qxl->ram + (unsigned long)qxl->rom->ram_header_offset); + + qxl_save_state(pScrn); + qxl_blank_screen(pScreen, SCREEN_SAVER_ON); + + miClearVisualTypes(); + if (!miSetVisualTypes(pScrn->depth, miGetDefaultVisualMask(pScrn->depth), + pScrn->rgbBits, pScrn->defaultVisual)) + goto out; + if (!miSetPixmapDepths()) + goto out; + + /* Note we do this before setting pScrn->virtualY to match our current + mode, so as to allocate a buffer large enough for the largest mode. + FIXME: add support for resizing the framebuffer on modeset. */ + qxl->fb = xcalloc(pScrn->virtualY * pScrn->displayWidth, 4); + if (!qxl->fb) + goto out; + + pScrn->virtualX = pScrn->currentMode->HDisplay; + pScrn->virtualY = pScrn->currentMode->VDisplay; + + if (!fbScreenInit(pScreen, qxl->fb, + pScrn->currentMode->HDisplay, + pScrn->currentMode->VDisplay, + pScrn->xDpi, pScrn->yDpi, pScrn->displayWidth, + pScrn->bitsPerPixel)) + { + goto out; + } + + visual = pScreen->visuals + pScreen->numVisuals; + while (--visual >= pScreen->visuals) + { + if ((visual->class | DynamicClass) == DirectColor) + { + visual->offsetRed = pScrn->offset.red; + visual->offsetGreen = pScrn->offset.green; + visual->offsetBlue = pScrn->offset.blue; + visual->redMask = pScrn->mask.red; + visual->greenMask = pScrn->mask.green; + visual->blueMask = pScrn->mask.blue; + } + } + + + fbPictureInit(pScreen, 0, 0); + + qxl->create_screen_resources = pScreen->CreateScreenResources; + pScreen->CreateScreenResources = qxl_create_screen_resources; + + /* Set up resources */ + qxl->mem = qxl_mem_create ((void *)((unsigned long)qxl->ram + (unsigned long)rom->pages_offset), + rom->num_io_pages * getpagesize()); + qxl->io_pages = (void *)((unsigned long)qxl->ram + (unsigned long)rom->pages_offset); + qxl->io_pages_physical = (void *)((unsigned long)qxl->ram_physical + (unsigned long)rom->pages_offset); + + qxl->command_ring = qxl_ring_create (&(ram_header->cmd_ring_hdr), + sizeof (struct qxl_command), + 32, qxl->io_base + QXL_IO_NOTIFY_CMD); + qxl->cursor_ring = qxl_ring_create (&(ram_header->cursor_ring_hdr), + sizeof (struct qxl_command), + 32, qxl->io_base + QXL_IO_NOTIFY_CURSOR); + qxl->release_ring = qxl_ring_create (&(ram_header->release_ring_hdr), + sizeof (uint64_t), + 8, 0); + + /* xf86DPMSInit(pScreen, xf86DPMSSet, 0); */ + +#if 0 /* XV accel */ + qxlInitVideo(pScreen); +#endif + + pScreen->SaveScreen = qxl_blank_screen; + qxl->close_screen = pScreen->CloseScreen; + pScreen->CloseScreen = qxl_close_screen; + + qxl->create_gc = pScreen->CreateGC; + pScreen->CreateGC = qxl_create_gc; + +#if 0 + qxl->paint_window_background = pScreen->PaintWindowBackground; + qxl->paint_window_border = pScreen->PaintWindowBorder; +#endif + qxl->copy_window = pScreen->CopyWindow; +#if 0 + pScreen->PaintWindowBackground = qxl_paint_window; + pScreen->PaintWindowBorder = qxl_paint_window; +#endif + pScreen->CopyWindow = qxl_copy_window; + + miDCInitialize(pScreen, xf86GetPointerScreenFuncs()); + + if (!miCreateDefColormap(pScreen)) + goto out; + + qxl_cursor_init (pScreen); + + CHECK_POINT(); + + qxl_switch_mode(scrnIndex, pScrn->currentMode, 0); + + CHECK_POINT(); + + return TRUE; + +out: + return FALSE; +} + +static Bool +qxl_enter_vt(int scrnIndex, int flags) +{ + ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; + + qxl_save_state(pScrn); + qxl_switch_mode(scrnIndex, pScrn->currentMode, 0); + + return TRUE; +} + +static void +qxl_leave_vt(int scrnIndex, int flags) +{ + ScrnInfoPtr pScrn = xf86Screens[scrnIndex]; + + qxl_restore_state(pScrn); +} + +static Bool +qxl_color_setup(ScrnInfoPtr pScrn) +{ + int scrnIndex = pScrn->scrnIndex; + Gamma gzeros = { 0.0, 0.0, 0.0 }; + rgb rzeros = { 0, 0, 0 }; + + if (!xf86SetDepthBpp(pScrn, 0, 0, 0, Support32bppFb)) + return FALSE; + + if (pScrn->depth != 15 && pScrn->depth != 24) + { + xf86DrvMsg(scrnIndex, X_ERROR, "Depth %d is not supported\n", + pScrn->depth); + return FALSE; + } + xf86PrintDepthBpp(pScrn); + + if (!xf86SetWeight(pScrn, rzeros, rzeros)) + return FALSE; + + if (!xf86SetDefaultVisual(pScrn, -1)) + return FALSE; + + if (!xf86SetGamma(pScrn, gzeros)) + return FALSE; + + return TRUE; +} + +static void +print_modes (qxl_screen_t *qxl, int scrnIndex) +{ + int i; + + for (i = 0; i < qxl->num_modes; ++i) + { + struct qxl_mode *m = qxl->modes + i; + + xf86DrvMsg (scrnIndex, X_INFO, + "%d: %dx%d, %d bits, stride %d, %dmm x %dmm, orientation %d\n", + m->id, m->x_res, m->y_res, m->bits, m->stride, m->x_mili, + m->y_mili, m->orientation); + } +} + +static Bool +qxl_check_device(ScrnInfoPtr pScrn, qxl_screen_t *qxl) +{ + int scrnIndex = pScrn->scrnIndex; + struct qxl_rom *rom = qxl->rom; + struct qxl_ram_header *ram_header = (void *)((unsigned long)qxl->ram + rom->ram_header_offset); + + CHECK_POINT(); + + if (rom->magic != 0x4f525851) { /* "QXRO" little-endian */ + xf86DrvMsg(scrnIndex, X_ERROR, "Bad ROM signature %x\n", rom->magic); + return FALSE; + } + + xf86DrvMsg(scrnIndex, X_INFO, "Device version %d.%d\n", + rom->id, rom->update_id); + + xf86DrvMsg(scrnIndex, X_INFO, "Compression level %d, log level %d\n", + rom->compression_level, + rom->log_level); + + xf86DrvMsg(scrnIndex, X_INFO, "Currently using mode #%d, list at 0x%x\n", + rom->mode, rom->modes_offset); + + xf86DrvMsg(scrnIndex, X_INFO, "%d io pages at 0x%x\n", + rom->num_io_pages, rom->pages_offset); + + xf86DrvMsg(scrnIndex, X_INFO, "%d byte draw area at 0x%x\n", + rom->draw_area_size, rom->draw_area_offset); + + xf86DrvMsg(scrnIndex, X_INFO, "RAM header offset: 0x%x\n", rom->ram_header_offset); + + if (ram_header->magic != 0x41525851) { /* "QXRA" little-endian */ + xf86DrvMsg(scrnIndex, X_ERROR, "Bad RAM signature %x at %p\n", + ram_header->magic, + &ram_header->magic); + return FALSE; + } + + xf86DrvMsg(scrnIndex, X_INFO, "Correct RAM signature %x\n", + ram_header->magic); + + qxl->draw_area_offset = rom->draw_area_offset; + qxl->draw_area_size = rom->draw_area_size; + pScrn->videoRam = rom->draw_area_size / 1024; + + return TRUE; +} + +static int +qxl_find_native_mode(ScrnInfoPtr pScrn, DisplayModePtr p) +{ + int i; + qxl_screen_t *qxl = pScrn->driverPrivate; + + CHECK_POINT(); + + for (i = 0; i < qxl->num_modes; i++) + { + struct qxl_mode *m = qxl->modes + i; + + if (m->x_res == p->HDisplay && + m->y_res == p->VDisplay && + m->bits == pScrn->bitsPerPixel) + { + if (m->bits == 16) + { + /* What QXL calls 16 bit is actually x1r5g5b515 */ + if (pScrn->depth == 15) + return i; + } + else if (m->bits == 32) + { + /* What QXL calls 32 bit is actually x8r8g8b8 */ + if (pScrn->depth == 24) + return i; + } + } + } + + return -1; +} + +static ModeStatus +qxl_valid_mode(int scrn, DisplayModePtr p, Bool flag, int pass) +{ + ScrnInfoPtr pScrn = xf86Screens[scrn]; + qxl_screen_t *qxl = pScrn->driverPrivate; + int bpp = pScrn->bitsPerPixel; + int mode_idx; + + /* FIXME: I don't think this is necessary now that we report the + * correct amount of video ram? + */ + if (p->HDisplay * p->VDisplay * (bpp/8) > qxl->draw_area_size) + return MODE_MEM; + + mode_idx = qxl_find_native_mode (pScrn, p); + if (mode_idx == -1) + return MODE_NOMODE; + + p->Private = (void *)(unsigned long)mode_idx; + + return MODE_OK; +} + +static void qxl_add_mode(ScrnInfoPtr pScrn, int width, int height, int type) +{ + DisplayModePtr mode; + + /* Skip already present modes */ + for (mode = pScrn->monitor->Modes; mode; mode = mode->next) + if (mode->HDisplay == width && mode->VDisplay == height) + return; + + mode = xnfcalloc(1, sizeof(DisplayModeRec)); + + mode->status = MODE_OK; + mode->type = type; + mode->HDisplay = width; + mode->HSyncStart = (width * 105 / 100 + 7) & ~7; + mode->HSyncEnd = (width * 115 / 100 + 7) & ~7; + mode->HTotal = (width * 130 / 100 + 7) & ~7; + mode->VDisplay = height; + mode->VSyncStart = height + 1; + mode->VSyncEnd = height + 4; + mode->VTotal = height * 1035 / 1000; + mode->Clock = mode->HTotal * mode->VTotal * 60 / 1000; + mode->Flags = V_NHSYNC | V_PVSYNC; + + xf86SetModeDefaultName(mode); + xf86ModesAdd(pScrn->monitor->Modes, mode); +} + +static Bool +qxl_pre_init(ScrnInfoPtr pScrn, int flags) +{ + int i, scrnIndex = pScrn->scrnIndex; + qxl_screen_t *qxl = NULL; + ClockRangePtr clockRanges = NULL; + int *linePitches = NULL; + DisplayModePtr mode; + unsigned int max_x = 0, max_y = 0; + + CHECK_POINT(); + + /* zaphod mode is for suckers and i choose not to implement it */ + if (xf86IsEntityShared(pScrn->entityList[0])) { + xf86DrvMsg(scrnIndex, X_ERROR, "No Zaphod mode for you\n"); + return FALSE; + } + + if (!pScrn->driverPrivate) + pScrn->driverPrivate = xnfcalloc(sizeof(qxl_screen_t), 1); + qxl = pScrn->driverPrivate; + + qxl->entity = xf86GetEntityInfo(pScrn->entityList[0]); + qxl->pci = xf86GetPciInfoForEntity(qxl->entity->index); +#ifndef XSERVER_LIBPCIACCESS + qxl->pci_tag = pciTag(qxl->pci->bus, qxl->pci->device, qxl->pci->func); +#endif + + pScrn->monitor = pScrn->confScreen->monitor; + + if (!qxl_color_setup(pScrn)) + goto out; + + /* option parsing and card differentiation */ + xf86CollectOptions(pScrn, NULL); + + if (!qxl_map_memory(qxl, scrnIndex)) + goto out; + + if (!qxl_check_device(pScrn, qxl)) + goto out; + + /* ddc stuff here */ + + clockRanges = xnfcalloc(sizeof(ClockRange), 1); + clockRanges->next = NULL; + clockRanges->minClock = 10000; + clockRanges->maxClock = 400000; + clockRanges->clockIndex = -1; + clockRanges->interlaceAllowed = clockRanges->doubleScanAllowed = 0; + clockRanges->ClockMulFactor = clockRanges->ClockDivFactor = 1; + pScrn->progClock = TRUE; + + /* override QXL monitor stuff */ + if (pScrn->monitor->nHsync <= 0) { + pScrn->monitor->hsync[0].lo = 29.0; + pScrn->monitor->hsync[0].hi = 160.0; + pScrn->monitor->nHsync = 1; + } + if (pScrn->monitor->nVrefresh <= 0) { + pScrn->monitor->vrefresh[0].lo = 50; + pScrn->monitor->vrefresh[0].hi = 75; + pScrn->monitor->nVrefresh = 1; + } + + /* Add any modes not in xorg's default mode list */ + for (i = 0; i < qxl->num_modes; i++) + if (qxl->modes[i].orientation == 0) { + qxl_add_mode(pScrn, qxl->modes[i].x_res, qxl->modes[i].y_res, + M_T_DRIVER); + if (qxl->modes[i].x_res > max_x) + max_x = qxl->modes[i].x_res; + if (qxl->modes[i].y_res > max_y) + max_y = qxl->modes[i].y_res; + } + + if (pScrn->display->virtualX == 0 && pScrn->display->virtualY == 0) { + /* It is possible for the largest x + largest y size combined leading + to a virtual size which will not fit into the framebuffer when this + happens we prefer max width and make height as large as possible */ + if (max_x * max_y * (pScrn->bitsPerPixel / 8) > qxl->draw_area_size) + pScrn->display->virtualY = qxl->draw_area_size / + (max_x * (pScrn->bitsPerPixel / 8)); + else + pScrn->display->virtualY = max_y; + + pScrn->display->virtualX = max_x; + } + + if (0 >= xf86ValidateModes(pScrn, pScrn->monitor->Modes, + pScrn->display->modes, clockRanges, linePitches, + 128, max_x, 128 * 4, 128, max_y, + pScrn->display->virtualX, + pScrn->display->virtualY, + 128 * 1024 * 1024, LOOKUP_BEST_REFRESH)) + goto out; + + CHECK_POINT(); + + xf86PruneDriverModes(pScrn); + pScrn->currentMode = pScrn->modes; + /* If no modes are specified in xorg.conf, default to 1024x768 */ + if (pScrn->display->modes == NULL || pScrn->display->modes[0] == NULL) + for (mode = pScrn->modes; mode; mode = mode->next) + if (mode->HDisplay == 1024 && mode->VDisplay == 768) { + pScrn->currentMode = mode; + break; + } + + xf86PrintModes(pScrn); + xf86SetDpi(pScrn, 0, 0); + + if (!xf86LoadSubModule(pScrn, "fb") || + !xf86LoadSubModule(pScrn, "ramdac") || + !xf86LoadSubModule(pScrn, "vgahw")) + { + goto out; + } + + print_modes (qxl, scrnIndex); + + /* VGA hardware initialisation */ + if (!vgaHWGetHWRec(pScrn)) + return FALSE; + + /* hate */ + qxl_unmap_memory(qxl, scrnIndex); + + CHECK_POINT(); + + xf86DrvMsg(scrnIndex, X_INFO, "PreInit complete\n"); + return TRUE; + +out: + if (clockRanges) + xfree(clockRanges); + if (qxl) + xfree(qxl); + + return FALSE; +} + +#ifdef XSERVER_LIBPCIACCESS +enum qxl_class +{ + CHIP_QXL_1, +}; + +static const struct pci_id_match qxl_device_match[] = { + { + PCI_VENDOR_RED_HAT, PCI_CHIP_QXL_0100, PCI_MATCH_ANY, PCI_MATCH_ANY, + 0x00030000, 0x00ffffff, CHIP_QXL_1 + }, + + { 0 }, +}; +#endif + +static SymTabRec qxlChips[] = +{ + { PCI_CHIP_QXL_0100, "QXL 1", }, + { -1, NULL } +}; + +#ifndef XSERVER_LIBPCIACCESS +static PciChipsets qxlPciChips[] = +{ + { PCI_CHIP_QXL_0100, PCI_CHIP_QXL_0100, RES_SHARED_VGA }, + { -1, -1, RES_UNDEFINED } +}; +#endif + +static void +qxl_identify(int flags) +{ + xf86PrintChipsets("qxl", "Driver for QXL virtual graphics", qxlChips); +} + +static void +qxl_init_scrn(ScrnInfoPtr pScrn) +{ + pScrn->driverVersion = 0; + pScrn->driverName = pScrn->name = "qxl"; + pScrn->PreInit = qxl_pre_init; + pScrn->ScreenInit = qxl_screen_init; + pScrn->SwitchMode = qxl_switch_mode; + pScrn->ValidMode = qxl_valid_mode; + pScrn->EnterVT = qxl_enter_vt; + pScrn->LeaveVT = qxl_leave_vt; +} diff --git a/src/compat/compat-qxl_image.c b/src/compat/compat-qxl_image.c new file mode 100644 index 0000000..d2ac12d --- /dev/null +++ b/src/compat/compat-qxl_image.c @@ -0,0 +1,255 @@ +#include <string.h> +#include <assert.h> +#include <stdlib.h> +#include "compat-qxl.h" +#include "compat-lookup3.h" + +typedef struct image_info_t image_info_t; + +struct image_info_t +{ + struct qxl_image *image; + int ref_count; + image_info_t *next; +}; + +#define HASH_SIZE 4096 +static image_info_t *image_table[HASH_SIZE]; + +static unsigned int +hash_and_copy (const uint8_t *src, int src_stride, + uint8_t *dest, int dest_stride, + int bytes_per_pixel, int width, int height) +{ + unsigned int hash = 0; + int i; + + for (i = 0; i < height; ++i) + { + const uint8_t *src_line = src + i * src_stride; + uint8_t *dest_line = dest + i * dest_stride; + int n_bytes = width * bytes_per_pixel; + + if (dest) + memcpy (dest_line, src_line, n_bytes); + + hash = hashlittle (src_line, n_bytes, hash); + } + + return hash; +} + +static image_info_t * +lookup_image_info (unsigned int hash, + int width, + int height) +{ + struct image_info_t *info = image_table[hash % HASH_SIZE]; + + while (info) + { + struct qxl_image *image = info->image; + + if (image->descriptor.id == hash && + image->descriptor.width == width && + image->descriptor.height == height) + { + return info; + } + + info = info->next; + } + +#if 0 + ErrorF ("lookup of %u failed\n", hash); +#endif + + return NULL; +} + +static image_info_t * +insert_image_info (unsigned int hash) +{ + struct image_info_t *info = malloc (sizeof (image_info_t)); + + if (!info) + return NULL; + + info->next = image_table[hash % HASH_SIZE]; + image_table[hash % HASH_SIZE] = info; + + return info; +} + +static void +remove_image_info (image_info_t *info) +{ + struct image_info_t **location = &image_table[info->image->descriptor.id % HASH_SIZE]; + + while (*location && (*location) != info) + location = &((*location)->next); + + if (*location) + *location = info->next; + + free (info); +} + +struct qxl_image * +qxl_image_create (qxl_screen_t *qxl, const uint8_t *data, + int x, int y, int width, int height, + int stride) +{ + unsigned int hash; + image_info_t *info; + + data += y * stride + x * qxl->bytes_per_pixel; + + hash = hash_and_copy (data, stride, NULL, -1, qxl->bytes_per_pixel, width, height); + + info = lookup_image_info (hash, width, height); + if (info) + { + int i, j; + +#if 0 + ErrorF ("reusing image %p with hash %u (%d x %d)\n", info->image, hash, width, height); +#endif + + info->ref_count++; + + for (i = 0; i < height; ++i) + { + struct qxl_data_chunk *chunk; + const uint8_t *src_line = data + i * stride; + uint32_t *dest_line; + + chunk = virtual_address (qxl, u64_to_pointer (info->image->u.bitmap.data)); + + dest_line = (uint32_t *)chunk->data + width * i; + + for (j = 0; j < width; ++j) + { + uint32_t *s = (uint32_t *)src_line; + uint32_t *d = (uint32_t *)dest_line; + + if (d[j] != s[j]) + { +#if 0 + ErrorF ("bad collision at (%d, %d)! %d != %d\n", j, i, s[j], d[j]); +#endif + goto out; + } + } + } + out: + return info->image; + } + else + { + struct qxl_image *image; + struct qxl_data_chunk *chunk; + int dest_stride = width * qxl->bytes_per_pixel; + image_info_t *info; + +#if 0 + ErrorF ("Must create new image of size %d %d\n", width, height); +#endif + + /* Chunk */ + + /* FIXME: Check integer overflow */ + chunk = qxl_allocnf (qxl, sizeof *chunk + height * dest_stride); + + chunk->data_size = height * dest_stride; + chunk->prev_chunk = 0; + chunk->next_chunk = 0; + + hash_and_copy (data, stride, + chunk->data, dest_stride, + qxl->bytes_per_pixel, width, height); + + /* Image */ + image = qxl_allocnf (qxl, sizeof *image); + + image->descriptor.id = 0; + image->descriptor.type = QXL_IMAGE_TYPE_BITMAP; + + image->descriptor.flags = 0; + image->descriptor.width = width; + image->descriptor.height = height; + + if (qxl->bytes_per_pixel == 2) + { + image->u.bitmap.format = QXL_BITMAP_FMT_16BIT; + } + else + { + image->u.bitmap.format = QXL_BITMAP_FMT_32BIT; + } + + image->u.bitmap.flags = QXL_BITMAP_TOP_DOWN; + image->u.bitmap.x = width; + image->u.bitmap.y = height; + image->u.bitmap.stride = width * qxl->bytes_per_pixel; + image->u.bitmap.palette = 0; + image->u.bitmap.data = physical_address (qxl, chunk); + +#if 0 + ErrorF ("%p has size %d %d\n", image, width, height); +#endif + + /* Add to hash table */ + if ((info = insert_image_info (hash))) + { + info->image = image; + info->ref_count = 1; + + image->descriptor.id = hash; + image->descriptor.flags = QXL_IMAGE_CACHE; + +#if 0 + ErrorF ("added with hash %u\n", hash); +#endif + } + + return image; + } +} + +void +qxl_image_destroy (qxl_screen_t *qxl, + struct qxl_image *image) +{ + struct qxl_data_chunk *chunk; + image_info_t *info; + + chunk = virtual_address (qxl, u64_to_pointer (image->u.bitmap.data)); + + info = lookup_image_info (image->descriptor.id, + image->descriptor.width, + image->descriptor.height); + + if (info && info->image == image) + { + --info->ref_count; + + if (info->ref_count != 0) + return; + +#if 0 + ErrorF ("removed %p from hash table\n", info->image); +#endif + + remove_image_info (info); + } + + qxl_free (qxl->mem, chunk); + qxl_free (qxl->mem, image); +} + +void +qxl_drop_image_cache (qxl_screen_t *qxl) +{ + memset (image_table, 0, HASH_SIZE * sizeof (image_info_t *)); +} diff --git a/src/compat/compat-qxl_mem.c b/src/compat/compat-qxl_mem.c new file mode 100644 index 0000000..2f6fa6a --- /dev/null +++ b/src/compat/compat-qxl_mem.c @@ -0,0 +1,321 @@ +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#include "compat-qxl.h" + +struct block +{ + unsigned long n_bytes; + + union + { + struct + { + struct block *next; + } unused; + + struct + { + uint8_t data[0]; + } used; + } u; +}; + +struct qxl_mem +{ + void * base; + unsigned long n_bytes; + + struct block *unused; + unsigned long total_allocated; + unsigned long total_freed; + unsigned long n_allocated_blocks; + unsigned long n_freed_blocks; +}; + +static void +initialize (struct qxl_mem *mem) +{ + mem->unused = (struct block *)mem->base; + mem->unused->n_bytes = mem->n_bytes; + mem->unused->u.unused.next = NULL; + + mem->total_allocated = 0; + mem->total_freed = 0; + mem->n_allocated_blocks = 0; + mem->n_freed_blocks = 0; +} + +struct qxl_mem * +qxl_mem_create (void *base, unsigned long n_bytes) +{ + struct qxl_mem *mem = NULL; + + mem = calloc (sizeof (*mem), 1); + if (!mem) + goto out; + + mem->base = base; + mem->n_bytes = n_bytes; + + initialize (mem); + +out: + return mem; +} + +void +qxl_mem_free_all (struct qxl_mem *mem) +{ + initialize (mem); +} + +void +qxl_mem_dump_stats (struct qxl_mem *mem, const char *header) +{ + struct block *b; + int n_blocks; + unsigned long max_block = 0; + unsigned long min_block = 0xffffffffffffffffUL; + + fprintf (stderr, "%s\n", header); + + n_blocks = 0; + for (b = mem->unused; b != NULL; b = b->u.unused.next) + { + fprintf (stderr, "block: %p (%lu bytes)\n", b, b->n_bytes); + + if (b->u.unused.next && b >= b->u.unused.next) + { + fprintf (stderr, "b: %p b->next: %p\n", + b, b->u.unused.next); + assert (0); + } + + if (b->u.unused.next && (void *)b + b->n_bytes >= b->u.unused.next) + { + fprintf (stderr, "OVERLAPPING BLOCKS b: %p b->next: %p\n", + b, b->u.unused.next); + assert (0); + } + + if (b->n_bytes > max_block) + max_block = b->n_bytes; + + if (b->n_bytes < min_block) + min_block = b->n_bytes; + + ++n_blocks; + } + + fprintf (stderr, "=========\n"); + + fprintf (stderr, "%d blocks\n", n_blocks); + fprintf (stderr, "min block: %lu bytes\n", min_block); + fprintf (stderr, "max block: %lu bytes\n", max_block); + fprintf (stderr, "total freed: %lu bytres\n", mem->total_freed); + fprintf (stderr, "total allocated: %lu bytes\n", + mem->total_allocated - mem->total_freed); + fprintf (stderr, "total free: %lu bytes\n", + mem->n_bytes - (mem->total_allocated - mem->total_freed)); +} + +void * +qxl_alloc (struct qxl_mem *mem, unsigned long n_bytes) +{ + struct block *b, *prev; + + mem->n_allocated_blocks++; + + /* Simply pretend the user asked to allocate the header as well. Then + * we can mostly ignore the difference between blocks and allocations + */ + n_bytes += sizeof (unsigned long); + + n_bytes = (n_bytes + 7) & ~((1 << 3) - 1); + + if (n_bytes < sizeof (struct block)) + n_bytes = sizeof (struct block); + + assert (mem->unused); + + prev = NULL; + for (b = mem->unused; b != NULL; prev = b, b = b->u.unused.next) + { + if (b->n_bytes >= n_bytes) + { + struct block *new_block; + + if (b->n_bytes - n_bytes >= sizeof (struct block)) + { + new_block = (void *)b + n_bytes; + + new_block->n_bytes = b->n_bytes - n_bytes; + + if (prev) + { + assert (prev < b); + assert (prev->u.unused.next == NULL || prev < prev->u.unused.next); + + new_block->u.unused.next = b->u.unused.next; + prev->u.unused.next = new_block; + } + else + { + assert (mem->unused == b); + + new_block->u.unused.next = mem->unused->u.unused.next; + mem->unused = new_block; + } + + b->n_bytes = n_bytes; + } + else + { +#if 0 + printf ("Exact match\n"); +#endif + + if (prev) + { + prev->u.unused.next = b->u.unused.next; + } + else + { + mem->unused = b->u.unused.next; + } + } + + mem->total_allocated += n_bytes; + + return (void *)b->u.used.data; + } + else + { +#if 0 + printf ("Skipping small block %d\n", b->n_bytes); +#endif + } + } + + /* If we get here, we are out of memory, so print some stats */ +#if 0 + fprintf (stderr, "Failing to allocate %lu bytes\n", n_bytes); + qxl_mem_dump_stats (mem, "out of memory"); +#endif + + return NULL; +} + +/* Finds the unused block before and the unused block after @data. Both + * before and after can be NULL if data is before the first or after the + * last unused block. + */ +static void +find_neighbours (struct qxl_mem *mem, void *data, + struct block **before, struct block **after) +{ + struct block *b; + *before = NULL; + *after = NULL; + + for (b = mem->unused; b != NULL; b = b->u.unused.next) + { + if ((void *)b < data) + *before = b; + + if ((void *)b > data) + { + *after = b; + break; + } + } + + if (*before) + assert ((*before)->u.unused.next == *after); +} + +void +qxl_free (struct qxl_mem *mem, void *d) +{ + struct block *b = d - sizeof (unsigned long); + struct block *before, *after; + + mem->total_freed += b->n_bytes; + mem->n_freed_blocks++; + +#if 0 + printf ("freeing %p (%d bytes)\n", b, b->n_bytes); + + qxl_mem_dump_stats (mem, "before free"); +#endif + + find_neighbours (mem, (void *)b, &before, &after); + + if (before) + { +#if 0 + printf (" free: merge before: %p\n", before->u.used.data); +#endif + + if ((void *)before + before->n_bytes == b) + { +#if 0 + printf (" free: merge with before (adding %d bytes)\n", b->n_bytes); +#endif + + /* Merge before and b */ + before->n_bytes += b->n_bytes; + b = before; + } + else + { +#if 0 + printf (" free: no merge with before\n"); +#endif + + before->u.unused.next = b; + } + } + else + { +#if 0 + printf (" free: no before\n"); +#endif + mem->unused = b; + } + + if (after) + { +#if 0 + printf (" free: after: %p\n", after->u.used.data); +#endif + if ((void *)b + b->n_bytes == after) + { +#if 0 + printf (" merge with after\n"); +#endif + b->n_bytes += after->n_bytes; + b->u.unused.next = after->u.unused.next; + } + else + { +#if 0 + printf (" no merge with after\n"); +#endif + b->u.unused.next = after; + } + } + else + { +#if 0 + printf (" free: no after\n"); +#endif + b->u.unused.next = NULL; + } + +#if 0 + qxl_mem_dump_stats (mem, "after free"); +#endif +} diff --git a/src/compat/compat-qxl_ring.c b/src/compat/compat-qxl_ring.c new file mode 100644 index 0000000..9a34583 --- /dev/null +++ b/src/compat/compat-qxl_ring.c @@ -0,0 +1,97 @@ +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include "compat-qxl.h" + +struct ring +{ + struct qxl_ring_header header; + uint8_t elements[0]; +}; + +struct qxl_ring +{ + volatile struct ring *ring; + int element_size; + int n_elements; + int prod_notify; +}; + +struct qxl_ring * +qxl_ring_create (struct qxl_ring_header *header, + int element_size, + int n_elements, + int prod_notify) +{ + struct qxl_ring *ring; + + ring = malloc (sizeof *ring); + if (!ring) + return NULL; + + ring->ring = (volatile struct ring *)header; + ring->element_size = element_size; + ring->n_elements = n_elements; + ring->prod_notify = prod_notify; + + return ring; +} + +void +qxl_ring_push (struct qxl_ring *ring, + const void *new_elt) +{ + volatile struct qxl_ring_header *header = &(ring->ring->header); + volatile uint8_t *elt; + int idx; + + while (header->prod - header->cons == header->num_items) + { + header->notify_on_cons = header->cons + 1; + + mem_barrier(); + } + + idx = header->prod & (ring->n_elements - 1); + elt = ring->ring->elements + idx * ring->element_size; + + memcpy((void *)elt, new_elt, ring->element_size); + + header->prod++; + + mem_barrier(); + + if (header->prod == header->notify_on_prod) + outb (ring->prod_notify, 0); +} + +Bool +qxl_ring_pop (struct qxl_ring *ring, + void *element) +{ + volatile struct qxl_ring_header *header = &(ring->ring->header); + volatile uint8_t *ring_elt; + int idx; + + if (header->cons == header->prod) + return FALSE; + + idx = header->cons & (ring->n_elements - 1); + ring_elt = ring->ring->elements + idx * ring->element_size; + + memcpy (element, (void *)ring_elt, ring->element_size); + + header->cons++; + + return TRUE; +} + +void +qxl_ring_wait_idle (struct qxl_ring *ring) +{ + while (ring->ring->header.cons != ring->ring->header.prod) + { + usleep (1000); + mem_barrier(); + } +} |