comparison src/video/ps2gs/SDL_gsvideo.c @ 70:f590dd383b5d

Added Linux PlayStation 2 Graphics Synthesizer support
author Sam Lantinga <slouken@lokigames.com>
date Sat, 16 Jun 2001 03:17:45 +0000
parents
children 0bfcf0d2b874
comparison
equal deleted inserted replaced
69:280ff3af2ecc 70:f590dd383b5d
1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997, 1998, 1999, 2000 Sam Lantinga
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 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 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 Sam Lantinga
20 slouken@devolution.com
21 */
22
23 #ifdef SAVE_RCSID
24 static char rcsid =
25 "@(#) $Id$";
26 #endif
27
28 /* Framebuffer console based SDL video driver implementation.
29 */
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <sys/ioctl.h>
36 #include <sys/mman.h>
37
38 #include "SDL.h"
39 #include "SDL_error.h"
40 #include "SDL_video.h"
41 #include "SDL_mouse.h"
42 #include "SDL_sysvideo.h"
43 #include "SDL_pixels_c.h"
44 #include "SDL_events_c.h"
45 #include "SDL_cursor_c.h"
46 #include "SDL_gsvideo.h"
47 #include "SDL_gsmouse_c.h"
48 #include "SDL_gsevents_c.h"
49 #include "SDL_gsyuv_c.h"
50
51
52 /* Initialization/Query functions */
53 static int GS_VideoInit(_THIS, SDL_PixelFormat *vformat);
54 static SDL_Rect **GS_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags);
55 static SDL_Surface *GS_SetVideoMode(_THIS, SDL_Surface *current, int width, int height, int bpp, Uint32 flags);
56 static int GS_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors);
57 static void GS_VideoQuit(_THIS);
58
59 /* Hardware surface functions */
60 static int GS_AllocHWSurface(_THIS, SDL_Surface *surface);
61 static int GS_LockHWSurface(_THIS, SDL_Surface *surface);
62 static void GS_UnlockHWSurface(_THIS, SDL_Surface *surface);
63 static void GS_FreeHWSurface(_THIS, SDL_Surface *surface);
64
65 /* GS driver bootstrap functions */
66
67 static int GS_Available(void)
68 {
69 int console, memory;
70
71 console = open(PS2_DEV_GS, O_RDWR, 0);
72 if ( console >= 0 ) {
73 close(console);
74 }
75 memory = open(PS2_DEV_MEM, O_RDWR, 0);
76 if ( memory >= 0 ) {
77 close(memory);
78 }
79 return((console >= 0) && (memory >= 0));
80 }
81
82 static void GS_DeleteDevice(SDL_VideoDevice *device)
83 {
84 free(device->hidden);
85 free(device);
86 }
87
88 static SDL_VideoDevice *GS_CreateDevice(int devindex)
89 {
90 SDL_VideoDevice *this;
91
92 /* Initialize all variables that we clean on shutdown */
93 this = (SDL_VideoDevice *)malloc(sizeof(SDL_VideoDevice));
94 if ( this ) {
95 memset(this, 0, (sizeof *this));
96 this->hidden = (struct SDL_PrivateVideoData *)
97 malloc((sizeof *this->hidden));
98 }
99 if ( (this == NULL) || (this->hidden == NULL) ) {
100 SDL_OutOfMemory();
101 if ( this ) {
102 free(this);
103 }
104 return(0);
105 }
106 memset(this->hidden, 0, (sizeof *this->hidden));
107 mouse_fd = -1;
108 keyboard_fd = -1;
109
110 /* Set the function pointers */
111 this->VideoInit = GS_VideoInit;
112 this->ListModes = GS_ListModes;
113 this->SetVideoMode = GS_SetVideoMode;
114 this->CreateYUVOverlay = GS_CreateYUVOverlay;
115 this->SetColors = GS_SetColors;
116 this->UpdateRects = NULL;
117 this->VideoQuit = GS_VideoQuit;
118 this->AllocHWSurface = GS_AllocHWSurface;
119 this->CheckHWBlit = NULL;
120 this->FillHWRect = NULL;
121 this->SetHWColorKey = NULL;
122 this->SetHWAlpha = NULL;
123 this->LockHWSurface = GS_LockHWSurface;
124 this->UnlockHWSurface = GS_UnlockHWSurface;
125 this->FlipHWSurface = NULL;
126 this->FreeHWSurface = GS_FreeHWSurface;
127 this->SetIcon = NULL;
128 this->SetCaption = NULL;
129 this->GetWMInfo = NULL;
130 this->FreeWMCursor = GS_FreeWMCursor;
131 this->CreateWMCursor = GS_CreateWMCursor;
132 this->ShowWMCursor = GS_ShowWMCursor;
133 this->MoveWMCursor = GS_MoveWMCursor;
134 this->InitOSKeymap = GS_InitOSKeymap;
135 this->PumpEvents = GS_PumpEvents;
136
137 this->free = GS_DeleteDevice;
138
139 return this;
140 }
141
142 VideoBootStrap PS2GS_bootstrap = {
143 "ps2gs", "PlayStation 2 Graphics Synthesizer",
144 GS_Available, GS_CreateDevice
145 };
146
147 /* These are the pixel formats for the 32, 24, and 16 bit video modes */
148 static struct {
149 int bpp;
150 Uint32 r;
151 Uint32 g;
152 Uint32 b;
153 } GS_pixelmasks[] = {
154 { 32, 0x000000FF, /* RGB little-endian */
155 0x0000FF00,
156 0x00FF0000 },
157 { 24, 0x000000FF, /* RGB little-endian */
158 0x0000FF00,
159 0x00FF0000 },
160 { 16, 0x0000001f, /* RGB little-endian */
161 0x000003e0,
162 0x00007c00 },
163 };
164 /* This is a mapping from SDL bytes-per-pixel to GS pixel format */
165 static int GS_formatmap[] = {
166 -1, /* 0 bpp, not a legal value */
167 -1, /* 8 bpp, not supported (yet?) */
168 PS2_GS_PSMCT16, /* 16 bpp */
169 PS2_GS_PSMCT24, /* 24 bpp */
170 PS2_GS_PSMCT32 /* 32 bpp */
171 };
172
173 static unsigned long long head_tags[] __attribute__((aligned(16))) = {
174 4 | (1LL << 60), /* GIFtag */
175 0x0e, /* A+D */
176 0, /* 2 */
177 PS2_GS_BITBLTBUF,
178 0, /* 4 */
179 PS2_GS_TRXPOS,
180 0, /* 6 */
181 PS2_GS_TRXREG,
182 0, /* 8 */
183 PS2_GS_TRXDIR
184 };
185
186 #define MAXIMG (32767 * 16)
187 #define MAXTAGS 8
188
189 static inline int loadimage_nonblock(int fd, struct ps2_image *image, int size,
190 unsigned long long *hm,
191 unsigned long long *im)
192 {
193 struct ps2_plist plist;
194 struct ps2_packet packet[1 + MAXTAGS * 2];
195 int isize;
196 int pnum, it, eop;
197 char *data;
198
199 /* initialize the variables */
200 data = (char *)image->ptr;
201 pnum = it = eop = 0;
202 plist.packet = packet;
203
204 /* make BITBLT packet */
205 packet[pnum].ptr = hm;
206 packet[pnum].len = sizeof(head_tags);
207 pnum++;
208 hm[2] = ((unsigned long long)image->fbp << 32) |
209 ((unsigned long long)image->fbw << 48) |
210 ((unsigned long long)image->psm << 56);
211 hm[4] = ((unsigned long long)image->x << 32) |
212 ((unsigned long long)image->y << 48);
213 hm[6] = (unsigned long long)image->w |
214 ((unsigned long long)image->h << 32);
215
216 /* make image mode tags */
217 while (!eop) {
218 isize = size > MAXIMG ? MAXIMG : size;
219 size -= isize;
220 eop = (size == 0);
221
222 packet[pnum].ptr = &im[it];
223 packet[pnum].len = sizeof(unsigned long long) * 2;
224 pnum++;
225 im[it++] = (isize >> 4) | (eop ? (1 << 15) : 0) | (2LL << 58);
226 im[it++] = 0;
227
228 packet[pnum].ptr = (void *)data;
229 packet[pnum].len = isize;
230 pnum++;
231 data += isize;
232 }
233 plist.num = pnum;
234
235 return ioctl(fd, PS2IOC_SENDL, &plist);
236 }
237
238 static int GS_VideoInit(_THIS, SDL_PixelFormat *vformat)
239 {
240 struct ps2_screeninfo vinfo;
241
242 /* Initialize the library */
243 console_fd = open(PS2_DEV_GS, O_RDWR, 0);
244 if ( console_fd < 0 ) {
245 SDL_SetError("Unable to open %s", PS2_DEV_GS);
246 return(-1);
247 }
248 memory_fd = open(PS2_DEV_MEM, O_RDWR, 0);
249 if ( memory_fd < 0 ) {
250 close(console_fd);
251 console_fd = -1;
252 SDL_SetError("Unable to open %s", PS2_DEV_MEM);
253 return(-1);
254 }
255
256 /* Determine the current screen depth */
257 if ( ioctl(console_fd, PS2IOC_GSCREENINFO, &vinfo) < 0 ) {
258 close(memory_fd);
259 close(console_fd);
260 console_fd = -1;
261 SDL_SetError("Couldn't get console pixel format");
262 return(-1);
263 }
264 if ( vinfo.mode != PS2_GS_VESA ) {
265 GS_VideoQuit(this);
266 SDL_SetError("Console must be in VESA video mode");
267 return(-1);
268 }
269 switch (vinfo.psm) {
270 /* Supported pixel formats */
271 case PS2_GS_PSMCT32:
272 case PS2_GS_PSMCT24:
273 case PS2_GS_PSMCT16:
274 break;
275 default:
276 GS_VideoQuit(this);
277 SDL_SetError("Unknown console pixel format: %d", vinfo.psm);
278 return(-1);
279 }
280 vformat->BitsPerPixel = GS_pixelmasks[vinfo.psm].bpp;
281 vformat->Rmask = GS_pixelmasks[vinfo.psm].r;
282 vformat->Gmask = GS_pixelmasks[vinfo.psm].g;
283 vformat->Bmask = GS_pixelmasks[vinfo.psm].b;
284 saved_vinfo = vinfo;
285
286 /* Enable mouse and keyboard support */
287 if ( GS_OpenKeyboard(this) < 0 ) {
288 GS_VideoQuit(this);
289 SDL_SetError("Unable to open keyboard");
290 return(-1);
291 }
292 if ( GS_OpenMouse(this) < 0 ) {
293 const char *sdl_nomouse;
294
295 sdl_nomouse = getenv("SDL_NOMOUSE");
296 if ( ! sdl_nomouse ) {
297 GS_VideoQuit(this);
298 SDL_SetError("Unable to open mouse");
299 return(-1);
300 }
301 }
302
303 /* We're done! */
304 return(0);
305 }
306
307 static SDL_Rect **GS_ListModes(_THIS, SDL_PixelFormat *format, Uint32 flags)
308 {
309 static SDL_Rect GS_mode_list[] = {
310 { 0, 0, 1280, 1024 },
311 { 0, 0, 1024, 768 },
312 { 0, 0, 800, 600 },
313 { 0, 0, 640, 480 }
314 };
315 static SDL_Rect *GS_modes[] = {
316 &GS_mode_list[0],
317 &GS_mode_list[1],
318 &GS_mode_list[2],
319 &GS_mode_list[3],
320 NULL
321 };
322 SDL_Rect **modes;
323
324 switch (format->BitsPerPixel) {
325 case 16:
326 case 24:
327 case 32:
328 modes = GS_modes;
329 break;
330 default:
331 modes = NULL;
332 break;
333 }
334 return(modes);
335 }
336
337 /* Various screen update functions available */
338 static void GS_DMAFullUpdate(_THIS, int numrects, SDL_Rect *rects);
339
340 static SDL_Surface *GS_SetVideoMode(_THIS, SDL_Surface *current,
341 int width, int height, int bpp, Uint32 flags)
342 {
343 struct ps2_screeninfo vinfo;
344
345 /* Set the terminal into graphics mode */
346 if ( GS_EnterGraphicsMode(this) < 0 ) {
347 return(NULL);
348 }
349
350 /* Set the video mode and get the final screen format */
351 if ( ioctl(console_fd, PS2IOC_GSCREENINFO, &vinfo) < 0 ) {
352 SDL_SetError("Couldn't get console screen info");
353 return(NULL);
354 }
355 if ( (vinfo.w != width) || (vinfo.h != height) ||
356 (GS_pixelmasks[vinfo.psm].bpp != bpp) ) {
357 switch (width) {
358 case 640:
359 vinfo.res = PS2_GS_640x480;
360 break;
361 case 800:
362 vinfo.res = PS2_GS_800x600;
363 break;
364 case 1024:
365 vinfo.res = PS2_GS_1024x768;
366 break;
367 case 1280:
368 vinfo.res = PS2_GS_1280x1024;
369 break;
370 default:
371 SDL_SetError("Unsupported resolution: %dx%d\n",
372 width, height);
373 return(NULL);
374 }
375 vinfo.res |= (PS2_GS_75Hz << 8);
376 vinfo.w = width;
377 vinfo.h = height;
378 vinfo.fbp = 0;
379 vinfo.psm = GS_formatmap[bpp/8];
380 if ( vinfo.psm < 0 ) {
381 SDL_SetError("Unsupported depth: %d bpp\n", bpp);
382 return(NULL);
383 }
384 if ( ioctl(console_fd, PS2IOC_SSCREENINFO, &vinfo) < 0 ) {
385 SDL_SetError("Couldn't set console screen info");
386 return(NULL);
387 }
388
389 /* Unmap the previous DMA buffer */
390 if ( mapped_mem ) {
391 munmap(mapped_mem, mapped_len);
392 mapped_mem = NULL;
393 }
394 }
395 if ( ! SDL_ReallocFormat(current, GS_pixelmasks[vinfo.psm].bpp,
396 GS_pixelmasks[vinfo.psm].r,
397 GS_pixelmasks[vinfo.psm].g,
398 GS_pixelmasks[vinfo.psm].b, 0) ) {
399 return(NULL);
400 }
401
402 /* Set up the new mode framebuffer */
403 current->flags = SDL_FULLSCREEN;
404 current->w = vinfo.w;
405 current->h = vinfo.h;
406 current->pitch = SDL_CalculatePitch(current);
407
408 /* Memory map the DMA area for block memory transfer */
409 if ( ! mapped_mem ) {
410 pixels_len = height * current->pitch;
411 mapped_len = pixels_len +
412 /* Screen update DMA command area */
413 sizeof(head_tags) + ((2 * MAXTAGS) * 16);
414 mapped_mem = mmap(0, mapped_len, PROT_READ|PROT_WRITE,
415 MAP_SHARED, memory_fd, 0);
416 if ( mapped_mem == MAP_FAILED ) {
417 SDL_SetError("Unable to map %d bytes for DMA",
418 mapped_len);
419 mapped_mem = NULL;
420 return(NULL);
421 }
422
423 /* Set up the entire screen for DMA transfer */
424 screen_image.ptr = mapped_mem;
425 screen_image.fbp = 0;
426 screen_image.fbw = (vinfo.w + 63) / 64;
427 screen_image.psm = vinfo.psm;
428 screen_image.x = 0;
429 screen_image.y = 0;
430 screen_image.w = vinfo.w;
431 screen_image.h = vinfo.h;
432
433 /* get screen image data size (qword aligned) */
434 screen_image_size = (vinfo.w * vinfo.h);
435 switch (screen_image.psm) {
436 case PS2_GS_PSMCT32:
437 screen_image_size *= 4;
438 break;
439 case PS2_GS_PSMCT24:
440 screen_image_size *= 3;
441 break;
442 case PS2_GS_PSMCT16:
443 screen_image_size *= 2;
444 break;
445 }
446 screen_image_size = (screen_image_size + 15) & ~15;
447
448 /* Set up the memory for screen update DMA commands */
449 head_tags_mem = (unsigned long long *)
450 (mapped_mem + pixels_len);
451 image_tags_mem = (unsigned long long *)
452 ((caddr_t)head_tags_mem + sizeof(head_tags));
453 memcpy(head_tags_mem, head_tags, sizeof(head_tags));
454 }
455 current->pixels = NULL;
456 if ( getenv("SDL_FULLSCREEN_UPDATE") ) {
457 /* Correct semantics */
458 current->flags |= SDL_ASYNCBLIT;
459 } else {
460 /* We lie here - the screen memory isn't really the visible
461 display memory and still requires an update, but this
462 has the desired effect for most applications.
463 */
464 current->flags |= SDL_HWSURFACE;
465 }
466
467 /* Set the update rectangle function */
468 this->UpdateRects = GS_DMAFullUpdate;
469
470 /* We're done */
471 return(current);
472 }
473
474 /* We don't support hardware surfaces yet */
475 static int GS_AllocHWSurface(_THIS, SDL_Surface *surface)
476 {
477 return(-1);
478 }
479 static void GS_FreeHWSurface(_THIS, SDL_Surface *surface)
480 {
481 return;
482 }
483 static int GS_LockHWSurface(_THIS, SDL_Surface *surface)
484 {
485 if ( surface == this->screen ) {
486 /* Since mouse motion affects 'pixels', lock it */
487 SDL_LockCursor();
488
489 /* Make sure any pending DMA has completed */
490 if ( dma_pending ) {
491 ioctl(console_fd, PS2IOC_SENDQCT, 1);
492 dma_pending = 0;
493 }
494
495 /* If the cursor is drawn on the DMA area, remove it */
496 if ( cursor_drawn ) {
497 surface->pixels = mapped_mem + surface->offset;
498 SDL_EraseCursorNoLock(this->screen);
499 cursor_drawn = 0;
500 }
501
502 /* Set the surface pixels to the base of the DMA area */
503 surface->pixels = mapped_mem;
504
505 /* We're finished! */
506 SDL_UnlockCursor();
507 }
508 return(0);
509 }
510 static void GS_UnlockHWSurface(_THIS, SDL_Surface *surface)
511 {
512 if ( surface == this->screen ) {
513 /* Since mouse motion affects 'pixels', lock it */
514 SDL_LockCursor();
515 surface->pixels = NULL;
516 SDL_UnlockCursor();
517 }
518 }
519
520 static void GS_DMAFullUpdate(_THIS, int numrects, SDL_Rect *rects)
521 {
522 /* Lock so we aren't interrupted by a mouse update */
523 SDL_LockCursor();
524
525 /* Make sure any pending DMA has completed */
526 if ( dma_pending ) {
527 ioctl(console_fd, PS2IOC_SENDQCT, 1);
528 dma_pending = 0;
529 }
530
531 /* If the mouse is visible, draw it on the DMA area */
532 if ( (SDL_cursorstate & CURSOR_VISIBLE) && !cursor_drawn ) {
533 this->screen->pixels = mapped_mem + this->screen->offset;
534 SDL_DrawCursorNoLock(this->screen);
535 this->screen->pixels = NULL;
536 cursor_drawn = 1;
537 }
538
539 /* Put the image onto the screen */
540 loadimage_nonblock(console_fd,
541 &screen_image, screen_image_size,
542 head_tags_mem, image_tags_mem);
543 dma_pending = 1;
544
545 /* We're finished! */
546 SDL_UnlockCursor();
547 }
548
549 static int GS_SetColors(_THIS, int firstcolor, int ncolors, SDL_Color *colors)
550 {
551 return(0);
552 }
553
554 static void GS_VideoQuit(_THIS)
555 {
556 /* Close console and input file descriptors */
557 if ( console_fd > 0 ) {
558 /* Unmap the video framebuffer */
559 if ( mapped_mem ) {
560 /* Unmap the video framebuffer */
561 munmap(mapped_mem, mapped_len);
562 mapped_mem = NULL;
563 }
564 close(memory_fd);
565
566 /* Restore the original video mode */
567 if ( GS_InGraphicsMode(this) ) {
568 ioctl(console_fd, PS2IOC_SSCREENINFO, &saved_vinfo);
569 }
570
571 /* We're all done with the graphics device */
572 close(console_fd);
573 console_fd = -1;
574 }
575 GS_CloseMouse(this);
576 GS_CloseKeyboard(this);
577 }