1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2012 Sam Lantinga
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19    Sam Lantinga
20    slouken@libsdl.org
21*/
22#include "SDL_config.h"
23
24#include "SDL_video.h"
25#include "../SDL_blit.h"
26#include "SDL_fbmatrox.h"
27#include "matrox_mmio.h"
28
29
30/* Wait for vertical retrace - taken from the XFree86 Matrox driver */
31static void WaitVBL(_THIS)
32{
33	int count;
34
35	/* find start of retrace */
36	mga_waitidle();
37	while (  (mga_in8(0x1FDA) & 0x08) )
38		;
39	while ( !(mga_in8(0x1FDA) & 0x08) )
40		;
41	/* wait until we're past the start */
42	count = mga_in32(0x1E20) + 2;
43	while ( mga_in32(0x1E20) < count )
44		;
45}
46static void WaitIdle(_THIS)
47{
48	mga_waitidle();
49}
50
51/* Sets video mem colorkey and accelerated blit function */
52static int SetHWColorKey(_THIS, SDL_Surface *surface, Uint32 key)
53{
54	return(0);
55}
56
57/* Sets per surface hardware alpha value */
58#if 0
59static int SetHWAlpha(_THIS, SDL_Surface *surface, Uint8 value)
60{
61	return(0);
62}
63#endif
64
65static int FillHWRect(_THIS, SDL_Surface *dst, SDL_Rect *rect, Uint32 color)
66{
67	int dstX, dstY;
68	Uint32 fxbndry;
69	Uint32 ydstlen;
70	Uint32 fillop;
71
72	/* Don't blit to the display surface when switched away */
73	if ( switched_away ) {
74		return -2; /* no hardware access */
75	}
76	if ( dst == this->screen ) {
77		SDL_mutexP(hw_lock);
78	}
79
80	switch (dst->format->BytesPerPixel) {
81	    case 1:
82		color |= (color<<8);
83	    case 2:
84		color |= (color<<16);
85		break;
86	}
87
88	/* Set up the X/Y base coordinates */
89	FB_dst_to_xy(this, dst, &dstX, &dstY);
90
91	/* Adjust for the current rectangle */
92	dstX += rect->x;
93	dstY += rect->y;
94
95	/* Set up the X boundaries */
96	fxbndry = (dstX | ((dstX+rect->w) << 16));
97
98	/* Set up the Y boundaries */
99	ydstlen = (rect->h | (dstY << 16));
100
101	/* Set up for color fill operation */
102	fillop = MGADWG_TRAP | MGADWG_SOLID |
103	         MGADWG_ARZERO | MGADWG_SGNZERO | MGADWG_SHIFTZERO;
104
105	/* Execute the operations! */
106	mga_wait(5);
107	mga_out32(MGAREG_DWGCTL, fillop | MGADWG_REPLACE);
108	mga_out32(MGAREG_FCOL, color);
109	mga_out32(MGAREG_FXBNDRY, fxbndry);
110	mga_out32(MGAREG_YDSTLEN + MGAREG_EXEC, ydstlen);
111
112	FB_AddBusySurface(dst);
113
114	if ( dst == this->screen ) {
115		SDL_mutexV(hw_lock);
116	}
117	return(0);
118}
119
120static int HWAccelBlit(SDL_Surface *src, SDL_Rect *srcrect,
121                       SDL_Surface *dst, SDL_Rect *dstrect)
122{
123	SDL_VideoDevice *this = current_video;
124	int pitch, w, h;
125	int srcX, srcY;
126	int dstX, dstY;
127	Uint32 sign;
128	Uint32 start, stop;
129	int skip;
130	Uint32 blitop;
131
132	/* FIXME: For now, only blit to display surface */
133	if ( dst->pitch != SDL_VideoSurface->pitch ) {
134		return(src->map->sw_blit(src, srcrect, dst, dstrect));
135	}
136
137	/* Don't blit to the display surface when switched away */
138	if ( switched_away ) {
139		return -2; /* no hardware access */
140	}
141	if ( dst == this->screen ) {
142		SDL_mutexP(hw_lock);
143	}
144
145	/* Calculate source and destination base coordinates (in pixels) */
146	w = dstrect->w;
147	h = dstrect->h;
148	FB_dst_to_xy(this, src, &srcX, &srcY);
149	FB_dst_to_xy(this, dst, &dstX, &dstY);
150
151	/* Adjust for the current blit rectangles */
152	srcX += srcrect->x;
153	srcY += srcrect->y;
154	dstX += dstrect->x;
155	dstY += dstrect->y;
156	pitch = dst->pitch/dst->format->BytesPerPixel;
157
158	/* Set up the blit direction (sign) flags */
159	sign = 0;
160	if ( srcX < dstX ) {
161		sign |= 1;
162	}
163	if ( srcY < dstY ) {
164		sign |= 4;
165		srcY += (h - 1);
166		dstY += (h - 1);
167	}
168
169	/* Set up the blit source row start, end, and skip (in pixels) */
170	stop = start = (srcY * pitch) + srcX;
171	if ( srcX < dstX ) {
172		start += (w - 1);
173	} else {
174		stop  += (w - 1);
175	}
176	if ( srcY < dstY ) {
177		skip = -pitch;
178	} else {
179		skip = pitch;
180	}
181
182	/* Set up the blit operation */
183	if ( (src->flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY ) {
184		Uint32 colorkey;
185
186		blitop = MGADWG_BFCOL | MGADWG_BITBLT |
187		         MGADWG_SHIFTZERO | MGADWG_RSTR | (0x0C << 16) |
188		         MGADWG_TRANSC;
189
190		colorkey = src->format->colorkey;
191		switch (dst->format->BytesPerPixel) {
192		    case 1:
193			colorkey |= (colorkey<<8);
194		    case 2:
195			colorkey |= (colorkey<<16);
196			break;
197		}
198		mga_wait(2);
199		mga_out32(MGAREG_FCOL, colorkey);
200		mga_out32(MGAREG_BCOL, 0xFFFFFFFF);
201	} else {
202		blitop = MGADWG_BFCOL | MGADWG_BITBLT |
203		         MGADWG_SHIFTZERO | MGADWG_RSTR | (0x0C << 16);
204	}
205	mga_wait(7);
206	mga_out32(MGAREG_SGN, sign);
207	mga_out32(MGAREG_AR3, start);
208	mga_out32(MGAREG_AR0, stop);
209	mga_out32(MGAREG_AR5, skip);
210	mga_out32(MGAREG_FXBNDRY, (dstX | ((dstX + w-1) << 16)));
211	mga_out32(MGAREG_YDSTLEN, (dstY << 16) | h);
212	mga_out32(MGAREG_DWGCTL + MGAREG_EXEC, blitop);
213
214	FB_AddBusySurface(src);
215	FB_AddBusySurface(dst);
216
217	if ( dst == this->screen ) {
218		SDL_mutexV(hw_lock);
219	}
220	return(0);
221}
222
223static int CheckHWBlit(_THIS, SDL_Surface *src, SDL_Surface *dst)
224{
225	int accelerated;
226
227	/* Set initial acceleration on */
228	src->flags |= SDL_HWACCEL;
229
230	/* Set the surface attributes */
231	if ( (src->flags & SDL_SRCALPHA) == SDL_SRCALPHA ) {
232		if ( ! this->info.blit_hw_A ) {
233			src->flags &= ~SDL_HWACCEL;
234		}
235	}
236	if ( (src->flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY ) {
237		if ( ! this->info.blit_hw_CC ) {
238			src->flags &= ~SDL_HWACCEL;
239		}
240	}
241
242	/* Check to see if final surface blit is accelerated */
243	accelerated = !!(src->flags & SDL_HWACCEL);
244	if ( accelerated ) {
245		src->map->hw_blit = HWAccelBlit;
246	}
247	return(accelerated);
248}
249
250void FB_MatroxAccel(_THIS, __u32 card)
251{
252	/* We have hardware accelerated surface functions */
253	this->CheckHWBlit = CheckHWBlit;
254	wait_vbl = WaitVBL;
255	wait_idle = WaitIdle;
256
257	/* The Matrox has an accelerated color fill */
258	this->info.blit_fill = 1;
259	this->FillHWRect = FillHWRect;
260
261	/* The Matrox has accelerated normal and colorkey blits. */
262	this->info.blit_hw = 1;
263	/* The Millenium I appears to do the colorkey test a word
264	   at a time, and the transparency is intverted. (?)
265	 */
266	if ( card != FB_ACCEL_MATROX_MGA2064W ) {
267		this->info.blit_hw_CC = 1;
268		this->SetHWColorKey = SetHWColorKey;
269	}
270
271#if 0 /* Not yet implemented? */
272	/* The Matrox G200/G400 has an accelerated alpha blit */
273	if ( (card == FB_ACCEL_MATROX_MGAG200)
274	  || (card == FB_ACCEL_MATROX_MGAG400)
275	) {
276		this->info.blit_hw_A = 1;
277		this->SetHWAlpha = SetHWAlpha;
278	}
279#endif
280}
281