1 /*
2 * ATI Radeon [789]XXX vga driver
3 * see /sys/src/cmd/aux/vga/radeon.c
4 */
5 #include "u.h"
6 #include "../port/lib.h"
7 #include "mem.h"
8 #include "dat.h"
9 #include "fns.h"
10 #include "io.h"
11 #include "../port/error.h"
12
13 #define Image IMAGE
14 #include <draw.h>
15 #include <memdraw.h>
16 #include <cursor.h>
17 #include "screen.h"
18
19 #include "/sys/src/cmd/aux/vga/radeon.h" /* ugh */
20
21 /* #define HW_ACCEL */
22
23 enum {
24 Kilo = 1024,
25 Meg = Kilo * Kilo,
26 };
27
28 static Pcidev*
radeonpci(void)29 radeonpci(void)
30 {
31 static Pcidev *p = nil;
32 struct pciids *ids;
33
34 while ((p = pcimatch(p, ATI_PCIVID, 0)) != nil)
35 for (ids = radeon_pciids; ids->did; ids++)
36 if (ids->did == p->did)
37 return p;
38 return nil;
39 }
40
41 /* mmio access */
42
43 static void
OUTREG8(ulong mmio,ulong offset,uchar val)44 OUTREG8(ulong mmio, ulong offset, uchar val)
45 {
46 ((uchar*)KADDR((mmio + offset)))[0] = val;
47 }
48
49 static void
OUTREG(ulong mmio,ulong offset,ulong val)50 OUTREG(ulong mmio, ulong offset, ulong val)
51 {
52 ((ulong*)KADDR((mmio + offset)))[0] = val;
53 }
54
55 static ulong
INREG(ulong mmio,ulong offset)56 INREG(ulong mmio, ulong offset)
57 {
58 return ((ulong*)KADDR((mmio + offset)))[0];
59 }
60
61 static void
OUTREGP(ulong mmio,ulong offset,ulong val,ulong mask)62 OUTREGP(ulong mmio, ulong offset, ulong val, ulong mask)
63 {
64 OUTREG(mmio, offset, (INREG(mmio, offset) & mask) | val);
65 }
66
67 static void
OUTPLL(ulong mmio,ulong offset,ulong val)68 OUTPLL(ulong mmio, ulong offset, ulong val)
69 {
70 OUTREG8(mmio, CLOCK_CNTL_INDEX, (offset & 0x3f) | PLL_WR_EN);
71 OUTREG(mmio, CLOCK_CNTL_DATA, val);
72 }
73
74 static ulong
INPLL(ulong mmio,ulong offset)75 INPLL(ulong mmio, ulong offset)
76 {
77 OUTREG8(mmio, CLOCK_CNTL_INDEX, offset & 0x3f);
78 return INREG(mmio, CLOCK_CNTL_DATA);
79 }
80
81 static void
OUTPLLP(ulong mmio,ulong offset,ulong val,ulong mask)82 OUTPLLP(ulong mmio, ulong offset, ulong val, ulong mask)
83 {
84 OUTPLL(mmio, offset, (INPLL(mmio, offset) & mask) | val);
85 }
86
87 static void
radeonlinear(VGAscr *,int,int)88 radeonlinear(VGAscr *, int, int)
89 {
90 }
91
92 static void
radeonenable(VGAscr * scr)93 radeonenable(VGAscr *scr)
94 {
95 Pcidev *p;
96
97 if (scr->mmio)
98 return;
99 p = radeonpci();
100 if (p == nil)
101 return;
102 scr->id = p->did;
103 scr->pci = p;
104
105 scr->mmio = vmap(p->mem[2].bar & ~0x0f, p->mem[2].size);
106 if(scr->mmio == 0)
107 return;
108 addvgaseg("radeonmmio", p->mem[2].bar & ~0x0f, p->mem[2].size);
109
110 vgalinearpci(scr);
111 if(scr->apsize)
112 addvgaseg("radeonscreen", scr->paddr, scr->apsize);
113 }
114
115 static void
radeoncurload(VGAscr * scr,Cursor * curs)116 radeoncurload(VGAscr *scr, Cursor *curs)
117 {
118 int x, y;
119 ulong *p;
120
121 if(scr->mmio == nil)
122 return;
123
124 p = (ulong*)KADDR(scr->storage);
125
126 for(y = 0; y < 64; y++){
127 int cv, sv;
128
129 if (y < 16) {
130 cv = curs->clr[2*y] << 8 | curs->clr[2*y+1];
131 sv = curs->set[2*y] << 8 | curs->set[2*y+1];
132 } else
133 cv = sv = 0;
134
135 for(x = 0; x < 64; x++){
136 ulong col = 0;
137 int c, s;
138
139 if (x < 16) {
140 c = (cv >> (15 - x)) & 1;
141 s = (sv >> (15 - x)) & 1;
142 } else
143 c = s = 0;
144
145 switch(c | s<<1) {
146 case 0:
147 col = 0;
148 break;
149 case 1:
150 col = ~0ul; /* white */
151 break;
152 case 2:
153 case 3:
154 col = 0xff000000; /* black */
155 break;
156 }
157
158 *p++ = col;
159 }
160 }
161
162 scr->offset.x = curs->offset.x;
163 scr->offset.y = curs->offset.y;
164 }
165
166 static int
radeoncurmove(VGAscr * scr,Point p)167 radeoncurmove(VGAscr *scr, Point p)
168 {
169 int x, y, ox, oy;
170 static ulong storage = 0;
171
172 if(scr->mmio == nil)
173 return 1;
174
175 if (storage == 0)
176 storage = scr->apsize - 1*Meg;
177
178 x = p.x + scr->offset.x;
179 y = p.y + scr->offset.y;
180 ox = oy = 0;
181
182 if (x < 0) {
183 ox = -x - 1;
184 x = 0;
185 }
186 if (y < 0) {
187 oy = -y - 1;
188 y = 0;
189 }
190
191 OUTREG((ulong)scr->mmio, CUR_OFFSET, storage + oy * 256);
192 OUTREG((ulong)scr->mmio, CUR_HORZ_VERT_OFF,
193 (ox & 0x7fff) << 16 | (oy & 0x7fff));
194 OUTREG((ulong)scr->mmio, CUR_HORZ_VERT_POSN,
195 (x & 0x7fff) << 16 | (y & 0x7fff));
196 return 0;
197 }
198
199 static void
radeoncurdisable(VGAscr * scr)200 radeoncurdisable(VGAscr *scr)
201 {
202 if(scr->mmio == nil)
203 return;
204 OUTREGP((ulong)scr->mmio, CRTC_GEN_CNTL, 0, ~CRTC_CUR_EN);
205 }
206
207 static void
radeoncurenable(VGAscr * scr)208 radeoncurenable(VGAscr *scr)
209 {
210 ulong storage;
211
212 if(scr->mmio == 0)
213 return;
214
215 radeoncurdisable(scr);
216 storage = scr->apsize - 1*Meg;
217 scr->storage = (ulong)KADDR(scr->paddr + storage);
218 radeoncurload(scr, &arrow);
219 radeoncurmove(scr, ZP);
220
221 OUTREGP((ulong)scr->mmio, CRTC_GEN_CNTL, CRTC_CUR_EN | 2<<20,
222 ~(CRTC_CUR_EN | 3<<20));
223 }
224
225 /* hw blank */
226
227 static void
radeonblank(VGAscr * scr,int blank)228 radeonblank(VGAscr* scr, int blank)
229 {
230 ulong mask;
231 char *cp;
232
233 if (scr->mmio == 0)
234 return;
235
236 // iprint("radeon: hwblank(%d)\n", blank);
237
238 mask = CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS | CRTC_VSYNC_DIS;
239 if (blank == 0) {
240 OUTREGP((ulong)scr->mmio, CRTC_EXT_CNTL, 0, ~mask);
241 return;
242 }
243
244 cp = getconf("*dpms");
245 if (cp) {
246 if (strcmp(cp, "standby") == 0)
247 OUTREGP((ulong)scr->mmio, CRTC_EXT_CNTL,
248 CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS, ~mask);
249 else if (strcmp(cp, "suspend") == 0)
250 OUTREGP((ulong)scr->mmio, CRTC_EXT_CNTL,
251 CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS, ~mask);
252 else if (strcmp(cp, "off") == 0)
253 OUTREGP((ulong)scr->mmio, CRTC_EXT_CNTL, mask, ~mask);
254 return;
255 }
256
257 OUTREGP((ulong)scr->mmio, CRTC_EXT_CNTL, mask, ~mask);
258 }
259
260 /* hw acceleration */
261
262 static void
radeonwaitfifo(VGAscr * scr,int entries)263 radeonwaitfifo(VGAscr *scr, int entries)
264 {
265 int i;
266
267 for (i = 0; i < 2000000; i++)
268 if (INREG((ulong)scr->mmio, RBBM_STATUS) & RBBM_FIFOCNT_MASK >=
269 entries)
270 return;
271 iprint("radeon: fifo timeout\n");
272 }
273
274 static void
radeonwaitidle(VGAscr * scr)275 radeonwaitidle(VGAscr *scr)
276 {
277 radeonwaitfifo(scr, 64);
278
279 for (; ; ) {
280 int i;
281
282 for (i = 0; i < 2000000; i++)
283 if (!(INREG((ulong)scr->mmio, RBBM_STATUS) & RBBM_ACTIVE))
284 return;
285
286 iprint("radeon: idle timed out: %uld entries, stat=0x%.8ulx\n",
287 INREG((ulong)scr->mmio, RBBM_STATUS) & RBBM_FIFOCNT_MASK,
288 INREG((ulong)scr->mmio, RBBM_STATUS));
289 }
290 }
291
292 static ulong dp_gui_master_cntl = 0;
293
294 static int
radeonfill(VGAscr * scr,Rectangle r,ulong color)295 radeonfill(VGAscr *scr, Rectangle r, ulong color)
296 {
297 if (scr->mmio == nil)
298 return 0;
299
300 radeonwaitfifo(scr, 6);
301 OUTREG((ulong)scr->mmio, DP_GUI_MASTER_CNTL,
302 dp_gui_master_cntl | GMC_BRUSH_SOLID_COLOR |
303 GMC_SRC_DATATYPE_COLOR | ROP3_P);
304 OUTREG((ulong)scr->mmio, DP_BRUSH_FRGD_CLR, color);
305 OUTREG((ulong)scr->mmio, DP_WRITE_MASK, ~0ul);
306 OUTREG((ulong)scr->mmio, DP_CNTL,
307 DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
308 OUTREG((ulong)scr->mmio, DST_Y_X, r.min.y << 16 | r.min.x);
309 OUTREG((ulong)scr->mmio, DST_WIDTH_HEIGHT, Dx(r) << 16 | Dy(r));
310
311 radeonwaitidle(scr);
312 return 1;
313 }
314
315 static int
radeonscroll(VGAscr * scr,Rectangle dst,Rectangle src)316 radeonscroll(VGAscr*scr, Rectangle dst, Rectangle src)
317 {
318 int xs, ys, xd, yd, w, h;
319 ulong dp_cntl = 0x20;
320
321 if (scr->mmio == nil)
322 return 0;
323
324 // iprint("radeon: hwscroll(dst:%R, src:%R)\n", dst, src);
325
326 xd = dst.min.x;
327 yd = dst.min.y;
328 xs = src.min.x;
329 ys = src.min.y;
330 w = Dx(dst);
331 h = Dy(dst);
332
333 if (ys < yd) {
334 ys += h - 1;
335 yd += h - 1;
336 } else
337 dp_cntl |= DST_Y_TOP_TO_BOTTOM;
338
339 if (xs < xd) {
340 xs += w - 1;
341 xd += w - 1;
342 } else
343 dp_cntl |= DST_X_LEFT_TO_RIGHT;
344
345 radeonwaitfifo(scr, 6);
346 OUTREG((ulong)scr->mmio, DP_GUI_MASTER_CNTL, dp_gui_master_cntl |
347 GMC_BRUSH_NONE | GMC_SRC_DATATYPE_COLOR | DP_SRC_SOURCE_MEMORY |
348 ROP3_S);
349 OUTREG((ulong)scr->mmio, DP_WRITE_MASK, ~0ul);
350 OUTREG((ulong)scr->mmio, DP_CNTL, dp_cntl);
351 OUTREG((ulong)scr->mmio, SRC_Y_X, ys << 16 | xs);
352 OUTREG((ulong)scr->mmio, DST_Y_X, yd << 16 | xd);
353 OUTREG((ulong)scr->mmio, DST_WIDTH_HEIGHT, w << 16 | h);
354
355 radeonwaitidle(scr);
356
357 // iprint("radeon: hwscroll(xs=%d ys=%d xd=%d yd=%d w=%d h=%d)\n",
358 // xs, ys, xd, yd, w, h);
359 return 1;
360 }
361
362 static void
radeondrawinit(VGAscr * scr)363 radeondrawinit(VGAscr*scr)
364 {
365 ulong bpp, dtype, i, pitch, clock_cntl_index, mclk_cntl, rbbm_soft_reset;
366
367 if (scr->mmio == 0)
368 return;
369
370 switch (scr->gscreen->depth) {
371 case 6:
372 case 8:
373 dtype = 2;
374 bpp = 1;
375 break;
376 case 15:
377 dtype = 3;
378 bpp = 2;
379 break;
380 case 16:
381 dtype = 4;
382 bpp = 2;
383 break;
384 case 32:
385 dtype = 6;
386 bpp = 4;
387 break;
388 default:
389 return;
390 }
391
392 /* disable 3D */
393 OUTREG((ulong)scr->mmio, RB3D_CNTL, 0);
394
395 /* flush engine */
396 OUTREGP((ulong)scr->mmio, RB2D_DSTCACHE_CTLSTAT,
397 RB2D_DC_FLUSH_ALL, ~RB2D_DC_FLUSH_ALL);
398 for (i = 0; i < 2000000; i++)
399 if (!(INREG((ulong)scr->mmio, RB2D_DSTCACHE_CTLSTAT) &
400 RB2D_DC_BUSY))
401 break;
402
403 /* reset 2D engine */
404 clock_cntl_index = INREG((ulong)scr->mmio, CLOCK_CNTL_INDEX);
405
406 mclk_cntl = INPLL((ulong)scr->mmio, MCLK_CNTL);
407 OUTPLL((ulong)scr->mmio, MCLK_CNTL, mclk_cntl | FORCEON_MCLKA |
408 FORCEON_MCLKB | FORCEON_YCLKA | FORCEON_YCLKB | FORCEON_MC |
409 FORCEON_AIC);
410 rbbm_soft_reset = INREG((ulong)scr->mmio, RBBM_SOFT_RESET);
411
412 OUTREG((ulong)scr->mmio, RBBM_SOFT_RESET, rbbm_soft_reset |
413 SOFT_RESET_CP | SOFT_RESET_HI | SOFT_RESET_SE | SOFT_RESET_RE |
414 SOFT_RESET_PP | SOFT_RESET_E2 | SOFT_RESET_RB);
415 INREG((ulong)scr->mmio, RBBM_SOFT_RESET);
416 OUTREG((ulong)scr->mmio, RBBM_SOFT_RESET, rbbm_soft_reset &
417 ~(SOFT_RESET_CP | SOFT_RESET_HI | SOFT_RESET_SE | SOFT_RESET_RE |
418 SOFT_RESET_PP | SOFT_RESET_E2 | SOFT_RESET_RB));
419 INREG((ulong)scr->mmio, RBBM_SOFT_RESET);
420
421 OUTPLL((ulong)scr->mmio, MCLK_CNTL, mclk_cntl);
422 OUTREG((ulong)scr->mmio, CLOCK_CNTL_INDEX, clock_cntl_index);
423
424 /* init 2D engine */
425 radeonwaitfifo(scr, 1);
426 OUTREG((ulong)scr->mmio, RB2D_DSTCACHE_MODE, 0);
427
428 pitch = Dx(scr->gscreen->r) * bpp;
429 radeonwaitfifo(scr, 4);
430 OUTREG((ulong)scr->mmio, DEFAULT_PITCH, pitch);
431 OUTREG((ulong)scr->mmio, DST_PITCH, pitch);
432 OUTREG((ulong)scr->mmio, SRC_PITCH, pitch);
433 OUTREG((ulong)scr->mmio, DST_PITCH_OFFSET_C, 0);
434
435 radeonwaitfifo(scr, 3);
436 OUTREG((ulong)scr->mmio, DEFAULT_OFFSET, 0);
437 OUTREG((ulong)scr->mmio, DST_OFFSET, 0);
438 OUTREG((ulong)scr->mmio, SRC_OFFSET, 0);
439
440 radeonwaitfifo(scr, 1);
441 OUTREGP((ulong)scr->mmio, DP_DATATYPE, 0, ~HOST_BIG_ENDIAN_EN);
442
443 radeonwaitfifo(scr, 1);
444 OUTREG((ulong)scr->mmio, DEFAULT_SC_BOTTOM_RIGHT,
445 DEFAULT_SC_RIGHT_MAX | DEFAULT_SC_BOTTOM_MAX);
446
447 dp_gui_master_cntl = dtype << GMC_DST_DATATYPE_SHIFT |
448 GMC_SRC_PITCH_OFFSET_CNTL | GMC_DST_PITCH_OFFSET_CNTL |
449 GMC_CLR_CMP_CNTL_DIS;
450 radeonwaitfifo(scr, 1);
451 OUTREG((ulong)scr->mmio, DP_GUI_MASTER_CNTL,
452 dp_gui_master_cntl | GMC_BRUSH_SOLID_COLOR | GMC_SRC_DATATYPE_COLOR);
453
454 radeonwaitfifo(scr, 7);
455 OUTREG((ulong)scr->mmio, DST_LINE_START, 0);
456 OUTREG((ulong)scr->mmio, DST_LINE_END, 0);
457 OUTREG((ulong)scr->mmio, DP_BRUSH_FRGD_CLR, ~0ul);
458 OUTREG((ulong)scr->mmio, DP_BRUSH_BKGD_CLR, 0);
459 OUTREG((ulong)scr->mmio, DP_SRC_FRGD_CLR, ~0ul);
460 OUTREG((ulong)scr->mmio, DP_SRC_BKGD_CLR, 0);
461 OUTREG((ulong)scr->mmio, DP_WRITE_MASK, ~0ul);
462
463 radeonwaitidle(scr);
464
465 scr->fill = radeonfill;
466 scr->scroll = radeonscroll;
467 hwaccel = 1;
468
469 scr->blank = radeonblank;
470 hwblank = 1;
471 }
472
473 /* hw overlay */
474
475 static void
radeonovlctl(VGAscr * scr,Chan * c,void * data,int len)476 radeonovlctl(VGAscr *scr, Chan *c, void *data, int len)
477 {
478 USED(scr, c, data, len);
479 }
480
481 static int
radeonovlwrite(VGAscr * scr,void * data,int len,vlong opt)482 radeonovlwrite(VGAscr *scr, void *data, int len, vlong opt)
483 {
484 USED(scr, data, len, opt);
485 return -1;
486 }
487
488 static void
radeonflush(VGAscr * scr,Rectangle r)489 radeonflush(VGAscr *scr, Rectangle r)
490 {
491 USED(scr, r);
492 }
493
494 /* Export */
495
496 VGAdev vgaradeondev = {
497 "radeon",
498
499 radeonenable,
500 0, /* disable */
501 0, /* page */
502 radeonlinear,
503
504 radeondrawinit,
505 #ifdef HW_ACCEL
506 radeonfill,
507
508 radeonovlctl,
509 radeonovlwrite,
510 radeonflush,
511 #endif
512 };
513 VGAcur vgaradeoncur = {
514 "radeonhwgc",
515 radeoncurenable,
516 radeoncurdisable,
517 radeoncurload,
518 radeoncurmove,
519 0 /* doespanning */
520 };
521