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