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
34static void
35CalcVCLK(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
86static void
87SetCrtcTimingValues(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
170status_t
171I810_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
252void
253I810_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
268void
269I810_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