SDL_Overlay *PS3_CreateYUVOverlay(_THIS, int width, int height, Uint32 format, SDL_Surface *display) {
	/* Only RGB packed pixel conversion supported */
	if ((display->format->BytesPerPixel != 2) &&
			(display->format->BytesPerPixel != 3) &&
			(display->format->BytesPerPixel != 4))
	{
		SDL_SetError ("Can't use YUV data on non 16/24/32 bit surfaces");
		return NULL;
	}

	/* Double-check the requested format. We'll only support YV12 */
	switch (format) {
	    case SDL_IYUV_OVERLAY:
		case SDL_YV12_OVERLAY:
			/* Supported YUV format */
			break;
		default:
			SDL_SetError("Unsupported YUV format");
			return NULL;
	}

	SDL_Overlay* overlay;
	struct private_yuvhwdata* hwdata;

	/* Create the overlay structure */
	overlay = (SDL_Overlay *) SDL_calloc(1, sizeof(SDL_Overlay));
	if (overlay == NULL) {
		SDL_OutOfMemory();
		return NULL;
	}
	SDL_memset(overlay, 0, (sizeof *overlay));

	/* Set the basic attributes */
	overlay->format = format;
	overlay->w = width;
	overlay->h = height;
	overlay->hwdata = NULL;

	/* Set up the PS3 YUV surface function structure */
	overlay->hwfuncs = &ps3_yuvfuncs;

	/* Create the pixel data and lookup tables */
	hwdata = (struct private_yuvhwdata *) SDL_calloc(1, sizeof(struct private_yuvhwdata));
	if (hwdata == NULL) {
		SDL_OutOfMemory();
		SDL_FreeYUVOverlay(overlay);
		return NULL;
	}
	overlay->hwdata = hwdata;

	hwdata->stretch = NULL;
	hwdata->display = display;

	/* Create SPU parms structure */
	hwdata->converter_parms = (struct yuv2rgb_parms_t *) memalign(16, sizeof(struct yuv2rgb_parms_t));
	hwdata->scaler_parms = (struct scale_parms_t *) memalign(16, sizeof(struct scale_parms_t));
	if (hwdata->converter_parms == NULL || hwdata->scaler_parms == NULL) {
		SDL_FreeYUVOverlay(overlay);
		SDL_OutOfMemory();
		return(NULL);
	}

	/* Set up the SPEs */
	scaler_thread_data = (spu_data_t *) malloc(sizeof(spu_data_t));
	converter_thread_data = (spu_data_t *) malloc(sizeof(spu_data_t));
	if (converter_thread_data == NULL || scaler_thread_data == NULL) {
		SDL_FreeYUVOverlay(overlay);
		SDL_OutOfMemory();
		return(NULL);
	}

	scaler_thread_data->program = bilin_scaler_spu;
	scaler_thread_data->program_name = "bilin_scaler_spu";
	scaler_thread_data->keepalive = 0;
	scaler_thread_data->booted = 0;

	converter_thread_data->program = yuv2rgb_spu;
	converter_thread_data->program_name = "yuv2rgb_spu";
	converter_thread_data->keepalive = 1;
	converter_thread_data->booted = 0;

	SPE_Start(this, converter_thread_data);

	hwdata->pixels = (Uint8 *) memalign(16, width * height + ((width * height) >> 1));
	if (hwdata->pixels == NULL) {
		SDL_FreeYUVOverlay(overlay);
		SDL_OutOfMemory();
		return(NULL);
	}

	/* Find the pitch and offset values for the overlay */
	overlay->pitches = hwdata->pitches;
	overlay->pixels = hwdata->planes;
	switch (format) {
	    case SDL_YV12_OVERLAY:
	    case SDL_IYUV_OVERLAY:
			overlay->pitches[0] = overlay->w;
			overlay->pitches[1] = overlay->pitches[0] / 2;
			overlay->pitches[2] = overlay->pitches[0] / 2;
			overlay->pixels[0] = (Uint8 *)hwdata->pixels;
			overlay->pixels[1] = overlay->pixels[0] +
				overlay->pitches[0] * overlay->h;
			overlay->pixels[2] = overlay->pixels[1] +
				overlay->pitches[1] * overlay->h / 2;
			overlay->planes = 3;
		break;
	    default:
		/* We should never get here (caught above) */
		break;
	}

	/* We're all done.. */
	return overlay;
}
static int PS3_VideoInit(_THIS, SDL_PixelFormat * vformat)
{
	
	enable_cursor(0);

	
	fb_parms = (struct fb_writer_parms_t *)
	    memalign(16, sizeof(struct fb_writer_parms_t));
	fb_thread_data = (spu_data_t *) malloc(sizeof(spu_data_t));
	if (fb_parms == NULL || fb_thread_data == NULL) {
		SDL_OutOfMemory();
		return -1;
	}
	fb_thread_data->program = fb_writer_spu;
	fb_thread_data->program_name = "fb_writer_spu";
	fb_thread_data->argp = (void *)fb_parms;
	fb_thread_data->keepalive = 1;
	fb_thread_data->booted = 0;

	SPE_Start(this, fb_thread_data);

	
	fb_dev_fd = open(PS3_DEV_FB, O_RDWR);
	if (fb_dev_fd < 0) {
		SDL_SetError("[PS3] Unable to open device %s", PS3_DEV_FB);
		return -1;
	}

	
	if (ioctl(fb_dev_fd, FBIOGET_VSCREENINFO, &fb_vinfo)) {
		SDL_SetError("[PS3] Can't get VSCREENINFO");
		if (fb_dev_fd >= 0)
			close(fb_dev_fd);
		fb_dev_fd = -1;
		return -1;
	}

	
	this->info.current_w = fb_vinfo.xres;
	this->info.current_h = fb_vinfo.yres;
	this->info.wm_available = 0;
	this->info.hw_available = 1;

	
	fb_orig_vinfo = fb_vinfo;

	
	fb_bits_per_pixel = fb_vinfo.bits_per_pixel;
	if (fb_bits_per_pixel == 16)
		fb_bits_per_pixel =
		    fb_vinfo.red.length + fb_vinfo.green.length +
		    fb_vinfo.blue.length;

	
	vformat->BitsPerPixel = fb_vinfo.bits_per_pixel;

	fb_vinfo.xres_virtual = fb_vinfo.xres;
	fb_vinfo.yres_virtual = fb_vinfo.yres;

	
	if (ioctl(fb_dev_fd, FBIOPUT_VSCREENINFO, &fb_vinfo)) {
		SDL_SetError("[PS3] Can't put VSCREENINFO");
		if (fb_dev_fd >= 0)
			close(fb_dev_fd);
		fb_dev_fd = -1;
		return -1;
	}

	s_fb_pixel_size = fb_vinfo.bits_per_pixel / 8;

	s_writeable_width = fb_vinfo.xres;
	s_writeable_height = fb_vinfo.yres;

	
	if (ioctl(fb_dev_fd, PS3FB_IOCTL_SCREENINFO, (unsigned long)&res) < 0) {
		SDL_SetError("[PS3] PS3FB_IOCTL_SCREENINFO failed");
	}
	deprintf(1, "[PS3] xres:%d yres:%d xoff:%d yoff:%d\n", res.xres, res.yres, res.xoff, res.yoff);

	
	if (res.num_frames < 2) {
		double_buffering = 0;
	} else {
		double_buffering = 1;
	}

	real_width = res.xres;
	real_height = res.yres;

	ioctl(fb_dev_fd, PS3FB_IOCTL_ON, 0);

	
	ioctl(fb_dev_fd, FBIOBLANK, 0);

	return 0;
}
int PS3_DisplayYUVOverlay(_THIS, SDL_Overlay *overlay, SDL_Rect *src, SDL_Rect *dst) {
	if ((overlay == NULL) || (overlay->hwdata == NULL)) {
		return -1;
	}

	Uint8 *lum, *Cr, *Cb;
	struct private_yuvhwdata *hwdata;
	SDL_Surface *display;

	hwdata = overlay->hwdata;
	display = hwdata->display;

	/* Do we have to scale? */
	if ((src->w != dst->w) || (src->h != dst->h) ) {
		hwdata->scale = 1;
		deprintf(1, "[PS3] We need to scale\n");
	} else {
		hwdata->scale = 0;
		deprintf(1, "[PS3] No scaling\n");
	}

	/* Find out where the various portions of the image are */
	switch (overlay->format) {
		case SDL_YV12_OVERLAY:
			lum = (Uint8 *)overlay->pixels[0];
			Cr =  (Uint8 *)overlay->pixels[1];
			Cb =  (Uint8 *)overlay->pixels[2];
			break;
		case SDL_IYUV_OVERLAY:
			lum = (Uint8 *)overlay->pixels[0];
			Cr =  (Uint8 *)overlay->pixels[2];
			Cb =  (Uint8 *)overlay->pixels[1];
			break;
		default:
			SDL_SetError("Unsupported YUV format in blit");
			return -1;
	}

	if (hwdata->scale) {
		/* Alloc mem for scaled YUV picture */
		hwdata->scaler_out = (Uint8 *) memalign(16, dst->w * dst->h + ((dst->w * dst->h) >> 1));
		if (hwdata->scaler_out == NULL) {
			SDL_FreeYUVOverlay(overlay);
			SDL_OutOfMemory();
			return -1;
		}

		/* Set parms for scaling */
		hwdata->scaler_parms->src_pixel_width = src->w;
		hwdata->scaler_parms->src_pixel_height = src->h;
		hwdata->scaler_parms->dst_pixel_width = dst->w;
		hwdata->scaler_parms->dst_pixel_height = dst->h;
		hwdata->scaler_parms->y_plane = lum;
		hwdata->scaler_parms->v_plane = Cr;
		hwdata->scaler_parms->u_plane = Cb;
		hwdata->scaler_parms->dstBuffer = hwdata->scaler_out;
		scaler_thread_data->argp = (void *)hwdata->scaler_parms;

		/* Scale the YUV overlay to given size */
		SPE_Start(this, scaler_thread_data);
		SPE_Stop(this, scaler_thread_data);

		/* Set parms for converting after scaling */
		hwdata->converter_parms->y_plane = hwdata->scaler_out;
		hwdata->converter_parms->v_plane = hwdata->scaler_out + dst->w * dst->h;
		hwdata->converter_parms->u_plane = hwdata->scaler_out + dst->w * dst->h + ((dst->w * dst->h) >> 2);
	} else {
int
PS3_VideoInit(_THIS)
{
    int i;

    deprintf(1, "PS3_VideoInit()\n");

    SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
    SDL_DisplayMode mode;

    /* Create SPU fb_parms and thread structure */
    data->fb_parms = (struct fb_writer_parms_t *)
        memalign(16, sizeof(struct fb_writer_parms_t));
    data->fb_thread_data = (spu_data_t *) malloc(sizeof(spu_data_t));
    if (data->fb_parms == NULL || data->fb_thread_data == NULL) {
        SDL_OutOfMemory();
        return -1;
    }
    data->fb_thread_data->program = fb_writer_spu;
    data->fb_thread_data->program_name = "fb_writer_spu";
    data->fb_thread_data->argp = (void *)data->fb_parms;
    data->fb_thread_data->keepalive = 1;
    data->fb_thread_data->booted = 0;

    SPE_Start(data->fb_thread_data);

    /* Open the device */
    data->fbdev = open(PS3DEV, O_RDWR);
    if (data->fbdev < 0) {
        SDL_SetError("[PS3] Unable to open device %s", PS3DEV);
        return -1;
    }

    /* Take control of frame buffer from kernel, for details see
     * http://felter.org/wesley/files/ps3/linux-20061110-docs/ApplicationProgrammingEnvironment.html
     * kernel will no longer flip the screen itself
     */
    ioctl(data->fbdev, PS3FB_IOCTL_ON, 0);

    /* Unblank screen */
    ioctl(data->fbdev, FBIOBLANK, 0);

    struct fb_fix_screeninfo fb_finfo;
    if (ioctl(data->fbdev, FBIOGET_FSCREENINFO, &fb_finfo)) {
        SDL_SetError("[PS3] Can't get fixed screeninfo");
        return (0);
    }

    /* Note: on PS3, fb_finfo.smem_len is enough for double buffering */
    if ((data->frame_buffer = (uint8_t *)mmap(0, fb_finfo.smem_len,
        PROT_READ | PROT_WRITE, MAP_SHARED,
        data->fbdev, 0)) == (uint8_t *) - 1) {
        SDL_SetError("[PS3] Can't mmap for %s", PS3DEV);
        return (0);
    } else {
        /* Enable double buffering */
    }

    /* Blank screen */
    memset(data->frame_buffer, 0x00, fb_finfo.smem_len);

    PS3_InitModes(_this);
    for (i = 0; i < _this->num_displays; ++i) {
        SDL_AddRenderDriver(&_this->displays[i], &SDL_PS3_RenderDriver);
    }

    /* We're done! */
    return 0;
}