xref: /haiku/src/add-ons/accelerants/s3/trio64_mode.cpp (revision 22440f4105cafc95cc1d49f9bc65bb395c527d86)
1 /*
2 	Haiku S3 Trio64 driver adapted from the X.org S3 driver.
3 
4 	Copyright 2001	Ani Joshi <ajoshi@unixbox.com>
5 
6 	Copyright 2008 Haiku, Inc.  All rights reserved.
7 	Distributed under the terms of the MIT license.
8 
9 	Authors:
10 	Gerald Zajac 2008
11 */
12 
13 
14 #include "accel.h"
15 #include "trio64.h"
16 
17 
18 #define BASE_FREQ		14.31818	// MHz
19 
20 
21 static void
22 Trio64_CalcClock(long freq, int min_m, int min_n1, int max_n1,
23 				int min_n2, int max_n2, long freq_min, long freq_max,
24 				uint8* mdiv, uint8* ndiv)
25 {
26 	uint8 best_n1 = 18, best_n2 = 2, best_m = 127;
27 
28 	double ffreq = freq / 1000.0 / BASE_FREQ;
29 	double ffreq_min = freq_min / 1000.0 / BASE_FREQ;
30 	double ffreq_max = freq_max / 1000.0 / BASE_FREQ;
31 
32 	if (ffreq < ffreq_min / (1 << max_n2)) {
33 		TRACE("Trio64_CalcClock() invalid frequency %1.3f Mhz [freq >= %1.3f Mhz]\n",
34 			   ffreq*BASE_FREQ, ffreq_min*BASE_FREQ / (1 << max_n2));
35 		ffreq = ffreq_min / (1 << max_n2);
36 	}
37 	if (ffreq > ffreq_max / (1 << min_n2)) {
38 		TRACE("Trio64_CalcClock() invalid frequency %1.3f Mhz [freq <= %1.3f Mhz]\n",
39 			   ffreq*BASE_FREQ, ffreq_max*BASE_FREQ / (1 << min_n2));
40 		ffreq = ffreq_max / (1 << min_n2);
41 	}
42 
43 	double best_diff = ffreq;
44 
45 	for (uint8 n2 = min_n2; n2 <= max_n2; n2++) {
46 		for (uint8 n1 = min_n1 + 2; n1 <= max_n1 + 2; n1++) {
47 			int m = (int)(ffreq * n1 * (1 << n2) + 0.5);
48 			if (m < min_m + 2 || m > 127 + 2)
49 				continue;
50 
51 			double div = (double)(m) / (double)(n1);
52 			if ((div >= ffreq_min) && (div <= ffreq_max)) {
53 				double diff = ffreq - div / (1 << n2);
54 				if (diff < 0.0)
55 					diff = -diff;
56 				if (diff < best_diff) {
57 					best_diff = diff;
58 					best_m = m;
59 					best_n1 = n1;
60 					best_n2 = n2;
61 				}
62 			}
63 		}
64 	}
65 
66 	if (max_n1 == 63)
67 		*ndiv = (best_n1 - 2) | (best_n2 << 6);
68 	else
69 		*ndiv = (best_n1 - 2) | (best_n2 << 5);
70 	*mdiv = best_m - 2;
71 }
72 
73 
74 
75 static bool
76 Trio64_ModeInit(const DisplayModeEx& mode)
77 {
78 	SharedInfo& si = *gInfo.sharedInfo;
79 
80 	TRACE("Trio64_ModeInit(%d x %d, %d KHz)\n",
81 		mode.timing.h_display, mode.timing.v_display, mode.timing.pixel_clock);
82 
83 	uint32 videoRamMB = si.videoMemSize / (1024 * 1024);	// MB's of video RAM
84 
85 	WriteCrtcReg(0x38, 0x48);			// unlock sys regs
86 	WriteCrtcReg(0x39, 0xa5);			// unlock sys regs
87 	WriteSeqReg(0x08, 0x06);			// unlock extended sequencer regs
88 
89 	WriteCrtcReg(0x45, 0x00, 0x01);		// turn off hardware cursor
90 
91 	uint8 sr12, sr13;
92 	Trio64_CalcClock(mode.timing.pixel_clock, 1, 1, 31, 0, 3, 135000, 270000,
93 					 &sr13, &sr12);
94 
95 	// Set clock registers.
96 
97 	WriteSeqReg(0x12, sr12);
98 	WriteSeqReg(0x13, sr13);
99 
100 	// Activate clock
101 
102 	uint8 tmp = ReadSeqReg(0x15) & ~0x21;
103 	WriteSeqReg(0x15, tmp | 0x02);
104 	WriteSeqReg(0x15, tmp | 0x22);
105 	WriteSeqReg(0x15, tmp | 0x02);
106 
107 	uint8 cr33 = ReadCrtcReg(0x33) & ~0x28;
108 	uint8 cr50 = 0;
109 	uint8 pixmux = 0;
110 
111 	if (si.chipType == S3_TRIO64_V2)
112 		cr33 |= 0x20;
113 
114 	switch (mode.bpp) {
115 		case 8:
116 			break;
117 		case 15:
118 			cr33 |= 0x08;
119 			cr50 = 0x10;
120 			pixmux = 0x30;
121 			break;
122 		case 16:
123 			cr33 |= 0x08;
124 			cr50 = 0x10;
125 			pixmux = 0x50;
126 			break;
127 		case 32:
128 			cr50 = 0x30;
129 			pixmux = 0xd0;
130 			break;
131 	}
132 
133 	bool bDisableAccelFuncs = false;
134 
135 	switch (mode.timing.h_display) {
136 		case 640:
137 			cr50 |= 0x40;
138 			break;
139 		case 800:
140 			cr50 |= 0x80;
141 			break;
142 		case 1024:
143 			cr50 |= 0x00;
144 			break;
145 		case 1152:
146 			cr50 |= 0x01;
147 			break;
148 		case 1280:
149 			cr50 |= 0xc0;
150 			break;
151 		case 1600:
152 			cr50 |= 0x81;
153 			break;
154 		default:
155 			bDisableAccelFuncs = true;	// use app_server default accel functions
156 			break;
157 	}
158 
159 	WriteCrtcReg(0x33, cr33);
160 	WriteCrtcReg(0x50, cr50);		// set number of bits per pixel & display width
161 	WriteCrtcReg(0x67, pixmux);		// set pixel format
162 
163 	WriteSeqReg(0x15, 0x00, 0x10);		// turn off pixel multiplex
164 	WriteSeqReg(0x18, 0x00, 0x80);
165 
166 	// Note that the 2D acceleration (drawing) functions in this accelerant work
167 	// only with the display widths defined in the above switch statement.  For
168 	// the other widths, the default functions in the app_server will be used.
169 
170 	si.bDisableAccelDraw = bDisableAccelFuncs;
171 
172 	// Set the standard CRTC vga regs.
173 
174 	uint8 crtc[25], cr3b, cr3c, cr5d, cr5e;
175 
176 	InitCrtcTimingValues(mode, (mode.bpp > 8) ? 2 : 1, crtc, cr3b, cr3c, cr5d, cr5e);
177 	crtc[0x17] = 0xe3;
178 
179 	WriteCrtcReg(0x11, 0x00, 0x80);	// unlock CRTC reg's 0-7 by clearing bit 7 of cr11
180 
181 	for (int k = 0; k < (int)B_COUNT_OF(crtc); k++) {
182 		WriteCrtcReg(k, crtc[k]);
183 	}
184 
185 	WriteCrtcReg(0x3b, cr3b);
186 	WriteCrtcReg(0x3c, cr3c);
187 	WriteCrtcReg(0x5d, cr5d);
188 	WriteCrtcReg(0x5e, cr5e);
189 
190 	uint8 miscOutReg = 0x2f;
191 
192 	if ( ! (mode.timing.flags & B_POSITIVE_HSYNC))
193 		miscOutReg |= 0x40;
194 	if ( ! (mode.timing.flags & B_POSITIVE_VSYNC))
195 		miscOutReg |= 0x80;
196 
197 	WriteMiscOutReg(miscOutReg);
198 
199 	uint8 cr58;
200 	if (videoRamMB <= 1)
201 		cr58 = 0x01;
202 	else if (videoRamMB <= 2)
203 		cr58 = 0x02;
204 	else
205 		cr58 = 0x03;
206 
207 	WriteCrtcReg(0x58, cr58 | 0x10, 0x13);	// enable linear addressing & set memory size
208 
209 	WriteCrtcReg(0x31, 0x08);
210 	WriteCrtcReg(0x32, 0x00);
211 	WriteCrtcReg(0x34, 0x10);
212 	WriteCrtcReg(0x3a, 0x15);
213 
214 	WriteCrtcReg(0x51, mode.bytesPerRow >> 7, 0x30);
215 	WriteCrtcReg(0x53, 0x18, 0x18);
216 
217 	int n = 255;
218 	int clock2 = mode.timing.pixel_clock * (mode.bpp / 8);
219 	if (videoRamMB < 2)
220 		clock2 *= 2;
221 	int m = (int)((gInfo.sharedInfo->mclk / 1000.0 * .72 + 16.867) * 89.736
222 			/ (clock2 / 1000.0 + 39) - 21.1543);
223 	if (videoRamMB < 2)
224 		m /= 2;
225 	if (m > 31)
226 		m = 31;
227 	else if (m < 0) {
228 		m = 0;
229 		n = 16;
230 	}
231 
232 	if (n < 0)
233 		n = 0;
234 	else if (n > 255)
235 		n = 255;
236 
237 	WriteCrtcReg(0x54, m << 3);
238 	WriteCrtcReg(0x60, n);
239 
240 	WriteCrtcReg(0x42, 0x00, 0x20);		// disable interlace mode
241 	WriteCrtcReg(0x66, 0x89, 0x8f);
242 
243 	WriteReg16(ADVFUNC_CNTL, 0x0001);		// enable enhanced functions
244 
245 	WriteReg16(SUBSYS_CNTL, 0x8000);		// reset graphics engine
246 	WriteReg16(SUBSYS_CNTL, 0x4000);		// enable graphics engine
247 	ReadReg16(SUBSYS_STAT);
248 
249 	WriteReg16(MULTIFUNC_CNTL, 0x5000 | 0x0004 | 0x000c);
250 
251 	gInfo.WaitQueue(5);
252 	WriteReg16(MULTIFUNC_CNTL, SCISSORS_L | 0);
253 	WriteReg16(MULTIFUNC_CNTL, SCISSORS_T | 0);
254 	WriteReg16(MULTIFUNC_CNTL, SCISSORS_R | (mode.timing.h_display - 1));
255 	WriteReg16(MULTIFUNC_CNTL, SCISSORS_B | ((si.maxFrameBufferSize / mode.bytesPerRow) - 1));
256 
257 	WriteReg32(WRT_MASK, ~0);		// enable all planes
258 
259 	TRACE("Trio64_ModeInit() exit\n");
260 	return true;
261 }
262 
263 
264 bool
265 Trio64_SetDisplayMode(const DisplayModeEx& mode)
266 {
267 	// The code to actually configure the display.
268 	// All the error checking must be done in ProposeDisplayMode(),
269 	// and assume that the mode values we get here are acceptable.
270 
271 	WriteSeqReg(0x01, 0x20, 0x20);		// blank the screen
272 
273 	if ( ! Trio64_ModeInit(mode)) {
274 		TRACE("Trio64_ModeInit() failed\n");
275 		return false;
276 	}
277 
278 	Trio64_AdjustFrame(mode);
279 
280 	WriteSeqReg(0x01, 0x00, 0x20);		// unblank the screen
281 
282 	return true;
283 }
284 
285 
286 
287 void
288 Trio64_AdjustFrame(const DisplayModeEx& mode)
289 {
290 	// Adjust start address in frame buffer.
291 
292 	int base = (((mode.v_display_start * mode.virtual_width + mode.h_display_start)
293 			* (mode.bpp / 8)) >> 2) & ~1;
294 	base += gInfo.sharedInfo->frameBufferOffset;
295 
296 	WriteCrtcReg(0x0c, (base >> 8) & 0xff);
297 	WriteCrtcReg(0x0d, base & 0xff);
298 	WriteCrtcReg(0x31, base >> 12, 0x30);	// put bits 16-17 in bits 4-5 of CR31
299 	WriteCrtcReg(0x51, base >> 18, 0x03);	// put bits 18-19 in bits 0-1 of CR51
300 }
301 
302 
303 void
304 Trio64_SetIndexedColors(uint count, uint8 first, uint8* colorData, uint32 flags)
305 {
306 	// Set the indexed color palette for 8-bit color depth mode.
307 
308 	(void)flags;		// avoid compiler warning for unused arg
309 
310 	if (gInfo.sharedInfo->displayMode.space != B_CMAP8)
311 		return ;
312 
313 	while (count--) {
314 		WriteIndexedColor(first++,	// color index
315 			colorData[0] >> 2,		// red
316 			colorData[1] >> 2,		// green
317 			colorData[2] >> 2);		// blue
318 		colorData += 3;
319 	}
320 }
321