xref: /haiku/src/add-ons/accelerants/intel_810/i810_mode.cpp (revision 1deede7388b04dbeec5af85cae7164735ea9e70d)
1 /*
2  * Copyright 2012 Haiku, Inc.  All rights reserved.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		Gerald Zajac
7 */
8 
9 /*!
10 	Haiku Intel-810 video driver was adapted from the X.org intel driver which
11 	has the following copyright.
12 
13 	Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
14 	All Rights Reserved.
15  */
16 
17 
18 #include "accelerant.h"
19 #include "i810_regs.h"
20 
21 #include <create_display_modes.h>		// common accelerant header file
22 #include <math.h>
23 #include <unistd.h>
24 
25 
26 // I810_CalcVCLK -- Determine closest clock frequency to the one requested.
27 #define MAX_VCO_FREQ 600.0
28 #define TARGET_MAX_N 30
29 #define REF_FREQ	 24.0
30 
31 #define CALC_VCLK(m,n,p) (double)m / ((double)n * (1 << p)) * 4 * REF_FREQ
32 
33 
34 static void
35 CalcVCLK(double freq, uint16& clkM, uint16& clkN, uint16& clkP) {
36 	int m, n, p;
37 	double f_out, f_best;
38 	double f_err;
39 	double f_vco;
40 	int m_best = 0, n_best = 0, p_best = 0;
41 	double f_target = freq;
42 	double errMax = 0.005;
43 	double errTarget = 0.001;
44 	double errBest = 999999.0;
45 
46 	p_best = p = int(log(MAX_VCO_FREQ / f_target) / log((double)2));
47 	// Make sure p is within range.
48 	if (p_best > 5) {
49 		p_best = p = 5;
50 	}
51 
52 	f_vco = f_target * (1 << p);
53 
54 	n = 2;
55 	do {
56 		n++;
57 		m = int(f_vco / (REF_FREQ / (double)n) / (double)4.0 + 0.5);
58 		if (m < 3)
59 			m = 3;
60 		f_out = CALC_VCLK(m, n, p);
61 		f_err = 1.0 - (f_target / f_out);
62 		if (fabs(f_err) < errMax) {
63 			m_best = m;
64 			n_best = n;
65 			f_best = f_out;
66 			errBest = f_err;
67 		}
68 	} while ((fabs(f_err) >= errTarget) && ((n <= TARGET_MAX_N)
69 		|| (fabs(errBest) > errMax)));
70 
71 	if (fabs(f_err) < errTarget) {
72 		m_best = m;
73 		n_best = n;
74 	}
75 
76 	clkM = (m_best - 2) & 0x3FF;
77 	clkN = (n_best - 2) & 0x3FF;
78 	clkP = (p_best << 4);
79 
80 	TRACE("Setting dot clock to %.1f MHz [ 0x%x 0x%x 0x%x ] [ %d %d %d ]\n",
81 		CALC_VCLK(m_best, n_best, p_best),
82 		clkM, clkN, clkP, m_best, n_best, p_best);
83 }
84 
85 
86 static void
87 SetCrtcTimingValues(const DisplayModeEx& mode)
88 {
89 	// Set the timing values for CRTC registers cr00 to cr18, and some extended
90 	// CRTC registers.
91 
92 	int hTotal = mode.timing.h_total / 8 - 5;
93 	int hDisp_e = mode.timing.h_display / 8 - 1;
94 	int hSync_s = mode.timing.h_sync_start / 8;
95 	int hSync_e = mode.timing.h_sync_end / 8;
96 	int hBlank_s = hDisp_e + 1;		// start of horizontal blanking
97 	int hBlank_e = hTotal;			// end of horizontal blanking
98 
99 	int vTotal = mode.timing.v_total - 2;
100 	int vDisp_e = mode.timing.v_display - 1;
101 	int vSync_s = mode.timing.v_sync_start;
102 	int vSync_e = mode.timing.v_sync_end;
103 	int vBlank_s = vDisp_e;			// start of vertical blanking
104 	int vBlank_e = vTotal;			// end of vertical blanking
105 
106 	uint16 offset = mode.bytesPerRow / 8;
107 
108 	// CRTC Controller values
109 
110 	uint8 crtc[25];
111 	crtc[0x00] = hTotal;
112 	crtc[0x01] = hDisp_e;
113 	crtc[0x02] = hBlank_s;
114 	crtc[0x03] = (hBlank_e & 0x1f) | 0x80;
115 	crtc[0x04] = hSync_s;
116 	crtc[0x05] = ((hSync_e & 0x1f) | ((hBlank_e & 0x20) << 2));
117 	crtc[0x06] = vTotal;
118 	crtc[0x07] = (((vTotal & 0x100) >> 8)
119 		| ((vDisp_e & 0x100) >> 7)
120 		| ((vSync_s & 0x100) >> 6)
121 		| ((vBlank_s & 0x100) >> 5)
122 		| 0x10
123 		| ((vTotal & 0x200) >> 4)
124 		| ((vDisp_e & 0x200) >> 3)
125 		| ((vSync_s & 0x200) >> 2));
126 
127 	crtc[0x08] = 0x00;
128 	crtc[0x09] = ((vBlank_s & 0x200) >> 4) | 0x40;
129 	crtc[0x0a] = 0x00;
130 	crtc[0x0b] = 0x00;
131 	crtc[0x0c] = 0x00;
132 	crtc[0x0d] = 0x00;
133 	crtc[0x0e] = 0x00;
134 	crtc[0x0f] = 0x00;
135 	crtc[0x10] = vSync_s;
136 	crtc[0x11] = (vSync_e & 0x0f) | 0x20;
137 	crtc[0x12] = vDisp_e;
138 	crtc[0x13] = offset;
139 	crtc[0x14] = 0x00;
140 	crtc[0x15] = vBlank_s;
141 	crtc[0x16] = vBlank_e;
142 	crtc[0x17] = 0xc3;
143 	crtc[0x18] = 0xff;
144 
145 	// Set the standard CRTC vga regs;  however, before setting them, unlock
146 	// CRTC reg's 0-7 by clearing bit 7 of cr11
147 
148 	WriteCrtcReg(0x11, crtc[0x11] & ~0x80);
149 
150 	for (uint8 j = 0; j <= 0x18; j++)
151 		WriteCrtcReg(j, crtc[j]);
152 
153 	// Set the extended CRTC reg's.
154 
155 	WriteCrtcReg(EXT_VERT_TOTAL, vTotal >> 8);
156 	WriteCrtcReg(EXT_VERT_DISPLAY, vDisp_e >> 8);
157 	WriteCrtcReg(EXT_VERT_SYNC_START, vSync_s >> 8);
158 	WriteCrtcReg(EXT_VERT_BLANK_START, vBlank_s >> 8);
159 	WriteCrtcReg(EXT_HORIZ_TOTAL, hTotal >> 8);
160 	WriteCrtcReg(EXT_HORIZ_BLANK, (hBlank_e & 0x40) >> 6);
161 	WriteCrtcReg(EXT_OFFSET, offset >> 8);
162 
163 	WriteCrtcReg(INTERLACE_CNTL, INTERLACE_DISABLE);	// turn off interlace
164 
165 	// Enable high resolution mode.
166 	WriteCrtcReg(IO_CTNL, ReadCrtcReg(IO_CTNL) | EXTENDED_CRTC_CNTL);
167 }
168 
169 
170 status_t
171 I810_SetDisplayMode(const DisplayModeEx& mode)
172 {
173 	if (mode.bitsPerPixel != 8 && mode.bitsPerPixel != 16) {
174 		// Only 8 & 16 bits/pixel are suppoted.
175 		TRACE("Unsupported color depth: %d bpp\n", mode.bitsPerPixel);
176 		return B_ERROR;
177 	}
178 
179 	snooze(50000);
180 
181 	// Turn off DRAM refresh.
182 	uint8 temp = INREG8(DRAM_ROW_CNTL_HI) & ~DRAM_REFRESH_RATE;
183 	OUTREG8(DRAM_ROW_CNTL_HI, temp | DRAM_REFRESH_DISABLE);
184 
185 	snooze(1000);			// wait 1 ms
186 
187 	// Calculate the VCLK that most closely matches the requested pixel clock,
188 	// and then set the M, N, and P values.
189 
190 	uint16 m, n, p;
191 	CalcVCLK(mode.timing.pixel_clock / 1000.0, m, n, p);
192 
193 	OUTREG16(VCLK2_VCO_M, m);
194 	OUTREG16(VCLK2_VCO_N, n);
195 	OUTREG8(VCLK2_VCO_DIV_SEL, p);
196 
197 	// Setup HSYNC & VSYNC polarity and select clock source 2 (0x08) for
198 	// programmable PLL.
199 
200 	uint8 miscOutReg = 0x08 | 0x01;
201 	if (!(mode.timing.flags & B_POSITIVE_HSYNC))
202 		miscOutReg |= 0x40;
203 	if (!(mode.timing.flags & B_POSITIVE_VSYNC))
204 		miscOutReg |= 0x80;
205 
206 	OUTREG8(MISC_OUT_W, miscOutReg);
207 
208 	SetCrtcTimingValues(mode);
209 
210 	OUTREG32(MEM_MODE, INREG32(MEM_MODE) | 4);
211 
212 	// Set the address mapping to use the frame buffer memory mapped via the
213 	// GTT table instead of the VGA buffer.
214 
215 	uint8 addrMapping = ReadGraphReg(ADDRESS_MAPPING);
216 	addrMapping &= 0xE0;		// preserve reserved bits 7:5
217 	addrMapping |= (GTT_MEM_MAP_ENABLE | LINEAR_MODE_ENABLE);
218 	WriteGraphReg(ADDRESS_MAPPING, addrMapping);
219 
220 	// Turn on DRAM refresh.
221 	temp = INREG8(DRAM_ROW_CNTL_HI) & ~DRAM_REFRESH_RATE;
222 	OUTREG8(DRAM_ROW_CNTL_HI, temp | DRAM_REFRESH_60HZ);
223 
224 	temp = INREG8(BITBLT_CNTL) & ~COLEXP_MODE;
225 	temp |= (mode.bitsPerPixel == 8 ? COLEXP_8BPP : COLEXP_16BPP);
226 	OUTREG8(BITBLT_CNTL, temp);
227 
228 	// Turn on 8 bit dac mode so that the indexed colors are displayed properly,
229 	// and put display in high resolution mode.
230 
231 	uint32 temp32 = INREG32(PIXPIPE_CONFIG) & 0xF3E062FC;
232 	temp32 |= (DAC_8_BIT | HIRES_MODE | NO_BLANK_DELAY |
233 		(mode.bitsPerPixel == 8 ? DISPLAY_8BPP_MODE : DISPLAY_16BPP_MODE));
234 	OUTREG32(PIXPIPE_CONFIG, temp32);
235 
236 	OUTREG16(EIR, 0);
237 
238 	temp32 = INREG32(FWATER_BLC);
239 	temp32 &= ~(LM_BURST_LENGTH | LM_FIFO_WATERMARK
240 		| MM_BURST_LENGTH | MM_FIFO_WATERMARK);
241 	temp32 |= I810_GetWatermark(mode);
242 	OUTREG32(FWATER_BLC, temp32);
243 
244 	// Enable high resolution mode.
245 	WriteCrtcReg(IO_CTNL, ReadCrtcReg(IO_CTNL) | EXTENDED_CRTC_CNTL);
246 
247 	I810_AdjustFrame(mode);
248 	return B_OK;
249 }
250 
251 
252 void
253 I810_AdjustFrame(const DisplayModeEx& mode)
254 {
255 	// Adjust start address in frame buffer.
256 
257 	uint32 address = ((mode.v_display_start * mode.virtual_width
258 		+ mode.h_display_start) * mode.bytesPerPixel) >> 2;
259 
260 	WriteCrtcReg(START_ADDR_LO, address & 0xff);
261 	WriteCrtcReg(START_ADDR_HI, (address >> 8) & 0xff);
262 	WriteCrtcReg(EXT_START_ADDR_HI, (address >> 22) & 0xff);
263 	WriteCrtcReg(EXT_START_ADDR,
264 		((address >> 16) & 0x3f) | EXT_START_ADDR_ENABLE);
265 }
266 
267 
268 void
269 I810_SetIndexedColors(uint count, uint8 first, uint8* colorData, uint32 flags)
270 {
271 	// Set the indexed color palette for 8-bit color depth mode.
272 
273 	(void)flags;		// avoid compiler warning for unused arg
274 
275 	if (gInfo.sharedInfo->displayMode.space != B_CMAP8)
276 		return ;
277 
278 	OUTREG8(DAC_MASK, 0xff);
279 	OUTREG8(DAC_W_INDEX, first);		// initial color index
280 
281 	while (count--) {
282 		OUTREG8(DAC_DATA, colorData[0]);	// red
283 		OUTREG8(DAC_DATA, colorData[1]);	// green
284 		OUTREG8(DAC_DATA, colorData[2]);	// blue
285 
286 		colorData += 3;
287 	}
288 }
289