src/video/ps2gs/SDL_gsyuv.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 17 Nov 2002 18:59:10 +0000
changeset 543 522e5202014d
parent 252 e8157fcb3114
child 1312 c9b51268668f
permissions -rw-r--r--
*** empty log message ***
     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@libsdl.org
    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.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.y + 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 	y += screen_image.y;
   441 	*hwdata->stretch_x1y1 = (x * 16) + ((y * 16) << 16);
   442 	x += (unsigned int)dstrect->w;
   443 	y += (unsigned int)dstrect->h;
   444 	*hwdata->stretch_x2y2 = (x * 16) + ((y * 16) << 16);
   445 	return ioctl(console_fd, PS2IOC_SENDL, &hwdata->plist);
   446 }
   447 
   448 void GS_FreeYUVOverlay(_THIS, SDL_Overlay *overlay)
   449 {
   450 	struct private_yuvhwdata *hwdata;
   451 
   452 	hwdata = overlay->hwdata;
   453 	if ( hwdata ) {
   454 		if ( hwdata->ipu_fd >= 0 ) {
   455 			close(hwdata->ipu_fd);
   456 		}
   457 		if ( hwdata->dma_mem ) {
   458 			munmap(hwdata->dma_mem, hwdata->dma_len);
   459 		}
   460 		if ( hwdata->plist.packet ) {
   461 			free(hwdata->plist.packet);
   462 		}
   463 		if ( hwdata->pixels ) {
   464 			free(hwdata->pixels);
   465 		}
   466 		free(hwdata);
   467 	}
   468 }