comparison src/video/ps2gs/SDL_gsyuv.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 717f739d6ec1
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 /* This is the Playstation 2 implementation of YUV video overlays */
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <unistd.h>
34 #include <sys/ioctl.h>
35 #include <sys/mman.h>
36 #include <asm/page.h> /* For definition of PAGE_SIZE */
37
38 #include "SDL_error.h"
39 #include "SDL_video.h"
40 #include "SDL_gsyuv_c.h"
41 #include "SDL_yuvfuncs.h"
42
43 /* The maximum number of 16x16 pixel block converted at once */
44 #define MAX_MACROBLOCKS 1024 /* 2^10 macroblocks at once */
45
46 /* The functions used to manipulate video overlays */
47 static struct private_yuvhwfuncs gs_yuvfuncs = {
48 GS_LockYUVOverlay,
49 GS_UnlockYUVOverlay,
50 GS_DisplayYUVOverlay,
51 GS_FreeYUVOverlay
52 };
53
54 struct private_yuvhwdata {
55 int ipu_fd;
56 Uint8 *pixels;
57 int macroblocks;
58 int dma_len;
59 caddr_t dma_mem;
60 caddr_t ipu_imem;
61 caddr_t ipu_omem;
62 caddr_t dma_tags;
63 unsigned long long *stretch_x1y1;
64 unsigned long long *stretch_x2y2;
65 struct ps2_plist plist;
66
67 /* These are just so we don't have to allocate them separately */
68 Uint16 pitches[3];
69 Uint8 *planes[3];
70 };
71
72 static int power_of_2(int value)
73 {
74 int shift;
75
76 for ( shift = 0; (1<<shift) < value; ++shift ) {
77 /* Keep looking */ ;
78 }
79 return(shift);
80 }
81
82 SDL_Overlay *GS_CreateYUVOverlay(_THIS, int width, int height, Uint32 format, SDL_Surface *display)
83 {
84 SDL_Overlay *overlay;
85 struct private_yuvhwdata *hwdata;
86 int map_offset;
87 unsigned long long *tags;
88 caddr_t base;
89 int bpp;
90 int fbp, fbw, psm;
91 int x, y, w, h;
92 int pnum;
93 struct ps2_packet *packet;
94 struct ps2_packet tex_packet;
95
96 /* We can only decode blocks of 16x16 pixels */
97 if ( (width & 15) || (height & 15) ) {
98 SDL_SetError("Overlay width/height must be multiples of 16");
99 return(NULL);
100 }
101 /* Make sure the image isn't too large for a single DMA transfer */
102 if ( ((width/16) * (height/16)) > MAX_MACROBLOCKS ) {
103 SDL_SetError("Overlay too large (maximum size: %d pixels)",
104 MAX_MACROBLOCKS * 16 * 16);
105 return(NULL);
106 }
107
108 /* Double-check the requested format. For simplicity, we'll only
109 support planar YUV formats.
110 */
111 switch (format) {
112 case SDL_YV12_OVERLAY:
113 case SDL_IYUV_OVERLAY:
114 /* Supported planar YUV format */
115 break;
116 default:
117 SDL_SetError("Unsupported YUV format");
118 return(NULL);
119 }
120
121 /* Create the overlay structure */
122 overlay = (SDL_Overlay *)malloc(sizeof *overlay);
123 if ( overlay == NULL ) {
124 SDL_OutOfMemory();
125 return(NULL);
126 }
127 memset(overlay, 0, (sizeof *overlay));
128
129 /* Fill in the basic members */
130 overlay->format = format;
131 overlay->w = width;
132 overlay->h = height;
133
134 /* Set up the YUV surface function structure */
135 overlay->hwfuncs = &gs_yuvfuncs;
136 overlay->hw_overlay = 1;
137
138 /* Create the pixel data */
139 hwdata = (struct private_yuvhwdata *)malloc(sizeof *hwdata);
140 overlay->hwdata = hwdata;
141 if ( hwdata == NULL ) {
142 SDL_FreeYUVOverlay(overlay);
143 SDL_OutOfMemory();
144 return(NULL);
145 }
146 hwdata->ipu_fd = -1;
147 hwdata->pixels = (Uint8 *)malloc(width*height*2);
148 if ( hwdata->pixels == NULL ) {
149 SDL_FreeYUVOverlay(overlay);
150 SDL_OutOfMemory();
151 return(NULL);
152 }
153 hwdata->macroblocks = (width/16) * (height/16);
154
155 /* Find the pitch and offset values for the overlay */
156 overlay->pitches = hwdata->pitches;
157 overlay->pixels = hwdata->planes;
158 switch (format) {
159 case SDL_YV12_OVERLAY:
160 case SDL_IYUV_OVERLAY:
161 overlay->pitches[0] = overlay->w;
162 overlay->pitches[1] = overlay->pitches[0] / 2;
163 overlay->pitches[2] = overlay->pitches[0] / 2;
164 overlay->pixels[0] = hwdata->pixels;
165 overlay->pixels[1] = overlay->pixels[0] +
166 overlay->pitches[0] * overlay->h;
167 overlay->pixels[2] = overlay->pixels[1] +
168 overlay->pitches[1] * overlay->h / 2;
169 overlay->planes = 3;
170 break;
171 default:
172 /* We should never get here (caught above) */
173 break;
174 }
175
176 /* Theoretically we could support several concurrent decode
177 streams queueing up on the same file descriptor, but for
178 simplicity we'll support only one. Opening the IPU more
179 than once will fail with EBUSY.
180 */
181 hwdata->ipu_fd = open("/dev/ps2ipu", O_RDWR);
182 if ( hwdata->ipu_fd < 0 ) {
183 SDL_FreeYUVOverlay(overlay);
184 SDL_SetError("Playstation 2 IPU busy");
185 return(NULL);
186 }
187
188 /* Allocate a DMA area for pixel conversion */
189 bpp = this->screen->format->BytesPerPixel;
190 map_offset = (mapped_len + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
191 hwdata->dma_len = hwdata->macroblocks * (16 * 16 + 8 * 8 + 8 * 8) +
192 width * height * bpp +
193 hwdata->macroblocks * (16 * sizeof(long long)) +
194 12 * sizeof(long long);
195 hwdata->dma_mem = mmap(0, hwdata->dma_len, PROT_READ|PROT_WRITE,
196 MAP_SHARED, memory_fd, map_offset);
197 if ( hwdata->dma_mem == MAP_FAILED ) {
198 hwdata->ipu_imem = (caddr_t)0;
199 SDL_FreeYUVOverlay(overlay);
200 SDL_SetError("Unable to map %d bytes for DMA", hwdata->dma_len);
201 return(NULL);
202 }
203 hwdata->ipu_imem = hwdata->dma_mem;
204 hwdata->ipu_omem = hwdata->ipu_imem +
205 hwdata->macroblocks * (16 * 16 + 8 * 8 + 8 * 8);
206 hwdata->dma_tags = hwdata->ipu_omem + width * height * bpp;
207
208 /* Allocate memory for the DMA packets */
209 hwdata->plist.num = hwdata->macroblocks * 4 + 1;
210 hwdata->plist.packet = (struct ps2_packet *)malloc(
211 hwdata->plist.num*sizeof(struct ps2_packet));
212 if ( ! hwdata->plist.packet ) {
213 SDL_FreeYUVOverlay(overlay);
214 SDL_OutOfMemory();
215 return(NULL);
216 }
217 pnum = 0;
218 packet = hwdata->plist.packet;
219
220 /* Set up the tags to send the image to the screen */
221 tags = (unsigned long long *)hwdata->dma_tags;
222 base = hwdata->ipu_omem;
223 fbp = screen_image.fbp;
224 fbw = screen_image.fbw;
225 psm = screen_image.psm;
226 y = screen_image.h; /* Offscreen video memory */
227 for ( h=height/16; h; --h ) {
228 x = 0; /* Visible video memory */
229 for ( w=width/16; w; --w ) {
230 /* The head tag */
231 packet[pnum].ptr = &tags[0];
232 packet[pnum].len = 10 * sizeof(*tags);
233 ++pnum;
234 tags[0] = 4 | (1LL << 60); /* GIFtag */
235 tags[1] = 0x0e; /* A+D */
236 tags[2] = ((unsigned long long)fbp << 32) |
237 ((unsigned long long)fbw << 48) |
238 ((unsigned long long)psm << 56);
239 tags[3] = PS2_GS_BITBLTBUF;
240 tags[4] = ((unsigned long long)x << 32) |
241 ((unsigned long long)y << 48);
242 tags[5] = PS2_GS_TRXPOS;
243 tags[6] = (unsigned long long)16 |
244 ((unsigned long long)16 << 32);
245 tags[7] = PS2_GS_TRXREG;
246 tags[8] = 0;
247 tags[9] = PS2_GS_TRXDIR;
248 /* Now the actual image data */
249 packet[pnum].ptr = &tags[10];
250 packet[pnum].len = 2 * sizeof(*tags);
251 ++pnum;
252 tags[10] = ((16*16*bpp) >> 4) | (2LL << 58);
253 tags[11] = 0;
254 packet[pnum].ptr = (void *)base;
255 packet[pnum].len = 16 * 16 * bpp;
256 ++pnum;
257 packet[pnum].ptr = &tags[12];
258 packet[pnum].len = 2 * sizeof(*tags);
259 ++pnum;
260 tags[12] = (0 >> 4) | (1 << 15) | (2LL << 58);
261 tags[13] = 0;
262
263 tags += 16;
264 base += 16 * 16 * bpp;
265
266 x += 16;
267 }
268 y += 16;
269 }
270
271 /* Set up the texture memory area for the video */
272 tex_packet.ptr = tags;
273 tex_packet.len = 8 * sizeof(*tags);
274 tags[0] = 3 | (1LL << 60); /* GIFtag */
275 tags[1] = 0x0e; /* A+D */
276 tags[2] = (screen_image.h * screen_image.w) / 64 +
277 ((unsigned long long)fbw << 14) +
278 ((unsigned long long)psm << 20) +
279 ((unsigned long long)power_of_2(width) << 26) +
280 ((unsigned long long)power_of_2(height) << 30) +
281 ((unsigned long long)1 << 34) +
282 ((unsigned long long)1 << 35);
283 tags[3] = PS2_GS_TEX0_1;
284 tags[4] = (1 << 5) + (1 << 6);
285 tags[5] = PS2_GS_TEX1_1;
286 tags[6] = 0;
287 tags[7] = PS2_GS_TEXFLUSH;
288 ioctl(console_fd, PS2IOC_SEND, &tex_packet);
289
290 /* Set up the tags for scaling the image */
291 packet[pnum].ptr = tags;
292 packet[pnum].len = 12 * sizeof(*tags);
293 ++pnum;
294 tags[0] = 5 | (1LL << 60); /* GIFtag */
295 tags[1] = 0x0e; /* A+D */
296 tags[2] = 6 + (1 << 4) + (1 << 8);
297 tags[3] = PS2_GS_PRIM;
298 tags[4] = ((unsigned long long)0 * 16) +
299 (((unsigned long long)0 * 16) << 16);
300 tags[5] = PS2_GS_UV;
301 tags[6] = 0; /* X1, Y1 */
302 tags[7] = PS2_GS_XYZ2;
303 hwdata->stretch_x1y1 = &tags[6];
304 tags[8] = ((unsigned long long)overlay->w * 16) +
305 (((unsigned long long)overlay->h * 16) << 16);
306 tags[9] = PS2_GS_UV;
307 tags[10] = 0; /* X2, Y2 */
308 tags[11] = PS2_GS_XYZ2;
309 hwdata->stretch_x2y2 = &tags[10];
310
311 /* We're all done.. */
312 return(overlay);
313 }
314
315 int GS_LockYUVOverlay(_THIS, SDL_Overlay *overlay)
316 {
317 return(0);
318 }
319
320 void GS_UnlockYUVOverlay(_THIS, SDL_Overlay *overlay)
321 {
322 return;
323 }
324
325 int GS_DisplayYUVOverlay(_THIS, SDL_Overlay *overlay, SDL_Rect *dstrect)
326 {
327 struct private_yuvhwdata *hwdata;
328 __u32 cmd;
329 struct ps2_packet packet;
330 int h, w, i;
331 Uint32 *lum, *Cr, *Cb;
332 int lum_pitch;
333 int crb_pitch;
334 Uint32 *lum_src, *Cr_src, *Cb_src;
335 Uint32 *src, *dst;
336 unsigned int x, y;
337 SDL_Surface *screen;
338
339 /* Find out where the various portions of the image are */
340 hwdata = overlay->hwdata;
341 switch (overlay->format) {
342 case SDL_YV12_OVERLAY:
343 lum = (Uint32 *)overlay->pixels[0];
344 Cr = (Uint32 *)overlay->pixels[1];
345 Cb = (Uint32 *)overlay->pixels[2];
346 break;
347 case SDL_IYUV_OVERLAY:
348 lum = (Uint32 *)overlay->pixels[0];
349 Cr = (Uint32 *)overlay->pixels[2];
350 Cb = (Uint32 *)overlay->pixels[1];
351 default:
352 SDL_SetError("Unsupported YUV format in blit (??)");
353 return(-1);
354 }
355 dst = (Uint32 *)hwdata->ipu_imem;
356 lum_pitch = overlay->w/4;
357 crb_pitch = (overlay->w/2)/4;
358
359 /* Copy blocks of 16x16 pixels to the DMA area */
360 for ( h=overlay->h/16; h; --h ) {
361 lum_src = lum;
362 Cr_src = Cr;
363 Cb_src = Cb;
364 for ( w=overlay->w/16; w; --w ) {
365 src = lum_src;
366 for ( i=0; i<16; ++i ) {
367 dst[0] = src[0];
368 dst[1] = src[1];
369 dst[2] = src[2];
370 dst[3] = src[3];
371 src += lum_pitch;
372 dst += 4;
373 }
374 src = Cb_src;
375 for ( i=0; i<8; ++i ) {
376 dst[0] = src[0];
377 dst[1] = src[1];
378 src += crb_pitch;
379 dst += 2;
380 }
381 src = Cr_src;
382 for ( i=0; i<8; ++i ) {
383 dst[0] = src[0];
384 dst[1] = src[1];
385 src += crb_pitch;
386 dst += 2;
387 }
388 lum_src += 16 / 4;
389 Cb_src += 8 / 4;
390 Cr_src += 8 / 4;
391 }
392 lum += lum_pitch * 16;
393 Cr += crb_pitch * 8;
394 Cb += crb_pitch * 8;
395 }
396
397 /* Send the macroblock data to the IPU */
398 #ifdef DEBUG_YUV
399 fprintf(stderr, "Sending data to IPU..\n");
400 #endif
401 packet.ptr = hwdata->ipu_imem;
402 packet.len = hwdata->macroblocks * (16 * 16 + 8 * 8 + 8 * 8);
403 ioctl(hwdata->ipu_fd, PS2IOC_SENDA, &packet);
404
405 /* Trigger the DMA to the IPU for conversion */
406 #ifdef DEBUG_YUV
407 fprintf(stderr, "Trigging conversion command\n");
408 #endif
409 cmd = (7 << 28) + hwdata->macroblocks;
410 if ( screen_image.psm == PS2_GS_PSMCT16 ) {
411 cmd += (1 << 27) + /* Output RGB 555 */
412 (1 << 26); /* Dither output */
413 }
414 ioctl(hwdata->ipu_fd, PS2IOC_SIPUCMD, &cmd);
415
416 /* Retrieve the converted image from the IPU */
417 #ifdef DEBUG_YUV
418 fprintf(stderr, "Retrieving data from IPU..\n");
419 #endif
420 packet.ptr = hwdata->ipu_omem;
421 packet.len = overlay->w * overlay->h *
422 this->screen->format->BytesPerPixel;
423 ioctl(hwdata->ipu_fd, PS2IOC_RECV, &packet);
424
425 #ifdef DEBUG_YUV
426 fprintf(stderr, "Copying image to screen..\n");
427 #endif
428 /* Wait for previous DMA to complete */
429 ioctl(console_fd, PS2IOC_SENDQCT, 1);
430
431 /* Send the current image to the screen and scale it */
432 screen = this->screen;
433 x = (unsigned int)dstrect->x;
434 y = (unsigned int)dstrect->y;
435 if ( screen->offset ) {
436 x += (screen->offset % screen->pitch) /
437 screen->format->BytesPerPixel;
438 y += (screen->offset / screen->pitch);
439 }
440 *hwdata->stretch_x1y1 = (x * 16) + ((y * 16) << 16);
441 x += (unsigned int)dstrect->w;
442 y += (unsigned int)dstrect->h;
443 *hwdata->stretch_x2y2 = (x * 16) + ((y * 16) << 16);
444 return ioctl(console_fd, PS2IOC_SENDL, &hwdata->plist);
445 }
446
447 void GS_FreeYUVOverlay(_THIS, SDL_Overlay *overlay)
448 {
449 struct private_yuvhwdata *hwdata;
450
451 hwdata = overlay->hwdata;
452 if ( hwdata ) {
453 if ( hwdata->ipu_fd >= 0 ) {
454 close(hwdata->ipu_fd);
455 }
456 if ( hwdata->dma_mem ) {
457 munmap(hwdata->dma_mem, hwdata->dma_len);
458 }
459 if ( hwdata->plist.packet ) {
460 free(hwdata->plist.packet);
461 }
462 if ( hwdata->pixels ) {
463 free(hwdata->pixels);
464 }
465 free(hwdata);
466 }
467 }