src/video/ps2gs/SDL_gsyuv.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 15 Dec 2009 08:11:06 +0000
changeset 3565 f43c8f688f77
parent 2859 99210400e8b9
child 3626 596468a8459e
permissions -rw-r--r--
Fixed bug #906

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