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