xref: /haiku/src/add-ons/accelerants/ati/rage128_mode.cpp (revision 6ab4ff112a084ee6f022e17b4d2668038c0b2014)
1 /*
2 	Haiku ATI video driver adapted from the X.org ATI driver.
3 
4 	Copyright 1999, 2000 ATI Technologies Inc., Markham, Ontario,
5 						 Precision Insight, Inc., Cedar Park, Texas, and
6 						 VA Linux Systems Inc., Fremont, California.
7 
8 	Copyright 2009 Haiku, Inc.  All rights reserved.
9 	Distributed under the terms of the MIT license.
10 
11 	Authors:
12 	Gerald Zajac 2009
13 */
14 
15 
16 #include "accelerant.h"
17 #include "rage128.h"
18 
19 #include <unistd.h>
20 
21 
22 struct DisplayParams {
23 	// CRTC registers
24 	uint32	crtc_gen_cntl;
25 	uint32	crtc_h_total_disp;
26 	uint32	crtc_h_sync_strt_wid;
27 	uint32	crtc_v_total_disp;
28 	uint32	crtc_v_sync_strt_wid;
29 	uint32	crtc_pitch;
30 
31 	// DDA register
32 	uint32	dda_config;
33 	uint32	dda_on_off;
34 
35 	// Computed PLL values
36 	int		feedback_div;
37 	int		post_div;
38 
39 	// PLL registers
40 	uint32	ppll_ref_div;
41 	uint32	ppll_div_3;
42 };
43 
44 
45 
46 static inline int
DivideWithRounding(int n,int d)47 DivideWithRounding(int n, int d)
48 {
49 	return (n + (d / 2)) / d;		// compute n/d with rounding
50 }
51 
52 
53 static int
MinimumBits(uint32 value)54 MinimumBits(uint32 value)
55 {
56 	// Compute minimum number of bits required to contain a value (ie, log
57 	// base 2 of value).
58 
59 	if (value == 0)
60 		return 1;
61 
62 	int numBits = 0;
63 
64 	while (value != 0) {
65 		value >>= 1;
66 		numBits++;
67 	}
68 
69 	return numBits;
70 }
71 
72 
73 static bool
CalculateCrtcRegisters(const DisplayModeEx & mode,DisplayParams & params)74 CalculateCrtcRegisters(const DisplayModeEx& mode, DisplayParams& params)
75 {
76 	// Define CRTC registers for requested video mode.
77 	// Return true if successful.
78 
79 	const uint8 hSyncFudge[] = { 0x00, 0x12, 0x09, 0x09, 0x06, 0x05 };
80 
81 	uint32 format;
82 
83 	switch (mode.bitsPerPixel) {
84 	case 8:
85 		format = 2;
86 		break;
87 	case 15:
88 		format = 3;		// 555
89 		break;
90 	case 16:
91 		format = 4;		// 565
92 		break;
93 	case 32:
94 		format = 6;		// xRGB
95 		break;
96 	default:
97 		TRACE("Unsupported color depth: %d bits/pixel\n", mode.bitsPerPixel);
98 		return false;
99 	}
100 
101 	params.crtc_gen_cntl = (R128_CRTC_EXT_DISP_EN
102 						   | R128_CRTC_EN
103 						   | (format << 8));
104 
105 	params.crtc_h_total_disp = (((mode.timing.h_total / 8) - 1) & 0xffff)
106 							   | (((mode.timing.h_display / 8) - 1) << 16);
107 
108 	int hSyncWidth = (mode.timing.h_sync_end - mode.timing.h_sync_start) / 8;
109 	if (hSyncWidth <= 0)
110 		hSyncWidth = 1;
111 	if (hSyncWidth > 0x3f)
112 		hSyncWidth = 0x3f;
113 
114 	int hSyncStart = mode.timing.h_sync_start - 8 + hSyncFudge[format - 1];
115 
116 	params.crtc_h_sync_strt_wid = (hSyncStart & 0xfff) | (hSyncWidth << 16)
117 		| ((mode.timing.flags & B_POSITIVE_HSYNC) ? 0 : R128_CRTC_H_SYNC_POL);
118 
119 	params.crtc_v_total_disp = (((mode.timing.v_total - 1) & 0xffff)
120 		| ((mode.timing.v_display - 1) << 16));
121 
122 	int vSyncWidth = mode.timing.v_sync_end - mode.timing.v_sync_start;
123 	if (vSyncWidth <= 0)
124 		vSyncWidth = 1;
125 	if (vSyncWidth > 0x1f)
126 		vSyncWidth = 0x1f;
127 
128 	params.crtc_v_sync_strt_wid = ((mode.timing.v_sync_start - 1) & 0xfff)
129 		| (vSyncWidth << 16)
130 		| ((mode.timing.flags & B_POSITIVE_VSYNC) ? 0 : R128_CRTC_V_SYNC_POL);
131 
132 	params.crtc_pitch = mode.timing.h_display / 8;
133 
134 	return true;
135 }
136 
137 
138 static bool
CalculateDDARegisters(const DisplayModeEx & mode,DisplayParams & params)139 CalculateDDARegisters(const DisplayModeEx& mode, DisplayParams& params)
140 {
141 	// Compute and write DDA registers for requested video mode.
142 	// Return true if successful.
143 
144 	SharedInfo& si = *gInfo.sharedInfo;
145 	R128_RAMSpec& memSpec = si.r128MemSpec;
146 	R128_PLLParams& pll = si.r128PLLParams;
147 
148 	int displayFifoWidth = 128;
149 	int displayFifoDepth = 32;
150 	int xClkFreq = pll.xclk;
151 
152 	int vClkFreq = DivideWithRounding(pll.reference_freq * params.feedback_div,
153 		pll.reference_div * params.post_div);
154 
155 	int bytesPerPixel = (mode.bitsPerPixel + 7) / 8;
156 
157 	int xClksPerTransfer = DivideWithRounding(xClkFreq * displayFifoWidth,
158 		vClkFreq * bytesPerPixel * 8);
159 
160 	int useablePrecision = MinimumBits(xClksPerTransfer) + 1;
161 
162 	int xClksPerTransferPrecise = DivideWithRounding(
163 		(xClkFreq * displayFifoWidth) << (11 - useablePrecision),
164 		vClkFreq * bytesPerPixel * 8);
165 
166 	int rOff = xClksPerTransferPrecise * (displayFifoDepth - 4);
167 
168 	int rOn = (4 * memSpec.memBurstLen
169 		+ 3 * MAX(memSpec.rasToCasDelay - 2, 0)
170 		+ 2 * memSpec.rasPercentage
171 		+ memSpec.writeRecovery
172 		+ memSpec.casLatency
173 		+ memSpec.readToWriteDelay
174 		+ xClksPerTransfer) << (11 - useablePrecision);
175 
176 	if (rOn + memSpec.loopLatency >= rOff) {
177 		TRACE("Error:  (rOn = %d) + (loopLatency = %d) >= (rOff = %d)\n",
178 			rOn, memSpec.loopLatency, rOff);
179 		return false;
180 	}
181 
182 	params.dda_config = xClksPerTransferPrecise | (useablePrecision << 16)
183 			| (memSpec.loopLatency << 20);
184 	params.dda_on_off = (rOn << 16) | rOff;
185 
186 	return true;
187 }
188 
189 
190 static bool
CalculatePLLRegisters(const DisplayModeEx & mode,DisplayParams & params)191 CalculatePLLRegisters(const DisplayModeEx& mode, DisplayParams& params)
192 {
193 	// Define PLL registers for requested video mode.
194 
195 	struct Divider {
196 		int divider;
197 		int bitValue;
198 	};
199 
200 	// The following data is from RAGE 128 VR/RAGE 128 GL Register Reference
201 	// Manual (Technical Reference Manual P/N RRG-G04100-C Rev. 0.04), page
202 	// 3-17 (PLL_DIV_[3:0]).
203 
204 	const Divider postDividers[] = {
205 		{ 1, 0 },		// VCLK_SRC
206 		{ 2, 1 },		// VCLK_SRC/2
207 		{ 4, 2 },		// VCLK_SRC/4
208 		{ 8, 3 },		// VCLK_SRC/8
209 		{ 3, 4 },		// VCLK_SRC/3
210 						// bitValue = 5 is reserved
211 		{ 6, 6 },		// VCLK_SRC/6
212 		{ 12, 7 }		// VCLK_SRC/12
213 	};
214 
215 	R128_PLLParams& pll = gInfo.sharedInfo->r128PLLParams;
216 	uint32 freq = mode.timing.pixel_clock / 10;
217 
218 	if (freq > pll.max_pll_freq)
219 		freq = pll.max_pll_freq;
220 	if (freq * 12 < pll.min_pll_freq)
221 		freq = pll.min_pll_freq / 12;
222 
223 	int bitValue = -1;
224 	uint32 output_freq;
225 
226 	for (int j = 0; j < (int)B_COUNT_OF(postDividers); j++) {
227 		output_freq = postDividers[j].divider * freq;
228 		if (output_freq >= pll.min_pll_freq && output_freq <= pll.max_pll_freq) {
229 			params.feedback_div = DivideWithRounding(pll.reference_div * output_freq,
230 				pll.reference_freq);
231 			params.post_div = postDividers[j].divider;
232 			bitValue = postDividers[j].bitValue;
233 			break;
234 		}
235 	}
236 
237 	if (bitValue < 0) {
238 		TRACE("CalculatePLLRegisters(), acceptable divider not found\n");
239 		return false;
240 	}
241 
242 	params.ppll_ref_div = pll.reference_div;
243 	params.ppll_div_3 = (params.feedback_div | (bitValue << 16));
244 
245 	return true;
246 }
247 
248 
249 static void
PLLWaitForReadUpdateComplete()250 PLLWaitForReadUpdateComplete()
251 {
252 	while (GetPLLReg(R128_PPLL_REF_DIV) & R128_PPLL_ATOMIC_UPDATE_R)
253 		;
254 }
255 
256 static void
PLLWriteUpdate()257 PLLWriteUpdate()
258 {
259 	PLLWaitForReadUpdateComplete();
260 
261 	SetPLLReg(R128_PPLL_REF_DIV, R128_PPLL_ATOMIC_UPDATE_W, R128_PPLL_ATOMIC_UPDATE_W);
262 }
263 
264 
265 static void
SetRegisters(DisplayParams & params)266 SetRegisters(DisplayParams& params)
267 {
268 	// Write the common registers (most will be set to zero).
269 	//-------------------------------------------------------
270 
271 	OUTREGM(R128_FP_GEN_CNTL, R128_FP_BLANK_DIS, R128_FP_BLANK_DIS);
272 
273 	OUTREG(R128_OVR_CLR, 0);
274 	OUTREG(R128_OVR_WID_LEFT_RIGHT, 0);
275 	OUTREG(R128_OVR_WID_TOP_BOTTOM, 0);
276 	OUTREG(R128_OV0_SCALE_CNTL, 0);
277 	OUTREG(R128_MPP_TB_CONFIG, 0);
278 	OUTREG(R128_MPP_GP_CONFIG, 0);
279 	OUTREG(R128_SUBPIC_CNTL, 0);
280 	OUTREG(R128_VIPH_CONTROL, 0);
281 	OUTREG(R128_I2C_CNTL_1, 0);
282 	OUTREG(R128_GEN_INT_CNTL, 0);
283 	OUTREG(R128_CAP0_TRIG_CNTL, 0);
284 	OUTREG(R128_CAP1_TRIG_CNTL, 0);
285 
286 	// If bursts are enabled, turn on discards and aborts.
287 
288 	uint32 busCntl = INREG(R128_BUS_CNTL);
289 	if (busCntl & (R128_BUS_WRT_BURST | R128_BUS_READ_BURST)) {
290 		busCntl |= R128_BUS_RD_DISCARD_EN | R128_BUS_RD_ABORT_EN;
291 		OUTREG(R128_BUS_CNTL, busCntl);
292 	}
293 
294 	// Write the DDA registers.
295 	//-------------------------
296 
297 	OUTREG(R128_DDA_CONFIG, params.dda_config);
298 	OUTREG(R128_DDA_ON_OFF, params.dda_on_off);
299 
300 	// Write the CRTC registers.
301 	//--------------------------
302 
303 	OUTREG(R128_CRTC_GEN_CNTL, params.crtc_gen_cntl);
304 
305 	OUTREGM(R128_DAC_CNTL, R128_DAC_MASK_ALL | R128_DAC_8BIT_EN,
306 			~(R128_DAC_RANGE_CNTL | R128_DAC_BLANKING));
307 
308 	OUTREG(R128_CRTC_H_TOTAL_DISP, params.crtc_h_total_disp);
309 	OUTREG(R128_CRTC_H_SYNC_STRT_WID, params.crtc_h_sync_strt_wid);
310 	OUTREG(R128_CRTC_V_TOTAL_DISP, params.crtc_v_total_disp);
311 	OUTREG(R128_CRTC_V_SYNC_STRT_WID, params.crtc_v_sync_strt_wid);
312 	OUTREG(R128_CRTC_OFFSET, 0);
313 	OUTREG(R128_CRTC_OFFSET_CNTL, 0);
314 	OUTREG(R128_CRTC_PITCH, params.crtc_pitch);
315 
316 	// Write the PLL registers.
317 	//-------------------------
318 
319 	OUTREGM(R128_CLOCK_CNTL_INDEX, R128_PLL_DIV_SEL, R128_PLL_DIV_SEL);
320 
321 	SetPLLReg(R128_VCLK_ECP_CNTL, R128_VCLK_SRC_SEL_CPUCLK, R128_VCLK_SRC_SEL_MASK);
322 
323 	SetPLLReg(R128_PPLL_CNTL, 0xffffffff,
324 		R128_PPLL_RESET | R128_PPLL_ATOMIC_UPDATE_EN | R128_PPLL_VGA_ATOMIC_UPDATE_EN);
325 
326 	PLLWaitForReadUpdateComplete();
327 	SetPLLReg(R128_PPLL_REF_DIV, params.ppll_ref_div, R128_PPLL_REF_DIV_MASK);
328 	PLLWriteUpdate();
329 
330 	PLLWaitForReadUpdateComplete();
331 	SetPLLReg(R128_PPLL_DIV_3, params.ppll_div_3,
332 		R128_PPLL_FB3_DIV_MASK | R128_PPLL_POST3_DIV_MASK);
333 	PLLWriteUpdate();
334 
335 	PLLWaitForReadUpdateComplete();
336 	SetPLLReg(R128_HTOTAL_CNTL, 0);
337 	PLLWriteUpdate();
338 
339 	SetPLLReg(R128_PPLL_CNTL, 0, R128_PPLL_RESET
340 								 | R128_PPLL_SLEEP
341 								 | R128_PPLL_ATOMIC_UPDATE_EN
342 								 | R128_PPLL_VGA_ATOMIC_UPDATE_EN);
343 
344 	snooze(5000);
345 
346 	SetPLLReg(R128_VCLK_ECP_CNTL, R128_VCLK_SRC_SEL_PPLLCLK,
347 				R128_VCLK_SRC_SEL_MASK);
348 }
349 
350 
351 
352 status_t
Rage128_SetDisplayMode(const DisplayModeEx & mode)353 Rage128_SetDisplayMode(const DisplayModeEx& mode)
354 {
355 	// The code to actually configure the display.
356 	// All the error checking must be done in ProposeDisplayMode(),
357 	// and assume that the mode values we get here are acceptable.
358 
359 	DisplayParams params;		// where computed parameters are saved
360 
361 	if (gInfo.sharedInfo->displayType == MT_VGA) {
362 		// Chip is connected to a monitor via a VGA connector.
363 
364 		if ( ! CalculateCrtcRegisters(mode, params))
365 			return B_BAD_VALUE;
366 
367 		if ( ! CalculatePLLRegisters(mode, params))
368 			return B_BAD_VALUE;
369 
370 		if ( ! CalculateDDARegisters(mode, params))
371 			return B_BAD_VALUE;
372 
373 		SetRegisters(params);
374 
375 	} else {
376 		// Chip is connected to a laptop LCD monitor; or via a DVI interface.
377 
378 		uint16 vesaMode = GetVesaModeNumber(display_mode(mode), mode.bitsPerPixel);
379 		if (vesaMode == 0)
380 			return B_BAD_VALUE;
381 
382 		if (ioctl(gInfo.deviceFileDesc, ATI_SET_VESA_DISPLAY_MODE,
383 				&vesaMode, sizeof(vesaMode)) != B_OK)
384 			return B_ERROR;
385 	}
386 
387 	Rage128_AdjustFrame(mode);
388 
389 	// Initialize the palette so that color depths > 8 bits/pixel will display
390 	// the correct colors.
391 
392 	// Select primary monitor and enable 8-bit color.
393 	OUTREGM(R128_DAC_CNTL, R128_DAC_8BIT_EN,
394 		R128_DAC_PALETTE_ACC_CTL | R128_DAC_8BIT_EN);
395 	OUTREG8(R128_PALETTE_INDEX, 0);		// set first color index
396 
397 	for (int i = 0; i < 256; i++)
398 		OUTREG(R128_PALETTE_DATA, (i << 16) | (i << 8) | i );
399 
400 	Rage128_EngineInit(mode);
401 
402 	return B_OK;
403 }
404 
405 
406 
407 void
Rage128_AdjustFrame(const DisplayModeEx & mode)408 Rage128_AdjustFrame(const DisplayModeEx& mode)
409 {
410 	// Adjust start address in frame buffer.
411 
412 	SharedInfo& si = *gInfo.sharedInfo;
413 
414 	int address = (mode.v_display_start * mode.virtual_width
415 			+ mode.h_display_start) * ((mode.bitsPerPixel + 1) / 8);
416 
417 	address &= ~0x07;
418 	address += si.frameBufferOffset;
419 
420 	OUTREG(R128_CRTC_OFFSET, address);
421 	return;
422 }
423 
424 
425 void
Rage128_SetIndexedColors(uint count,uint8 first,uint8 * colorData,uint32 flags)426 Rage128_SetIndexedColors(uint count, uint8 first, uint8* colorData, uint32 flags)
427 {
428 	// Set the indexed color palette for 8-bit color depth mode.
429 
430 	(void)flags;		// avoid compiler warning for unused arg
431 
432 	if (gInfo.sharedInfo->displayMode.space != B_CMAP8)
433 		return ;
434 
435 	// Select primary monitor and enable 8-bit color.
436 	OUTREGM(R128_DAC_CNTL, R128_DAC_8BIT_EN,
437 		R128_DAC_PALETTE_ACC_CTL | R128_DAC_8BIT_EN);
438 	OUTREG8(R128_PALETTE_INDEX, first);		// set first color index
439 
440 	while (count--) {
441 		OUTREG(R128_PALETTE_DATA, ((colorData[0] << 16)	// red
442 								 | (colorData[1] << 8)	// green
443 								 |  colorData[2]));		// blue
444 		colorData += 3;
445 	}
446 }
447