xref: /haiku/src/add-ons/accelerants/neomagic/engine/nm_acc.c (revision 93aeb8c3bc3f13cb1f282e3e749258a23790d947)
1 /* nm Acceleration functions */
2 
3 /* Author:
4    Rudolf Cornelissen 3/2004-8/2004.
5 */
6 
7 /*
8 	General note about NeoMagic cards:
9 	The Neomagic acceleration engines apparantly contain several faults which is the
10 	main reason for the differences in setup for different engines.
11 */
12 
13 #define MODULE_BIT 0x00080000
14 
15 #include "nm_std.h"
16 
17 static status_t nm_acc_wait_fifo(uint32 n);
18 
19 /*
20 	acceleration notes:
21 
22 	-> functions Be's app_server uses:
23 	fill span (horizontal only)
24 	fill rectangle (these 2 are very similar)
25 	invert rectangle
26 	blit
27 
28 	-> Splitting up the acc routines for all cards does not have noticable effects
29 	on the acceleration speed, although all those switch statements would vanish.
30 */
31 
32 status_t nm_acc_wait_idle()
33 {
34 	/* wait until engine completely idle */
35 	/* Note:
36 	 * because we have no FIFO functionality we have to make sure we return ASAP(!).
37 	 * snoozing() for just 1 microSecond already more than halfs the engine
38 	 * performance... */
39 	while (ACCR(STATUS) & 0x00000001);
40 
41 	return B_OK;
42 }
43 
44 /* wait for enough room in fifo (apparantly works on NM2090 and NM2093 only!) */
45 static status_t nm_acc_wait_fifo(uint32 n)
46 {
47 	while (((ACCR(STATUS) & 0x0000ff00) >> 8) < n)
48 	{
49 		/* snooze a bit so I do not hammer the bus */
50 		snooze (10);
51 	}
52 }
53 
54 /* AFAIK this must be done for every new screenmode.
55  * Engine required init. */
56 status_t nm_acc_init()
57 {
58 	/* Set pixel width */
59 	switch(si->dm.space)
60 	{
61 	case B_CMAP8:
62 		/* b8-9 determine engine colordepth */
63 		si->engine.control = (1 << 8);
64 		si->engine.depth = 1;
65 		break;
66 	case B_RGB15_LITTLE:case B_RGB16_LITTLE:
67 		/* b8-9 determine engine colordepth */
68 		si->engine.control = (2 << 8);
69 		si->engine.depth = 2;
70 		break;
71 	case B_RGB24_LITTLE:
72 		/* b8-9 determine engine colordepth */
73 		si->engine.control = (3 << 8);
74 		si->engine.depth = 3;
75 		/* no acceleration supported on NM2070 - NM2160: let them fallthrough... */
76 		if (si->ps.card_type >= NM2200) break;
77 	default:
78 		LOG(8,("ACC: init, invalid bit depth\n"));
79 		return B_ERROR;
80 	}
81 
82 	/* setup memory pitch (b10-12) on newer cards:
83 	 * this works with a table, there are very few fixed settings.. */
84 	if(si->ps.card_type > NM2070)
85 	{
86 		switch(si->fbc.bytes_per_row / si->engine.depth)
87 		{
88 		case 640:
89 			si->engine.control |= (2 << 10);
90 			break;
91 		case 800:
92 			si->engine.control |= (3 << 10);
93 			break;
94 		case 1024:
95 			si->engine.control |= (4 << 10);
96 			break;
97 		case 1152:
98 			si->engine.control |= (5 << 10);
99 			break;
100 		case 1280:
101 			si->engine.control |= (6 << 10);
102 			break;
103 		case 1600:
104 			si->engine.control |= (7 << 10);
105 			break;
106 		default:
107 			LOG(8,("ACC: init, invalid mode width\n"));
108 			return B_ERROR;
109 		}
110 	}
111 
112 	/* enable engine FIFO (fixme: works this way on pre NM2200 only (engine.control)) */
113 	/* fixme when/if possible:
114 	 * does not work on most cards.. (tried NM2070 and NM2160)
115 	 * workaround: always wait until engine completely idle before programming. */
116 	switch (si->ps.card_type)
117 	{
118 	case NM2090:
119 	case NM2093:
120 		si->engine.control |= (1 << 27);
121 		break;
122 	default:
123 		break;
124 	}
125 
126 	/* setup buffer startadress */
127 	/* fixme when/if possible:
128 	 * not possible on all cards or not enough specs known. (tried NM2160)
129 	 * workaround: place cursor bitmap at _end_ of cardRAM instead of in _beginning_. */
130 
131 	/* setup clipping (fixme: works this way on pre NM2200 only (engine.control)) */
132 	/* note:
133 	 * on NM2160 the max acc engine width of 1600 pixels can be programmed, but
134 	 * the max. height is only 1023 pixels (height register holds just 10 bits)!
135 	 * note also:
136 	 * while the vertical clipping feature can do upto and including 1023 pixels,
137 	 * the engine itself can do upto and including 1024 pixels vertically.
138 	 * So:
139 	 * Don't use the engine's clipping feature as we want to get the max out of the
140 	 * engine. We won't export the acc hooks for modes beyond the acc engine's
141 	 * capabilities. */
142 //	si->engine.control |= (1 << 26);
143 //	ACCW(CLIPLT, 0);
144 //	ACCW(CLIPRB, ((si->dm.virtual_height << 16) | (si->dm.virtual_width & 0x0000ffff)));
145 
146 	/* init some extra registers on some cards */
147 	switch(si->ps.card_type)
148 	{
149 	case NM2070:
150 		/* make sure the previous command (if any) is completed */
151 //		does not work yet:
152 //		nm_acc_wait_fifo(5);
153 //		so:
154 		nm_acc_wait_idle();
155 
156 		/* setup memory pitch */
157 		ACCW(2070_PLANEMASK, 0x0000ffff);
158 		ACCW(2070_SRCPITCH, si->fbc.bytes_per_row);
159 		ACCW(2070_SRCBITOFF, 0);
160 		ACCW(2070_DSTPITCH, si->fbc.bytes_per_row);
161 		ACCW(2070_DSTBITOFF, 0);
162 		break;
163 	case NM2200:
164 	case NM2230:
165 	case NM2360:
166 	case NM2380:
167 		/* make sure the previous command (if any) is completed */
168 //		does not work yet:
169 //		nm_acc_wait_fifo(2);
170 //		so:
171 		nm_acc_wait_idle();
172 
173 		/* setup engine depth and engine destination-pitch */
174 		ACCW(STATUS, ((si->engine.control & 0x0000ffff) << 16));
175 		/* setup engine source-pitch */
176 		ACCW(2200_SRC_PITCH,
177 			((si->fbc.bytes_per_row << 16) | (si->fbc.bytes_per_row & 0x0000ffff)));
178 		break;
179 	default:
180 		/* nothing to do */
181 		break;
182 	}
183 
184 	return B_OK;
185 }
186 
187 /* screen to screen blit - i.e. move windows around and scroll within them. */
188 status_t nm_acc_blit(uint16 xs,uint16 ys,uint16 xd,uint16 yd,uint16 w,uint16 h)
189 {
190 	/* make sure the previous command (if any) is completed */
191 	switch (si->ps.card_type)
192 	{
193 	case NM2090:
194 	case NM2093:
195 		nm_acc_wait_fifo(4);
196 		break;
197 	default:
198 		nm_acc_wait_idle();
199 		break;
200 	}
201 
202     if ((yd < ys) || ((yd == ys) && (xd < xs)))
203     {
204 		/* start with upper left corner */
205 		switch (si->ps.card_type)
206 		{
207 		case NM2070:
208 			/* use ROP GXcopy (b16-19), and use linear adressing system */
209 			ACCW(CONTROL, si->engine.control | 0x000c0000);
210 			/* send command and exexute (warning: order of programming regs is important!) */
211 			ACCW(2070_XYEXT, ((h << 16) | (w & 0x0000ffff)));
212 			ACCW(SRCSTARTOFF, ((ys * si->fbc.bytes_per_row) + (xs * si->engine.depth)));
213 			ACCW(2070_DSTSTARTOFF, ((yd * si->fbc.bytes_per_row) + (xd * si->engine.depth)));
214 			break;
215 		case NM2090:
216 		case NM2093:
217 		case NM2097:
218 		case NM2160:
219 			/* use ROP GXcopy (b16-19), and use XY coord. system (b24-25) */
220 			ACCW(CONTROL, si->engine.control | 0x830c0000);
221 			/* send command and exexute (warning: order of programming regs is important!) */
222 			ACCW(SRCSTARTOFF, ((ys << 16) | (xs & 0x0000ffff)));
223 			ACCW(2090_DSTSTARTOFF, ((yd << 16) | (xd & 0x0000ffff)));
224 			ACCW(2090_XYEXT, (((h + 1) << 16) | ((w + 1) & 0x0000ffff)));
225 			break;
226 		default: /* NM2200 and later */
227 			/* use ROP GXcopy (b16-19), and use linear adressing system */
228 			//fixme? it seems CONTROL nolonger needs direction, and can be pgm'd just once...
229 			ACCW(CONTROL, (/*si->engine.control |*/ 0x800c0000));
230 			/* send command and exexute (warning: order of programming regs is important!) */
231 			ACCW(SRCSTARTOFF, ((ys * si->fbc.bytes_per_row) + (xs * si->engine.depth)));
232 			ACCW(2090_DSTSTARTOFF, ((yd * si->fbc.bytes_per_row) + (xd * si->engine.depth)));
233 			ACCW(2090_XYEXT, (((h + 1) << 16) | ((w + 1) & 0x0000ffff)));
234 			break;
235 		}
236 	}
237     else
238     {
239 		/* start with lower right corner */
240 		switch (si->ps.card_type)
241 		{
242 		case NM2070:
243 			/* use ROP GXcopy (b16-19), and use linear adressing system */
244 			ACCW(CONTROL, (si->engine.control | 0x000c0013));
245 			/* send command and exexute (warning: order of programming regs is important!) */
246 			ACCW(2070_XYEXT, ((h << 16) | (w & 0x0000ffff)));
247 			ACCW(SRCSTARTOFF, (((ys + h) * si->fbc.bytes_per_row) + ((xs + w) * si->engine.depth)));
248 			ACCW(2070_DSTSTARTOFF, (((yd + h) * si->fbc.bytes_per_row) + ((xd + w) * si->engine.depth)));
249 			break;
250 		case NM2090:
251 		case NM2093:
252 		case NM2097:
253 		case NM2160:
254 			/* use ROP GXcopy (b16-19), and use XY coord. system (b24-25) */
255 			ACCW(CONTROL, (si->engine.control | 0x830c0013));
256 			/* send command and exexute (warning: order of programming regs is important!) */
257 			ACCW(SRCSTARTOFF, (((ys + h) << 16) | ((xs + w) & 0x0000ffff)));
258 			ACCW(2090_DSTSTARTOFF, (((yd + h) << 16) | ((xd + w) & 0x0000ffff)));
259 			ACCW(2090_XYEXT, (((h + 1) << 16) | ((w + 1) & 0x0000ffff)));
260 			break;
261 		default: /* NM2200 and later */
262 			/* use ROP GXcopy (b16-19), and use linear adressing system */
263 			//fixme? it seems CONTROL nolonger needs direction, and can be pgm'd just once...
264 			ACCW(CONTROL, (/*si->engine.control |*/ 0x800c0013));
265 			/* send command and exexute (warning: order of programming regs is important!) */
266 			ACCW(SRCSTARTOFF, (((ys + h) * si->fbc.bytes_per_row) + ((xs + w) * si->engine.depth)));
267 			ACCW(2090_DSTSTARTOFF, (((yd + h) * si->fbc.bytes_per_row) + ((xd + w) * si->engine.depth)));
268 			ACCW(2090_XYEXT, (((h + 1) << 16) | ((w + 1) & 0x0000ffff)));
269 			break;
270 		}
271 	}
272 
273 	return B_OK;
274 }
275 
276 /* rectangle fill - i.e. workspace and window background color */
277 /* span fill - i.e. (selected) menuitem background color (Dano) */
278 status_t nm_acc_setup_rectangle(uint32 color)
279 {
280 	/* make sure the previous command (if any) is completed */
281 	switch (si->ps.card_type)
282 	{
283 	case NM2090:
284 	case NM2093:
285 		nm_acc_wait_fifo(2);
286 		break;
287 	default:
288 		nm_acc_wait_idle();
289 		break;
290 	}
291 
292 	switch (si->ps.card_type)
293 	{
294 	case NM2070:
295 		/* use ROP GXcopy (b16-19), use linear adressing system, do foreground color (b3) */
296 		ACCW(CONTROL, (si->engine.control | 0x000c0008));
297 		/* setup color */
298 		if (si->engine.depth == 1)
299 			ACCW(FGCOLOR, color);
300 		else
301 			/* swap colorbytes */
302 			ACCW(FGCOLOR, (((color & 0xff00) >> 8) | ((color & 0x00ff) << 8)));
303 		break;
304 	case NM2090:
305 	case NM2093:
306 	case NM2097:
307 	case NM2160:
308 		/* use ROP GXcopy (b16-19), use XY coord. system (b24-25), do foreground color (b3) */
309 		ACCW(CONTROL, (si->engine.control | 0x830c0008));
310 		/* setup color */
311 		ACCW(FGCOLOR, color);
312 		break;
313 	default: /* NM2200 and later */
314 		/* use ROP GXcopy (b16-19), use XY coord. system (b24-25), do foreground color (b3) */
315 		ACCW(CONTROL, (/*si->engine.control |*/ 0x830c0008));
316 		/* setup color */
317 		ACCW(FGCOLOR, color);
318 		break;
319 	}
320 
321 	return B_OK;
322 }
323 
324 status_t nm_acc_rectangle(uint32 xs,uint32 xe,uint32 ys,uint32 yl)
325 {
326 	/* The engine does not take kindly if we try to let it fill a rect with
327 	 * zero width. Dano's app_server occasionally tries to let us do that though!
328 	 * Testable with BeRoMeter 1.2.6, all Ellipses tests. Effect of zero width fill:
329 	 * horizontal lines across the entire screen at top and bottom of ellipses. */
330 	if (xe == xs) return B_OK;
331 
332 	/* make sure the previous command (if any) is completed */
333 	switch (si->ps.card_type)
334 	{
335 	case NM2090:
336 	case NM2093:
337 		nm_acc_wait_fifo(2);
338 		break;
339 	default:
340 		nm_acc_wait_idle();
341 		break;
342 	}
343 
344 	/* send command and exexute (warning: order of programming regs is important!) */
345 	switch (si->ps.card_type)
346 	{
347 	case NM2070:
348 		ACCW(2070_XYEXT, (((yl - 1) << 16) | ((xe - xs - 1) & 0x0000ffff)));
349 		ACCW(2070_DSTSTARTOFF, ((ys * si->fbc.bytes_per_row) + (xs * si->engine.depth)));
350 		break;
351 	default: /* NM2090 and later */
352 		ACCW(2090_DSTSTARTOFF, ((ys << 16) | (xs & 0x0000ffff)));
353 		ACCW(2090_XYEXT, ((yl << 16) | ((xe - xs) & 0x0000ffff)));
354 		break;
355 	}
356 
357 	return B_OK;
358 }
359 
360 /* rectangle invert - i.e. text cursor and text selection */
361 status_t nm_acc_setup_rect_invert()
362 {
363 	/* make sure the previous command (if any) is completed */
364 	switch (si->ps.card_type)
365 	{
366 	case NM2090:
367 	case NM2093:
368 		nm_acc_wait_fifo(2);
369 		break;
370 	default:
371 		nm_acc_wait_idle();
372 		break;
373 	}
374 
375 	switch (si->ps.card_type)
376 	{
377 	case NM2070:
378 		/* use ROP GXinvert (b16-19), use linear adressing system. */
379 		/* note:
380 		 * although selecting foreground color (b3) should have no influence, NM2070
381 		 * thinks otherwise if depth is not 8-bit. In 8-bit depth ROP takes precedence
382 		 * over source-select, but in other spaces it's vice-versa (forcing GXcopy!). */
383 		ACCW(CONTROL, (si->engine.control | 0x00050000));
384 		break;
385 	case NM2090:
386 	case NM2093:
387 	case NM2097:
388 	case NM2160:
389 		/* use ROP GXinvert (b16-19), use XY coord. system (b24-25), do foreground color (b3) */
390 		ACCW(CONTROL, (si->engine.control | 0x83050008));
391 		break;
392 	default: /* NM2200 and later */
393 		/* use ROP GXinvert (b16-19), use XY coord. system (b24-25), do foreground color (b3) */
394 		ACCW(CONTROL, (/*si->engine.control |*/ 0x83050008));
395 		break;
396 	}
397 	/* reset color (just to be 'safe') */
398 	ACCW(FGCOLOR, 0);
399 
400 	return B_OK;
401 }
402 
403 status_t nm_acc_rectangle_invert(uint32 xs,uint32 xe,uint32 ys,uint32 yl)
404 {
405 	/* The engine probably also does not take kindly if we try to let it invert a
406 	 * rect with zero width... (see nm_acc_rectangle() routine for explanation.) */
407 	if (xe == xs) return B_OK;
408 
409 	/* make sure the previous command (if any) is completed */
410 	switch (si->ps.card_type)
411 	{
412 	case NM2090:
413 	case NM2093:
414 		nm_acc_wait_fifo(2);
415 		break;
416 	default:
417 		nm_acc_wait_idle();
418 		break;
419 	}
420 
421 	/* send command and exexute (warning: order of programming regs is important!) */
422 	switch (si->ps.card_type)
423 	{
424 	case NM2070:
425 		ACCW(2070_XYEXT, (((yl - 1) << 16) | ((xe - xs - 1) & 0x0000ffff)));
426 		ACCW(2070_DSTSTARTOFF, ((ys * si->fbc.bytes_per_row) + (xs * si->engine.depth)));
427 		break;
428 	default: /* NM2090 and later */
429 		ACCW(2090_DSTSTARTOFF, ((ys << 16) | (xs & 0x0000ffff)));
430 		ACCW(2090_XYEXT, ((yl << 16) | ((xe - xs) & 0x0000ffff)));
431 		break;
432 	}
433 
434 	return B_OK;
435 }
436 
437 /* screen to screen tranparent blit */
438 status_t nm_acc_transparent_blit(uint16 xs,uint16 ys,uint16 xd,uint16 yd,uint16 w,uint16 h,uint32 colour)
439 {
440 	//fixme: implement.
441 
442 	return B_ERROR;
443 }
444 
445 /* screen to screen scaled filtered blit - i.e. scale video in memory */
446 status_t nm_acc_video_blit(uint16 xs,uint16 ys,uint16 ws, uint16 hs,
447 	uint16 xd,uint16 yd,uint16 wd,uint16 hd)
448 {
449 	//fixme: implement.
450 
451 	return B_OK;
452 }
453