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