xref: /haiku/src/add-ons/accelerants/neomagic/engine/nm_crtc.c (revision 83b1a68c52ba3e0e8796282759f694b7fdddf06d)
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, 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 	char msg[100];
390 	uint8 temp, size_outputs;
391 
392 	sprintf(msg, "CRTC: setting DPMS: ");
393 
394 	/* start synchronous reset: required before turning screen off! */
395 	ISASEQW(RESET, 0x01);
396 
397 	/* turn screen off */
398 	temp = ISASEQR(CLKMODE);
399 	/* we need to wait a bit or the card will mess-up it's register values.. (NM2160) */
400 	snooze(10);
401 
402 	if (display)
403 	{
404 		ISASEQW(CLKMODE, (temp & ~0x20));
405 
406 		/* end synchronous reset if display should be enabled */
407 		ISASEQW(RESET, 0x03);
408 		sprintf(msg, "%sdisplay on, ", msg);
409 	}
410 	else
411 	{
412 		ISASEQW(CLKMODE, (temp | 0x20));
413 		sprintf(msg, "%sdisplay off, ", msg);
414 	}
415 
416 	temp = 0x00;
417 	if (h)
418 	{
419 		sprintf(msg, "%shsync enabled, ", msg);
420 	}
421 	else
422 	{
423 		temp |= 0x10;
424 		sprintf(msg, "%shsync disabled, ", msg);
425 	}
426 	if (v)
427 	{
428 		sprintf(msg, "%svsync enabled\n", msg);
429 	}
430 	else
431 	{
432 		temp |= 0x20;
433 		sprintf(msg, "%svsync disabled\n", msg);
434 	}
435 
436 	LOG(4, (msg));
437 
438 	/* read panelsize and currently active outputs */
439 	size_outputs = nm_general_output_read();
440 	/* we need to wait a bit or the card will mess-up it's register values.. (NM2160) */
441 	snooze(10);
442 
443 	if (si->ps.card_type < NM2200)
444 	{
445 		/* no full DPMS support */
446 		if (temp)
447 		{
448 		    /* Turn panel plus backlight and external monitor's sync signals off */
449     		ISAGRPHW(PANELCTRL1, (size_outputs & 0xfc));
450 		}
451 		else
452 		{
453 		    /* restore 'previous' output device(s) */
454     		ISAGRPHW(PANELCTRL1, size_outputs);
455 		}
456 	}
457 	else
458 	{
459 		if (temp)
460 		{
461 		    /* Turn panel plus backlight off */
462    			ISAGRPHW(PANELCTRL1, (size_outputs & 0xfd));
463 		}
464 		else
465 		{
466 		    /* restore 'previous' panel situation */
467    			ISAGRPHW(PANELCTRL1, size_outputs);
468 		}
469 
470 		/* if external monitor is active, update it's DPMS state */
471 		if (size_outputs & 0x01)
472 		{
473 			/* we have full DPMS support for external monitors */
474 			//fixme: checkout if so...
475 			temp |= ((ISAGRPHR(ENSETRESET) & 0x0f) | 0x80);
476 			/* we need to wait a bit or the card will mess-up it's register values.. (NM2160) */
477 			snooze(10);
478 			ISAGRPHW(ENSETRESET, temp);
479 
480 			snooze(10);
481 			LOG(4,("CRTC: DPMS readback $%02x, programmed $%02x\n", ISAGRPHR(ENSETRESET), temp));
482 		}
483 	}
484 
485 	return B_OK;
486 }
487 
488 status_t nm_crtc_set_display_pitch()
489 {
490 	uint32 offset;
491 
492 	LOG(4,("CRTC: setting card pitch (offset between lines)\n"));
493 
494 	/* figure out offset value hardware needs: same for all Neomagic cards */
495 	offset = si->fbc.bytes_per_row / 8;
496 
497 	LOG(2,("CRTC: offset register set to: $%04x\n", offset));
498 
499 	/* program the card */
500 	ISACRTCW(PITCHL, (offset & 0xff));
501 	//fixme: test for max supported pitch if possible,
502 	//not all bits below will be implemented.
503 	//NM2160: confirmed b0 and b1 in register below to exist and work.
504 	if (si->ps.card_type != NM2070)
505 		ISAGRPHW(CRTC_PITCHE, ((offset & 0xff00) >> 8));
506 
507 	return B_OK;
508 }
509 
510 status_t nm_crtc_set_display_start(uint32 startadd,uint8 bpp)
511 {
512 	uint8 val;
513 	uint32 timeout = 0;
514 
515 	LOG(2,("CRTC: relative startadd: $%06x\n",startadd));
516 	LOG(2,("CRTC: frameRAM: $%08x\n",si->framebuffer));
517 	LOG(2,("CRTC: framebuffer: $%08x\n",si->fbc.frame_buffer));
518 
519 	/* make sure we _just_ left retrace, because otherwise distortions might occur
520 	 * during our reprogramming (no double buffering) (verified on NM2160) */
521 
522 	/* we might have no retraces during setmode! So:
523 	 * wait 25mS max. for retrace to occur (refresh > 40Hz) */
524 	//fixme? move this function to the kernel driver... is much 'faster'.
525 	while ((!(ISARB(INSTAT1) & 0x08)) && (timeout < (25000/4)))
526 	{
527 		snooze(4);
528 		timeout++;
529 	}
530 	/* now wait until retrace ends (with timeout) */
531 	timeout = 0;
532 	while ((ISARB(INSTAT1) & 0x08) && (timeout < (25000/4)))
533 	{
534 		snooze(4);
535 		timeout++;
536 	}
537 
538 	/* set standard VGA registers */
539 	/* (startadress in 32bit words (b2 - b17) */
540     ISACRTCW(FBSTADDH, ((startadd & 0x03fc00) >> 10));
541     ISACRTCW(FBSTADDL, ((startadd & 0x0003fc) >> 2));
542 
543 	/* set NM extended register */
544 	//fixme: NM2380 _must_ have one more bit (has >4Mb RAM)!!
545 	//this is testable via virtualscreen in 640x480x8 mode...
546 	//(b4 is >256Kb adresswrap bit, so that's already occupied)
547 	val = ISAGRPHR(FBSTADDE);
548 	/* we need to wait a bit or the card will mess-up it's register values.. (NM2160) */
549 	snooze(10);
550 	if (si->ps.card_type < NM2200)
551 		/* extended bits: (b18-20) */
552 		ISAGRPHW(FBSTADDE,(((startadd >> 18) & 0x07) | (val & 0xf8)));
553 	else
554 		/* extended bits: (b18-21) */
555 		ISAGRPHW(FBSTADDE,(((startadd >> 18) & 0x0f) | (val & 0xf0)));
556 
557 	/* set byte adress: (b0 - 1):
558 	 * Neomagic cards work with _pixel_ offset here. */
559 	switch(bpp)
560 	{
561 	case 8:
562 		ISAATBW(HORPIXPAN, (startadd & 0x00000003));
563 		break;
564 	case 15:
565 	case 16:
566 		ISAATBW(HORPIXPAN, ((startadd & 0x00000002) >> 1));
567 		break;
568 	case 24:
569 		ISAATBW(HORPIXPAN, ((4 - (startadd & 0x00000003)) & 0x03));
570 		break;
571 	}
572 
573 	return B_OK;
574 }
575 
576 /* setup centering mode for current internal or simultaneous flatpanel mode */
577 status_t nm_crtc_center(display_mode target, bool crt_only)
578 {
579 	/* note:
580 	 * NM2070 apparantly doesn't support horizontal centering this way... */
581 
582 	uint8 vcent1, vcent2, vcent3, vcent4, vcent5;
583 	uint8 hcent1, hcent2, hcent3, hcent4, hcent5;
584 	uint8 ctrl2, ctrl3;
585 
586 	/* preset no centering */
587 	uint16 hoffset = 0;
588 	uint16 voffset = 0;
589 	vcent1 = vcent2 = vcent3 = vcent4 = vcent5 = 0x00;
590 	hcent1 = hcent2 = hcent3 = hcent4 = hcent5 = 0x00;
591 	ctrl2 = ctrl3 = 0x00;
592 
593 	/* calculate offsets for centering if prudent */
594 	if (!crt_only)
595 	{
596 		if (target.timing.h_display < si->ps.panel_width)
597 		{
598 			hoffset = (si->ps.panel_width - target.timing.h_display);
599 			/* adjust for register contraints:
600 			 * horizontal center granularity is 16 pixels */
601 			hoffset = ((hoffset >> 4) - 1);
602 			/* turn on horizontal centering? */
603 			ctrl3 = 0x10;
604 		}
605 
606 		if (target.timing.v_display < si->ps.panel_height)
607 		{
608 			voffset = (si->ps.panel_height - target.timing.v_display);
609 			/* adjust for register contraints:
610 			 * vertical center granularity is 2 pixels */
611 			voffset = ((voffset >> 1) - 2);
612 			/* turn on vertical centering? */
613 			ctrl2 = 0x01;
614 		}
615 
616 		switch(target.timing.h_display)
617 		{
618 		case 640:
619 			hcent1 = hoffset;
620 			vcent3 = voffset;
621 			break;
622 		case 800:
623 			hcent2 = hoffset;
624 			switch(target.timing.v_display)
625 			{
626 			case 480:
627 				//Linux fixme: check this out...
628 				vcent3 = voffset;
629 				break;
630 			case 600:
631 				vcent4 = voffset;
632 				break;
633 			}
634 			break;
635 		case 1024:
636 			hcent5 = hoffset;
637 			vcent5 = voffset;
638 			break;
639 		case 1280:
640 			/* this mode equals the largest possible panel on the newest chip:
641 			 * so no centering needed here. */
642 			break;
643 		default:
644 			//fixme?: block non-standard modes? for now: not centered.
645 			break;
646 		}
647 	}
648 
649 	/* now program the card's registers */
650 	ISAGRPHW(PANELVCENT1, vcent1);
651 	ISAGRPHW(PANELVCENT2, vcent2);
652 	ISAGRPHW(PANELVCENT3, vcent3);
653 	if (si->ps.card_type > NM2070)
654 	{
655 		ISAGRPHW(PANELVCENT4, vcent4);
656 		ISAGRPHW(PANELHCENT1, hcent1);
657 		ISAGRPHW(PANELHCENT2, hcent2);
658 		ISAGRPHW(PANELHCENT3, hcent3);
659 	}
660 	if (si->ps.card_type >= NM2160)
661 	{
662 		ISAGRPHW(PANELHCENT4, hcent4);
663 	}
664 	if (si->ps.card_type >= NM2200)
665 	{
666 		ISAGRPHW(PANELVCENT5, vcent5);
667 		ISAGRPHW(PANELHCENT5, hcent5);
668 	}
669 
670 	/* program panel control register 2: don't touch bit 3-5 */
671 	ctrl2 |= (ISAGRPHR(PANELCTRL2) & 0x38);
672 	/* we need to wait a bit or the card will mess-up it's register values.. (NM2160) */
673 	snooze(10);
674 	ISAGRPHW(PANELCTRL2, ctrl2);
675 
676 	if (si->ps.card_type > NM2070)
677 	{
678 		/* program panel control register 3: don't touch bit 7-5 and bit 3-0 */
679 		ctrl3 |= (ISAGRPHR(PANELCTRL3) & 0xef);
680 		/* we need to wait a bit or the card will mess-up it's register values.. (NM2160) */
681 		snooze(10);
682 		ISAGRPHW(PANELCTRL3, ctrl3);
683 	}
684 
685 	return B_OK;
686 }
687 
688 /* program panel modeline if needed */
689 status_t nm_crtc_prg_panel()
690 {
691 status_t stat = B_ERROR;
692 
693 	/* only NM2070 requires this apparantly (because it's BIOS doesn't do it OK) */
694 	if (si->ps.card_type > NM2070) return B_OK;
695 
696 	switch(si->ps.panel_width)
697 	{
698 	case 640:
699 		/* 640x480 panels are only used on NM2070 */
700 		ISACRTCW(PANEL_0x40, 0x5f);
701 		ISACRTCW(PANEL_0x41, 0x50);
702 		ISACRTCW(PANEL_0x42, 0x02);
703 		ISACRTCW(PANEL_0x43, 0x55);
704 		ISACRTCW(PANEL_0x44, 0x81);
705 		ISACRTCW(PANEL_0x45, 0x0b);
706 		ISACRTCW(PANEL_0x46, 0x2e);
707 		ISACRTCW(PANEL_0x47, 0xea);
708 		ISACRTCW(PANEL_0x48, 0x0c);
709 		ISACRTCW(PANEL_0x49, 0xe7);
710 		ISACRTCW(PANEL_0x4a, 0x04);
711 		ISACRTCW(PANEL_0x4b, 0x2d);
712 		ISACRTCW(PANEL_0x4c, 0x28);
713 		ISACRTCW(PANEL_0x4d, 0x90);
714 		ISACRTCW(PANEL_0x4e, 0x2b);
715 		ISACRTCW(PANEL_0x4f, 0xa0);
716 		stat = B_OK;
717 		break;
718 	case 800:
719 		switch(si->ps.panel_height)
720 		{
721 		case 600:
722 			/* 800x600 panels are used on all cards... */
723 			ISACRTCW(PANEL_0x40, 0x7f);
724 			ISACRTCW(PANEL_0x41, 0x63);
725 			ISACRTCW(PANEL_0x42, 0x02);
726 			ISACRTCW(PANEL_0x43, 0x6c);
727 			ISACRTCW(PANEL_0x44, 0x1c);
728 			ISACRTCW(PANEL_0x45, 0x72);
729 			ISACRTCW(PANEL_0x46, 0xe0);
730 			ISACRTCW(PANEL_0x47, 0x58);
731 			ISACRTCW(PANEL_0x48, 0x0c);
732 			ISACRTCW(PANEL_0x49, 0x57);
733 			ISACRTCW(PANEL_0x4a, 0x73);
734 			ISACRTCW(PANEL_0x4b, 0x3d);
735 			ISACRTCW(PANEL_0x4c, 0x31);
736 			ISACRTCW(PANEL_0x4d, 0x01);
737 			ISACRTCW(PANEL_0x4e, 0x36);
738 			ISACRTCW(PANEL_0x4f, 0x1e);
739 			if (si->ps.card_type > NM2070)
740 			{
741 				ISACRTCW(PANEL_0x50, 0x6b);
742 				ISACRTCW(PANEL_0x51, 0x4f);
743 				ISACRTCW(PANEL_0x52, 0x0e);
744 				ISACRTCW(PANEL_0x53, 0x58);
745 				ISACRTCW(PANEL_0x54, 0x88);
746 				ISACRTCW(PANEL_0x55, 0x33);
747 				ISACRTCW(PANEL_0x56, 0x27);
748 				ISACRTCW(PANEL_0x57, 0x16);
749 				ISACRTCW(PANEL_0x58, 0x2c);
750 				ISACRTCW(PANEL_0x59, 0x94);
751 			}
752 			stat = B_OK;
753 			break;
754 		case 480:
755 			/* ...while 800x480 widescreen panels are not used on NM2070. */
756 			ISACRTCW(PANEL_0x40, 0x7f);
757 			ISACRTCW(PANEL_0x41, 0x63);
758 			ISACRTCW(PANEL_0x42, 0x02);
759 			ISACRTCW(PANEL_0x43, 0x6b);
760 			ISACRTCW(PANEL_0x44, 0x1b);
761 			ISACRTCW(PANEL_0x45, 0x72);
762 			ISACRTCW(PANEL_0x46, 0xe0);
763 			ISACRTCW(PANEL_0x47, 0x1c);
764 			ISACRTCW(PANEL_0x48, 0x00);
765 			ISACRTCW(PANEL_0x49, 0x57);
766 			ISACRTCW(PANEL_0x4a, 0x73);
767 			ISACRTCW(PANEL_0x4b, 0x3e);
768 			ISACRTCW(PANEL_0x4c, 0x31);
769 			ISACRTCW(PANEL_0x4d, 0x01);
770 			ISACRTCW(PANEL_0x4e, 0x36);
771 			ISACRTCW(PANEL_0x4f, 0x1e);
772 			ISACRTCW(PANEL_0x50, 0x6b);
773 			ISACRTCW(PANEL_0x51, 0x4f);
774 			ISACRTCW(PANEL_0x52, 0x0e);
775 			ISACRTCW(PANEL_0x53, 0x57);
776 			ISACRTCW(PANEL_0x54, 0x87);
777 			ISACRTCW(PANEL_0x55, 0x33);
778 			ISACRTCW(PANEL_0x56, 0x27);
779 			ISACRTCW(PANEL_0x57, 0x16);
780 			ISACRTCW(PANEL_0x58, 0x2c);
781 			ISACRTCW(PANEL_0x59, 0x94);
782 			stat = B_OK;
783 			break;
784 		}
785 		break;
786 	case 1024:
787 		switch(si->ps.panel_height)
788 		{
789 		case 768:
790 			/* 1024x768 panels are only used on later cards
791 			 * (NM2097 and later ?) */
792 			ISACRTCW(PANEL_0x40, 0xa3);
793 			ISACRTCW(PANEL_0x41, 0x7f);
794 			ISACRTCW(PANEL_0x42, 0x06);
795 			ISACRTCW(PANEL_0x43, 0x85);
796 			ISACRTCW(PANEL_0x44, 0x96);
797 			ISACRTCW(PANEL_0x45, 0x24);
798 			ISACRTCW(PANEL_0x46, 0xe5);
799 			ISACRTCW(PANEL_0x47, 0x02);
800 			ISACRTCW(PANEL_0x48, 0x08);
801 			ISACRTCW(PANEL_0x49, 0xff);
802 			ISACRTCW(PANEL_0x4a, 0x25);
803 			ISACRTCW(PANEL_0x4b, 0x4f);
804 			ISACRTCW(PANEL_0x4c, 0x40);
805 			ISACRTCW(PANEL_0x4d, 0x00);
806 			ISACRTCW(PANEL_0x4e, 0x44);
807 			ISACRTCW(PANEL_0x4f, 0x0c);
808 			ISACRTCW(PANEL_0x50, 0x7a);
809 			ISACRTCW(PANEL_0x51, 0x56);
810 			ISACRTCW(PANEL_0x52, 0x00);
811 			ISACRTCW(PANEL_0x53, 0x5d);
812 			ISACRTCW(PANEL_0x54, 0x0e);
813 			ISACRTCW(PANEL_0x55, 0x3b);
814 			ISACRTCW(PANEL_0x56, 0x2b);
815 			ISACRTCW(PANEL_0x57, 0x00);
816 			ISACRTCW(PANEL_0x58, 0x2f);
817 			ISACRTCW(PANEL_0x59, 0x18);
818 			ISACRTCW(PANEL_0x60, 0x88);
819 			ISACRTCW(PANEL_0x61, 0x63);
820 			ISACRTCW(PANEL_0x62, 0x0b);
821 			ISACRTCW(PANEL_0x63, 0x69);
822 			ISACRTCW(PANEL_0x64, 0x1a);
823 			stat = B_OK;
824 			break;
825 	    case 480:
826 			/* 1024x480 widescreen panels are only used on later cards
827 			 * (NM2097 and later ?) */
828 			ISACRTCW(PANEL_0x40, 0xa3);
829 			ISACRTCW(PANEL_0x41, 0x7f);
830 			ISACRTCW(PANEL_0x42, 0x1b);
831 			ISACRTCW(PANEL_0x43, 0x89);
832 			ISACRTCW(PANEL_0x44, 0x16);
833 			ISACRTCW(PANEL_0x45, 0x0b);
834 			ISACRTCW(PANEL_0x46, 0x2c);
835 			ISACRTCW(PANEL_0x47, 0xe8);
836 			ISACRTCW(PANEL_0x48, 0x0c);
837 			ISACRTCW(PANEL_0x49, 0xe7);
838 			ISACRTCW(PANEL_0x4a, 0x09);
839 			ISACRTCW(PANEL_0x4b, 0x4f);
840 			ISACRTCW(PANEL_0x4c, 0x40);
841 			ISACRTCW(PANEL_0x4d, 0x00);
842 			ISACRTCW(PANEL_0x4e, 0x44);
843 			ISACRTCW(PANEL_0x4f, 0x0c);
844 			ISACRTCW(PANEL_0x50, 0x7a);
845 			ISACRTCW(PANEL_0x51, 0x56);
846 			ISACRTCW(PANEL_0x52, 0x00);
847 			ISACRTCW(PANEL_0x53, 0x5d);
848 			ISACRTCW(PANEL_0x54, 0x0e);
849 			ISACRTCW(PANEL_0x55, 0x3b);
850 			ISACRTCW(PANEL_0x56, 0x2a);
851 			ISACRTCW(PANEL_0x57, 0x00);
852 			ISACRTCW(PANEL_0x58, 0x2f);
853 			ISACRTCW(PANEL_0x59, 0x18);
854 			ISACRTCW(PANEL_0x60, 0x88);
855 			ISACRTCW(PANEL_0x61, 0x63);
856 			ISACRTCW(PANEL_0x62, 0x0b);
857 			ISACRTCW(PANEL_0x63, 0x69);
858 			ISACRTCW(PANEL_0x64, 0x1a);
859 			stat = B_OK;
860 			break;
861 		}
862 		break;
863 	case 1280:
864 		/* no info available */
865 		break;
866 	}
867 
868 	if (stat != B_OK)
869 		LOG(2,("CRTC: unable to program panel: unknown modeline needed.\n"));
870 
871 	return stat;
872 }
873 
874 status_t nm_crtc_cursor_init()
875 {
876 	int i;
877 	vuint32 * fb;
878 	uint32 curadd, curreg;
879 
880 	/* the cursor is at the end of cardRAM */
881 	curadd = ((si->ps.memory_size * 1024) - si->ps.curmem_size);
882 
883 	/* set cursor bitmap adress on a 1kb boundary, and move the bits around
884 	 * so they get placed at the correct registerbits */
885 	curreg = (((curadd >> 10) & 0x000f) << 8);
886 	curreg |= (((curadd >> 10) & 0x0ff0) >> 4);
887 	/* NM2380 must have an extra bit for > 4Mb: assuming it to be on b12... */
888 	curreg |= ((curadd >> 10) & 0x1000);
889 
890 	if (si->ps.card_type < NM2200)
891 		CR1W(CURADDRESS, curreg);
892 	else
893 		CR1W(22CURADDRESS, curreg);
894 
895 	/*set cursor colour*/
896 	if (si->ps.card_type < NM2200)
897 	{
898 		/* background is black */
899 		CR1W(CURBGCOLOR, 0x00000000);
900 		/* foreground is white */
901 		CR1W(CURFGCOLOR, 0x00ffffff);
902 	}
903 	else
904 	{
905 		/* background is black */
906 		CR1W(22CURBGCOLOR, 0x00000000);
907 		/* foreground is white */
908 		CR1W(22CURFGCOLOR, 0x00ffffff);
909 	}
910 
911 	/* we must set a valid colordepth to get full RAM access on Neomagic cards:
912 	 * in old pre 8-bit color VGA modes some planemask is in effect apparantly,
913 	 * allowing access only to every 7th and 8th RAM byte across the entire RAM. */
914 	nm_crtc_depth(BPP8);
915 
916 	/* clear cursor: so we need full RAM access! */
917 	fb = ((vuint32 *)(((uint32)si->framebuffer) + curadd));
918 	for (i = 0; i < (1024/4); i++)
919 	{
920 		fb[i] = 0;
921 	}
922 
923 	/* activate hardware cursor */
924 	nm_crtc_cursor_show();
925 
926 	return B_OK;
927 }
928 
929 status_t nm_crtc_cursor_show()
930 {
931 	if (si->ps.card_type < NM2200)
932 	{
933 		CR1W(CURCTRL, 0x00000001);
934 	}
935 	else
936 	{
937 		CR1W(22CURCTRL, 0x00000001);
938 	}
939 	return B_OK;
940 }
941 
942 status_t nm_crtc_cursor_hide()
943 {
944 //linux fixme: using this kills PCI(?) access sometimes, so use ISA access as below...
945 /*
946 	if (si->ps.card_type < NM2200)
947 	{
948 		CR1W(CURCTRL, 0x00000000);
949 	}
950 	else
951 	{
952 		CR1W(22CURCTRL, 0x00000000);
953 	}
954 */
955 	/* disable cursor */
956 	ISAGRPHW(CURCTRL,0x00);
957 
958 	return B_OK;
959 }
960 
961 /*set up cursor shape*/
962 status_t nm_crtc_cursor_define(uint8* andMask,uint8* xorMask)
963 {
964 	uint8 y;
965 	vuint8 * cursor;
966 
967 	/* get a pointer to the cursor: it's at the end of cardRAM */
968 	cursor = (vuint8*) si->framebuffer;
969 	cursor += ((si->ps.memory_size * 1024) - si->ps.curmem_size);
970 
971 	/*draw the cursor*/
972 	for(y=0;y<16;y++)
973 	{
974 		cursor[y*16+8]=~*andMask++;
975 		cursor[y*16+0]=*xorMask++;
976 		cursor[y*16+9]=~*andMask++;
977 		cursor[y*16+1]=*xorMask++;
978 	}
979 
980 	//test.. only valid for <NM2200!!
981 /*	{
982 		float pclk;
983 		uint8 n,m,x = 1;
984 		n = ISAGRPHR(PLLC_NL);
985 		m = ISAGRPHR(PLLC_M);
986 		LOG(4,("CRTC: PLLSEL $%02x\n", ISARB(MISCR)));
987 		LOG(4,("CRTC: PLLN $%02x\n", n));
988 		LOG(4,("CRTC: PLLM $%02x\n", m));
989 
990 		if (n & 0x80) x = 2;
991 		n &= 0x7f;
992 		pclk = ((si->ps.f_ref * (n + 1)) / ((m + 1) * x));
993 		LOG(2,("CRTC: Pixelclock is %fMHz\n", pclk));
994 		nm_general_output_select();
995 	}
996 */
997 	return B_OK;
998 }
999 
1000 /*position the cursor*/
1001 status_t nm_crtc_cursor_position(uint16 x ,uint16 y)
1002 {
1003 //NM2160 is ok without this, still verify the rest..:
1004 	/* make sure we are not in retrace, because the register(s) might get copied
1005 	 * during our reprogramming them (double buffering feature) */
1006 /*	fixme!?
1007 	while (ACCR(STATUS) & 0x08)
1008 	{
1009 		snooze(4);
1010 	}
1011 */
1012 	if (si->ps.card_type < NM2200)
1013 	{
1014 		CR1W(CURX, (uint32)x);
1015 		CR1W(CURY, (uint32)y);
1016 	}
1017 	else
1018 	{
1019 		CR1W(22CURX, (uint32)x);
1020 		CR1W(22CURY, (uint32)y);
1021 	}
1022 
1023 	return B_OK;
1024 }
1025