vgasavage.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. #include "u.h"
  2. #include "../port/lib.h"
  3. #include "mem.h"
  4. #include "dat.h"
  5. #include "fns.h"
  6. #include "io.h"
  7. #include "../port/error.h"
  8. #define Image IMAGE
  9. #include <draw.h>
  10. #include <memdraw.h>
  11. #include <cursor.h>
  12. #include "screen.h"
  13. enum {
  14. PCIS3 = 0x5333, /* PCI VID */
  15. SAVAGE3D = 0x8A20, /* PCI DID */
  16. SAVAGE3DMV = 0x8A21,
  17. SAVAGE4 = 0x8A22,
  18. PROSAVAGEP = 0x8A25,
  19. PROSAVAGEK = 0x8A26,
  20. SAVAGEMXMV = 0x8C10,
  21. SAVAGEMX = 0x8C11,
  22. SAVAGEIXMV = 0x8C12,
  23. SAVAGEIX = 0x8C13,
  24. SUPERSAVAGEIXC16 = 0x8C2E,
  25. SAVAGE2000 = 0x9102,
  26. VIRGE = 0x5631,
  27. VIRGEGX2 = 0x8A10,
  28. VIRGEDXGX = 0x8A01,
  29. VIRGEVX = 0x883D,
  30. VIRGEMX = 0x8C01,
  31. VIRGEMXP = 0x8C03,
  32. AURORA64VPLUS = 0x8812,
  33. };
  34. /*
  35. * Savage4 et al. acceleration.
  36. *
  37. * This is based only on the Savage4 documentation.
  38. * It is expected to work on other Savage cards as well,
  39. * but has not been tried.
  40. *
  41. * There are five ways to access the 2D graphics engine registers:
  42. * - Old MMIO non-packed format
  43. * - Old MMIO packed format
  44. * - New MMIO non-packed format
  45. * - New MMIO packed format
  46. * - Burst Command Interface (BCI)
  47. *
  48. * Of these, the manual hints that the first three are deprecated,
  49. * and it does not document any of those three well enough to use.
  50. *
  51. * I have tried for many hours with no success to understand the BCI
  52. * interface well enough to use it. It is not well documented, and the
  53. * XFree86 driver seems to completely contradict what little documentation
  54. * there is.
  55. *
  56. * This leaves the packed new MMIO.
  57. * The manual contradicts itself here, claming that the registers
  58. * start at 0x2008100 as well as at 0x0008100 from the base of the
  59. * mmio segment. Since the segment is only 512k, we assume that
  60. * the latter is the correct offset.
  61. *
  62. * According to the manual, only 16-bit reads of the 2D registers
  63. * are supported: 32-bit reads will return garbage in the upper word.
  64. * 32-bit writes must be enabled explicitly.
  65. *
  66. * 32-bit reads of the status registers seem just fine.
  67. */
  68. /* 2D graphics engine registers for Savage4; others appear to be mostly the same */
  69. enum {
  70. SubsystemStatus = 0x8504, /* Subsystem Status: read only */
  71. /* read only: whether we get interrupts on various events */
  72. VsyncInt = 1<<0, /* vertical sync */
  73. GeBusyInt = 1<<1, /* 2D graphics engine busy */
  74. BfifoFullInt = 1<<2, /* BIU FIFO full */
  75. BfifoEmptyInt = 1<<3, /* BIU FIFO empty */
  76. CfifoFullInt = 1<<4, /* command FIFO full */
  77. CfifoEmptyInt = 1<<5, /* command FIFO empty */
  78. BciInt = 1<<6, /* BCI */
  79. LpbInt = 1<<7, /* LPB */
  80. CbHiInt = 1<<16, /* COB upper threshold */
  81. CbLoInt = 1<<17, /* COB lower threshold */
  82. SubsystemCtl = 0x8504, /* Subsystem Control: write only */
  83. /* clear interrupts for various events */
  84. VsyncClr = 1<<0,
  85. GeBusyClr = 1<<1,
  86. BfifoFullClr = 1<<2,
  87. BfifoEmptyClr = 1<<3,
  88. CfifoFullClr = 1<<4,
  89. CfifoEmptyClr = 1<<5,
  90. BciClr = 1<<6,
  91. LpbClr = 1<<7,
  92. CbHiClr = 1<<16,
  93. CbLoClr = 1<<17,
  94. /* enable interrupts for various events */
  95. VsyncEna = 1<<8,
  96. Busy2DEna = 1<<9,
  97. BfifoFullEna = 1<<10,
  98. BfifoEmptyEna = 1<<11,
  99. CfifoFullEna = 1<<12,
  100. CfifoEmptyEna = 1<<13,
  101. SubsysBciEna = 1<<14,
  102. CbHiEna = 1<<24,
  103. CbLoEna = 1<<25,
  104. /* 2D graphics engine software reset */
  105. GeSoftReset = 1<<15,
  106. FifoStatus = 0x8508, /* FIFO status: read only */
  107. CwbEmpty = 1<<0, /* command write buffer empty */
  108. CrbEmpty = 1<<1, /* command read buffer empty */
  109. CobEmpty = 1<<2, /* command overflow buffer empty */
  110. CfifoEmpty = 1<<3, /* command FIFO empty */
  111. CwbFull = 1<<8, /* command write buffer full */
  112. CrbFull = 1<<9, /* command read buffer full */
  113. CobFull = 1<<10, /* command overflow buffer full */
  114. CfifoFull = 1<<11, /* command FIFO full */
  115. AdvFunCtl = 0x850C, /* Advanced Function Control: read/write */
  116. GeEna = 1<<0, /* enable 2D/3D engine */
  117. /*
  118. * according to the manual, BigPixel should be
  119. * set when bpp >= 8 (bpp != 4), and then CR50_5-4 are
  120. * used to figure out bpp example. however, it does bad things
  121. * to the screen in 8bpp mode.
  122. */
  123. BigPixel = 1<<2, /* 8 or more bpp enhanced mode */
  124. LaEna = 1<<3, /* linear addressing ena: or'ed with CR58_4 */
  125. Mclk_2 = 0<<8, /* 2D engine clock divide: MCLK/2 */
  126. Mclk_4 = 1<<8, /* " MCLK/4 */
  127. Mclk = 2<<8, /* " MCLK */
  128. /* Mclk = 3<<8, /* " MCLK */
  129. Ic33mhz = 1<<16, /* Internal clock 33 MHz (instead of 66) */
  130. WakeupReg = 0x8510, /* Wakeup: read/write */
  131. WakeupBit = 1<<0, /* wake up: or'ed with 3C3_0 */
  132. SourceY = 0x8100, /* UL corner of bitblt source */
  133. SourceX = 0x8102, /* " */
  134. RectY = 0x8100, /* UL corner of rectangle fill */
  135. RectX = 0x8102, /* " */
  136. DestY = 0x8108, /* UL corner of bitblt dest */
  137. DestX = 0x810A, /* " */
  138. Height = 0x8148, /* bitblt, image xfer rectangle height */
  139. Width = 0x814A, /* bitblt, image xfer rectangle width */
  140. StartY = 0x8100, /* Line draw: first point*/
  141. StartX = 0x8102, /* " */
  142. /*
  143. * For line draws, the following must be programmed:
  144. * axial step constant = 2*min(|dx|,|dy|)
  145. * diagonal step constant = 2*[min(|dx|,|dy|) - max(|dx|,|dy|)]
  146. * error term = 2*min(|dx|,|dy|) - max(|dx|,|dy| - 1
  147. * [sic] when start X < end X
  148. * error term = 2*min(|dx|,|dy|) - max(|dx|,|dy|
  149. * [sic] when start X >= end X
  150. */
  151. AxialStep = 0x8108,
  152. DiagonalStep = 0x810A,
  153. LineError = 0x8110,
  154. MinorLength = 0x8148, /* pixel count along minor axis */
  155. MajorLength = 0x814A, /* pixel count along major axis */
  156. DrawCmd = 0x8118, /* Drawing Command: write only */
  157. CmdMagic = 0<<1,
  158. AcrossPlane = 1<<1, /* across the plane mode */
  159. LastPixelOff = 1<<2, /* last pixel of line or vector draw not drawn */
  160. Radial = 1<<3, /* enable radial direction (else axial) */
  161. DoDraw = 1<<4, /* draw pixels (else only move current pos) */
  162. DrawRight = 1<<5, /* axial drawing direction: left to right */
  163. /* DrawLeft = 0<<5, */
  164. MajorY = 1<<6,
  165. /* MajorX = 0<<6, */
  166. DrawDown = 1<<7,
  167. /* DrawUp = 0<<7, */
  168. Degree0 = 0<<5, /* drawing direction when Radial */
  169. Degree45 = 1<<5,
  170. /* ... */
  171. Degree315 = 7<<5,
  172. UseCPUData = 1<<8,
  173. /* image write bus transfer width */
  174. Bus8 = 0<<9,
  175. Bus16 = 1<<9,
  176. /*
  177. * in Bus32 mode, doubleword bits beyond the image rect width are
  178. * discarded. each line starts on a new doubleword.
  179. * Bus32AP is intended for across-the-plane mode and
  180. * rounds to byte boundaries instead.
  181. */
  182. Bus32 = 2<<9,
  183. Bus32AP = 3<<9,
  184. CmdNop = 0<<13, /* nop */
  185. CmdLine = 1<<13, /* draw line */
  186. CmdFill = 2<<13, /* fill rectangle */
  187. CmdBitblt = 6<<13, /* bitblt */
  188. CmdPatblt = 7<<13, /* 8x8 pattern blt */
  189. SrcGBD = 0<<16,
  190. SrcPBD = 1<<16,
  191. SrcSBD = 2<<16,
  192. DstGBD = 0<<18,
  193. DstPBD = 1<<18,
  194. DstSBD = 2<<18,
  195. /* color sources, controls */
  196. BgColor = 0x8120, /* Background Color: read/write */
  197. FgColor = 0x8124, /* Foreground Color: read/write */
  198. BitplaneWmask = 0x8128, /* Bitplane Write Mask: read/write */
  199. BitplaneRmask = 0x812C, /* Bitplane Read Mask: read/write */
  200. CmpColor = 0x8130, /* Color Compare: read/write */
  201. BgMix = 0x8134,
  202. FgMix = 0x8136,
  203. MixNew = 7,
  204. SrcBg = 0<<5,
  205. SrcFg = 1<<5,
  206. SrcCPU = 2<<5,
  207. SrcDisp = 3<<5,
  208. /* clipping rectangle */
  209. TopScissors = 0x8138, /* Top Scissors: write only */
  210. LeftScissors = 0x813A, /* Left Scissors: write only */
  211. BottomScissors = 0x813C, /* Bottom Scissors: write only */
  212. RightScissors = 0x813E, /* Right Scissors: write only */
  213. /*
  214. * Registers with Magic were indirectly accessed in older modes.
  215. * It is not clear whether the Magic is necessary.
  216. * In the older modes, writes to these registers were pipelined,
  217. * so that you had to issue an engine command and wait for engine
  218. * idle before reading a write back. It is not clear if this is
  219. * still the case either.
  220. */
  221. PixCtl = 0x8140, /* Pixel Control: write only */
  222. PixMagic = 0xA<<12,
  223. PixMixFg = 0<<6, /* foreground mix register always */
  224. PixMixCPU = 2<<6, /* CPU data determines mix register */
  225. PixMixDisp = 3<<6, /* display data determines mix register */
  226. MfMisc2Ctl = 0x8142, /* Multifunction Control Misc. 2: write only */
  227. MfMisc2Magic = 0xD<<12,
  228. DstShift = 0, /* 3 bits: destination base address in MB */
  229. SrcShift = 4, /* 3 bits: source base address in MB */
  230. WaitFifoEmpty = 2<<8, /* wait for write FIFO empty between draws */
  231. MfMiscCtl = 0x8144, /* Multifunction Control Misc: write only */
  232. MfMiscMagic = 0xE<<12,
  233. UseHighBits = 1<<4, /* select upper 16 bits for 32-bit reg access */
  234. ClipInvert = 1<<5, /* only touch pixels outside clip rectangle */
  235. SkipSame = 0<<6, /* ignore pixels with color CmpColor */
  236. SkipDifferent = 1<<7, /* ignore pixels not color CmpColor */
  237. CmpEna = 1<<8, /* enable color compare */
  238. W32Ena = 1<<9, /* enable 32-bit register write */
  239. ClipDis = 1<<11, /* disable clipping */
  240. /*
  241. * The bitmap descriptor 1 registers contain the starting
  242. * address of the bitmap (in bytes).
  243. * The bitmap descriptor 2 registesr contain stride (in pixels)
  244. * in the lower 16 bits, depth (in bits) in the next 8 bits,
  245. * and whether block write is disabled.
  246. */
  247. GBD1 = 0x8168, /* Global Bitmap Descriptor 1: read/write */
  248. GBD2 = 0x816C, /* Global Bitmap Descriptor 2: read/write */
  249. /* GBD2-only bits */
  250. BDS64 = 1<<0, /* bitmap descriptor size 64 bits */
  251. GBDBciEna = 1<<3, /* BCI enable */
  252. /* generic BD2 bits */
  253. BlockWriteDis = 1<<28,
  254. StrideShift = 0,
  255. DepthShift = 16,
  256. PBD1 = 0x8170, /* Primary Bitmap Descriptor: read/write */
  257. PBD2 = 0x8174,
  258. SBD1 = 0x8178, /* Secondary Bitmap Descriptor: read/write */
  259. SBD2 = 0x817C,
  260. };
  261. /* mastered data transfer registers */
  262. /* configuration/status registers */
  263. enum {
  264. XStatus0 = 0x48C00, /* Status Word 0: read only */
  265. /* rev. A silicon differs from rev. B; use AltStatus0 */
  266. CBEMaskA = 0x1FFFF, /* filled command buffer entries */
  267. CBEShiftA = 0,
  268. BciIdleA = 1<<17, /* BCI idle */
  269. Ge3IdleA = 1<<18, /* 3D engine idle */
  270. Ge2IdleA = 1<<19, /* 2D engine idle */
  271. McpIdleA = 1<<20, /* motion compensation processor idle */
  272. MeIdleA = 1<<22, /* master engine idle */
  273. PfPendA = 1<<23, /* page flip pending */
  274. CBEMaskB = 0x1FFFFF,
  275. CBEShiftB = 0,
  276. BciIdleB = 1<<25,
  277. Ge3IdleB = 1<<26,
  278. Ge2IdleB = 1<<27,
  279. McpIdleB = 1<<28,
  280. MeIdleB = 1<<30,
  281. PfPendB = 1<<31,
  282. AltStatus0 = 0x48C60, /* Alternate Status Word 0: read only */
  283. CBEMask = 0x1FFFF,
  284. CBEShift = 0,
  285. /* the Savage4 manual says bits 17..23 for these, like Status0 */
  286. /* empirically, they are bits 21..26 */
  287. BciIdle = 1<<21,
  288. Ge3Idle = 1<<22,
  289. Ge2Idle = 1<<23,
  290. McpIdle = 1<<24,
  291. MeIdle = 1<<25,
  292. PfPend = 1<<26,
  293. XStatus1 = 0x48C04, /* Status Word 1: read only */
  294. /* contains event tag 1, event tag 0, both 16 bits */
  295. XStatus2 = 0x48C08, /* Status Word 2: read only */
  296. ScanMask = 0x3FF, /* current scan line */
  297. ScanShift = 0,
  298. VRTMask = 0x7F100, /* vert retrace count */
  299. VRTShift = 11,
  300. CbThresh = 0x48C10, /* Command Buffer Thresholds: read/write */
  301. CobOff = 0x48C14, /* Command Overflow Buffer: read/write */
  302. CobPtr = 0x48C18, /* Command Overflow Buffer Pointers: read/write */
  303. CobEna = 1<<2, /* command overflow buffer enable */
  304. CobBciEna = 1<<3, /* BCI function enable */
  305. CbeMask = 0xFFFF8000, /* no. of entries in command buffer */
  306. CbeShift = 15,
  307. AltStatus1 = 0x48C64, /* Alternate Status Word 1: read onnly */
  308. /* contains current texture surface tag, vertex buffer tag */
  309. };
  310. struct {
  311. ulong idletimeout;
  312. ulong tostatw[16];
  313. } savagestats;
  314. enum {
  315. Maxloop = 1<<20
  316. };
  317. static void
  318. savagewaitidle(VGAscr *scr)
  319. {
  320. long x;
  321. ulong *statw, mask, goal;
  322. switch(scr->id){
  323. case SAVAGE4:
  324. case PROSAVAGEP:
  325. case PROSAVAGEK:
  326. /* wait for engine idle and FIFO empty */
  327. statw = (ulong*)((uchar*)scr->mmio+AltStatus0);
  328. mask = CBEMask | Ge2Idle;
  329. goal = Ge2Idle;
  330. break;
  331. /* case SAVAGEMXMV: ? */
  332. /* case SAVAGEMX: ? */
  333. /* case SAVAGEIX: ? */
  334. case SUPERSAVAGEIXC16:
  335. case SAVAGEIXMV:
  336. case SAVAGEMXMV:
  337. /* wait for engine idle and FIFO empty */
  338. statw = (ulong*)((uchar*)scr->mmio+XStatus0);
  339. mask = CBEMaskA | Ge2IdleA;
  340. goal = Ge2IdleA;
  341. break;
  342. default:
  343. /*
  344. * best we can do: can't print or we'll call ourselves.
  345. * savageinit is supposed to not let this happen.
  346. */
  347. return;
  348. }
  349. for(x=0; x<Maxloop; x++)
  350. if((*statw & mask) == goal)
  351. return;
  352. savagestats.tostatw[savagestats.idletimeout++&15] = *statw;
  353. savagestats.tostatw[savagestats.idletimeout++&15] = (ulong)statw;
  354. }
  355. static int
  356. savagefill(VGAscr *scr, Rectangle r, ulong sval)
  357. {
  358. uchar *mmio;
  359. mmio = (uchar*)scr->mmio;
  360. *(ulong*)(mmio+FgColor) = sval;
  361. *(ulong*)(mmio+BgColor) = sval;
  362. *(ulong*)(mmio+BgMix) = SrcFg|MixNew;
  363. *(ulong*)(mmio+FgMix) = SrcFg|MixNew;
  364. *(ushort*)(mmio+RectY) = r.min.y;
  365. *(ushort*)(mmio+RectX) = r.min.x;
  366. *(ushort*)(mmio+Width) = Dx(r)-1;
  367. *(ushort*)(mmio+Height) = Dy(r)-1;
  368. *(ulong*)(mmio+DrawCmd) = CmdMagic | DoDraw | CmdFill | DrawRight | DrawDown;
  369. savagewaitidle(scr);
  370. return 1;
  371. }
  372. static int
  373. savagescroll(VGAscr *scr, Rectangle r, Rectangle sr)
  374. {
  375. uchar *mmio;
  376. ulong cmd;
  377. Point dp, sp;
  378. cmd = CmdMagic | DoDraw | CmdBitblt | SrcPBD | DstGBD;
  379. if(r.min.x <= sr.min.x){
  380. cmd |= DrawRight;
  381. dp.x = r.min.x;
  382. sp.x = sr.min.x;
  383. }else{
  384. dp.x = r.max.x-1;
  385. sp.x = sr.max.x-1;
  386. }
  387. if(r.min.y <= sr.min.y){
  388. cmd |= DrawDown;
  389. dp.y = r.min.y;
  390. sp.y = sr.min.y;
  391. }else{
  392. dp.y = r.max.y-1;
  393. sp.y = sr.max.y-1;
  394. }
  395. mmio = (uchar*)scr->mmio;
  396. *(ushort*)(mmio+SourceX) = sp.x;
  397. *(ushort*)(mmio+SourceY) = sp.y;
  398. *(ushort*)(mmio+DestX) = dp.x;
  399. *(ushort*)(mmio+DestY) = dp.y;
  400. *(ushort*)(mmio+Width) = Dx(r)-1;
  401. *(ushort*)(mmio+Height) = Dy(r)-1;
  402. *(ulong*)(mmio+BgMix) = SrcDisp|MixNew;
  403. *(ulong*)(mmio+FgMix) = SrcDisp|MixNew;
  404. *(ulong*)(mmio+DrawCmd) = cmd;
  405. savagewaitidle(scr);
  406. return 1;
  407. }
  408. static void
  409. savageblank(VGAscr*, int blank)
  410. {
  411. uchar seqD;
  412. /*
  413. * Will handle DPMS to monitor
  414. */
  415. vgaxo(Seqx, 8, vgaxi(Seqx,8)|0x06);
  416. seqD = vgaxi(Seqx, 0xD);
  417. seqD &= 0x03;
  418. if(blank)
  419. seqD |= 0x50;
  420. vgaxo(Seqx, 0xD, seqD);
  421. /*
  422. * Will handle LCD
  423. */
  424. if(blank)
  425. vgaxo(Seqx, 0x31, vgaxi(Seqx, 0x31) & ~0x10);
  426. else
  427. vgaxo(Seqx, 0x31, vgaxi(Seqx, 0x31) | 0x10);
  428. }
  429. void
  430. savageinit(VGAscr *scr)
  431. {
  432. uchar *mmio;
  433. ulong bd;
  434. /* if you add chip IDs here be sure to update savagewaitidle */
  435. switch(scr->id){
  436. case SAVAGE4:
  437. case PROSAVAGEP:
  438. case PROSAVAGEK:
  439. case SAVAGEIXMV:
  440. case SUPERSAVAGEIXC16:
  441. case SAVAGEMXMV:
  442. break;
  443. default:
  444. print("unknown savage %.4lux\n", scr->id);
  445. return;
  446. }
  447. mmio = (uchar*)scr->mmio;
  448. if(mmio == nil) {
  449. print("savageinit: no mmio\n");
  450. return;
  451. }
  452. /* 2D graphics engine software reset */
  453. *(ushort*)(mmio+SubsystemCtl) = GeSoftReset;
  454. delay(2);
  455. *(ushort*)(mmio+SubsystemCtl) = 0;
  456. savagewaitidle(scr);
  457. /* disable BCI as much as possible */
  458. *(ushort*)(mmio+CobPtr) &= ~CobBciEna;
  459. *(ushort*)(mmio+GBD2) &= ~GBDBciEna;
  460. savagewaitidle(scr);
  461. /* enable 32-bit writes, disable clipping */
  462. *(ushort*)(mmio+MfMiscCtl) = MfMiscMagic|W32Ena|ClipDis;
  463. savagewaitidle(scr);
  464. /* enable all read, write planes */
  465. *(ulong*)(mmio+BitplaneRmask) = ~0;
  466. *(ulong*)(mmio+BitplaneWmask) = ~0;
  467. savagewaitidle(scr);
  468. /* turn on linear access, 2D engine */
  469. *(ulong*)(mmio+AdvFunCtl) |= GeEna|LaEna;
  470. savagewaitidle(scr);
  471. /* set bitmap descriptors */
  472. bd = (scr->gscreen->depth<<DepthShift) |
  473. (Dx(scr->gscreen->r)<<StrideShift) | BlockWriteDis
  474. | BDS64;
  475. *(ulong*)(mmio+GBD1) = 0;
  476. *(ulong*)(mmio+GBD2) = bd;
  477. *(ulong*)(mmio+PBD1) = 0;
  478. *(ulong*)(mmio+PBD2) = bd;
  479. *(ulong*)(mmio+SBD1) = 0;
  480. *(ulong*)(mmio+SBD2) = bd;
  481. /*
  482. * For some reason, the GBD needs to get programmed twice,
  483. * once before the PBD, SBD, and once after.
  484. * This empirically makes it get set right.
  485. * I would like to better understand the ugliness
  486. * going on here.
  487. */
  488. *(ulong*)(mmio+GBD1) = 0;
  489. *(ulong*)(mmio+GBD2) = bd;
  490. *(ushort*)(mmio+GBD2+2) = bd>>16;
  491. savagewaitidle(scr);
  492. scr->fill = savagefill;
  493. scr->scroll = savagescroll;
  494. scr->blank = savageblank;
  495. hwblank = 0;
  496. }