1/* CTRC functionality */
2/* Author:
3   Rudolf Cornelissen 4/2003-1/2006
4*/
5
6#define MODULE_BIT 0x00040000
7
8#include "nm_std.h"
9
10/* Adjust passed parameters to a valid mode line */
11status_t nm_crtc_validate_timing(
12	uint16 *hd_e,uint16 *hs_s,uint16 *hs_e,uint16 *ht,
13	uint16 *vd_e,uint16 *vs_s,uint16 *vs_e,uint16 *vt
14)
15{
16/* horizontal */
17	/* make all parameters multiples of 8 */
18	*hd_e &= 0xfff8;
19	*hs_s &= 0xfff8;
20	*hs_e &= 0xfff8;
21	*ht   &= 0xfff8;
22
23	/* confine to required number of bits, taking logic into account */
24	if (*hd_e > ((0xff - 2) << 3)) *hd_e = ((0xff - 2) << 3);
25	if (*hs_s > ((0xff - 1) << 3)) *hs_s = ((0xff - 1) << 3);
26	if (*hs_e > ( 0xff      << 3)) *hs_e = ( 0xff      << 3);
27	if (*ht   > ((0xff + 5) << 3)) *ht   = ((0xff + 5) << 3);
28
29	/* NOTE: keep horizontal timing at multiples of 8! */
30	/* confine to a reasonable width */
31	if (*hd_e < 640) *hd_e = 640;
32	if (*hd_e > si->ps.max_crtc_width) *hd_e = si->ps.max_crtc_width;
33
34	/* if hor. total does not leave room for a sensible sync pulse,*/
35	/* increase it! */
36	if (*ht < (*hd_e + 80)) *ht = (*hd_e + 80);
37
38	/* if hor. total does not adhere to max. blanking pulse width,*/
39	/* decrease it! */
40	if (*ht > (*hd_e + 0x1f8)) *ht = (*hd_e + 0x1f8);
41
42	/* make sure sync pulse is not during display */
43	if (*hs_e > (*ht - 8)) *hs_e = (*ht - 8);
44	if (*hs_s < (*hd_e + 8)) *hs_s = (*hd_e + 8);
45
46	/* correct sync pulse if it is too long:
47	 * there are only 5 bits available to save this in the card registers! */
48	if (*hs_e > (*hs_s + 0xf8)) *hs_e = (*hs_s + 0xf8);
49
50/*vertical*/
51	/* confine to required number of bits, taking logic into account */
52	if (si->ps.card_type < NM2200)
53	{
54		if (*vd_e > (0x3ff - 2)) *vd_e = (0x3ff - 2);
55		if (*vs_s > (0x3ff - 1)) *vs_s = (0x3ff - 1);
56		if (*vs_e >  0x3ff     ) *vs_e =  0x3ff     ;
57		if (*vt   > (0x3ff + 2)) *vt   = (0x3ff + 2);
58	}
59	else
60	{
61		if (*vd_e > (0x7ff - 2)) *vd_e = (0x7ff - 2);
62		if (*vs_s > (0x7ff - 1)) *vs_s = (0x7ff - 1);
63		if (*vs_e >  0x7ff     ) *vs_e =  0x7ff     ;
64		if (*vt   > (0x7ff + 2)) *vt   = (0x7ff + 2);
65	}
66
67	/* confine to a reasonable height */
68	if (*vd_e < 480) *vd_e = 480;
69	if (*vd_e > si->ps.max_crtc_height) *vd_e = si->ps.max_crtc_height;
70
71	/*if vertical total does not leave room for a sync pulse, increase it!*/
72	if (*vt < (*vd_e + 3)) *vt = (*vd_e + 3);
73
74	/* if vert. total does not adhere to max. blanking pulse width,*/
75	/* decrease it! */
76	if (*vt > (*vd_e + 0xff)) *vt = (*vd_e + 0xff);
77
78	/* make sure sync pulse is not during display */
79	if (*vs_e > (*vt - 1)) *vs_e = (*vt - 1);
80	if (*vs_s < (*vd_e + 1)) *vs_s = (*vd_e + 1);
81
82	/* correct sync pulse if it is too long:
83	 * there are only 4 bits available to save this in the card registers! */
84	if (*vs_e > (*vs_s + 0x0f)) *vs_e = (*vs_s + 0x0f);
85
86	return B_OK;
87}
88
89/* set a mode line */
90status_t nm_crtc_set_timing(display_mode target, bool crt_only)
91{
92	uint8 temp;
93
94	uint32 htotal;		/*total horizontal total VCLKs*/
95	uint32 hdisp_e;            /*end of horizontal display (begins at 0)*/
96	uint32 hsync_s;            /*begin of horizontal sync pulse*/
97	uint32 hsync_e;            /*end of horizontal sync pulse*/
98	uint32 hblnk_s;            /*begin horizontal blanking*/
99	uint32 hblnk_e;            /*end horizontal blanking*/
100
101	uint32 vtotal;		/*total vertical total scanlines*/
102	uint32 vdisp_e;            /*end of vertical display*/
103	uint32 vsync_s;            /*begin of vertical sync pulse*/
104	uint32 vsync_e;            /*end of vertical sync pulse*/
105	uint32 vblnk_s;            /*begin vertical blanking*/
106	uint32 vblnk_e;            /*end vertical blanking*/
107
108	uint32 linecomp;	/*split screen and vdisp_e interrupt*/
109
110	LOG(4,("CRTC: setting timing\n"));
111
112	/* modify visible sceensize if needed */
113	/* (note that MOVE_CURSOR checks for a panning/scrolling mode itself,
114	 *  the display_mode as placed in si->dm may _not_ be modified!) */
115	if (!crt_only)
116	{
117		if (target.timing.h_display > si->ps.panel_width)
118		{
119			target.timing.h_display = si->ps.panel_width;
120			LOG(4,
121				("CRTC: req. width > panel width: setting panning mode\n"));
122		}
123		if (target.timing.v_display > si->ps.panel_height)
124		{
125			target.timing.v_display = si->ps.panel_height;
126			LOG(4,
127				("CRTC: req. height > panel height: setting scrolling "
128				"mode\n"));
129		}
130
131		/* modify sync polarities
132		 * (needed to maintain correct panel centering):
133		 * both polarities must be negative (confirmed NM2160) */
134		target.timing.flags &= ~(B_POSITIVE_HSYNC | B_POSITIVE_VSYNC);
135	}
136
137	/* Modify parameters as required by standard VGA */
138	htotal = ((target.timing.h_total >> 3) - 5);
139	hdisp_e = ((target.timing.h_display >> 3) - 1);
140	hblnk_s = hdisp_e;
141	hblnk_e = (htotal + 0); /* this register differs from standard VGA! */
142		/* (says + 4) */
143	hsync_s = (target.timing.h_sync_start >> 3);
144	hsync_e = (target.timing.h_sync_end >> 3);
145
146	vtotal = target.timing.v_total - 2;
147	vdisp_e = target.timing.v_display - 1;
148	vblnk_s = vdisp_e;
149	vblnk_e = (vtotal + 1);
150	vsync_s = target.timing.v_sync_start;//-1;
151	vsync_e = target.timing.v_sync_end;//-1;
152
153	/* prevent memory adress counter from being reset */
154	/* (linecomp may not occur) */
155	linecomp = target.timing.v_display;
156
157	if (crt_only)
158	{
159		LOG(4,("CRTC: CRT only mode, setting full timing...\n"));
160
161		/* log the mode that will be set */
162		LOG(2,("CRTC:\n\tHTOT:%x\n\tHDISPEND:%x\n\tHBLNKS:%x\n\tHBLNKE:%x\n\tHSYNCS:%x\n\tHSYNCE:%x\n\t",htotal,hdisp_e,hblnk_s,hblnk_e,hsync_s,hsync_e));
163		LOG(2,("VTOT:%x\n\tVDISPEND:%x\n\tVBLNKS:%x\n\tVBLNKE:%x\n\tVSYNCS:%x\n\tVSYNCE:%x\n",vtotal,vdisp_e,vblnk_s,vblnk_e,vsync_s,vsync_e));
164
165		/* actually program the card! */
166		/* unlock CRTC registers at index 0-7 */
167		temp = (ISACRTCR(VSYNCE) & 0x7f);
168		/* we need to wait a bit or the card will mess-up it's */
169		/* register values.. */
170		snooze(10);
171		ISACRTCW(VSYNCE, temp);
172		/* horizontal standard VGA regs */
173		ISACRTCW(HTOTAL, (htotal & 0xff));
174		ISACRTCW(HDISPE, (hdisp_e & 0xff));
175		ISACRTCW(HBLANKS, (hblnk_s & 0xff));
176		/* also unlock vertical retrace registers in advance */
177		ISACRTCW(HBLANKE, ((hblnk_e & 0x1f) | 0x80));
178		ISACRTCW(HSYNCS, (hsync_s & 0xff));
179		ISACRTCW(HSYNCE, ((hsync_e & 0x1f) | ((hblnk_e & 0x20) << 2)));
180
181		/* vertical standard VGA regs */
182		ISACRTCW(VTOTAL, (vtotal & 0xff));
183		ISACRTCW(OVERFLOW,
184		(
185			((vtotal & 0x100) >> (8 - 0)) | ((vtotal & 0x200) >> (9 - 5)) |
186			((vdisp_e & 0x100) >> (8 - 1)) | ((vdisp_e & 0x200) >> (9 - 6)) |
187			((vsync_s & 0x100) >> (8 - 2)) | ((vsync_s & 0x200) >> (9 - 7)) |
188			((vblnk_s & 0x100) >> (8 - 3)) | ((linecomp & 0x100) >> (8 - 4))
189		));
190		ISACRTCW(PRROWSCN, 0x00); /* not used */
191		ISACRTCW(MAXSCLIN, (((vblnk_s & 0x200) >> (9 - 5))
192			| ((linecomp & 0x200) >> (9 - 6))));
193		ISACRTCW(VSYNCS, (vsync_s & 0xff));
194		temp = (ISACRTCR(VSYNCE) & 0xf0);
195		/* we need to wait a bit or the card will mess-up it's */
196		/* register values.. */
197		snooze(10);
198		ISACRTCW(VSYNCE, (temp | (vsync_e & 0x0f)));
199		ISACRTCW(VDISPE, (vdisp_e & 0xff));
200		ISACRTCW(VBLANKS, (vblnk_s & 0xff));
201		ISACRTCW(VBLANKE, (vblnk_e & 0xff));
202//linux: (BIOSmode)
203//	regp->CRTC[23] = 0xC3;
204		ISACRTCW(LINECOMP, (linecomp & 0xff));
205
206		/* horizontal - no extended regs available or needed on */
207		/* NeoMagic chips */
208
209		/* vertical - extended regs */
210//fixme: checkout if b2 or 3 should be switched! (linux contains error here)
211//fixme: linecomp should also have an extra bit... testable by setting linecomp
212//to 100 for example and then try out writing an '1' to b2, b3(!) and the rest
213//for screenorig reset visible on upper half of the screen or not at all..
214		if (si->ps.card_type >= NM2200)
215			ISACRTCW(VEXT,
216			(
217			 	((vtotal & 0x400) >> (10 - 0)) |
218				((vdisp_e & 0x400) >> (10 - 1)) |
219				((vblnk_s & 0x400) >> (10 - 2)) |
220				((vsync_s & 0x400) >> (10 - 3))/*|
221				((linecomp&0x400)>>3)*/
222			));
223	}
224	else
225	{
226		LOG(4,
227			("CRTC: internal flatpanel active, setting display region only\n"));
228
229		/* actually program the card! */
230		/* unlock CRTC registers at index 0-7 */
231		temp = (ISACRTCR(VSYNCE) & 0x7f);
232		/* we need to wait a bit or the card will mess-up it's */
233		/* register values.. */
234		snooze(10);
235		ISACRTCW(VSYNCE, temp);
236		/* horizontal standard VGA regs */
237		ISACRTCW(HDISPE, (hdisp_e & 0xff));
238
239		/* vertical standard VGA regs */
240		temp = (ISACRTCR(OVERFLOW) & ~0x52);
241		/* we need to wait a bit or the card will mess-up it's */
242		/* register values.. */
243		snooze(10);
244		ISACRTCW(OVERFLOW,
245		(
246			temp |
247			((vdisp_e & 0x100) >> (8 - 1)) |
248			((vdisp_e & 0x200) >> (9 - 6)) |
249			((linecomp & 0x100) >> (8 - 4))
250		));
251
252		ISACRTCW(PRROWSCN, 0x00); /* not used */
253
254		temp = (ISACRTCR(MAXSCLIN) & ~0x40);
255		/* we need to wait a bit or the card will mess-up it's */
256		/* register values.. */
257		snooze(10);
258		ISACRTCW(MAXSCLIN, (temp | ((linecomp & 0x200) >> (9 - 6))));
259
260		ISACRTCW(VDISPE, (vdisp_e & 0xff));
261//linux:(BIOSmode)
262//	regp->CRTC[23] = 0xC3;
263		ISACRTCW(LINECOMP, (linecomp & 0xff));
264
265		/* horizontal - no extended regs available or needed on */
266		/* NeoMagic chips */
267
268		/* vertical - extended regs */
269//fixme: linecomp should have an extra bit... testable by setting linecomp
270//to 100 for example and then try out writing an '1' to b2, b3(!) and the rest
271//for screenorig reset visible on upper half of the screen or not at all..
272		if (si->ps.card_type >= NM2200)
273		{
274			temp = (ISACRTCR(VEXT) & ~0x02);
275			/* we need to wait a bit or the card will mess-up it's */
276			/* register values.. */
277			snooze(10);
278			ISACRTCW(VEXT,
279			(
280				temp |
281				((vdisp_e & 0x400) >> (10 - 1))/*|
282				((linecomp&0x400)>>3)*/
283			));
284		}
285	}
286
287	/* setup HSYNC & VSYNC polarity */
288	LOG(2,("CRTC: sync polarity: "));
289	temp = ISARB(MISCR);
290	if (target.timing.flags & B_POSITIVE_HSYNC)
291	{
292		LOG(2,("H:pos "));
293		temp &= ~0x40;
294	}
295	else
296	{
297		LOG(2,("H:neg "));
298		temp |= 0x40;
299	}
300	if (target.timing.flags & B_POSITIVE_VSYNC)
301	{
302		LOG(2,("V:pos "));
303		temp &= ~0x80;
304	}
305	else
306	{
307		LOG(2,("V:neg "));
308		temp |= 0x80;
309	}
310	/* we need to wait a bit or the card will mess-up it's register values.. */
311	snooze(10);
312	ISAWB(MISCW, temp);
313	LOG(2,(", MISC reg readback: $%02x\n", ISARB(MISCR)));
314
315	/* program 'fixed' mode if needed */
316	if (si->ps.card_type != NM2070)
317	{
318		uint8 width;
319
320		temp = ISAGRPHR(PANELCTRL1);
321		/* we need to wait a bit or the card will mess-up it's register values.. */
322		snooze(10);
323
324		switch (target.timing.h_display)
325		{
326		case 1280:
327			width = (3 << 5);
328			break;
329		case 1024:
330			width = (2 << 5);
331			break;
332		case 800:
333			width = (1 << 5);
334			break;
335		case 640:
336		default: //fixme: non-std modes should be in between above modes?!?
337			width = (0 << 5);
338			break;
339		}
340
341		switch (si->ps.card_type)
342		{
343		case NM2090:
344		case NM2093:
345		case NM2097:
346		case NM2160:
347			//fixme: checkout b6????
348			ISAGRPHW(PANELCTRL1, ((temp & ~0x20) | (width & 0x20)));
349			break;
350		default:
351			/* NM2200 and later */
352			ISAGRPHW(PANELCTRL1, ((temp & ~0x60) | (width & 0x60)));
353			break;
354		}
355	}
356
357	return B_OK;
358}
359
360status_t nm_crtc_depth(int mode)
361{
362	uint8 vid_delay = 0;
363
364	LOG(4,("CRTC: setting colordepth to be displayed\n"));
365
366	/* set VCLK scaling */
367	switch(mode)
368	{
369	case BPP8:
370		vid_delay = 0x01;
371		break;
372	case BPP15:
373		vid_delay = 0x02;
374		break;
375	case BPP16:
376		vid_delay = 0x03;
377		break;
378	case BPP24:
379		vid_delay = 0x04;
380		break;
381	default:
382		LOG(4,("CRTC: colordepth not supported, aborting!\n"));
383		return B_ERROR;
384		break;
385	}
386
387	switch (si->ps.card_type)
388	{
389	case NM2070:
390		vid_delay |= (ISAGRPHR(COLDEPTH) & 0xf0);
391		break;
392	default:
393		vid_delay |= (ISAGRPHR(COLDEPTH) & 0x70);
394		break;
395	}
396	/* we need to wait a bit or the card will mess-up it's */
397	/* register values.. (NM2160) */
398	snooze(10);
399	ISAGRPHW(COLDEPTH, vid_delay);
400
401	snooze(10);
402	LOG(4,("CRTC: colordepth register readback $%02x\n", (ISAGRPHR(COLDEPTH))));
403
404	return B_OK;
405}
406
407status_t nm_crtc_dpms(bool display, bool h, bool v)
408{
409	char msg[100];
410	const char* displayStatus;
411	const char* horizontalSync;
412	const char* verticalSync;
413
414	uint8 temp, size_outputs;
415
416	sprintf(msg, "CRTC: setting DPMS: ");
417
418	/* start synchronous reset: required before turning screen off! */
419	ISASEQW(RESET, 0x01);
420
421	/* turn screen off */
422	temp = ISASEQR(CLKMODE);
423	/* we need to wait a bit or the card will mess-up it's */
424	/* register values.. (NM2160) */
425	snooze(10);
426
427	if (display)
428	{
429		ISASEQW(CLKMODE, (temp & ~0x20));
430
431		/* end synchronous reset if display should be enabled */
432		ISASEQW(RESET, 0x03);
433		displayStatus = "on";
434	}
435	else
436	{
437		ISASEQW(CLKMODE, (temp | 0x20));
438		displayStatus = "off";
439	}
440
441	temp = 0x00;
442	if (h)
443	{
444		horizontalSync = "enabled";
445	}
446	else
447	{
448		temp |= 0x10;
449		horizontalSync = "disabled";
450	}
451	if (v)
452	{
453		verticalSync = "enabled";
454	}
455	else
456	{
457		temp |= 0x20;
458		verticalSync = "disabled";
459	}
460
461	snprintf(msg, sizeof(msg),
462	"CRTC: setting DPMS: display %s, hsync %s, vsync %s\n",
463		displayStatus, horizontalSync, verticalSync);
464	LOG(4, (msg));
465
466	/* read panelsize and currently active outputs */
467	size_outputs = nm_general_output_read();
468	/* we need to wait a bit or the card will mess-up it's register */
469	/* values.. (NM2160) */
470	snooze(10);
471
472	if (si->ps.card_type < NM2200)
473	{
474		/* no full DPMS support */
475		if (temp)
476		{
477		    /* Turn panel plus backlight and external monitor's */
478			/* sync signals off */
479    		ISAGRPHW(PANELCTRL1, (size_outputs & 0xfc));
480		}
481		else
482		{
483		    /* restore 'previous' output device(s) */
484    		ISAGRPHW(PANELCTRL1, size_outputs);
485		}
486	}
487	else
488	{
489		if (temp)
490		{
491		    /* Turn panel plus backlight off */
492   			ISAGRPHW(PANELCTRL1, (size_outputs & 0xfd));
493		}
494		else
495		{
496		    /* restore 'previous' panel situation */
497   			ISAGRPHW(PANELCTRL1, size_outputs);
498		}
499
500		/* if external monitor is active, update it's DPMS state */
501		if (size_outputs & 0x01)
502		{
503			/* we have full DPMS support for external monitors */
504			//fixme: checkout if so...
505			temp |= ((ISAGRPHR(ENSETRESET) & 0x0f) | 0x80);
506			/* we need to wait a bit or the card will mess-up it's */
507			/* register values.. (NM2160) */
508			snooze(10);
509			ISAGRPHW(ENSETRESET, temp);
510
511			snooze(10);
512			LOG(4,("CRTC: DPMS readback $%02x, programmed $%02x\n",
513				ISAGRPHR(ENSETRESET), temp));
514		}
515	}
516
517	return B_OK;
518}
519
520status_t nm_crtc_set_display_pitch()
521{
522	uint32 offset;
523
524	LOG(4,("CRTC: setting card pitch (offset between lines)\n"));
525
526	/* figure out offset value hardware needs: same for all Neomagic cards */
527	offset = si->fbc.bytes_per_row / 8;
528
529	LOG(2,("CRTC: offset register set to: $%04x\n", offset));
530
531	/* program the card */
532	ISACRTCW(PITCHL, (offset & 0xff));
533	//fixme: test for max supported pitch if possible,
534	//not all bits below will be implemented.
535	//NM2160: confirmed b0 and b1 in register below to exist and work.
536	if (si->ps.card_type != NM2070)
537		ISAGRPHW(CRTC_PITCHE, ((offset & 0xff00) >> 8));
538
539	return B_OK;
540}
541
542status_t nm_crtc_set_display_start(uint32 startadd,uint8 bpp)
543{
544	uint8 val;
545	uint32 timeout = 0;
546
547	LOG(2,("CRTC: relative startadd: $%06x\n",startadd));
548	LOG(2,("CRTC: frameRAM: $%08x\n",si->framebuffer));
549	LOG(2,("CRTC: framebuffer: $%08x\n",si->fbc.frame_buffer));
550
551	/* make sure we _just_ left retrace, because otherwise distortions */
552	/* might occur during our reprogramming (no double buffering) */
553	/* (verified on NM2160) */
554
555	/* we might have no retraces during setmode! So:
556	 * wait 25mS max. for retrace to occur (refresh > 40Hz) */
557	//fixme? move this function to the kernel driver... is much 'faster'.
558	while ((!(ISARB(INSTAT1) & 0x08)) && (timeout < (25000/4)))
559	{
560		snooze(4);
561		timeout++;
562	}
563	/* now wait until retrace ends (with timeout) */
564	timeout = 0;
565	while ((ISARB(INSTAT1) & 0x08) && (timeout < (25000/4)))
566	{
567		snooze(4);
568		timeout++;
569	}
570
571	/* set standard VGA registers */
572	/* (startadress in 32bit words (b2 - b17) */
573    ISACRTCW(FBSTADDH, ((startadd & 0x03fc00) >> 10));
574    ISACRTCW(FBSTADDL, ((startadd & 0x0003fc) >> 2));
575
576	/* set NM extended register */
577	//fixme: NM2380 _must_ have one more bit (has >4Mb RAM)!!
578	//this is testable via virtualscreen in 640x480x8 mode...
579	//(b4 is >256Kb adresswrap bit, so that's already occupied)
580	val = ISAGRPHR(FBSTADDE);
581	/* we need to wait a bit or the card will mess-up it's */
582	/* register values.. (NM2160) */
583	snooze(10);
584	if (si->ps.card_type < NM2200)
585		/* extended bits: (b18-20) */
586		ISAGRPHW(FBSTADDE,(((startadd >> 18) & 0x07) | (val & 0xf8)));
587	else
588		/* extended bits: (b18-21) */
589		ISAGRPHW(FBSTADDE,(((startadd >> 18) & 0x0f) | (val & 0xf0)));
590
591	/* set byte adress: (b0 - 1):
592	 * Neomagic cards work with _pixel_ offset here. */
593	switch(bpp)
594	{
595	case 8:
596		ISAATBW(HORPIXPAN, (startadd & 0x00000003));
597		break;
598	case 15:
599	case 16:
600		ISAATBW(HORPIXPAN, ((startadd & 0x00000002) >> 1));
601		break;
602	case 24:
603		ISAATBW(HORPIXPAN, ((4 - (startadd & 0x00000003)) & 0x03));
604		break;
605	}
606
607	return B_OK;
608}
609
610/* setup centering mode for current internal or simultaneous  */
611/* flatpanel mode */
612status_t nm_crtc_center(display_mode target, bool crt_only)
613{
614	/* note:
615	 * NM2070 apparantly doesn't support horizontal */
616	/* centering this way... */
617
618	uint8 vcent1, vcent2, vcent3, vcent4, vcent5;
619	uint8 hcent1, hcent2, hcent3, hcent4, hcent5;
620	uint8 ctrl2, ctrl3;
621
622	/* preset no centering */
623	uint16 hoffset = 0;
624	uint16 voffset = 0;
625	vcent1 = vcent2 = vcent3 = vcent4 = vcent5 = 0x00;
626	hcent1 = hcent2 = hcent3 = hcent4 = hcent5 = 0x00;
627	ctrl2 = ctrl3 = 0x00;
628
629	/* calculate offsets for centering if prudent */
630	if (!crt_only)
631	{
632		if (target.timing.h_display < si->ps.panel_width)
633		{
634			hoffset = (si->ps.panel_width - target.timing.h_display);
635			/* adjust for register contraints:
636			 * horizontal center granularity is 16 pixels */
637			hoffset = ((hoffset >> 4) - 1);
638			/* turn on horizontal centering? */
639			ctrl3 = 0x10;
640		}
641
642		if (target.timing.v_display < si->ps.panel_height)
643		{
644			voffset = (si->ps.panel_height - target.timing.v_display);
645			/* adjust for register contraints:
646			 * vertical center granularity is 2 pixels */
647			voffset = ((voffset >> 1) - 2);
648			/* turn on vertical centering? */
649			ctrl2 = 0x01;
650		}
651
652		switch(target.timing.h_display)
653		{
654		case 640:
655			hcent1 = hoffset;
656			vcent3 = voffset;
657			break;
658		case 800:
659			hcent2 = hoffset;
660			switch(target.timing.v_display)
661			{
662			case 480:
663				//Linux fixme: check this out...
664				vcent3 = voffset;
665				break;
666			case 600:
667				vcent4 = voffset;
668				break;
669			}
670			break;
671		case 1024:
672			hcent5 = hoffset;
673			vcent5 = voffset;
674			break;
675		case 1280:
676			/* this mode equals the largest possible panel on the *
677			 * newest chip: so no centering needed here. */
678			break;
679		default:
680			//fixme?: block non-standard modes? for now: not centered.
681			break;
682		}
683	}
684
685	/* now program the card's registers */
686	ISAGRPHW(PANELVCENT1, vcent1);
687	ISAGRPHW(PANELVCENT2, vcent2);
688	ISAGRPHW(PANELVCENT3, vcent3);
689	if (si->ps.card_type > NM2070)
690	{
691		ISAGRPHW(PANELVCENT4, vcent4);
692		ISAGRPHW(PANELHCENT1, hcent1);
693		ISAGRPHW(PANELHCENT2, hcent2);
694		ISAGRPHW(PANELHCENT3, hcent3);
695	}
696	if (si->ps.card_type >= NM2160)
697	{
698		ISAGRPHW(PANELHCENT4, hcent4);
699	}
700	if (si->ps.card_type >= NM2200)
701	{
702		ISAGRPHW(PANELVCENT5, vcent5);
703		ISAGRPHW(PANELHCENT5, hcent5);
704	}
705
706	/* program panel control register 2: don't touch bit 3-5 */
707	ctrl2 |= (ISAGRPHR(PANELCTRL2) & 0x38);
708	/* we need to wait a bit or the card will mess-up it's register */
709	/* values.. (NM2160) */
710	snooze(10);
711	ISAGRPHW(PANELCTRL2, ctrl2);
712
713	if (si->ps.card_type > NM2070)
714	{
715		/* program panel control register 3: don't touch bit 7-5 */
716		/* and bit 3-0 */
717		ctrl3 |= (ISAGRPHR(PANELCTRL3) & 0xef);
718		/* we need to wait a bit or the card will mess-up it's  */
719		/* register values.. (NM2160) */
720		snooze(10);
721		ISAGRPHW(PANELCTRL3, ctrl3);
722	}
723
724	return B_OK;
725}
726
727/* program panel modeline if needed */
728status_t nm_crtc_prg_panel()
729{
730status_t stat = B_ERROR;
731
732	/* only NM2070 requires this apparantly */
733	/* (because it's BIOS doesn't do it OK) */
734	if (si->ps.card_type > NM2070) return B_OK;
735
736	switch(si->ps.panel_width)
737	{
738	case 640:
739		/* 640x480 panels are only used on NM2070 */
740		ISACRTCW(PANEL_0x40, 0x5f);
741		ISACRTCW(PANEL_0x41, 0x50);
742		ISACRTCW(PANEL_0x42, 0x02);
743		ISACRTCW(PANEL_0x43, 0x55);
744		ISACRTCW(PANEL_0x44, 0x81);
745		ISACRTCW(PANEL_0x45, 0x0b);
746		ISACRTCW(PANEL_0x46, 0x2e);
747		ISACRTCW(PANEL_0x47, 0xea);
748		ISACRTCW(PANEL_0x48, 0x0c);
749		ISACRTCW(PANEL_0x49, 0xe7);
750		ISACRTCW(PANEL_0x4a, 0x04);
751		ISACRTCW(PANEL_0x4b, 0x2d);
752		ISACRTCW(PANEL_0x4c, 0x28);
753		ISACRTCW(PANEL_0x4d, 0x90);
754		ISACRTCW(PANEL_0x4e, 0x2b);
755		ISACRTCW(PANEL_0x4f, 0xa0);
756		stat = B_OK;
757		break;
758	case 800:
759		switch(si->ps.panel_height)
760		{
761		case 600:
762			/* 800x600 panels are used on all cards... */
763			ISACRTCW(PANEL_0x40, 0x7f);
764			ISACRTCW(PANEL_0x41, 0x63);
765			ISACRTCW(PANEL_0x42, 0x02);
766			ISACRTCW(PANEL_0x43, 0x6c);
767			ISACRTCW(PANEL_0x44, 0x1c);
768			ISACRTCW(PANEL_0x45, 0x72);
769			ISACRTCW(PANEL_0x46, 0xe0);
770			ISACRTCW(PANEL_0x47, 0x58);
771			ISACRTCW(PANEL_0x48, 0x0c);
772			ISACRTCW(PANEL_0x49, 0x57);
773			ISACRTCW(PANEL_0x4a, 0x73);
774			ISACRTCW(PANEL_0x4b, 0x3d);
775			ISACRTCW(PANEL_0x4c, 0x31);
776			ISACRTCW(PANEL_0x4d, 0x01);
777			ISACRTCW(PANEL_0x4e, 0x36);
778			ISACRTCW(PANEL_0x4f, 0x1e);
779			if (si->ps.card_type > NM2070)
780			{
781				ISACRTCW(PANEL_0x50, 0x6b);
782				ISACRTCW(PANEL_0x51, 0x4f);
783				ISACRTCW(PANEL_0x52, 0x0e);
784				ISACRTCW(PANEL_0x53, 0x58);
785				ISACRTCW(PANEL_0x54, 0x88);
786				ISACRTCW(PANEL_0x55, 0x33);
787				ISACRTCW(PANEL_0x56, 0x27);
788				ISACRTCW(PANEL_0x57, 0x16);
789				ISACRTCW(PANEL_0x58, 0x2c);
790				ISACRTCW(PANEL_0x59, 0x94);
791			}
792			stat = B_OK;
793			break;
794		case 480:
795			/* ...while 800x480 widescreen panels are not used on NM2070. */
796			ISACRTCW(PANEL_0x40, 0x7f);
797			ISACRTCW(PANEL_0x41, 0x63);
798			ISACRTCW(PANEL_0x42, 0x02);
799			ISACRTCW(PANEL_0x43, 0x6b);
800			ISACRTCW(PANEL_0x44, 0x1b);
801			ISACRTCW(PANEL_0x45, 0x72);
802			ISACRTCW(PANEL_0x46, 0xe0);
803			ISACRTCW(PANEL_0x47, 0x1c);
804			ISACRTCW(PANEL_0x48, 0x00);
805			ISACRTCW(PANEL_0x49, 0x57);
806			ISACRTCW(PANEL_0x4a, 0x73);
807			ISACRTCW(PANEL_0x4b, 0x3e);
808			ISACRTCW(PANEL_0x4c, 0x31);
809			ISACRTCW(PANEL_0x4d, 0x01);
810			ISACRTCW(PANEL_0x4e, 0x36);
811			ISACRTCW(PANEL_0x4f, 0x1e);
812			ISACRTCW(PANEL_0x50, 0x6b);
813			ISACRTCW(PANEL_0x51, 0x4f);
814			ISACRTCW(PANEL_0x52, 0x0e);
815			ISACRTCW(PANEL_0x53, 0x57);
816			ISACRTCW(PANEL_0x54, 0x87);
817			ISACRTCW(PANEL_0x55, 0x33);
818			ISACRTCW(PANEL_0x56, 0x27);
819			ISACRTCW(PANEL_0x57, 0x16);
820			ISACRTCW(PANEL_0x58, 0x2c);
821			ISACRTCW(PANEL_0x59, 0x94);
822			stat = B_OK;
823			break;
824		}
825		break;
826	case 1024:
827		switch(si->ps.panel_height)
828		{
829		case 768:
830			/* 1024x768 panels are only used on later cards
831			 * (NM2097 and later ?) */
832			ISACRTCW(PANEL_0x40, 0xa3);
833			ISACRTCW(PANEL_0x41, 0x7f);
834			ISACRTCW(PANEL_0x42, 0x06);
835			ISACRTCW(PANEL_0x43, 0x85);
836			ISACRTCW(PANEL_0x44, 0x96);
837			ISACRTCW(PANEL_0x45, 0x24);
838			ISACRTCW(PANEL_0x46, 0xe5);
839			ISACRTCW(PANEL_0x47, 0x02);
840			ISACRTCW(PANEL_0x48, 0x08);
841			ISACRTCW(PANEL_0x49, 0xff);
842			ISACRTCW(PANEL_0x4a, 0x25);
843			ISACRTCW(PANEL_0x4b, 0x4f);
844			ISACRTCW(PANEL_0x4c, 0x40);
845			ISACRTCW(PANEL_0x4d, 0x00);
846			ISACRTCW(PANEL_0x4e, 0x44);
847			ISACRTCW(PANEL_0x4f, 0x0c);
848			ISACRTCW(PANEL_0x50, 0x7a);
849			ISACRTCW(PANEL_0x51, 0x56);
850			ISACRTCW(PANEL_0x52, 0x00);
851			ISACRTCW(PANEL_0x53, 0x5d);
852			ISACRTCW(PANEL_0x54, 0x0e);
853			ISACRTCW(PANEL_0x55, 0x3b);
854			ISACRTCW(PANEL_0x56, 0x2b);
855			ISACRTCW(PANEL_0x57, 0x00);
856			ISACRTCW(PANEL_0x58, 0x2f);
857			ISACRTCW(PANEL_0x59, 0x18);
858			ISACRTCW(PANEL_0x60, 0x88);
859			ISACRTCW(PANEL_0x61, 0x63);
860			ISACRTCW(PANEL_0x62, 0x0b);
861			ISACRTCW(PANEL_0x63, 0x69);
862			ISACRTCW(PANEL_0x64, 0x1a);
863			stat = B_OK;
864			break;
865	    case 480:
866			/* 1024x480 widescreen panels are only used on later cards
867			 * (NM2097 and later ?) */
868			ISACRTCW(PANEL_0x40, 0xa3);
869			ISACRTCW(PANEL_0x41, 0x7f);
870			ISACRTCW(PANEL_0x42, 0x1b);
871			ISACRTCW(PANEL_0x43, 0x89);
872			ISACRTCW(PANEL_0x44, 0x16);
873			ISACRTCW(PANEL_0x45, 0x0b);
874			ISACRTCW(PANEL_0x46, 0x2c);
875			ISACRTCW(PANEL_0x47, 0xe8);
876			ISACRTCW(PANEL_0x48, 0x0c);
877			ISACRTCW(PANEL_0x49, 0xe7);
878			ISACRTCW(PANEL_0x4a, 0x09);
879			ISACRTCW(PANEL_0x4b, 0x4f);
880			ISACRTCW(PANEL_0x4c, 0x40);
881			ISACRTCW(PANEL_0x4d, 0x00);
882			ISACRTCW(PANEL_0x4e, 0x44);
883			ISACRTCW(PANEL_0x4f, 0x0c);
884			ISACRTCW(PANEL_0x50, 0x7a);
885			ISACRTCW(PANEL_0x51, 0x56);
886			ISACRTCW(PANEL_0x52, 0x00);
887			ISACRTCW(PANEL_0x53, 0x5d);
888			ISACRTCW(PANEL_0x54, 0x0e);
889			ISACRTCW(PANEL_0x55, 0x3b);
890			ISACRTCW(PANEL_0x56, 0x2a);
891			ISACRTCW(PANEL_0x57, 0x00);
892			ISACRTCW(PANEL_0x58, 0x2f);
893			ISACRTCW(PANEL_0x59, 0x18);
894			ISACRTCW(PANEL_0x60, 0x88);
895			ISACRTCW(PANEL_0x61, 0x63);
896			ISACRTCW(PANEL_0x62, 0x0b);
897			ISACRTCW(PANEL_0x63, 0x69);
898			ISACRTCW(PANEL_0x64, 0x1a);
899			stat = B_OK;
900			break;
901		}
902		break;
903	case 1280:
904		/* no info available */
905		break;
906	}
907
908	if (stat != B_OK)
909		LOG(2,("CRTC: unable to program panel: unknown modeline needed.\n"));
910
911	return stat;
912}
913
914status_t nm_crtc_cursor_init()
915{
916	int i;
917	vuint32 * fb;
918	uint32 curadd, curreg;
919
920	/* the cursor is at the end of cardRAM */
921	curadd = ((si->ps.memory_size * 1024) - si->ps.curmem_size);
922
923	/* set cursor bitmap adress on a 1kb boundary, and move the bits around
924	 * so they get placed at the correct registerbits */
925	curreg = (((curadd >> 10) & 0x000f) << 8);
926	curreg |= (((curadd >> 10) & 0x0ff0) >> 4);
927	/* NM2380 must have an extra bit for > 4Mb: assuming it to be on b12... */
928	curreg |= ((curadd >> 10) & 0x1000);
929
930	if (si->ps.card_type < NM2200)
931		CR1W(CURADDRESS, curreg);
932	else
933		CR1W(22CURADDRESS, curreg);
934
935	/*set cursor colour*/
936	if (si->ps.card_type < NM2200)
937	{
938		/* background is black */
939		CR1W(CURBGCOLOR, 0x00000000);
940		/* foreground is white */
941		CR1W(CURFGCOLOR, 0x00ffffff);
942	}
943	else
944	{
945		/* background is black */
946		CR1W(22CURBGCOLOR, 0x00000000);
947		/* foreground is white */
948		CR1W(22CURFGCOLOR, 0x00ffffff);
949	}
950
951	/* we must set a valid colordepth to get full RAM access on Neomagic cards:
952	 * in old pre 8-bit color VGA modes some planemask is in effect apparantly,
953	 * allowing access only to every 7th and 8th RAM byte across the
954	 * entire RAM. */
955	nm_crtc_depth(BPP8);
956
957	/* clear cursor: so we need full RAM access! */
958	fb = ((vuint32 *)(((uintptr_t)si->framebuffer) + curadd));
959	for (i = 0; i < (1024/4); i++)
960	{
961		fb[i] = 0;
962	}
963
964	/* activate hardware cursor */
965	nm_crtc_cursor_show();
966
967	return B_OK;
968}
969
970status_t nm_crtc_cursor_show()
971{
972	if (si->ps.card_type < NM2200)
973	{
974		CR1W(CURCTRL, 0x00000001);
975	}
976	else
977	{
978		CR1W(22CURCTRL, 0x00000001);
979	}
980	return B_OK;
981}
982
983status_t nm_crtc_cursor_hide()
984{
985//linux fixme: using this kills PCI(?) access sometimes,
986//so use ISA access as below...
987/*
988	if (si->ps.card_type < NM2200)
989	{
990		CR1W(CURCTRL, 0x00000000);
991	}
992	else
993	{
994		CR1W(22CURCTRL, 0x00000000);
995	}
996*/
997	/* disable cursor */
998	ISAGRPHW(CURCTRL,0x00);
999
1000	return B_OK;
1001}
1002
1003/*set up cursor shape*/
1004status_t nm_crtc_cursor_define(uint8* andMask,uint8* xorMask)
1005{
1006	uint8 y;
1007	vuint8 * cursor;
1008
1009	/* get a pointer to the cursor: it's at the end of cardRAM */
1010	cursor = (vuint8*) si->framebuffer;
1011	cursor += ((si->ps.memory_size * 1024) - si->ps.curmem_size);
1012
1013	/*draw the cursor*/
1014	for(y=0;y<16;y++)
1015	{
1016		cursor[y*16+8]=~*andMask++;
1017		cursor[y*16+0]=*xorMask++;
1018		cursor[y*16+9]=~*andMask++;
1019		cursor[y*16+1]=*xorMask++;
1020	}
1021
1022	//test.. only valid for <NM2200!!
1023/*	{
1024		float pclk;
1025		uint8 n,m,x = 1;
1026		n = ISAGRPHR(PLLC_NL);
1027		m = ISAGRPHR(PLLC_M);
1028		LOG(4,("CRTC: PLLSEL $%02x\n", ISARB(MISCR)));
1029		LOG(4,("CRTC: PLLN $%02x\n", n));
1030		LOG(4,("CRTC: PLLM $%02x\n", m));
1031
1032		if (n & 0x80) x = 2;
1033		n &= 0x7f;
1034		pclk = ((si->ps.f_ref * (n + 1)) / ((m + 1) * x));
1035		LOG(2,("CRTC: Pixelclock is %fMHz\n", pclk));
1036		nm_general_output_select();
1037	}
1038*/
1039	return B_OK;
1040}
1041
1042/*position the cursor*/
1043status_t nm_crtc_cursor_position(uint16 x ,uint16 y)
1044{
1045//NM2160 is ok without this, still verify the rest..:
1046	/* make sure we are not in retrace, because the register(s) might get copied
1047	 * during our reprogramming them (double buffering feature) */
1048/*	fixme!?
1049	while (ACCR(STATUS) & 0x08)
1050	{
1051		snooze(4);
1052	}
1053*/
1054	if (si->ps.card_type < NM2200)
1055	{
1056		CR1W(CURX, (uint32)x);
1057		CR1W(CURY, (uint32)y);
1058	}
1059	else
1060	{
1061		CR1W(22CURX, (uint32)x);
1062		CR1W(22CURY, (uint32)y);
1063	}
1064
1065	return B_OK;
1066}
1067