xref: /haiku/src/add-ons/accelerants/ati/mach64_mode.cpp (revision 80829ec813241c9a702a45e39929286f13c55093)
1 /*
2 	Haiku ATI video driver adapted from the X.org ATI driver.
3 
4 	Copyright 1992,1993,1994,1995,1996,1997 by Kevin E. Martin, Chapel Hill, North Carolina.
5 	Copyright 1997 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
6 
7 	Copyright 2009 Haiku, Inc.  All rights reserved.
8 	Distributed under the terms of the MIT license.
9 
10 	Authors:
11 	Gerald Zajac 2009
12 */
13 
14 
15 #include "accelerant.h"
16 #include "mach64.h"
17 
18 #include <unistd.h>
19 
20 
21 
22 
SetClockRegisters(const DisplayModeEx & mode)23 static void SetClockRegisters(const DisplayModeEx& mode)
24 {
25 	SharedInfo& si = *gInfo.sharedInfo;
26 	M64_Params& params = si.m64Params;
27 
28 	int p;
29 	int postDiv;
30 	bool extendedDiv = false;
31 	uint32 pixelClock = mode.timing.pixel_clock;
32 
33 	if (pixelClock > params.maxPixelClock)
34 		pixelClock = params.maxPixelClock;
35 
36 	double q = ((pixelClock / 10.0) * params.refDivider) / (2.0 * params.refFreq);
37 
38 	if (si.chipType >= MACH64_264VTB) {
39 		if (q > 255) {
40 			TRACE("SetClockRegisters(): Warning: q > 255\n");
41 			q = 255;
42 			p = 0;
43 			postDiv = 1;
44 		} else if (q > 127.5) {
45 			p = 0;
46 			postDiv = 1;
47 		} else if (q > 85) {
48 			p = 1;
49 			postDiv = 2;
50 		} else if (q > 63.75) {
51 			p = 0;
52 			postDiv = 3;
53 			extendedDiv = true;
54 		} else if (q > 42.5) {
55 			p = 2;
56 			postDiv = 4;
57 		} else if (q > 31.875) {
58 			p = 2;
59 			postDiv = 6;
60 			extendedDiv = true;
61 		} else if (q > 21.25) {
62 			p = 3;
63 			postDiv = 8;
64 		} else if (q >= 10.6666666667) {
65 			p = 3;
66 			postDiv = 12;
67 			extendedDiv = true;
68 		} else {
69 			TRACE("SetClockRegisters(): Warning: q < 10.66666667\n");
70 			p = 3;
71 			postDiv = 12;
72 			extendedDiv = true;
73 		}
74 	} else {
75 		if (q > 255) {
76 			TRACE("SetClockRegisters(): Warning: q > 255\n");
77 			q = 255;
78 			p = 0;
79 		} else if (q > 127.5)
80 			p = 0;
81 		else if (q > 63.75)
82 			p = 1;
83 		else if (q > 31.875)
84 			p = 2;
85 		else if (q >= 16)
86 			p = 3;
87 		else {
88 			TRACE("SetClockRegisters(): Warning: q < 16\n");
89 			p = 3;
90 		}
91 		postDiv = 1 << p;
92 	}
93 
94 	uint8 fbDiv = uint8(q * postDiv);
95 
96 	// With some chips such as those with ID's 4750 & 475A, the display has
97 	// ripples when using resolution 1440x900 at 60 Hz refresh rate.
98 	// Decrementing fbDiv by 1 seems to fix this problem.
99 
100 	if (mode.timing.h_display == 1440 && pixelClock < 108000)
101 		fbDiv--;
102 
103 	int clkNum = params.clockNumberToProgram;
104 
105 	OUTREG8(CLOCK_CNTL, clkNum | CLOCK_STROBE);
106 
107 	// Temporarily switch to accelerator mode.
108 	uint32 crtc_gen_cntl = INREG(CRTC_GEN_CNTL);
109 	if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
110 		OUTREG(CRTC_GEN_CNTL, crtc_gen_cntl | CRTC_EXT_DISP_EN);
111 
112 	// Reset VCLK generator.
113 	uint8 vclkCntl = Mach64_GetPLLReg(PLL_VCLK_CNTL);
114 	Mach64_SetPLLReg(PLL_VCLK_CNTL, vclkCntl | PLL_VCLK_RESET);
115 
116 	// Set post-divider.
117 	uint8 tmp = Mach64_GetPLLReg(PLL_VCLK_POST_DIV);
118 	Mach64_SetPLLReg(PLL_VCLK_POST_DIV,
119 		(tmp & ~(0x03 << (2 * clkNum))) | (p << (2 * clkNum)));
120 
121 	// Set feedback divider.
122 	Mach64_SetPLLReg(PLL_VCLK0_FB_DIV + clkNum, fbDiv);
123 
124 	// Set extended post-divider.
125 	if (si.chipType >= MACH64_264VTB) {
126 		tmp = Mach64_GetPLLReg(PLL_XCLK_CNTL);
127 		if (extendedDiv)
128 			Mach64_SetPLLReg(PLL_XCLK_CNTL, tmp | (0x10 << clkNum));
129 		else
130 			Mach64_SetPLLReg(PLL_XCLK_CNTL, tmp & ~(0x10 << clkNum));
131 	}
132 
133 	// End VCLK generator reset.
134 	Mach64_SetPLLReg(PLL_VCLK_CNTL, vclkCntl & ~PLL_VCLK_RESET);
135 
136 	snooze(5000);
137 	INREG8(DAC_W_INDEX);    // Clear DAC counter
138 
139 	if (!(crtc_gen_cntl & CRTC_EXT_DISP_EN))
140 		OUTREG(CRTC_GEN_CNTL, crtc_gen_cntl);	// Restore register
141 
142 	// Save parameters that will be used for computing the DSP parameters.
143 
144 	params.vClkPostDivider = postDiv;
145 	params.vClkFeedbackDivider = fbDiv;
146 
147 	return;
148 }
149 
150 
151 static void
SetDSPRegisters(const DisplayModeEx & mode)152 SetDSPRegisters(const DisplayModeEx& mode)
153 {
154 	// Set up DSP register values for a VTB or later.
155 
156 	SharedInfo& si = *gInfo.sharedInfo;
157 	M64_Params& params = si.m64Params;
158 
159 #define Maximum_DSP_PRECISION	7
160 
161 	uint8 mClkFeedbackDivider = Mach64_GetPLLReg(PLL_MCLK_FB_DIV);
162 
163 	/* Compute a memory-to-screen bandwidth ratio */
164 	uint32 multiplier = uint32(mClkFeedbackDivider) * params.vClkPostDivider;
165 	uint32 divider = uint32(params.vClkFeedbackDivider) * params.xClkRefDivider;
166 	divider *= ((mode.bitsPerPixel + 1) / 4);
167 
168 	// Start by assuming a display FIFO width of 64 bits.
169 
170 	int vshift = (6 - 2) - params.xClkPostDivider;
171 
172 	int RASMultiplier = params.xClkMaxRASDelay;
173 	int RASDivider = 1;
174 
175 	// Determine dsp_precision first.
176 
177 	int tmp = Mach64_Divide(multiplier * params.displayFIFODepth, divider, vshift, -1);
178 	int dsp_precision;
179 
180 	for (dsp_precision = -5; tmp; dsp_precision++)
181 		tmp >>= 1;
182 	if (dsp_precision < 0)
183 		dsp_precision = 0;
184 	else if (dsp_precision > Maximum_DSP_PRECISION)
185 		dsp_precision = Maximum_DSP_PRECISION;
186 
187 	int xshift = 6 - dsp_precision;
188 	vshift += xshift;
189 
190 	int dsp_off = Mach64_Divide(multiplier * (params.displayFIFODepth - 1),
191 			divider, vshift, -1) - Mach64_Divide(1, 1, vshift - xshift, 1);
192 
193 	int dsp_on = Mach64_Divide(multiplier, divider, vshift, 1);
194 	tmp = Mach64_Divide(RASMultiplier, RASDivider, xshift, 1);
195 	if (dsp_on < tmp)
196 		dsp_on = tmp;
197 	dsp_on += (tmp * 2) +
198 			  Mach64_Divide(params.xClkPageFaultDelay, 1, xshift, 1);
199 
200 	/* Calculate rounding factor and apply it to dsp_on */
201 	tmp = ((1 << (Maximum_DSP_PRECISION - dsp_precision)) - 1) >> 1;
202 	dsp_on = ((dsp_on + tmp) / (tmp + 1)) * (tmp + 1);
203 
204 	if (dsp_on >= ((dsp_off / (tmp + 1)) * (tmp + 1))) {
205 		dsp_on = dsp_off - Mach64_Divide(multiplier, divider, vshift, -1);
206 		dsp_on = (dsp_on / (tmp + 1)) * (tmp + 1);
207 	}
208 
209 	int dsp_xclks = Mach64_Divide(multiplier, divider, vshift + 5, 1);
210 
211 	// Build DSP register contents.
212 
213 	uint32 dsp_on_off = SetBits(dsp_on, DSP_ON)
214 						| SetBits(dsp_off, DSP_OFF);
215 	uint32 dsp_config = SetBits(dsp_precision, DSP_PRECISION)
216 						| SetBits(dsp_xclks, DSP_XCLKS_PER_QW)
217 						| SetBits(params.displayLoopLatency, DSP_LOOP_LATENCY);
218 
219 	OUTREG(DSP_ON_OFF, dsp_on_off);
220 	OUTREG(DSP_CONFIG, dsp_config);
221 }
222 
223 
224 static void
SetCrtcRegisters(const DisplayModeEx & mode)225 SetCrtcRegisters(const DisplayModeEx& mode)
226 {
227 	// Calculate the CRTC register values for requested video mode, and then set
228 	// set the registers to the calculated values.
229 
230 	SharedInfo& si = *gInfo.sharedInfo;
231 
232 	uint32 crtc_h_total_disp = ((mode.timing.h_total / 8) - 1)
233 							   | (((mode.timing.h_display / 8) - 1) << 16);
234 
235 	int hSyncWidth = (mode.timing.h_sync_end - mode.timing.h_sync_start) / 8;
236 	if (hSyncWidth > 0x3f)
237 		hSyncWidth = 0x3f;
238 
239 	int hSyncStart = mode.timing.h_sync_start / 8 - 1;
240 
241 	uint32 crtc_h_sync_strt_wid = (hSyncWidth << 16)
242 		| (hSyncStart & 0xff) | ((hSyncStart & 0x100) << 4)
243 		| ((mode.timing.flags & B_POSITIVE_HSYNC) ? 0 : CRTC_H_SYNC_NEG);
244 
245 	uint32 crtc_v_total_disp = ((mode.timing.v_total - 1)
246 		| ((mode.timing.v_display - 1) << 16));
247 
248 	int vSyncWidth = mode.timing.v_sync_end - mode.timing.v_sync_start;
249 	if (vSyncWidth > 0x1f)
250 		vSyncWidth = 0x1f;
251 
252 	uint32 crtc_v_sync_strt_wid = (mode.timing.v_sync_start - 1)
253 		| (vSyncWidth << 16)
254 		| ((mode.timing.flags & B_POSITIVE_VSYNC) ? 0 : CRTC_V_SYNC_NEG);
255 
256 	uint32 crtc_off_pitch = SetBits(mode.timing.h_display >> 3, CRTC_PITCH);
257 
258 	uint32 crtc_gen_cntl = INREG(CRTC_GEN_CNTL) &
259 		~(CRTC_DBL_SCAN_EN | CRTC_INTERLACE_EN |
260 		  CRTC_HSYNC_DIS | CRTC_VSYNC_DIS | CRTC_CSYNC_EN |
261 		  CRTC_PIX_BY_2_EN | CRTC_VGA_XOVERSCAN |
262 		  CRTC_PIX_WIDTH | CRTC_BYTE_PIX_ORDER |
263 		  CRTC_VGA_128KAP_PAGING | CRTC_VFC_SYNC_TRISTATE |
264 		  CRTC_LOCK_REGS |               // already off, but ...
265 		  CRTC_SYNC_TRISTATE | CRTC_DISP_REQ_EN |
266 		  CRTC_VGA_TEXT_132 | CRTC_CUR_B_TEST);
267 
268 	crtc_gen_cntl |= CRTC_EXT_DISP_EN | CRTC_EN | CRTC_VGA_LINEAR | CRTC_CNT_EN;
269 
270 	switch (mode.bitsPerPixel) {
271 	case 8:
272 		crtc_gen_cntl |= CRTC_PIX_WIDTH_8BPP;
273 		break;
274 	case 15:
275 		crtc_gen_cntl |= CRTC_PIX_WIDTH_15BPP;
276 		break;
277 	case 16:
278 		crtc_gen_cntl |= CRTC_PIX_WIDTH_16BPP;
279 		break;
280 	case 32:
281 		crtc_gen_cntl |= CRTC_PIX_WIDTH_32BPP;
282 		break;
283 	default:
284 		TRACE("Undefined color depth, bitsPerPixel: %d\n", mode.bitsPerPixel);
285 		break;
286 	}
287 
288 	// For now, set display FIFO low water mark as high as possible.
289 	if (si.chipType < MACH64_264VTB)
290 		crtc_gen_cntl |= CRTC_FIFO_LWM;
291 
292 
293 	// Write the CRTC registers.
294 	//--------------------------
295 
296 	// Stop CRTC.
297 	OUTREG(CRTC_GEN_CNTL, crtc_gen_cntl & ~(CRTC_EXT_DISP_EN | CRTC_EN));
298 
299 	OUTREG(CRTC_H_TOTAL_DISP, crtc_h_total_disp);
300 	OUTREG(CRTC_H_SYNC_STRT_WID, crtc_h_sync_strt_wid);
301 	OUTREG(CRTC_V_TOTAL_DISP, crtc_v_total_disp);
302 	OUTREG(CRTC_V_SYNC_STRT_WID, crtc_v_sync_strt_wid);
303 
304 	OUTREG(CRTC_OFF_PITCH, crtc_off_pitch);
305 
306 	// Clear overscan registers.
307 	OUTREG(OVR_CLR, 0);
308 	OUTREG(OVR_WID_LEFT_RIGHT, 0);
309 	OUTREG(OVR_WID_TOP_BOTTOM, 0);
310 
311 	// Finalise CRTC setup and turn on the screen.
312 	OUTREG(CRTC_GEN_CNTL, crtc_gen_cntl);
313 
314 	return;
315 }
316 
317 
318 
319 status_t
Mach64_SetDisplayMode(const DisplayModeEx & mode)320 Mach64_SetDisplayMode(const DisplayModeEx& mode)
321 {
322 	// The code to actually configure the display.
323 	// All the error checking must be done in ProposeDisplayMode(),
324 	// and assume that the mode values we get here are acceptable.
325 
326 	SharedInfo& si = *gInfo.sharedInfo;
327 
328 	if (si.displayType == MT_VGA) {
329 		// Chip is connected to a monitor via a VGA connector.
330 
331 		SetCrtcRegisters(mode);
332 		SetClockRegisters(mode);
333 
334 		if (si.chipType >= MACH64_264VTB)
335 			SetDSPRegisters(mode);
336 
337 	} else {
338 		// Chip is connected to a laptop LCD monitor; or via a DVI interface.
339 
340 		uint16 vesaMode = GetVesaModeNumber(display_mode(mode), mode.bitsPerPixel);
341 		if (vesaMode == 0)
342 			return B_BAD_VALUE;
343 
344 		status_t status = ioctl(gInfo.deviceFileDesc, ATI_SET_VESA_DISPLAY_MODE,
345 				&vesaMode, sizeof(vesaMode));
346 		if (status != B_OK)
347 			return status;
348 	}
349 
350 	Mach64_AdjustFrame(mode);
351 
352 	// Initialize the palette so that the various color depths will display
353 	// the correct colors.
354 
355 	OUTREGM(DAC_CNTL, DAC_8BIT_EN, DAC_8BIT_EN);
356 	OUTREG8(DAC_MASK, 0xff);
357 	OUTREG8(DAC_W_INDEX, 0);		// initial color index
358 
359 	for (int i = 0; i < 256; i++) {
360 		OUTREG8(DAC_DATA, i);
361 		OUTREG8(DAC_DATA, i);
362 		OUTREG8(DAC_DATA, i);
363 	}
364 
365 	Mach64_EngineInit(mode);
366 
367 	return B_OK;
368 }
369 
370 
371 
372 void
Mach64_AdjustFrame(const DisplayModeEx & mode)373 Mach64_AdjustFrame(const DisplayModeEx& mode)
374 {
375 	// Adjust start address in frame buffer.
376 
377 	SharedInfo& si = *gInfo.sharedInfo;
378 
379 	int address = (mode.v_display_start * mode.virtual_width
380 			+ mode.h_display_start) * ((mode.bitsPerPixel + 1) / 8);
381 
382 	address &= ~0x07;
383 	address += si.frameBufferOffset;
384 
385 	OUTREGM(CRTC_OFF_PITCH, address, 0xfffff);
386 	return;
387 }
388 
389 
390 void
Mach64_SetIndexedColors(uint count,uint8 first,uint8 * colorData,uint32 flags)391 Mach64_SetIndexedColors(uint count, uint8 first, uint8* colorData, uint32 flags)
392 {
393 	// Set the indexed color palette for 8-bit color depth mode.
394 
395 	(void)flags;		// avoid compiler warning for unused arg
396 
397 	if (gInfo.sharedInfo->displayMode.space != B_CMAP8)
398 		return ;
399 
400 	OUTREG8(DAC_MASK, 0xff);
401 	OUTREG8(DAC_W_INDEX, first);		// initial color index
402 
403 	while (count--) {
404 		OUTREG8(DAC_DATA, colorData[0]);	// red
405 		OUTREG8(DAC_DATA, colorData[1]);	// green
406 		OUTREG8(DAC_DATA, colorData[2]);	// blue
407 
408 		colorData += 3;
409 	}
410 }
411