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
22struct 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
46static inline int
47DivideWithRounding(int n, int d)
48{
49	return (n + (d / 2)) / d;		// compute n/d with rounding
50}
51
52
53static int
54MinimumBits(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
73static bool
74CalculateCrtcRegisters(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
138static bool
139CalculateDDARegisters(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
190static bool
191CalculatePLLRegisters(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
249static void
250PLLWaitForReadUpdateComplete()
251{
252	while (GetPLLReg(R128_PPLL_REF_DIV) & R128_PPLL_ATOMIC_UPDATE_R)
253		;
254}
255
256static void
257PLLWriteUpdate()
258{
259	PLLWaitForReadUpdateComplete();
260
261	SetPLLReg(R128_PPLL_REF_DIV, R128_PPLL_ATOMIC_UPDATE_W, R128_PPLL_ATOMIC_UPDATE_W);
262}
263
264
265static void
266SetRegisters(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
352status_t
353Rage128_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
407void
408Rage128_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
425void
426Rage128_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