| 1 | /* |
| 2 | * SDL - Simple DirectMedia Layer |
| 3 | * CELL BE Support for PS3 Framebuffer |
| 4 | * Copyright (C) 2008, 2009 International Business Machines Corporation |
| 5 | * |
| 6 | * This library is free software; you can redistribute it and/or modify it |
| 7 | * under the terms of the GNU Lesser General Public License as published |
| 8 | * by the Free Software Foundation; either version 2.1 of the License, or |
| 9 | * (at your option) any later version. |
| 10 | * |
| 11 | * This library is distributed in the hope that it will be useful, but |
| 12 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | * Lesser General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU Lesser General Public |
| 17 | * License along with this library; if not, write to the Free Software |
| 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 |
| 19 | * USA |
| 20 | * |
| 21 | * Martin Lowinski <lowinski [at] de [dot] ibm [ibm] com> |
| 22 | * Dirk Herrendoerfer <d.herrendoerfer [at] de [dot] ibm [dot] com> |
| 23 | * SPE code based on research by: |
| 24 | * Rene Becker |
| 25 | * Thimo Emmerich |
| 26 | */ |
| 27 | |
| 28 | #include "SDL_config.h" |
| 29 | |
| 30 | #include "SDL_video.h" |
| 31 | #include "../SDL_sysvideo.h" |
| 32 | #include "SDL_ps3events_c.h" |
| 33 | #include "SDL_ps3video.h" |
| 34 | #include "SDL_ps3yuv_c.h" |
| 35 | #include "spulibs/spu_common.h" |
| 36 | |
| 37 | #include <fcntl.h> |
| 38 | #include <stdlib.h> |
| 39 | #include <sys/ioctl.h> |
| 40 | #include <linux/kd.h> |
| 41 | #include <sys/mman.h> |
| 42 | |
| 43 | #include <linux/fb.h> |
| 44 | #include <asm/ps3fb.h> |
| 45 | #include <libspe2.h> |
| 46 | #include <malloc.h> |
| 47 | |
| 48 | /* SDL_VideoDevice functions */ |
| 49 | static int PS3_Available(); |
| 50 | static SDL_VideoDevice *PS3_CreateDevice(int devindex); |
| 51 | static int PS3_VideoInit(_THIS, SDL_PixelFormat * vformat); |
| 52 | static void PS3_VideoQuit(_THIS); |
| 53 | static void PS3_DeleteDevice(SDL_VideoDevice * device); |
| 54 | static SDL_Surface *PS3_SetVideoMode(_THIS, SDL_Surface * current, int width, int height, int bpp, Uint32 flags); |
| 55 | static SDL_Rect **PS3_ListModes(_THIS, SDL_PixelFormat * format, Uint32 flags); |
| 56 | |
| 57 | /* Hardware surface functions */ |
| 58 | static int PS3_AllocHWSurface(_THIS, SDL_Surface * surface); |
| 59 | static void PS3_FreeHWSurface(_THIS, SDL_Surface * surface); |
| 60 | static int PS3_LockHWSurface(_THIS, SDL_Surface * surface); |
| 61 | static void PS3_UnlockHWSurface(_THIS, SDL_Surface * surface); |
| 62 | static int PS3_FlipDoubleBuffer(_THIS, SDL_Surface * surface); |
| 63 | static void PS3_DoubleBufferUpdate(_THIS, int numrects, SDL_Rect * rects); |
| 64 | |
| 65 | /* SPU specific functions */ |
| 66 | int SPE_Start(_THIS, spu_data_t * spe_data); |
| 67 | int SPE_Stop(_THIS, spu_data_t * spe_data); |
| 68 | int SPE_Boot(_THIS, spu_data_t * spe_data); |
| 69 | int SPE_Shutdown(_THIS, spu_data_t * spe_data); |
| 70 | int SPE_SendMsg(_THIS, spu_data_t * spe_data, unsigned int msg); |
| 71 | int SPE_WaitForMsg(_THIS, spu_data_t * spe_data, unsigned int msg); |
| 72 | void SPE_RunContext(void *thread_argp); |
| 73 | |
| 74 | /* Helpers */ |
| 75 | void enable_cursor(int enable); |
| 76 | |
| 77 | /* Stores the SPE executable name of fb_writer_spu */ |
| 78 | extern spe_program_handle_t fb_writer_spu; |
| 79 | |
| 80 | /* SDL PS3 bootstrap function for checking availability */ |
| 81 | static int PS3_Available() |
| 82 | { |
| 83 | return 1; |
| 84 | } |
| 85 | |
| 86 | /* SDL PS3 bootstrap function for creating the device */ |
| 87 | static SDL_VideoDevice *PS3_CreateDevice(int devindex) |
| 88 | { |
| 89 | SDL_VideoDevice *this; |
| 90 | |
| 91 | /* Initialise SDL_VideoDevice */ |
| 92 | this = (SDL_VideoDevice *) SDL_malloc(sizeof(SDL_VideoDevice)); |
| 93 | if (this) { |
| 94 | memset(this, 0, sizeof *this); |
| 95 | this->hidden = (struct SDL_PrivateVideoData *) |
| 96 | SDL_malloc(sizeof(struct SDL_PrivateVideoData)); |
| 97 | } |
| 98 | /* Error handling */ |
| 99 | if ((this == NULL) || (this->hidden == NULL)) { |
| 100 | SDL_OutOfMemory(); |
| 101 | if (this) |
| 102 | SDL_free(this); |
| 103 | return 0; |
| 104 | } |
| 105 | memset(this->hidden, 0, sizeof(struct SDL_PrivateVideoData)); |
| 106 | |
| 107 | /* Set the function pointers */ |
| 108 | this->VideoInit = PS3_VideoInit; |
| 109 | this->ListModes = PS3_ListModes; |
| 110 | this->SetVideoMode = PS3_SetVideoMode; |
| 111 | this->SetColors = 0; |
| 112 | this->CreateYUVOverlay = PS3_CreateYUVOverlay; |
| 113 | this->UpdateRects = 0; |
| 114 | this->VideoQuit = PS3_VideoQuit; |
| 115 | this->AllocHWSurface = PS3_AllocHWSurface; |
| 116 | this->CheckHWBlit = 0; |
| 117 | this->FillHWRect = 0; |
| 118 | this->SetHWColorKey = 0; |
| 119 | this->SetHWAlpha = 0; |
| 120 | this->LockHWSurface = PS3_LockHWSurface; |
| 121 | this->UnlockHWSurface = PS3_UnlockHWSurface; |
| 122 | this->FlipHWSurface = PS3_FlipDoubleBuffer; |
| 123 | this->FreeHWSurface = PS3_FreeHWSurface; |
| 124 | this->SetCaption = 0; |
| 125 | this->SetIcon = 0; |
| 126 | this->IconifyWindow = 0; |
| 127 | this->GrabInput = 0; |
| 128 | this->GetWMInfo = 0; |
| 129 | this->InitOSKeymap = PS3_InitOSKeymap; |
| 130 | this->PumpEvents = PS3_PumpEvents; |
| 131 | |
| 132 | this->free = PS3_DeleteDevice; |
| 133 | |
| 134 | return this; |
| 135 | } |
| 136 | |
| 137 | |
| 138 | /* Bootstraping (see SDL_sysvideo.h) */ |
| 139 | VideoBootStrap PS3_bootstrap = { |
| 140 | "ps3", "PS3 Cell SPU Driver", |
| 141 | PS3_Available, PS3_CreateDevice |
| 142 | }; |
| 143 | |
| 144 | |
| 145 | /* Delete the device */ |
| 146 | static void PS3_DeleteDevice(SDL_VideoDevice * device) |
| 147 | { |
| 148 | free(device->hidden); |
| 149 | free(device); |
| 150 | } |
| 151 | |
| 152 | |
| 153 | /* Initialise the PS3 video device */ |
| 154 | static int PS3_VideoInit(_THIS, SDL_PixelFormat * vformat) |
| 155 | { |
| 156 | /* Hide the cursor */ |
| 157 | enable_cursor(0); |
| 158 | |
| 159 | /* Create SPU fb_parms and thread structure */ |
| 160 | fb_parms = (struct fb_writer_parms_t *) |
| 161 | memalign(16, sizeof(struct fb_writer_parms_t)); |
| 162 | fb_thread_data = (spu_data_t *) malloc(sizeof(spu_data_t)); |
| 163 | if (fb_parms == NULL || fb_thread_data == NULL) { |
| 164 | SDL_OutOfMemory(); |
| 165 | return -1; |
| 166 | } |
| 167 | fb_thread_data->program = fb_writer_spu; |
| 168 | fb_thread_data->program_name = "fb_writer_spu"; |
| 169 | fb_thread_data->argp = (void *)fb_parms; |
| 170 | fb_thread_data->keepalive = 1; |
| 171 | fb_thread_data->booted = 0; |
| 172 | |
| 173 | SPE_Start(this, fb_thread_data); |
| 174 | |
| 175 | /* Open the device */ |
| 176 | fb_dev_fd = open(PS3_DEV_FB, O_RDWR); |
| 177 | if (fb_dev_fd < 0) { |
| 178 | SDL_SetError("[PS3] Unable to open device %s", PS3_DEV_FB); |
| 179 | return -1; |
| 180 | } |
| 181 | |
| 182 | /* Get vscreeninfo */ |
| 183 | if (ioctl(fb_dev_fd, FBIOGET_VSCREENINFO, &fb_vinfo)) { |
| 184 | SDL_SetError("[PS3] Can't get VSCREENINFO"); |
| 185 | if (fb_dev_fd >= 0) |
| 186 | close(fb_dev_fd); |
| 187 | fb_dev_fd = -1; |
| 188 | return -1; |
| 189 | } |
| 190 | |
| 191 | /* Fill in our hardware acceleration capabilities */ |
| 192 | this->info.current_w = fb_vinfo.xres; |
| 193 | this->info.current_h = fb_vinfo.yres; |
| 194 | this->info.wm_available = 0; |
| 195 | this->info.hw_available = 1; |
| 196 | |
| 197 | /* Backup the original vinfo to restore later */ |
| 198 | fb_orig_vinfo = fb_vinfo; |
| 199 | |
| 200 | /* 16 and 15 bpp is reported as 16 bpp */ |
| 201 | fb_bits_per_pixel = fb_vinfo.bits_per_pixel; |
| 202 | if (fb_bits_per_pixel == 16) |
| 203 | fb_bits_per_pixel = |
| 204 | fb_vinfo.red.length + fb_vinfo.green.length + |
| 205 | fb_vinfo.blue.length; |
| 206 | |
| 207 | /* Set SDL_PixelFormat */ |
| 208 | vformat->BitsPerPixel = fb_vinfo.bits_per_pixel; |
| 209 | |
| 210 | fb_vinfo.xres_virtual = fb_vinfo.xres; |
| 211 | fb_vinfo.yres_virtual = fb_vinfo.yres; |
| 212 | |
| 213 | /* Put vscreeninfo */ |
| 214 | if (ioctl(fb_dev_fd, FBIOPUT_VSCREENINFO, &fb_vinfo)) { |
| 215 | SDL_SetError("[PS3] Can't put VSCREENINFO"); |
| 216 | if (fb_dev_fd >= 0) |
| 217 | close(fb_dev_fd); |
| 218 | fb_dev_fd = -1; |
| 219 | return -1; |
| 220 | } |
| 221 | |
| 222 | s_fb_pixel_size = fb_vinfo.bits_per_pixel / 8; |
| 223 | |
| 224 | s_writeable_width = fb_vinfo.xres; |
| 225 | s_writeable_height = fb_vinfo.yres; |
| 226 | |
| 227 | /* Get ps3 screeninfo */ |
| 228 | if (ioctl(fb_dev_fd, PS3FB_IOCTL_SCREENINFO, (unsigned long)&res) < 0) { |
| 229 | SDL_SetError("[PS3] PS3FB_IOCTL_SCREENINFO failed"); |
| 230 | } |
| 231 | deprintf(1, "[PS3] xres:%d yres:%d xoff:%d yoff:%d\n", res.xres, res.yres, res.xoff, res.yoff); |
| 232 | |
| 233 | /* Only use double buffering if enough fb memory is available */ |
| 234 | if (res.num_frames < 2) { |
| 235 | double_buffering = 0; |
| 236 | } else { |
| 237 | double_buffering = 1; |
| 238 | } |
| 239 | |
| 240 | real_width = res.xres; |
| 241 | real_height = res.yres; |
| 242 | |
| 243 | /* |
| 244 | * Take control of frame buffer from kernel, for details see |
| 245 | * http://felter.org/wesley/files/ps3/linux-20061110-docs/ApplicationProgrammingEnvironment.html |
| 246 | * kernel will no longer flip the screen itself |
| 247 | */ |
| 248 | ioctl(fb_dev_fd, PS3FB_IOCTL_ON, 0); |
| 249 | |
| 250 | /* Unblank screen */ |
| 251 | ioctl(fb_dev_fd, FBIOBLANK, 0); |
| 252 | |
| 253 | return 0; |
| 254 | } |
| 255 | |
| 256 | |
| 257 | /* List available PS3 resolutions */ |
| 258 | static SDL_Rect **PS3_ListModes(_THIS, SDL_PixelFormat * format, Uint32 flags) |
| 259 | { |
| 260 | /* A list of video resolutions that we query for (sorted largest to |
| 261 | * smallest) |
| 262 | */ |
| 263 | static SDL_Rect PS3_resolutions[] = { |
| 264 | {0, 0, 1920, 1080}, // 1080p 16:9 HD |
| 265 | {0, 0, 1600, 1200}, // WUXGA |
| 266 | {0, 0, 1280, 1024}, // SXGA |
| 267 | {0, 0, 1280, 720}, // 720p 16:9 HD |
| 268 | {0, 0, 1024, 768}, // WXGA |
| 269 | {0, 0, 1024, 576}, // 576p 16:9 |
| 270 | {0, 0, 853, 480}, // 480p 16:9 |
| 271 | {0, 0, 720, 576}, // 576p 4:3 (PAL) |
| 272 | {0, 0, 720, 480}, // 480p 16:9 (NTSC) |
| 273 | }; |
| 274 | static SDL_Rect *PS3_modes[] = { |
| 275 | &PS3_resolutions[0], |
| 276 | &PS3_resolutions[1], |
| 277 | &PS3_resolutions[2], |
| 278 | &PS3_resolutions[3], |
| 279 | &PS3_resolutions[4], |
| 280 | &PS3_resolutions[5], |
| 281 | &PS3_resolutions[6], |
| 282 | &PS3_resolutions[7], |
| 283 | &PS3_resolutions[8], |
| 284 | NULL |
| 285 | }; |
| 286 | SDL_Rect **modes = PS3_modes; |
| 287 | |
| 288 | return modes; |
| 289 | } |
| 290 | |
| 291 | |
| 292 | /* Get a list of the available display modes */ |
| 293 | static SDL_Surface *PS3_SetVideoMode(_THIS, SDL_Surface * current, int width, int height, int bpp, Uint32 flags) |
| 294 | { |
| 295 | s_bounded_input_width = width < s_writeable_width ? width : s_writeable_width; |
| 296 | s_bounded_input_height = height < s_writeable_height ? height : s_writeable_height; |
| 297 | s_bounded_input_width_offset = (s_writeable_width - s_bounded_input_width) >> 1; |
| 298 | s_bounded_input_height_offset = (s_writeable_height - s_bounded_input_height) >> 1; |
| 299 | s_input_line_length = width * s_fb_pixel_size; |
| 300 | |
| 301 | current->flags |= flags; |
| 302 | |
| 303 | if (ioctl(fb_dev_fd, FBIOGET_FSCREENINFO, &fb_finfo)) { |
| 304 | SDL_SetError("[PS3] Can't get fixed screeninfo"); |
| 305 | return NULL; |
| 306 | } |
| 307 | |
| 308 | if (fb_finfo.type != FB_TYPE_PACKED_PIXELS) { |
| 309 | SDL_SetError("[PS3] type %s not supported", |
| 310 | fb_finfo.type); |
| 311 | return NULL; |
| 312 | } |
| 313 | |
| 314 | /* Note: on PS3, fb_finfo.smem_len is enough for double buffering */ |
| 315 | if ((frame_buffer = |
| 316 | (uint8_t *) mmap(0, fb_finfo.smem_len, |
| 317 | PROT_READ | PROT_WRITE, MAP_SHARED, |
| 318 | fb_dev_fd, 0)) == (uint8_t *) - 1) { |
| 319 | SDL_SetError("[PS3] Can't mmap for %s", PS3_DEV_FB); |
| 320 | return NULL; |
| 321 | } else { |
| 322 | current->flags |= SDL_DOUBLEBUF; |
| 323 | } |
| 324 | if (!SDL_ReallocFormat(current, fb_bits_per_pixel, 0, 0, 0, 0)) { |
| 325 | return (NULL); |
| 326 | } |
| 327 | |
| 328 | /* Blank screen */ |
| 329 | memset(frame_buffer, 0x00, fb_finfo.smem_len); |
| 330 | |
| 331 | /* Centering */ |
| 332 | s_center[0] = |
| 333 | frame_buffer + s_bounded_input_width_offset * s_fb_pixel_size + |
| 334 | s_bounded_input_height_offset * fb_finfo.line_length; |
| 335 | s_center[1] = s_center[0] + real_height * fb_finfo.line_length; |
| 336 | s_center_index = 0; |
| 337 | |
| 338 | current->flags |= SDL_FULLSCREEN; |
| 339 | current->w = width; |
| 340 | current->h = height; |
| 341 | current->pitch = SDL_CalculatePitch(current); |
| 342 | |
| 343 | /* Alloc aligned mem for current->pixels */ |
| 344 | s_pixels = memalign(16, current->h * current->pitch); |
| 345 | current->pixels = (void *)s_pixels; |
| 346 | if (!current->pixels) { |
| 347 | SDL_OutOfMemory(); |
| 348 | return NULL; |
| 349 | } |
| 350 | |
| 351 | /* Set the update rectangle function */ |
| 352 | this->UpdateRects = PS3_DoubleBufferUpdate; |
| 353 | |
| 354 | return current; |
| 355 | } |
| 356 | |
| 357 | |
| 358 | /* Copy screen to framebuffer and flip */ |
| 359 | void PS3_DoubleBufferUpdate(_THIS, int numrects, SDL_Rect * rects) |
| 360 | { |
| 361 | if (converter_thread_data && converter_thread_data->booted) |
| 362 | SPE_WaitForMsg(this, converter_thread_data, SPU_FIN); |
| 363 | |
| 364 | /* Adjust centering */ |
| 365 | s_bounded_input_width_offset = (s_writeable_width - s_bounded_input_width) >> 1; |
| 366 | s_bounded_input_height_offset = (s_writeable_height - s_bounded_input_height) >> 1; |
| 367 | s_center[0] = frame_buffer + s_bounded_input_width_offset * s_fb_pixel_size + |
| 368 | s_bounded_input_height_offset * fb_finfo.line_length; |
| 369 | s_center[1] = s_center[0] + real_height * fb_finfo.line_length; |
| 370 | |
| 371 | /* Set SPU parms for copying the surface to framebuffer */ |
| 372 | fb_parms->data = (unsigned char *)s_pixels; |
| 373 | fb_parms->center = s_center[s_center_index]; |
| 374 | fb_parms->out_line_stride = fb_finfo.line_length; |
| 375 | fb_parms->in_line_stride = s_input_line_length; |
| 376 | fb_parms->bounded_input_height = s_bounded_input_height; |
| 377 | fb_parms->bounded_input_width = s_bounded_input_width; |
| 378 | fb_parms->fb_pixel_size = s_fb_pixel_size; |
| 379 | |
| 380 | deprintf(3, "[PS3->SPU] fb_thread_data->argp = 0x%x\n", fb_thread_data->argp); |
| 381 | |
| 382 | /* Copying.. */ |
| 383 | SPE_SendMsg(this, fb_thread_data, SPU_START); |
| 384 | SPE_SendMsg(this, fb_thread_data, (unsigned int)fb_thread_data->argp); |
| 385 | |
| 386 | SPE_WaitForMsg(this, fb_thread_data, SPU_FIN); |
| 387 | |
| 388 | /* Flip the pages */ |
| 389 | if (double_buffering) |
| 390 | s_center_index = s_center_index ^ 0x01; |
| 391 | PS3_FlipDoubleBuffer(this, this->screen); |
| 392 | } |
| 393 | |
| 394 | |
| 395 | /* Enable/Disable cursor */ |
| 396 | void enable_cursor(int enable) |
| 397 | { |
| 398 | int fd = open("/dev/console", O_RDWR | O_NONBLOCK); |
| 399 | if (fd >= 0) { |
| 400 | ioctl(fd, KDSETMODE, enable ? KD_TEXT : KD_GRAPHICS); |
| 401 | close(fd); |
| 402 | } |
| 403 | } |
| 404 | |
| 405 | |
| 406 | static int PS3_AllocHWSurface(_THIS, SDL_Surface * surface) |
| 407 | { |
| 408 | return -1; |
| 409 | } |
| 410 | |
| 411 | |
| 412 | static void PS3_FreeHWSurface(_THIS, SDL_Surface * surface) |
| 413 | { |
| 414 | return; |
| 415 | } |
| 416 | |
| 417 | |
| 418 | static int PS3_LockHWSurface(_THIS, SDL_Surface * surface) |
| 419 | { |
| 420 | return 0; |
| 421 | } |
| 422 | |
| 423 | |
| 424 | static void PS3_UnlockHWSurface(_THIS, SDL_Surface * surface) |
| 425 | { |
| 426 | return; |
| 427 | } |
| 428 | |
| 429 | |
| 430 | /* Blit/Flip buffer to the screen. Must be called after each frame! */ |
| 431 | int PS3_FlipDoubleBuffer(_THIS, SDL_Surface * surface) |
| 432 | { |
| 433 | unsigned long crt = 0; |
| 434 | /* Wait for vsync */ |
| 435 | deprintf(1, "[PS3] Wait for vsync\n"); |
| 436 | ioctl(fb_dev_fd, FBIO_WAITFORVSYNC, &crt); |
| 437 | /* Page flip */ |
| 438 | deprintf(1, "[PS3] Page flip to buffer #%u 0x%x\n", s_center_index, s_center[s_center_index]); |
| 439 | ioctl(fb_dev_fd, PS3FB_IOCTL_FSEL, (unsigned long)&s_center_index); |
| 440 | return 1; |
| 441 | } |
| 442 | |
| 443 | |
| 444 | /* Start the SPE thread */ |
| 445 | int SPE_Start(_THIS, spu_data_t * spe_data) |
| 446 | { |
| 447 | deprintf(2, "[PS3->SPU] Start SPE: %s\n", spe_data->program_name); |
| 448 | if (!(spe_data->booted)) |
| 449 | SPE_Boot(this, spe_data); |
| 450 | |
| 451 | /* To allow re-running of context, spe_ctx_entry has to be set before each call */ |
| 452 | spe_data->entry = SPE_DEFAULT_ENTRY; |
| 453 | spe_data->error_code = 0; |
| 454 | |
| 455 | /* Create SPE thread and run */ |
| 456 | deprintf(2, "[PS3->SPU] Create Thread: %s\n", spe_data->program_name); |
| 457 | if (pthread_create |
| 458 | (&spe_data->thread, NULL, (void *)&SPE_RunContext, (void *)spe_data)) { |
| 459 | deprintf(2, "[PS3->SPU] Could not create pthread for spe: %s\n", spe_data->program_name); |
| 460 | SDL_SetError("[PS3->SPU] Could not create pthread for spe"); |
| 461 | return -1; |
| 462 | } |
| 463 | |
| 464 | if (spe_data->keepalive) |
| 465 | SPE_WaitForMsg(this, spe_data, SPU_READY); |
| 466 | } |
| 467 | |
| 468 | |
| 469 | /* Stop the SPE thread */ |
| 470 | int SPE_Stop(_THIS, spu_data_t * spe_data) |
| 471 | { |
| 472 | deprintf(2, "[PS3->SPU] Stop SPE: %s\n", spe_data->program_name); |
| 473 | /* Wait for SPE thread to complete */ |
| 474 | deprintf(2, "[PS3->SPU] Wait for SPE thread to complete: %s\n", spe_data->program_name); |
| 475 | if (pthread_join(spe_data->thread, NULL)) { |
| 476 | deprintf(2, "[PS3->SPU] Failed joining the thread: %s\n", spe_data->program_name); |
| 477 | SDL_SetError("[PS3->SPU] Failed joining the thread"); |
| 478 | return -1; |
| 479 | } |
| 480 | |
| 481 | return 0; |
| 482 | } |
| 483 | |
| 484 | |
| 485 | /* Create SPE context and load program */ |
| 486 | int SPE_Boot(_THIS, spu_data_t * spe_data) |
| 487 | { |
| 488 | /* Create SPE context */ |
| 489 | deprintf(2, "[PS3->SPU] Create SPE Context: %s\n", spe_data->program_name); |
| 490 | spe_data->ctx = spe_context_create(0, NULL); |
| 491 | if (spe_data->ctx == NULL) { |
| 492 | deprintf(2, "[PS3->SPU] Failed creating SPE context: %s\n", spe_data->program_name); |
| 493 | SDL_SetError("[PS3->SPU] Failed creating SPE context"); |
| 494 | return -1; |
| 495 | } |
| 496 | |
| 497 | /* Load SPE object into SPE local store */ |
| 498 | deprintf(2, "[PS3->SPU] Load Program into SPE: %s\n", spe_data->program_name); |
| 499 | if (spe_program_load(spe_data->ctx, &spe_data->program)) { |
| 500 | deprintf(2, "[PS3->SPU] Failed loading program into SPE context: %s\n", spe_data->program_name); |
| 501 | SDL_SetError |
| 502 | ("[PS3->SPU] Failed loading program into SPE context"); |
| 503 | return -1; |
| 504 | } |
| 505 | spe_data->booted = 1; |
| 506 | deprintf(2, "[PS3->SPU] SPE boot successful\n"); |
| 507 | |
| 508 | return 0; |
| 509 | } |
| 510 | |
| 511 | /* (Stop and) shutdown the SPE */ |
| 512 | int SPE_Shutdown(_THIS, spu_data_t * spe_data) |
| 513 | { |
| 514 | if (spe_data->keepalive && spe_data->booted) { |
| 515 | SPE_SendMsg(this, spe_data, SPU_EXIT); |
| 516 | SPE_Stop(this, spe_data); |
| 517 | } |
| 518 | |
| 519 | /* Destroy SPE context */ |
| 520 | deprintf(2, "[PS3->SPU] Destroy SPE context: %s\n", spe_data->program_name); |
| 521 | if (spe_context_destroy(spe_data->ctx)) { |
| 522 | deprintf(2, "[PS3->SPU] Failed destroying context: %s\n", spe_data->program_name); |
| 523 | SDL_SetError("[PS3->SPU] Failed destroying context"); |
| 524 | return -1; |
| 525 | } |
| 526 | deprintf(2, "[PS3->SPU] SPE shutdown successful: %s\n", spe_data->program_name); |
| 527 | return 0; |
| 528 | } |
| 529 | |
| 530 | |
| 531 | /* Send message to the SPE via mailboxe */ |
| 532 | int SPE_SendMsg(_THIS, spu_data_t * spe_data, unsigned int msg) |
| 533 | { |
| 534 | deprintf(2, "[PS3->SPU] Sending message %u to %s\n", msg, spe_data->program_name); |
| 535 | /* Send one message, block until message was sent */ |
| 536 | unsigned int spe_in_mbox_msgs[1]; |
| 537 | spe_in_mbox_msgs[0] = msg; |
| 538 | int in_mbox_write = spe_in_mbox_write(spe_data->ctx, spe_in_mbox_msgs, 1, SPE_MBOX_ALL_BLOCKING); |
| 539 | |
| 540 | if (1 > in_mbox_write) { |
| 541 | deprintf(2, "[PS3->SPU] No message could be written to %s\n", spe_data->program_name); |
| 542 | SDL_SetError("[PS3->SPU] No message could be written"); |
| 543 | return -1; |
| 544 | } |
| 545 | return 0; |
| 546 | } |
| 547 | |
| 548 | |
| 549 | /* Read 1 message from SPE, block until at least 1 message was received */ |
| 550 | int SPE_WaitForMsg(_THIS, spu_data_t * spe_data, unsigned int msg) |
| 551 | { |
| 552 | deprintf(2, "[PS3->SPU] Waiting for message from %s\n", spe_data->program_name); |
| 553 | unsigned int out_messages[1]; |
| 554 | while (!spe_out_mbox_status(spe_data->ctx)); |
| 555 | int mbox_read = spe_out_mbox_read(spe_data->ctx, out_messages, 1); |
| 556 | deprintf(2, "[PS3->SPU] Got message from %s, message was %u\n", spe_data->program_name, out_messages[0]); |
| 557 | if (out_messages[0] == msg) |
| 558 | return 0; |
| 559 | else |
| 560 | return -1; |
| 561 | } |
| 562 | |
| 563 | |
| 564 | /* Re-runnable invocation of the spe_context_run call */ |
| 565 | void SPE_RunContext(void *thread_argp) |
| 566 | { |
| 567 | /* argp is the pointer to argument to be passed to the SPE program */ |
| 568 | spu_data_t *args = (spu_data_t *) thread_argp; |
| 569 | deprintf(3, "[PS3->SPU] void* argp=0x%x\n", (unsigned int)args->argp); |
| 570 | |
| 571 | /* Run it.. */ |
| 572 | deprintf(2, "[PS3->SPU] Run SPE program: %s\n", args->program_name); |
| 573 | if (spe_context_run |
| 574 | (args->ctx, &args->entry, 0, (void *)args->argp, NULL, |
| 575 | NULL) < 0) { |
| 576 | deprintf(2, "[PS3->SPU] Failed running SPE context: %s\n", args->program_name); |
| 577 | SDL_SetError("[PS3->SPU] Failed running SPE context: %s", args->program_name); |
| 578 | exit(1); |
| 579 | } |
| 580 | |
| 581 | pthread_exit(NULL); |
| 582 | } |
| 583 | |
| 584 | |
| 585 | /* Quits the video driver */ |
| 586 | static void PS3_VideoQuit(_THIS) |
| 587 | { |
| 588 | if (fb_dev_fd > 0) { |
| 589 | /* Restore the original video mode */ |
| 590 | if (ioctl(fb_dev_fd, FBIOPUT_VSCREENINFO, &fb_orig_vinfo)) |
| 591 | SDL_SetError("[PS3] Can't restore original fb_var_screeninfo"); |
| 592 | |
| 593 | /* Give control of frame buffer to kernel */ |
| 594 | ioctl(fb_dev_fd, PS3FB_IOCTL_OFF, 0); |
| 595 | close(fb_dev_fd); |
| 596 | fb_dev_fd = -1; |
| 597 | } |
| 598 | |
| 599 | if (frame_buffer) { |
| 600 | munmap(frame_buffer, fb_finfo.smem_len); |
| 601 | frame_buffer = 0; |
| 602 | } |
| 603 | |
| 604 | if (fb_parms) |
| 605 | free((void *)fb_parms); |
| 606 | if (fb_thread_data) { |
| 607 | SPE_Shutdown(this, fb_thread_data); |
| 608 | free((void *)fb_thread_data); |
| 609 | } |
| 610 | |
| 611 | if (this->screen) { |
| 612 | if (double_buffering && this->screen->pixels) { |
| 613 | free(this->screen->pixels); |
| 614 | } |
| 615 | this->screen->pixels = NULL; |
| 616 | } |
| 617 | |
| 618 | enable_cursor(1); |
| 619 | deprintf(1, "[PS3] VideoQuit\n"); |
| 620 | } |
| 621 | |