xref: /plan9/sys/src/cmd/aux/vga/radeon.c (revision 8cf6001e50e647a07ccf484b8e2f9940411befb9)
1b2495906SDavid du Colombier #include <u.h>
2b2495906SDavid du Colombier #include <libc.h>
3b2495906SDavid du Colombier #include <bio.h>
4b2495906SDavid du Colombier 
5b2495906SDavid du Colombier #include "pci.h"
6b2495906SDavid du Colombier #include "vga.h"
7b2495906SDavid du Colombier #include "radeon.h"
8b2495906SDavid du Colombier 
9b2495906SDavid du Colombier static int	debug = 0;
10b2495906SDavid du Colombier 
11b2495906SDavid du Colombier #define DBGPRINT	if (debug) print
12b2495906SDavid du Colombier 
13b2495906SDavid du Colombier enum {
14b2495906SDavid du Colombier 	Kilo	 = 1024,
15b2495906SDavid du Colombier 	Mega	 = Kilo *Kilo,
16b2495906SDavid du Colombier };
17b2495906SDavid du Colombier 
18b2495906SDavid du Colombier enum {
19b2495906SDavid du Colombier 	DISPLAY_CRT,
20b2495906SDavid du Colombier 	DISPLAY_FP,
21b2495906SDavid du Colombier 	DISPLAY_LCD,
22b2495906SDavid du Colombier };
23b2495906SDavid du Colombier 
24b2495906SDavid du Colombier typedef struct Radeon Radeon;
25b2495906SDavid du Colombier struct Radeon {
26*8cf6001eSDavid du Colombier 	uintptr	mmio;
27b2495906SDavid du Colombier 	Pcidev	*pci;
28b2495906SDavid du Colombier 	uchar	*bios;
29b2495906SDavid du Colombier 
30b2495906SDavid du Colombier 	ulong	fbsize;
31b2495906SDavid du Colombier 	int	display_type;
32b2495906SDavid du Colombier 
33b2495906SDavid du Colombier 	ulong	ovr_clr;
34b2495906SDavid du Colombier 	ulong	ovr_wid_top_bottom;
35b2495906SDavid du Colombier 	ulong	ovr_wid_left_right;
36b2495906SDavid du Colombier 	ulong	ov0_scale_cntl;
37b2495906SDavid du Colombier 	ulong	subpic_cntl;
38b2495906SDavid du Colombier 	ulong	viph_control;
39b2495906SDavid du Colombier 	ulong	i2c_cntl_1;
40b2495906SDavid du Colombier 	ulong	rbbm_soft_reset;
41b2495906SDavid du Colombier 	ulong	cap0_trig_cntl;
42b2495906SDavid du Colombier 	ulong	cap1_trig_cntl;
43b2495906SDavid du Colombier 	ulong	gen_int_cntl;
44b2495906SDavid du Colombier 	ulong	bus_cntl;
45b2495906SDavid du Colombier 
46b2495906SDavid du Colombier 	ulong	crtc_gen_cntl;
47b2495906SDavid du Colombier 	ulong	crtc_ext_cntl;
48b2495906SDavid du Colombier 	ulong	dac_cntl;
49b2495906SDavid du Colombier 
50b2495906SDavid du Colombier 	ulong	crtc_h_total_disp;
51b2495906SDavid du Colombier 	ulong	crtc_h_sync_strt_wid;
52b2495906SDavid du Colombier 	ulong	crtc_v_total_disp;
53b2495906SDavid du Colombier 	ulong	crtc_v_sync_strt_wid;
54b2495906SDavid du Colombier 
55b2495906SDavid du Colombier 	ulong	crtc_pitch;
56b2495906SDavid du Colombier 
57b2495906SDavid du Colombier 	ulong	crtc_offset;
58b2495906SDavid du Colombier 	ulong	crtc_offset_cntl;
59b2495906SDavid du Colombier 
60b2495906SDavid du Colombier 	ulong	htotal_cntl;
61b2495906SDavid du Colombier 
62b2495906SDavid du Colombier 	ulong	surface_cntl;
63b2495906SDavid du Colombier 
64b2495906SDavid du Colombier 	int	r300_workaround;
65b2495906SDavid du Colombier 
66b2495906SDavid du Colombier 	/* inited from rom */
67b2495906SDavid du Colombier 	ushort	reference_freq;
68b2495906SDavid du Colombier 	ushort	reference_div;
69b2495906SDavid du Colombier 	ushort	xclk;
70b2495906SDavid du Colombier 	ulong	max_pll_freq;
71b2495906SDavid du Colombier 	ulong	min_pll_freq;
72b2495906SDavid du Colombier 
73b2495906SDavid du Colombier 	ulong	pll_output_freq;
74b2495906SDavid du Colombier 	ulong	feedback_div;
75b2495906SDavid du Colombier 	ulong	dot_clock_freq;
76b2495906SDavid du Colombier 
77b2495906SDavid du Colombier 	ulong	post_div;
78b2495906SDavid du Colombier 	ulong	ppll_ref_div;
79b2495906SDavid du Colombier 	ulong	ppll_div_3;
80b2495906SDavid du Colombier };
81b2495906SDavid du Colombier 
82b2495906SDavid du Colombier /* from io.c */
83b2495906SDavid du Colombier extern char *readbios(long len, long offset);
84b2495906SDavid du Colombier 
85b2495906SDavid du Colombier static void radeon300_workaround(Radeon*radeon);
86b2495906SDavid du Colombier 
87b2495906SDavid du Colombier static void
OUTREG8(Radeon * radeon,ulong offset,uchar val)88b2495906SDavid du Colombier OUTREG8(Radeon*radeon, ulong offset, uchar val)
89b2495906SDavid du Colombier {
90b2495906SDavid du Colombier 	((uchar *)(radeon->mmio + offset))[0] = val;
91b2495906SDavid du Colombier }
92b2495906SDavid du Colombier 
93b2495906SDavid du Colombier static void
OUTREG(Radeon * radeon,ulong offset,ulong val)94b2495906SDavid du Colombier OUTREG(Radeon*radeon, ulong offset, ulong val)
95b2495906SDavid du Colombier {
96b2495906SDavid du Colombier 	((ulong *)(radeon->mmio + offset))[0] = val;
97b2495906SDavid du Colombier }
98b2495906SDavid du Colombier 
99b2495906SDavid du Colombier static ulong
INREG(Radeon * radeon,ulong offset)100b2495906SDavid du Colombier INREG(Radeon*radeon, ulong offset)
101b2495906SDavid du Colombier {
102b2495906SDavid du Colombier 	return ((ulong *)(radeon->mmio + offset))[0];
103b2495906SDavid du Colombier }
104b2495906SDavid du Colombier 
105b2495906SDavid du Colombier static void
OUTREGP(Radeon * radeon,ulong offset,ulong val,ulong mask)106b2495906SDavid du Colombier OUTREGP(Radeon*radeon, ulong offset, ulong val, ulong mask)
107b2495906SDavid du Colombier {
108b2495906SDavid du Colombier 	OUTREG(radeon, offset, (INREG(radeon, offset) & mask) | val);
109b2495906SDavid du Colombier }
110b2495906SDavid du Colombier 
111b2495906SDavid du Colombier static void
OUTPLL(Radeon * radeon,ulong offset,ulong val)112b2495906SDavid du Colombier OUTPLL(Radeon*radeon, ulong offset, ulong val)
113b2495906SDavid du Colombier {
114b2495906SDavid du Colombier 	OUTREG8(radeon, CLOCK_CNTL_INDEX,
115b2495906SDavid du Colombier 		(offset & 0x3f) | PLL_WR_EN);
116b2495906SDavid du Colombier 	OUTREG(radeon, CLOCK_CNTL_DATA, val);
117b2495906SDavid du Colombier }
118b2495906SDavid du Colombier 
119b2495906SDavid du Colombier static ulong
INPLL(Radeon * radeon,ulong offset)120b2495906SDavid du Colombier INPLL(Radeon*radeon, ulong offset)
121b2495906SDavid du Colombier {
122b2495906SDavid du Colombier 	ulong data;
123b2495906SDavid du Colombier 
124b2495906SDavid du Colombier 	OUTREG8(radeon, CLOCK_CNTL_INDEX, offset & 0x3f);
125b2495906SDavid du Colombier 	data = INREG(radeon, CLOCK_CNTL_DATA);
126b2495906SDavid du Colombier 	if (radeon->r300_workaround)
127b2495906SDavid du Colombier 		radeon300_workaround(radeon);
128b2495906SDavid du Colombier 	return data;
129b2495906SDavid du Colombier }
130b2495906SDavid du Colombier 
131b2495906SDavid du Colombier static void
OUTPLLP(Radeon * radeon,ulong offset,ulong val,ulong mask)132b2495906SDavid du Colombier OUTPLLP(Radeon*radeon, ulong offset, ulong val, ulong mask)
133b2495906SDavid du Colombier {
134b2495906SDavid du Colombier 	OUTPLL(radeon, offset, (INPLL(radeon, offset) & mask) | val);
135b2495906SDavid du Colombier }
136b2495906SDavid du Colombier 
137b2495906SDavid du Colombier static void
radeon300_workaround(Radeon * radeon)138b2495906SDavid du Colombier radeon300_workaround(Radeon*radeon)
139b2495906SDavid du Colombier {
140b2495906SDavid du Colombier 	ulong save, tmp;
141b2495906SDavid du Colombier 
142b2495906SDavid du Colombier 	save = INREG(radeon, CLOCK_CNTL_INDEX);
143b2495906SDavid du Colombier 	tmp = save & ~(0x3f | PLL_WR_EN);
144b2495906SDavid du Colombier 	OUTREG(radeon, CLOCK_CNTL_INDEX, tmp);
145b2495906SDavid du Colombier 	tmp = INREG(radeon, CLOCK_CNTL_DATA);
146b2495906SDavid du Colombier 	OUTREG(radeon, CLOCK_CNTL_INDEX, save);
147b2495906SDavid du Colombier 	USED(tmp);
148b2495906SDavid du Colombier }
149b2495906SDavid du Colombier 
150b2495906SDavid du Colombier static void
radeon_getbiosparams(Radeon * radeon)151b2495906SDavid du Colombier radeon_getbiosparams(Radeon*radeon)
152b2495906SDavid du Colombier {
153b2495906SDavid du Colombier 	ulong addr;
154b2495906SDavid du Colombier 	ushort offset, pib;
155b2495906SDavid du Colombier 	uchar *bios;
156b2495906SDavid du Colombier 
157b2495906SDavid du Colombier 	radeon->bios = nil;
158b2495906SDavid du Colombier 	addr = 0xC0000;
159b2495906SDavid du Colombier 	bios = (uchar *)readbios(0x10000, addr);
160b2495906SDavid du Colombier 	if (bios[0] != 0x55 || bios[1] != 0xAA) {
161b2495906SDavid du Colombier 		addr = 0xE0000;
162b2495906SDavid du Colombier 		bios = (uchar *)readbios(0x10000, addr);
163b2495906SDavid du Colombier 		if (bios[0] != 0x55 || bios[1] != 0xAA) {
164b2495906SDavid du Colombier 			print("radeon: bios not found\n");
165b2495906SDavid du Colombier 			return;
166b2495906SDavid du Colombier 		}
167b2495906SDavid du Colombier 	}
168b2495906SDavid du Colombier 
169b2495906SDavid du Colombier 	radeon->bios = bios;
170b2495906SDavid du Colombier 	offset = BIOS16(radeon, BIOS_START);
171b2495906SDavid du Colombier 
172b2495906SDavid du Colombier 	pib = BIOS16(radeon, offset + 0x30);
173b2495906SDavid du Colombier 
174b2495906SDavid du Colombier 	radeon->reference_freq	 = BIOS16(radeon, pib + 0x0e);
175b2495906SDavid du Colombier 	radeon->reference_div	 = BIOS16(radeon, pib + 0x10);
176b2495906SDavid du Colombier 	radeon->min_pll_freq	 = BIOS32(radeon, pib + 0x12);
177b2495906SDavid du Colombier 	radeon->max_pll_freq	 = BIOS32(radeon, pib + 0x16);
178b2495906SDavid du Colombier 	radeon->xclk		 = BIOS16(radeon, pib + 0x08);
179b2495906SDavid du Colombier 
180b2495906SDavid du Colombier 	DBGPRINT("radeon: bios=0x%08ulx offset=0x%ux\n", addr, offset);
181b2495906SDavid du Colombier 	DBGPRINT("radeon: pll_info_block: 0x%ux\n", pib);
182b2495906SDavid du Colombier 	DBGPRINT("radeon: reference_freq: %ud\n", radeon->reference_freq);
183b2495906SDavid du Colombier 	DBGPRINT("radeon: reference_div:  %ud\n", radeon->reference_div);
184b2495906SDavid du Colombier 	DBGPRINT("radeon: min_pll_freq:   %uld\n", radeon->min_pll_freq);
185b2495906SDavid du Colombier 	DBGPRINT("radeon: max_pll_freq:   %uld\n", radeon->max_pll_freq);
186b2495906SDavid du Colombier 	DBGPRINT("radeon: xclk:           %ud\n", radeon->xclk);
187b2495906SDavid du Colombier }
188b2495906SDavid du Colombier 
189b2495906SDavid du Colombier static Pcidev *
radeonpci(int * isr300)190b2495906SDavid du Colombier radeonpci(int *isr300)
191b2495906SDavid du Colombier {
192b2495906SDavid du Colombier 	static Pcidev * p = nil;
193b2495906SDavid du Colombier 	struct pciids *ids;
194b2495906SDavid du Colombier 
195b2495906SDavid du Colombier 	DBGPRINT("radeon: ATI Technologies Inc. Radeon [789]xxx drivers (v0.1)\n");
196b2495906SDavid du Colombier 	while ((p = pcimatch(p, ATI_PCIVID, 0)) != nil)
197b2495906SDavid du Colombier 		for (ids = radeon_pciids; ids->did; ids++)
198b2495906SDavid du Colombier 			if (ids->did == p->did) {
199b2495906SDavid du Colombier 				DBGPRINT("radeon: Found %s\n", ids->name);
200b2495906SDavid du Colombier 				DBGPRINT("radeon: did:%04ux rid:%02ux\n",
201b2495906SDavid du Colombier 					p->did, p->rid);
202b2495906SDavid du Colombier 				if (isr300)
203b2495906SDavid du Colombier 					*isr300 = ids->type == ATI_R300;
204b2495906SDavid du Colombier 				return p;
205b2495906SDavid du Colombier 			}
206b2495906SDavid du Colombier 	DBGPRINT("radeon: not found!\n");
207b2495906SDavid du Colombier 	return nil;
208b2495906SDavid du Colombier }
209b2495906SDavid du Colombier 
210b2495906SDavid du Colombier static void
vga_disable(Vga * vga)211b2495906SDavid du Colombier vga_disable(Vga*vga)
212b2495906SDavid du Colombier {
213b2495906SDavid du Colombier 	Ctlr *c;
214b2495906SDavid du Colombier 
215b2495906SDavid du Colombier 	for (c = vga->link; c; c = c->link)
216b2495906SDavid du Colombier 		if (strncmp(c->name, "vga", 3) == 0)
217b2495906SDavid du Colombier 			c->load = nil;
218b2495906SDavid du Colombier }
219b2495906SDavid du Colombier 
220b2495906SDavid du Colombier static void
snarf(Vga * vga,Ctlr * ctlr)221b2495906SDavid du Colombier snarf(Vga *vga, Ctlr *ctlr)
222b2495906SDavid du Colombier {
223b2495906SDavid du Colombier 	int isr300;
224b2495906SDavid du Colombier 	ulong tmp;
225b2495906SDavid du Colombier 	uintptr mmio;
226b2495906SDavid du Colombier 	Pcidev *p;
227b2495906SDavid du Colombier 	Radeon *radeon;
228b2495906SDavid du Colombier 
229b2495906SDavid du Colombier 	if (vga->private == nil) {
230b2495906SDavid du Colombier 		vga_disable(vga);
231b2495906SDavid du Colombier 
232b2495906SDavid du Colombier 		vga->private = alloc(sizeof(Radeon));
233b2495906SDavid du Colombier 		radeon = vga->private;
234b2495906SDavid du Colombier 
235b2495906SDavid du Colombier 		p = radeonpci(&isr300);
236b2495906SDavid du Colombier 		if (p == nil)
237b2495906SDavid du Colombier 			error("%s: not found\n", ctlr->name);
238b2495906SDavid du Colombier 
239b2495906SDavid du Colombier 		vgactlw("type", ctlr->name);
240b2495906SDavid du Colombier 
241b2495906SDavid du Colombier 		mmio = (uintptr)segattach(0, "radeonmmio", (void *)0,
242b2495906SDavid du Colombier 			p->mem[2].size);
243b2495906SDavid du Colombier 		if (mmio == ~0)
244b2495906SDavid du Colombier 			error("%s: can't attach mmio segment\n", ctlr->name);
245b2495906SDavid du Colombier 
246*8cf6001eSDavid du Colombier 		DBGPRINT("radeon: mmio address: %08#p [size=%#x]\n",
247*8cf6001eSDavid du Colombier 			(void *)mmio, p->mem[2].size);
248b2495906SDavid du Colombier 
249b2495906SDavid du Colombier 		radeon->pci = p;
250b2495906SDavid du Colombier 		radeon->r300_workaround = isr300;
251b2495906SDavid du Colombier 		radeon->mmio = mmio;
252b2495906SDavid du Colombier 	}
253b2495906SDavid du Colombier 
254b2495906SDavid du Colombier 	radeon = vga->private;
255b2495906SDavid du Colombier 	radeon->fbsize = INREG(radeon, CONFIG_MEMSIZE);
256b2495906SDavid du Colombier 	vga->vmz = radeon->fbsize;
257b2495906SDavid du Colombier 	DBGPRINT("radeon: frame buffer size=%uld [%uldMB]\n",
258b2495906SDavid du Colombier 		radeon->fbsize, radeon->fbsize / Mega);
259b2495906SDavid du Colombier 
260b2495906SDavid du Colombier 	tmp = INREG(radeon, FP_GEN_CNTL);
261b2495906SDavid du Colombier 	if (tmp & FP_EN_TMDS)
262b2495906SDavid du Colombier 		radeon->display_type = DISPLAY_FP;
263b2495906SDavid du Colombier 	else
264b2495906SDavid du Colombier 		radeon->display_type = DISPLAY_CRT;
265b2495906SDavid du Colombier 
266b2495906SDavid du Colombier 	DBGPRINT("radeon: display type: %s\n",
267b2495906SDavid du Colombier 		radeon->display_type == DISPLAY_CRT? "CRT": "FLAT PANEL");
268b2495906SDavid du Colombier 
269b2495906SDavid du Colombier 	if (radeon->display_type != DISPLAY_CRT)
270b2495906SDavid du Colombier 		error("unsupported NON CRT Display\n");
271b2495906SDavid du Colombier 
272b2495906SDavid du Colombier 	radeon_getbiosparams(radeon);
273b2495906SDavid du Colombier 	radeon->bus_cntl = INREG(radeon, BUS_CNTL);
274b2495906SDavid du Colombier 	DBGPRINT("radeon: PPLL_CNTL=0x%08ulx\n", INPLL(radeon, PPLL_CNTL));
275b2495906SDavid du Colombier 	ctlr->flag |= Fsnarf;
276b2495906SDavid du Colombier }
277b2495906SDavid du Colombier 
278b2495906SDavid du Colombier static void
options(Vga *,Ctlr * ctlr)279b2495906SDavid du Colombier options(Vga*, Ctlr*ctlr)
280b2495906SDavid du Colombier {
281b2495906SDavid du Colombier 	ctlr->flag |= Hlinear | Foptions;
282b2495906SDavid du Colombier }
283b2495906SDavid du Colombier 
284b2495906SDavid du Colombier static int
radeondiv(int n,int d)285b2495906SDavid du Colombier radeondiv(int n, int d)
286b2495906SDavid du Colombier {
287b2495906SDavid du Colombier 	return (n + d/2) / d;
288b2495906SDavid du Colombier }
289b2495906SDavid du Colombier 
290b2495906SDavid du Colombier static void
radeon_init_common_registers(Radeon * radeon)291b2495906SDavid du Colombier radeon_init_common_registers(Radeon*radeon)
292b2495906SDavid du Colombier {
293b2495906SDavid du Colombier 	radeon->ovr_clr		= 0;
294b2495906SDavid du Colombier 	radeon->ovr_wid_left_right	= 0;
295b2495906SDavid du Colombier 	radeon->ovr_wid_top_bottom	= 0;
296b2495906SDavid du Colombier 	radeon->ov0_scale_cntl	= 0;
297b2495906SDavid du Colombier 	radeon->subpic_cntl	= 0;
298b2495906SDavid du Colombier 	radeon->viph_control	= 0;
299b2495906SDavid du Colombier 	radeon->i2c_cntl_1	= 0;
300b2495906SDavid du Colombier 	radeon->rbbm_soft_reset	= 0;
301b2495906SDavid du Colombier 	radeon->cap0_trig_cntl	= 0;
302b2495906SDavid du Colombier 	radeon->cap1_trig_cntl	= 0;
303b2495906SDavid du Colombier 	if (radeon->bus_cntl & BUS_READ_BURST)
304b2495906SDavid du Colombier 		radeon->bus_cntl |= BUS_RD_DISCARD_EN;
305b2495906SDavid du Colombier }
306b2495906SDavid du Colombier 
307b2495906SDavid du Colombier static void
radeon_init_crtc_registers(Radeon * radeon,Mode * mode)308b2495906SDavid du Colombier radeon_init_crtc_registers(Radeon*radeon, Mode*mode)
309b2495906SDavid du Colombier {
310b2495906SDavid du Colombier 	int format, dac6bit, hsync_wid, vsync_wid, hsync_start, hsync_fudge;
311b2495906SDavid du Colombier 	int bpp;
312b2495906SDavid du Colombier 	static int hsync_fudge_default[] = {
313b2495906SDavid du Colombier 		0x00, 0x12, 0x09, 0x09, 0x06, 0x05,
314b2495906SDavid du Colombier 	};
315b2495906SDavid du Colombier 
316b2495906SDavid du Colombier 	format = 0;
317b2495906SDavid du Colombier 	bpp = 0;
318b2495906SDavid du Colombier 	dac6bit = 0;
319b2495906SDavid du Colombier 	switch (mode->z) {
320b2495906SDavid du Colombier 	case 6:
321b2495906SDavid du Colombier 		format = 2;
322b2495906SDavid du Colombier 		dac6bit = 1;
323b2495906SDavid du Colombier 		bpp =  8;
324b2495906SDavid du Colombier 		break;
325b2495906SDavid du Colombier 	case 8:
326b2495906SDavid du Colombier 		format = 2;
327b2495906SDavid du Colombier 		dac6bit = 0;
328b2495906SDavid du Colombier 		bpp =  8;
329b2495906SDavid du Colombier 		break;
330b2495906SDavid du Colombier 	case 15:
331b2495906SDavid du Colombier 		format = 3;
332b2495906SDavid du Colombier 		dac6bit = 0;
333b2495906SDavid du Colombier 		bpp = 16;
334b2495906SDavid du Colombier 		break;
335b2495906SDavid du Colombier 	case 16:
336b2495906SDavid du Colombier 		format = 4;
337b2495906SDavid du Colombier 		dac6bit = 0;
338b2495906SDavid du Colombier 		bpp = 16;
339b2495906SDavid du Colombier 		break;
340b2495906SDavid du Colombier 	case 24:
341b2495906SDavid du Colombier 		format = 5;
342b2495906SDavid du Colombier 		dac6bit = 0;
343b2495906SDavid du Colombier 		bpp = 24;
344b2495906SDavid du Colombier 		break;
345b2495906SDavid du Colombier 	case 32:
346b2495906SDavid du Colombier 		format = 6;
347b2495906SDavid du Colombier 		dac6bit = 0;
348b2495906SDavid du Colombier 		bpp = 32;
349b2495906SDavid du Colombier 		break;
350b2495906SDavid du Colombier 	default:
351b2495906SDavid du Colombier 		error("radeon: unsupported mode depth %d\n", mode->z);
352b2495906SDavid du Colombier 	}
353b2495906SDavid du Colombier 	hsync_fudge = hsync_fudge_default[format-1];
354b2495906SDavid du Colombier 
355b2495906SDavid du Colombier 	DBGPRINT("mode->z = %d (format = %d, bpp = %d, dac6bit = %s)\n",
356b2495906SDavid du Colombier 		mode->z, format, bpp, dac6bit? "true": "false");
357b2495906SDavid du Colombier 
358b2495906SDavid du Colombier 	radeon->crtc_gen_cntl = CRTC_EN | CRTC_EXT_DISP_EN | format << 8 |
359b2495906SDavid du Colombier 		(mode->interlace? CRTC_INTERLACE_EN: 0);
360b2495906SDavid du Colombier 	radeon->crtc_ext_cntl = VGA_ATI_LINEAR | XCRT_CNT_EN | CRTC_CRT_ON;
361b2495906SDavid du Colombier 	radeon->dac_cntl = DAC_MASK_ALL | DAC_VGA_ADR_EN |
362b2495906SDavid du Colombier 		(dac6bit? 0: DAC_8BIT_EN);
363b2495906SDavid du Colombier 
364b2495906SDavid du Colombier 	radeon->crtc_h_total_disp = ((mode->ht/8 - 1) & 0x3ff) |
365b2495906SDavid du Colombier 		((mode->x/8 - 1) & 0x1ff) << 16;
366b2495906SDavid du Colombier 
367b2495906SDavid du Colombier 	hsync_wid = (mode->ehb - mode->shb) / 8;
368b2495906SDavid du Colombier 	if (hsync_wid == 0)
369b2495906SDavid du Colombier 		hsync_wid = 1;
370b2495906SDavid du Colombier 
371b2495906SDavid du Colombier 	hsync_start = mode->shb - 8 + hsync_fudge;
372b2495906SDavid du Colombier 
373b2495906SDavid du Colombier 	DBGPRINT("hsync_start=%d hsync_wid=%d hsync_fudge=%d\n",
374b2495906SDavid du Colombier 		hsync_start, hsync_wid, hsync_fudge);
375b2495906SDavid du Colombier 
376b2495906SDavid du Colombier 	radeon->crtc_h_sync_strt_wid = ((hsync_start & 0x1fff) |
377b2495906SDavid du Colombier 		(hsync_wid & 0x3f) << 16 | (mode->hsync? CRTC_H_SYNC_POL: 0));
378b2495906SDavid du Colombier 	radeon->crtc_v_total_disp = ((mode->vt - 1) & 0xffff) |
379b2495906SDavid du Colombier 		(mode->y - 1) << 16;
380b2495906SDavid du Colombier 
381b2495906SDavid du Colombier 	vsync_wid = mode->vre - mode->vrs;
382b2495906SDavid du Colombier 	if (!vsync_wid)
383b2495906SDavid du Colombier 		vsync_wid = 1;
384b2495906SDavid du Colombier 
385b2495906SDavid du Colombier 	radeon->crtc_v_sync_strt_wid = (((mode->vrs - 1) & 0xfff) |
386b2495906SDavid du Colombier 		(vsync_wid & 0x1f) << 16 | (mode->vsync? CRTC_V_SYNC_POL: 0));
387b2495906SDavid du Colombier 	radeon->crtc_offset = 0;
388b2495906SDavid du Colombier 	radeon->crtc_offset_cntl = INREG(radeon, CRTC_OFFSET_CNTL);
389b2495906SDavid du Colombier 	radeon->crtc_pitch = (mode->x * bpp + bpp * 8 - 1) / (bpp * 8);
390b2495906SDavid du Colombier 	radeon->crtc_pitch |= radeon->crtc_pitch << 16;
391b2495906SDavid du Colombier }
392b2495906SDavid du Colombier 
393b2495906SDavid du Colombier struct divider {
394b2495906SDavid du Colombier 	int	divider;
395b2495906SDavid du Colombier 	int	bitvalue;
396b2495906SDavid du Colombier };
397b2495906SDavid du Colombier 
398b2495906SDavid du Colombier static void
radeon_init_pll_registers(Radeon * radeon,ulong freq)399b2495906SDavid du Colombier radeon_init_pll_registers(Radeon*radeon, ulong freq)
400b2495906SDavid du Colombier {
401b2495906SDavid du Colombier 	struct divider *post_div;
402b2495906SDavid du Colombier 	static struct divider post_divs[] = {
403b2495906SDavid du Colombier 		{ 1, 0 },
404b2495906SDavid du Colombier 		{ 2, 1 },
405b2495906SDavid du Colombier 		{ 4, 2 },
406b2495906SDavid du Colombier 		{ 8, 3 },
407b2495906SDavid du Colombier 		{ 3, 4 },
408b2495906SDavid du Colombier 		{ 16, 5 },
409b2495906SDavid du Colombier 		{ 6, 6 },
410b2495906SDavid du Colombier 		{ 12, 7 },
411b2495906SDavid du Colombier 		{ 0, 0 }
412b2495906SDavid du Colombier 	};
413b2495906SDavid du Colombier 
414b2495906SDavid du Colombier 	DBGPRINT("radeon: initpll: freq=%uld\n", freq);
415b2495906SDavid du Colombier 
416b2495906SDavid du Colombier 	if (freq > radeon->max_pll_freq)
417b2495906SDavid du Colombier 		freq = radeon->max_pll_freq;
418b2495906SDavid du Colombier 	if (freq * 12 < radeon->min_pll_freq)
419b2495906SDavid du Colombier 		freq = radeon->min_pll_freq / 12;
420b2495906SDavid du Colombier 
421b2495906SDavid du Colombier 	for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
422b2495906SDavid du Colombier 		radeon->pll_output_freq = post_div->divider * freq;
423b2495906SDavid du Colombier 		if (radeon->pll_output_freq >= radeon->min_pll_freq &&
424b2495906SDavid du Colombier 		    radeon->pll_output_freq <= radeon->max_pll_freq)
425b2495906SDavid du Colombier 			break;
426b2495906SDavid du Colombier 	}
427b2495906SDavid du Colombier 
428b2495906SDavid du Colombier 	radeon->dot_clock_freq = freq;
429b2495906SDavid du Colombier 	radeon->feedback_div = radeondiv(radeon->reference_div *
430b2495906SDavid du Colombier 		radeon->pll_output_freq, radeon->reference_freq);
431b2495906SDavid du Colombier 	radeon->post_div = post_div->divider;
432b2495906SDavid du Colombier 
433b2495906SDavid du Colombier 	DBGPRINT("dc=%uld, of=%uld, fd=%uld, pd=%uld\n", radeon->dot_clock_freq,
434b2495906SDavid du Colombier 		radeon->pll_output_freq, radeon->feedback_div, radeon->post_div);
435b2495906SDavid du Colombier 
436b2495906SDavid du Colombier 	radeon->ppll_ref_div = radeon->reference_div;
437b2495906SDavid du Colombier 	radeon->ppll_div_3 = radeon->feedback_div | post_div->bitvalue << 16;
438b2495906SDavid du Colombier 	radeon->htotal_cntl = 0;
439b2495906SDavid du Colombier 	radeon->surface_cntl = 0;
440b2495906SDavid du Colombier }
441b2495906SDavid du Colombier 
442b2495906SDavid du Colombier static void
init(Vga * vga,Ctlr * ctlr)443b2495906SDavid du Colombier init(Vga*vga, Ctlr*ctlr)
444b2495906SDavid du Colombier {
445b2495906SDavid du Colombier 	Radeon *radeon;
446b2495906SDavid du Colombier 	Mode *mode;
447b2495906SDavid du Colombier 
448b2495906SDavid du Colombier 	radeon	 = vga->private;
449b2495906SDavid du Colombier 	mode	 = vga->mode;
450b2495906SDavid du Colombier 
451b2495906SDavid du Colombier 	DBGPRINT("radeon: monitor type = '%s'\n", mode->type);
452b2495906SDavid du Colombier 	DBGPRINT("radeon: size = '%s'\n", mode->size);
453b2495906SDavid du Colombier 	DBGPRINT("radeon: chan = '%s'\n", mode->chan);
454b2495906SDavid du Colombier 	DBGPRINT("radeon: freq=%d deffreq=%d x=%d y=%d z=%d\n",
455b2495906SDavid du Colombier 		mode->frequency, mode->deffrequency, mode->x, mode->y, mode->z);
456b2495906SDavid du Colombier 	DBGPRINT("radeon: ht=%d shb=%d ehb=%d shs=%d ehs=%d hsync='%c'\n",
457b2495906SDavid du Colombier 		mode->ht, mode->shb, mode->ehb, mode->shs, mode->ehs,
458b2495906SDavid du Colombier 		mode->hsync? mode->hsync: ' ');
459b2495906SDavid du Colombier 	DBGPRINT("radeon: vt=%d vrs=%d vre=%d vsync='%c'\n",
460b2495906SDavid du Colombier 		mode->vt, mode->vrs, mode->vre, mode->vsync? mode->vsync: ' ');
461b2495906SDavid du Colombier 
462b2495906SDavid du Colombier 	radeon_init_common_registers(radeon);
463b2495906SDavid du Colombier 	radeon_init_crtc_registers(radeon, mode);
464b2495906SDavid du Colombier 	radeon_init_pll_registers(radeon, mode->frequency / 10000);
465b2495906SDavid du Colombier 	ctlr->flag |= Finit | Ulinear;
466b2495906SDavid du Colombier }
467b2495906SDavid du Colombier 
468b2495906SDavid du Colombier static void
radeon_blank(Radeon * radeon)469b2495906SDavid du Colombier radeon_blank(Radeon*radeon)
470b2495906SDavid du Colombier {
471b2495906SDavid du Colombier 	OUTREGP(radeon, CRTC_EXT_CNTL,
472b2495906SDavid du Colombier 		  CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS | CRTC_HSYNC_DIS,
473b2495906SDavid du Colombier 		~(CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS | CRTC_HSYNC_DIS));
474b2495906SDavid du Colombier }
475b2495906SDavid du Colombier 
476b2495906SDavid du Colombier static void
radeon_unblank(Radeon * radeon)477b2495906SDavid du Colombier radeon_unblank(Radeon*radeon)
478b2495906SDavid du Colombier {
479b2495906SDavid du Colombier 	OUTREGP(radeon, CRTC_EXT_CNTL, CRTC_CRT_ON,
480b2495906SDavid du Colombier 		~(CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS | CRTC_HSYNC_DIS));
481b2495906SDavid du Colombier }
482b2495906SDavid du Colombier 
483b2495906SDavid du Colombier static void
radeon_load_common_registers(Radeon * radeon)484b2495906SDavid du Colombier radeon_load_common_registers(Radeon*radeon)
485b2495906SDavid du Colombier {
486b2495906SDavid du Colombier 	OUTREG(radeon, OVR_CLR, 		radeon->ovr_clr);
487b2495906SDavid du Colombier 	OUTREG(radeon, OVR_WID_LEFT_RIGHT, 	radeon->ovr_wid_left_right);
488b2495906SDavid du Colombier 	OUTREG(radeon, OVR_WID_TOP_BOTTOM, 	radeon->ovr_wid_top_bottom);
489b2495906SDavid du Colombier 	OUTREG(radeon, OV0_SCALE_CNTL, 		radeon->ov0_scale_cntl);
490b2495906SDavid du Colombier 	OUTREG(radeon, SUBPIC_CNTL, 		radeon->subpic_cntl);
491b2495906SDavid du Colombier 	OUTREG(radeon, VIPH_CONTROL, 		radeon->viph_control);
492b2495906SDavid du Colombier 	OUTREG(radeon, I2C_CNTL_1, 		radeon->i2c_cntl_1);
493b2495906SDavid du Colombier 	OUTREG(radeon, GEN_INT_CNTL, 		radeon->gen_int_cntl);
494b2495906SDavid du Colombier 	OUTREG(radeon, CAP0_TRIG_CNTL, 		radeon->cap0_trig_cntl);
495b2495906SDavid du Colombier 	OUTREG(radeon, CAP1_TRIG_CNTL, 		radeon->cap1_trig_cntl);
496b2495906SDavid du Colombier 	OUTREG(radeon, BUS_CNTL, 		radeon->bus_cntl);
497b2495906SDavid du Colombier 	OUTREG(radeon, SURFACE_CNTL, 		radeon->surface_cntl);
498b2495906SDavid du Colombier }
499b2495906SDavid du Colombier 
500b2495906SDavid du Colombier static void
radeon_load_crtc_registers(Radeon * radeon)501b2495906SDavid du Colombier radeon_load_crtc_registers(Radeon*radeon)
502b2495906SDavid du Colombier {
503b2495906SDavid du Colombier 	OUTREG(radeon, CRTC_GEN_CNTL, radeon->crtc_gen_cntl);
504b2495906SDavid du Colombier 	OUTREGP(radeon, CRTC_EXT_CNTL, radeon->crtc_ext_cntl,
505b2495906SDavid du Colombier 		CRTC_VSYNC_DIS | CRTC_HSYNC_DIS | CRTC_DISPLAY_DIS);
506b2495906SDavid du Colombier 	OUTREGP(radeon, DAC_CNTL, radeon->dac_cntl,
507b2495906SDavid du Colombier 		DAC_RANGE_CNTL | DAC_BLANKING);
508b2495906SDavid du Colombier 	OUTREG(radeon, CRTC_H_TOTAL_DISP, 	radeon->crtc_h_total_disp);
509b2495906SDavid du Colombier 	OUTREG(radeon, CRTC_H_SYNC_STRT_WID, 	radeon->crtc_h_sync_strt_wid);
510b2495906SDavid du Colombier 	OUTREG(radeon, CRTC_V_TOTAL_DISP, 	radeon->crtc_v_total_disp);
511b2495906SDavid du Colombier 	OUTREG(radeon, CRTC_V_SYNC_STRT_WID, 	radeon->crtc_v_sync_strt_wid);
512b2495906SDavid du Colombier 	OUTREG(radeon, CRTC_OFFSET, 		radeon->crtc_offset);
513b2495906SDavid du Colombier 	OUTREG(radeon, CRTC_OFFSET_CNTL, 	radeon->crtc_offset_cntl);
514b2495906SDavid du Colombier 	OUTREG(radeon, CRTC_PITCH, 		radeon->crtc_pitch);
515b2495906SDavid du Colombier }
516b2495906SDavid du Colombier 
517b2495906SDavid du Colombier static void
radeon_pllwaitupd(Radeon * radeon)518b2495906SDavid du Colombier radeon_pllwaitupd(Radeon*radeon)
519b2495906SDavid du Colombier {
520b2495906SDavid du Colombier 	int i;
521b2495906SDavid du Colombier 
522b2495906SDavid du Colombier 	for (i = 0; i < 10000; i++)
523b2495906SDavid du Colombier 		if (!(INPLL(radeon, PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R))
524b2495906SDavid du Colombier 			break;
525b2495906SDavid du Colombier }
526b2495906SDavid du Colombier 
527b2495906SDavid du Colombier static void
radeon_pllwriteupd(Radeon * radeon)528b2495906SDavid du Colombier radeon_pllwriteupd(Radeon*radeon)
529b2495906SDavid du Colombier {
530b2495906SDavid du Colombier 	while (INPLL(radeon, PPLL_REF_DIV) & PPLL_ATOMIC_UPDATE_R)
531b2495906SDavid du Colombier 		;
532b2495906SDavid du Colombier 	OUTPLLP(radeon, PPLL_REF_DIV, PPLL_ATOMIC_UPDATE_W, ~PPLL_ATOMIC_UPDATE_W);
533b2495906SDavid du Colombier }
534b2495906SDavid du Colombier 
535b2495906SDavid du Colombier static void
radeon_load_pll_registers(Radeon * radeon)536b2495906SDavid du Colombier radeon_load_pll_registers(Radeon*radeon)
537b2495906SDavid du Colombier {
538b2495906SDavid du Colombier 	OUTPLLP(radeon, VCLK_ECP_CNTL,
539b2495906SDavid du Colombier 		VCLK_SRC_SEL_CPUCLK, ~VCLK_SRC_SEL_MASK);
540b2495906SDavid du Colombier 	OUTPLLP(radeon, PPLL_CNTL,
541b2495906SDavid du Colombier 	      PPLL_RESET | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN,
542b2495906SDavid du Colombier 	    ~(PPLL_RESET | PPLL_ATOMIC_UPDATE_EN | PPLL_VGA_ATOMIC_UPDATE_EN));
543b2495906SDavid du Colombier 	OUTREGP(radeon, CLOCK_CNTL_INDEX, PLL_DIV_SEL, ~PLL_DIV_SEL);
544b2495906SDavid du Colombier 	if (radeon->r300_workaround) {
545b2495906SDavid du Colombier 		DBGPRINT("r300_workaround\n");
546b2495906SDavid du Colombier 		if (radeon->ppll_ref_div & R300_PPLL_REF_DIV_ACC_MASK)
547b2495906SDavid du Colombier 			/*
548b2495906SDavid du Colombier 			 * When restoring console mode, use saved PPLL_REF_DIV
549b2495906SDavid du Colombier 			 * setting.
550b2495906SDavid du Colombier 			 */
551b2495906SDavid du Colombier 			OUTPLLP(radeon, PPLL_REF_DIV, radeon->ppll_ref_div, 0);
552b2495906SDavid du Colombier 		else
553b2495906SDavid du Colombier 			/* R300 uses ref_div_acc field as real ref divider */
554b2495906SDavid du Colombier 			OUTPLLP(radeon, PPLL_REF_DIV,
555b2495906SDavid du Colombier 			    radeon->ppll_ref_div << R300_PPLL_REF_DIV_ACC_SHIFT,
556b2495906SDavid du Colombier 				~R300_PPLL_REF_DIV_ACC_MASK);
557b2495906SDavid du Colombier 	} else
558b2495906SDavid du Colombier 		OUTPLLP(radeon, PPLL_REF_DIV, radeon->ppll_ref_div,
559b2495906SDavid du Colombier 			~PPLL_REF_DIV_MASK);
560b2495906SDavid du Colombier 
561b2495906SDavid du Colombier 	OUTPLLP(radeon, PPLL_DIV_3, radeon->ppll_div_3, ~PPLL_FB3_DIV_MASK);
562b2495906SDavid du Colombier 	OUTPLLP(radeon, PPLL_DIV_3, radeon->ppll_div_3, ~PPLL_POST3_DIV_MASK);
563b2495906SDavid du Colombier 
564b2495906SDavid du Colombier 	radeon_pllwriteupd(radeon);
565b2495906SDavid du Colombier 	radeon_pllwaitupd(radeon);
566b2495906SDavid du Colombier 
567b2495906SDavid du Colombier 	OUTPLL(radeon, HTOTAL_CNTL, radeon->htotal_cntl);
568b2495906SDavid du Colombier 	OUTPLLP(radeon, PPLL_CNTL, 0,
569b2495906SDavid du Colombier 		~(PPLL_RESET | PPLL_SLEEP | PPLL_ATOMIC_UPDATE_EN |
570b2495906SDavid du Colombier 		  PPLL_VGA_ATOMIC_UPDATE_EN));
571b2495906SDavid du Colombier 
572b2495906SDavid du Colombier 	if (debug) {
573b2495906SDavid du Colombier 		Bprint(&stdout, "Wrote: 0x%08ulx 0x%08ulx 0x%08ulx (0x%08ulx)\n",
574b2495906SDavid du Colombier 			radeon->ppll_ref_div, radeon->ppll_div_3,
575b2495906SDavid du Colombier 			radeon->htotal_cntl, INPLL(radeon, PPLL_CNTL));
576b2495906SDavid du Colombier 		Bprint(&stdout, "Wrote: rd=%uld, fd=%uld, pd=%uld\n",
577b2495906SDavid du Colombier 			radeon->ppll_ref_div & PPLL_REF_DIV_MASK,
578b2495906SDavid du Colombier 			radeon->ppll_div_3 & PPLL_FB3_DIV_MASK,
579b2495906SDavid du Colombier 			(radeon->ppll_div_3 & PPLL_POST3_DIV_MASK) >> 16);
580b2495906SDavid du Colombier 	}
581b2495906SDavid du Colombier 
582b2495906SDavid du Colombier 	/* Let the clock to lock */
583b2495906SDavid du Colombier 	sleep(5);
584b2495906SDavid du Colombier 
585b2495906SDavid du Colombier 	OUTPLLP(radeon, VCLK_ECP_CNTL, VCLK_SRC_SEL_PPLLCLK, ~VCLK_SRC_SEL_MASK);
586b2495906SDavid du Colombier }
587b2495906SDavid du Colombier 
588b2495906SDavid du Colombier static void
load(Vga * vga,Ctlr * ctlr)589b2495906SDavid du Colombier load(Vga*vga, Ctlr*ctlr)
590b2495906SDavid du Colombier {
591b2495906SDavid du Colombier 	Radeon *radeon;
592b2495906SDavid du Colombier 
593b2495906SDavid du Colombier 	radeon = (Radeon *) vga->private;
594b2495906SDavid du Colombier 	radeon_blank(radeon);
595b2495906SDavid du Colombier 	radeon_load_common_registers(radeon);
596b2495906SDavid du Colombier 	radeon_load_crtc_registers(radeon);
597b2495906SDavid du Colombier 	radeon_load_pll_registers(radeon);
598b2495906SDavid du Colombier 	radeon_unblank(radeon);
599b2495906SDavid du Colombier 
600b2495906SDavid du Colombier 	/* init palette [gamma] */
601b2495906SDavid du Colombier 	if (vga->mode->z > 8) {
602b2495906SDavid du Colombier 		int i;
603b2495906SDavid du Colombier 
604b2495906SDavid du Colombier 		OUTREG(radeon, PALETTE_INDEX, 0);
605b2495906SDavid du Colombier 		for (i = 0; i < 256; i++)
606b2495906SDavid du Colombier 			OUTREG(radeon, PALETTE_DATA, i << 16 | i << 8 | i);
607b2495906SDavid du Colombier 	}
608b2495906SDavid du Colombier 
609b2495906SDavid du Colombier 	ctlr->flag |= Fload;
610b2495906SDavid du Colombier }
611b2495906SDavid du Colombier 
612b2495906SDavid du Colombier static void
dump(Vga * vga,Ctlr * ctlr)613b2495906SDavid du Colombier dump(Vga*vga, Ctlr*ctlr)
614b2495906SDavid du Colombier {
615b2495906SDavid du Colombier 	Radeon *radeon;
616b2495906SDavid du Colombier 
617b2495906SDavid du Colombier 	USED(ctlr);
618b2495906SDavid du Colombier 	radeon = (Radeon *)vga->private;
619b2495906SDavid du Colombier 	USED(radeon);
620b2495906SDavid du Colombier }
621b2495906SDavid du Colombier 
622b2495906SDavid du Colombier Ctlr radeon = {
623b2495906SDavid du Colombier 	"radeon",
624b2495906SDavid du Colombier 	snarf,
625b2495906SDavid du Colombier 	options,
626b2495906SDavid du Colombier 	init,
627b2495906SDavid du Colombier 	load,
628b2495906SDavid du Colombier 	dump,
629b2495906SDavid du Colombier };
630b2495906SDavid du Colombier Ctlr radeonhwgc = {
631b2495906SDavid du Colombier 	"radeonhwgc",
632b2495906SDavid du Colombier 	0, 					/* snarf */
633b2495906SDavid du Colombier 	0, 					/* options */
634b2495906SDavid du Colombier 	0, 					/* init */
635b2495906SDavid du Colombier 	0, 					/* load */
636b2495906SDavid du Colombier 	0, 					/* dump */
637b2495906SDavid du Colombier };
638