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