xref: /haiku/src/add-ons/accelerants/neomagic/engine/nm_crtc.c (revision c237c4ce593ee823d9867fd997e51e4c447f5623)
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 */
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,*/
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 */
90 status_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 
360 status_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 
407 status_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 
520 status_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 
542 status_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 */
612 status_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 */
728 status_t nm_crtc_prg_panel()
729 {
730 status_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 
914 status_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 
970 status_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 
983 status_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*/
1004 status_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*/
1043 status_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