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