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
23static 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
151static void
152SetDSPRegisters(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
224static void
225SetCrtcRegisters(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
319status_t
320Mach64_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
372void
373Mach64_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
390void
391Mach64_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