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