xref: /haiku/src/add-ons/accelerants/matrox/engine/tvp3026.c (revision b408aaf70ae331b21e3928c90e917ebb2a053405)
1bc29b740Sshatty /*
2bc29b740Sshatty 	Program the Texas TVP3026
3bc29b740Sshatty 	using Texas Instrument TVP3026 manual SLA098B July 1996
4bc29b740Sshatty 
5*b408aaf7Sshatty 	Authors:
6*b408aaf7Sshatty 	Apsed May 2002 plus a lot of time after;
7*b408aaf7Sshatty 	Rudolf Cornelissen 3/2003.
8bc29b740Sshatty */
9bc29b740Sshatty 
10bc29b740Sshatty #define MODULE_BIT 0x00010000
11bc29b740Sshatty 
12bc29b740Sshatty #include <OS.h> // system_time, snooze
13bc29b740Sshatty #include "mga_std.h"
14bc29b740Sshatty 
15bc29b740Sshatty #define FVCO_MIN  110.00000 // MHz
16bc29b740Sshatty #define FPLL_MCLK 100.00000 // MHz
17bc29b740Sshatty 
18*b408aaf7Sshatty //r: ??
19bc29b740Sshatty #define MGAVGA_INSTS0       0x1FC2
20bc29b740Sshatty 
21bc29b740Sshatty #define WAIT_FOR_PLL_LOCK( pll, on_error) do { \
22bc29b740Sshatty 	bigtime_t start, now; \
23bc29b740Sshatty 	float     delay; \
24bc29b740Sshatty 	int       tmo; \
25bc29b740Sshatty 	\
26bc29b740Sshatty 	start = system_time(); \
27bc29b740Sshatty 	for (tmo = 0; tmo < 100 * 1000 * 1000; tmo++) { \
28*b408aaf7Sshatty 		int status = DXIR (pll ## PLLDATA); \
29bc29b740Sshatty 		if (status & 0x40) break; \
30bc29b740Sshatty 		/* snooze(10); */ \
31bc29b740Sshatty 	} \
32bc29b740Sshatty 	now = system_time(); \
33bc29b740Sshatty 	delay = (now - start) / 1000.0; \
34bc29b740Sshatty 	LOG(2,("mil2 %s PLL locked in %fms for %d loops\n", #pll, delay, tmo)); \
35bc29b740Sshatty 	if (tmo == 100 * 1000 * 1000) { \
36bc29b740Sshatty 		LOG(8,("mil2 %s PLL not locked in %fms for %d loops\n", #pll, delay, tmo)); \
37bc29b740Sshatty 		on_error; \
38bc29b740Sshatty 	} \
39bc29b740Sshatty } while (0)
40bc29b740Sshatty 
41bc29b740Sshatty /*program the pixpll - frequency in MHz*/
mil2_dac_set_pix_pll(float f_need,int bpp)42bc29b740Sshatty status_t mil2_dac_set_pix_pll (float f_need, int bpp)
43bc29b740Sshatty {
44bc29b740Sshatty 	uint8    n, m, p, q;
45bc29b740Sshatty 	int      z, k;
46bc29b740Sshatty 	float    fd;
47bc29b740Sshatty 	status_t result;
48bc29b740Sshatty 
49*b408aaf7Sshatty 	display_mode target;
50*b408aaf7Sshatty 	target.timing.pixel_clock = (f_need * 1000);
51*b408aaf7Sshatty 
52bc29b740Sshatty 	LOG(4,("mil2_dac_set_pix_pll need %fMHz, %dbpp\n", f_need, bpp));
53*b408aaf7Sshatty 	result = gx00_dac_pix_pll_find(target, &fd, &m, &n, &p, 0);
54bc29b740Sshatty 	if (result != B_OK) return result;
55bc29b740Sshatty 
56bc29b740Sshatty 	// follows (!strictly) Appendix C, extended mode setup
57bc29b740Sshatty 	// 1st stop the PLLs,
58bc29b740Sshatty 	switch (bpp) {
59*b408aaf7Sshatty 		case  8: DXIW(TVP_CLOCKSEL, 0x25); break;
60*b408aaf7Sshatty 		case 16: DXIW(TVP_CLOCKSEL, 0x15); break;
61*b408aaf7Sshatty 		case 24: DXIW(TVP_CLOCKSEL, 0x25); break;
62*b408aaf7Sshatty 		case 32: DXIW(TVP_CLOCKSEL, 0x05); break;
63bc29b740Sshatty 		default: return B_ERROR;
64bc29b740Sshatty 	}
65*b408aaf7Sshatty 	DXIW(TVP_PLLADDR, 0x2a);        // 0x2c: 2.4 select P to ...
66*b408aaf7Sshatty 	DXIW(TVP_LOOPLLDATA, 0x00);     // 0x2f: 2.4.1 ... stop the loop PLL
67*b408aaf7Sshatty 	DXIW(TVP_PIXPLLDATA, 0x00);     // 0x2d: 2.4.1 ... stop the pixel PLL
68bc29b740Sshatty 	VGAW (MISCW, VGAR(MISCR) | 0x0c); // PLLSEL(1,0) set to 1x
69bc29b740Sshatty 
70bc29b740Sshatty 	// 2nd setup the pixel PLL
71bc29b740Sshatty 	LOG(2,("mil2_dac_set_pix_pll pix PLL, nmp 0x%02x 0x%02x 0x%02x\n",
72bc29b740Sshatty 		n, m, p));
73*b408aaf7Sshatty 	DXIW(TVP_PLLADDR, 0x00);        // 0x2c: 2.4 select N to ...
74*b408aaf7Sshatty 	DXIW(TVP_PIXPLLDATA, n | 0xc0); // 0x2d: ... load n, m, p and ...
75*b408aaf7Sshatty 	DXIW(TVP_PIXPLLDATA, m);
76*b408aaf7Sshatty 	DXIW(TVP_PIXPLLDATA, p | 0xb0);
77*b408aaf7Sshatty 	WAIT_FOR_PLL_LOCK (TVP_PIX, return B_ERROR); // ... wait for PLL lock
78bc29b740Sshatty 
79bc29b740Sshatty 	// now compute parameters for the loop PLL (24bpp not available) see 2.4.3.1
80bc29b740Sshatty 	k = 1; // ?? external division factor between RCLK and LCLK
81*b408aaf7Sshatty //	does 32bit DAC path exists for MIL1/2? if so, do this via si->ps...
82*b408aaf7Sshatty //	n = (65 - (4 * (PIXEL_BUS_WIDTH64? 64: 32)) / bpp);
83*b408aaf7Sshatty 	n = (65 - (4 * 64) / bpp);
84bc29b740Sshatty 	m = 61;
85bc29b740Sshatty 	z = (FVCO_MIN * (65 - n)) / (4 * fd * k);
86bc29b740Sshatty 	q = 0;
87bc29b740Sshatty 	if      (z <  2) p = 0; // this is TRUNC (log2 (z))
88bc29b740Sshatty 	else if (z <  4) p = 1;
89bc29b740Sshatty 	else if (z <  8) p = 2;
90bc29b740Sshatty 	else if (z < 16) p = 3;
91bc29b740Sshatty 	else { // but not this
92bc29b740Sshatty 		p = 3;
93bc29b740Sshatty 		q = 1 + (z - 16) / 16;
94bc29b740Sshatty 	}
95bc29b740Sshatty 	LOG(2,("mil2_dac_set_pix_pll loop PLL, nmpq 0x%02x 0x%02x 0x%02x 0x%02x\n",
96bc29b740Sshatty 		n, m, p, q));
97*b408aaf7Sshatty 	DXIW(TVP_MEMCLKCTRL, (DXIR(TVP_MEMCLKCTRL) & 0xf8) | q | 0x20); // 0x39: 2.4.2 table 2.13
98bc29b740Sshatty 	LOG(2,("mil2_dac_set_pix_pll loop PLL, nmpq 0x%02x 0x%02x 0x%02x 0x%02x\n",
99bc29b740Sshatty 		n, m, p, q));
100bc29b740Sshatty 
101bc29b740Sshatty 	// now setup the loop PLL
102bc29b740Sshatty 	LOG(2,("mil2_dac_set_pix_pll loop PLL, nmpq 0x%02x 0x%02x 0x%02x 0x%02x\n",
103bc29b740Sshatty 		n, m, p, q));
104*b408aaf7Sshatty 	DXIW(TVP_PLLADDR, 0x00);        // 0x2c: 2.4 select N to ...
105*b408aaf7Sshatty 	DXIW(TVP_LOOPLLDATA, n | 0xc0); // 0x2f: ... load n, m, p and ...
106*b408aaf7Sshatty 	DXIW(TVP_LOOPLLDATA, m);
107*b408aaf7Sshatty 	DXIW(TVP_LOOPLLDATA, p | 0xf0);
108*b408aaf7Sshatty 	WAIT_FOR_PLL_LOCK (TVP_LOO, return B_ERROR); // ... wait for PLL lock
109bc29b740Sshatty 
110bc29b740Sshatty 	return B_OK;
111bc29b740Sshatty }
112bc29b740Sshatty 
113bc29b740Sshatty // set MEM clock MCLK , shall be <= 100MHz
mil2_dac_set_mem_pll(float f_need,float * mclk)114bc29b740Sshatty static status_t mil2_dac_set_mem_pll (float f_need, float *mclk)
115bc29b740Sshatty {
116bc29b740Sshatty 	status_t status;
117bc29b740Sshatty 	uint8    n, m, p;
118bc29b740Sshatty 	uint8    n_pix, m_pix, p_pix;
119bc29b740Sshatty 	uint8    memclkctrl;
120bc29b740Sshatty 
121*b408aaf7Sshatty 	display_mode target;
122*b408aaf7Sshatty 	target.timing.pixel_clock = (f_need * 1000);
123*b408aaf7Sshatty 
124*b408aaf7Sshatty 	LOG(4,("mil2_dac_set_sys_pll need %fMHz\n", f_need));
125*b408aaf7Sshatty 	//fixme: MIL has same restrictions for pixel and system PLL, so Apsed did this:
126*b408aaf7Sshatty 	status = gx00_dac_pix_pll_find(target, mclk, &m, &n, &p, 0);
127bc29b740Sshatty 	if (status != B_OK) return status;
128bc29b740Sshatty 
129bc29b740Sshatty 	// follows (!strictly) TVP3026 2.4.2.1, extended mode setup
130bc29b740Sshatty 
131bc29b740Sshatty 	// 0) save PIXPLL nmp to restore it at end
132*b408aaf7Sshatty 	DXIW(TVP_PLLADDR, 0x00);
133*b408aaf7Sshatty 	n_pix = DXIR(TVP_PIXPLLDATA);
134*b408aaf7Sshatty 	DXIW(TVP_PLLADDR, 0x15);
135*b408aaf7Sshatty 	m_pix = DXIR(TVP_PIXPLLDATA);
136*b408aaf7Sshatty 	DXIW(TVP_PLLADDR, 0x2a);
137*b408aaf7Sshatty 	p_pix = DXIR(TVP_PIXPLLDATA);
138bc29b740Sshatty 
139bc29b740Sshatty 	// 1) disable pixel PLL, set pixel PLL at MCLK freq and poll for lock
140*b408aaf7Sshatty 	DXIW(TVP_PLLADDR, 0x2a);        // 0x2c: 2.4 select P to ...
141*b408aaf7Sshatty 	DXIW(TVP_PIXPLLDATA, 0x00);     // 0x2d: 2.4.1 ... stop the PLL
142*b408aaf7Sshatty 	DXIW(TVP_PLLADDR, 0x00);        // 0x2c: 2.4 select N to ...
143*b408aaf7Sshatty 	DXIW(TVP_PIXPLLDATA, n | 0xc0); // 0x2d: ... load n, m, p and ...
144*b408aaf7Sshatty 	DXIW(TVP_PIXPLLDATA, m);
145*b408aaf7Sshatty 	DXIW(TVP_PIXPLLDATA, p | 0xb0);
146*b408aaf7Sshatty 	WAIT_FOR_PLL_LOCK (TVP_PIX, return B_ERROR); // ... wait for PLL lock
147bc29b740Sshatty 
148bc29b740Sshatty 	// 2) select pixel clock as dot clock source
149bc29b740Sshatty 	VGAW (MISCW, VGAR(MISCR) | 0x0c); // PLLSEL(1,0) set to 1x
150bc29b740Sshatty 
151bc29b740Sshatty 	// 3) output dot clock on MCLK pin
152*b408aaf7Sshatty 	memclkctrl = DXIR(TVP_MEMCLKCTRL) & 0xe7;
153*b408aaf7Sshatty 	DXIW(TVP_MEMCLKCTRL, memclkctrl | (0x00 << 3));
154*b408aaf7Sshatty 	DXIW(TVP_MEMCLKCTRL, memclkctrl | (0x01 << 3));
155bc29b740Sshatty 
156bc29b740Sshatty 	// 4) disable mem PLL, set mem PLL at MCLK freq and poll for lock
157*b408aaf7Sshatty 	DXIW(TVP_PLLADDR, 0x2a);
158*b408aaf7Sshatty 	DXIW(TVP_MEMPLLDATA, 0x00);
159*b408aaf7Sshatty 	DXIW(TVP_PLLADDR, 0x00);
160*b408aaf7Sshatty 	DXIW(TVP_MEMPLLDATA, n | 0xc0);
161*b408aaf7Sshatty 	DXIW(TVP_MEMPLLDATA, m);
162*b408aaf7Sshatty 	DXIW(TVP_MEMPLLDATA, p | 0xb0);
163*b408aaf7Sshatty 	WAIT_FOR_PLL_LOCK (TVP_MEM, return B_ERROR);
164bc29b740Sshatty 
165bc29b740Sshatty 	// 5) output mem clock on MCLK pin
166*b408aaf7Sshatty 	DXIW(TVP_MEMCLKCTRL, memclkctrl | (0x02 << 3));
167*b408aaf7Sshatty 	DXIW(TVP_MEMCLKCTRL, memclkctrl | (0x03 << 3));
168bc29b740Sshatty 
169bc29b740Sshatty 	// 6) restaure pixel clock as it was
170*b408aaf7Sshatty 	DXIW(TVP_PLLADDR, 0x2a);
171*b408aaf7Sshatty 	DXIW(TVP_PIXPLLDATA, 0x00);
172*b408aaf7Sshatty 	DXIW(TVP_PLLADDR, 0x00);
173*b408aaf7Sshatty 	DXIW(TVP_PIXPLLDATA, n_pix | 0xc0);
174*b408aaf7Sshatty 	DXIW(TVP_PIXPLLDATA, m_pix);
175*b408aaf7Sshatty 	DXIW(TVP_PIXPLLDATA, p_pix | 0xb0);
176*b408aaf7Sshatty 	WAIT_FOR_PLL_LOCK (TVP_PIX, return B_ERROR);
177bc29b740Sshatty 
178bc29b740Sshatty 	return B_OK;
179bc29b740Sshatty }
180bc29b740Sshatty 
181bc29b740Sshatty /* initialize TVP3026: memory clock PLL ... */
mil2_dac_init(void)182bc29b740Sshatty status_t mil2_dac_init (void)
183bc29b740Sshatty {
184bc29b740Sshatty 	status_t status;
185bc29b740Sshatty 	float    need = 60.0; // MHz FPLL_MCLK
186bc29b740Sshatty 	float    mclk;
187bc29b740Sshatty 	uint32   option;
188bc29b740Sshatty 	uint32   rfhcnt, nogscale, memconfig;
189bc29b740Sshatty 
190bc29b740Sshatty 	LOG(4, ("mil2_dac_init MISC 0x%02x\n", VGAR(MISCR)));
191bc29b740Sshatty 	CFGW(DEVCTRL,(2|CFGR(DEVCTRL))); // enable device response (already enabled here!)
192bc29b740Sshatty 	VGAW_I(CRTC,0x11,0);             // allow me to change CRTC
193bc29b740Sshatty 	VGAW_I(CRTCEXT,3,0x80);          // use powergraphix (+ trash other bits, they are set later)
194bc29b740Sshatty 	VGAW(MISCW,0x08);                // set MGA pixel clock in MISC - PLLSEL10 of TVP3026 is 1X
195bc29b740Sshatty 
196bc29b740Sshatty 	// set MEM clock MCLK following TVP3026 2.4.2.1
197bc29b740Sshatty 	status = mil2_dac_set_mem_pll (need, &mclk); //FPLL_MCLK);
198bc29b740Sshatty 	if (status != B_OK) return status;
199bc29b740Sshatty 
200bc29b740Sshatty 	// update OPTION for refresh count following page 3-18
201bc29b740Sshatty 	// 33.2mus >= (rfhcnt31*512 + rfhcnt0*64 + 1)*gclk_period*factor
202bc29b740Sshatty 	// rfhcnt31*512 + rfhcnt0*64 <= -1 + 33.2mus*mclk/factor
203bc29b740Sshatty 	if (1) {
204bc29b740Sshatty 		int mclk_p   = 1;
205bc29b740Sshatty 		int nogscale = 1;
206bc29b740Sshatty 		int f_pll    = mclk * 1000 * 333 / (10000 << mclk_p);
207bc29b740Sshatty 		int rfhcnt   = (f_pll - 128) / 256;
208bc29b740Sshatty 		if (rfhcnt > 15) rfhcnt = 15;
209bc29b740Sshatty 		LOG(2,("mil2_dac_init: refresh count %d nogscale %d for %fMHz\n",
210bc29b740Sshatty 			rfhcnt, nogscale, mclk));
211bc29b740Sshatty 	}
212bc29b740Sshatty 	for (nogscale = 1; nogscale >= 0; nogscale--) {
213bc29b740Sshatty 		int max = -1 + 33.2 * mclk / (nogscale? 1: 4);
214bc29b740Sshatty 		for (rfhcnt = 15; rfhcnt > 0; rfhcnt--) {
215bc29b740Sshatty 			int value = (rfhcnt & 0x0e) * 256 + (rfhcnt & 0x01) * 64;
216bc29b740Sshatty 			LOG(2,("mil2_dac_init factor %d, rfhcnt %2d: %d ?<= %d\n",
217bc29b740Sshatty 				nogscale, rfhcnt, value, max));
218bc29b740Sshatty 			if (value <= max) goto rfhcnt_found;
219bc29b740Sshatty 		}
220bc29b740Sshatty 	}
221bc29b740Sshatty 	LOG(8,("mil2_dac_init: cant get valid refresh count for %fMHz\n", mclk));
222bc29b740Sshatty 	return B_ERROR;
223bc29b740Sshatty rfhcnt_found:
224bc29b740Sshatty 	LOG(2,("mil2_dac_init: found refresh count %d nogscale %d for %fMHz\n",
225bc29b740Sshatty 		rfhcnt, nogscale, mclk));
226bc29b740Sshatty 
227*b408aaf7Sshatty 	memconfig = 0x01; // worst case scenario: 64 bits RAMDAC (= tvp3026m) with >2Mb RAM.
228bc29b740Sshatty 	option = CFGR(OPTION) & 0xffd0cfff;
229bc29b740Sshatty 	CFGW(OPTION, option | (nogscale << 21) | (rfhcnt << 16) | (memconfig << 12));
230bc29b740Sshatty 	LOG(2,("mil2_dac_init: OPTION 0x%08x\n", CFGR(OPTION)));
231bc29b740Sshatty 
232*b408aaf7Sshatty 	//r: select indirect cursor control register and set defaults
233*b408aaf7Sshatty 	DXIW(CURCTRL, 0x00);
234bc29b740Sshatty 	return B_OK;
235bc29b740Sshatty }
236