xref: /plan9-contrib/sys/src/9/pc/vgaradeon.c (revision cb8c047aa49e908a428eac8b13623e1b242fa11e)
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