e14743d1 |
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 | |