vgasavage.c 16 KB

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