eb326eede8b66c4975f143a07312f31b42b74b89
[picodrive.git] / platform / pandora / plat.c
1 /*\r
2  * PicoDrive\r
3  * (C) notaz, 2010,2011\r
4  *\r
5  * This work is licensed under the terms of MAME license.\r
6  * See COPYING file in the top-level directory.\r
7  */\r
8 \r
9 #include <stdio.h>\r
10 #include <unistd.h>\r
11 #include <sys/types.h>\r
12 #include <sys/stat.h>\r
13 #include <fcntl.h>\r
14 #include <sys/ioctl.h>\r
15 #include <unistd.h>\r
16 #include <linux/fb.h>\r
17 #include <linux/omapfb.h>\r
18 #include <linux/input.h>\r
19 \r
20 #include "../common/emu.h"\r
21 #include "../common/arm_utils.h"\r
22 #include "../common/input_pico.h"\r
23 #include "../common/keyboard.h"\r
24 #include "../common/version.h"\r
25 #include "../libpicofe/input.h"\r
26 #include "../libpicofe/menu.h"\r
27 #include "../libpicofe/plat.h"\r
28 #include "../libpicofe/linux/in_evdev.h"\r
29 #include "../libpicofe/linux/sndout_oss.h"\r
30 #include "../libpicofe/linux/fbdev.h"\r
31 #include "../libpicofe/linux/xenv.h"\r
32 #include "plat.h"\r
33 #include "asm_utils.h"\r
34 \r
35 #include <pico/pico_int.h>\r
36 \r
37 #define LAYER_MEM_SIZE (320*240*2 * 4)\r
38 \r
39 static struct vout_fbdev *main_fb, *layer_fb;\r
40 // g_layer_* - in use, g_layer_c* - configured custom\r
41 int g_layer_cx = 80, g_layer_cy, g_layer_cw = 640, g_layer_ch = 480;\r
42 int saved_start_line = 0, saved_line_count = 240;\r
43 int saved_start_col = 0, saved_col_count = 320;\r
44 static int g_layer_x, g_layer_y;\r
45 static int g_layer_w = 320, g_layer_h = 240;\r
46 static int g_osd_start_x, g_osd_fps_x, g_osd_y, doing_bg_frame;\r
47 \r
48 static unsigned char __attribute__((aligned(4))) fb_copy[320 * 240 * 2];\r
49 static void *temp_frame;\r
50 const char *renderer_names[] = { NULL };\r
51 const char *renderer_names32x[] = { NULL };\r
52 \r
53 static struct in_default_bind in_evdev_defbinds[] =\r
54 {\r
55         { KEY_UP,       IN_BINDTYPE_PLAYER12, GBTN_UP },\r
56         { KEY_DOWN,     IN_BINDTYPE_PLAYER12, GBTN_DOWN },\r
57         { KEY_LEFT,     IN_BINDTYPE_PLAYER12, GBTN_LEFT },\r
58         { KEY_RIGHT,    IN_BINDTYPE_PLAYER12, GBTN_RIGHT },\r
59         { KEY_A,        IN_BINDTYPE_PLAYER12, GBTN_A },\r
60         { KEY_S,        IN_BINDTYPE_PLAYER12, GBTN_B },\r
61         { KEY_D,        IN_BINDTYPE_PLAYER12, GBTN_C },\r
62         { KEY_ENTER,    IN_BINDTYPE_PLAYER12, GBTN_START },\r
63         { KEY_R,        IN_BINDTYPE_EMU, PEVB_RESET },\r
64         { KEY_F,        IN_BINDTYPE_EMU, PEVB_FF },\r
65         { KEY_BACKSPACE,IN_BINDTYPE_EMU, PEVB_FF },\r
66         { KEY_BACKSLASH,IN_BINDTYPE_EMU, PEVB_MENU },\r
67         { KEY_SPACE,    IN_BINDTYPE_EMU, PEVB_MENU },\r
68         { KEY_LEFTCTRL, IN_BINDTYPE_EMU, PEVB_MENU },\r
69         { KEY_HOME,     IN_BINDTYPE_PLAYER12, GBTN_A },\r
70         { KEY_PAGEDOWN, IN_BINDTYPE_PLAYER12, GBTN_B },\r
71         { KEY_END,      IN_BINDTYPE_PLAYER12, GBTN_C },\r
72         { KEY_LEFTALT,  IN_BINDTYPE_PLAYER12, GBTN_START },\r
73         { KEY_1,        IN_BINDTYPE_EMU, PEVB_STATE_SAVE },\r
74         { KEY_2,        IN_BINDTYPE_EMU, PEVB_STATE_LOAD },\r
75         { KEY_3,        IN_BINDTYPE_EMU, PEVB_SSLOT_PREV },\r
76         { KEY_4,        IN_BINDTYPE_EMU, PEVB_SSLOT_NEXT },\r
77         { KEY_5,        IN_BINDTYPE_EMU, PEVB_PICO_PPREV },\r
78         { KEY_6,        IN_BINDTYPE_EMU, PEVB_PICO_PNEXT },\r
79         { KEY_7,        IN_BINDTYPE_EMU, PEVB_PICO_STORY },\r
80         { KEY_8,        IN_BINDTYPE_EMU, PEVB_PICO_PAD },\r
81         { 0, 0, 0 }\r
82 };\r
83 \r
84 static const struct menu_keymap key_pbtn_map[] =\r
85 {\r
86         { KEY_UP,       PBTN_UP },\r
87         { KEY_DOWN,     PBTN_DOWN },\r
88         { KEY_LEFT,     PBTN_LEFT },\r
89         { KEY_RIGHT,    PBTN_RIGHT },\r
90         /* Pandora */\r
91         { KEY_END,      PBTN_MOK },\r
92         { KEY_PAGEDOWN, PBTN_MBACK },\r
93         { KEY_HOME,     PBTN_MA2 },\r
94         { KEY_PAGEUP,   PBTN_MA3 },\r
95         { KEY_LEFTCTRL,   PBTN_MENU },\r
96         { KEY_RIGHTSHIFT, PBTN_L },\r
97         { KEY_RIGHTCTRL,  PBTN_R },\r
98         /* "normal" keyboards */\r
99         { KEY_ENTER,    PBTN_MOK },\r
100         { KEY_ESC,      PBTN_MBACK },\r
101         { KEY_SEMICOLON,  PBTN_MA2 },\r
102         { KEY_APOSTROPHE, PBTN_MA3 },\r
103         { KEY_BACKSLASH,  PBTN_MENU },\r
104         { KEY_LEFTBRACE,  PBTN_L },\r
105         { KEY_RIGHTBRACE, PBTN_R },\r
106 };\r
107 \r
108 static const struct in_pdata pandora_evdev_pdata = {\r
109         .defbinds = in_evdev_defbinds,\r
110         .key_map = key_pbtn_map,\r
111         .kmap_size = sizeof(key_pbtn_map) / sizeof(key_pbtn_map[0]),\r
112 };\r
113 \r
114 void pemu_prep_defconfig(void)\r
115 {\r
116         defaultConfig.EmuOpt |= EOPT_VSYNC|EOPT_16BPP;\r
117         defaultConfig.s_PicoOpt |= POPT_EN_MCD_GFX;\r
118         defaultConfig.scaling = SCALE_2x2_3x2;\r
119 }\r
120 \r
121 void pemu_validate_config(void)\r
122 {\r
123         currentConfig.CPUclock = plat_target_cpu_clock_get();\r
124 }\r
125 \r
126 static void draw_cd_leds(void)\r
127 {\r
128         int old_reg;\r
129         old_reg = Pico_mcd->s68k_regs[0];\r
130 \r
131         // 16-bit modes\r
132         unsigned int *p = (unsigned int *)((short *)g_screen_ptr + g_screen_width*2+4);\r
133         unsigned int col_g = (old_reg & 2) ? 0x06000600 : 0;\r
134         unsigned int col_r = (old_reg & 1) ? 0xc000c000 : 0;\r
135         *p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r; p += g_screen_width/2 - 12/2;\r
136         *p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r; p += g_screen_width/2 - 12/2;\r
137         *p++ = col_g; *p++ = col_g; p+=2; *p++ = col_r; *p++ = col_r;\r
138 }\r
139 \r
140 static void draw_pico_ptr(void)\r
141 {\r
142         int up = (PicoPicohw.pen_pos[0]|PicoPicohw.pen_pos[1]) & 0x8000;\r
143         int o = (up ? 0x0000 : 0xffff), _ = (up ? 0xffff : 0x0000);\r
144         int x = pico_pen_x, y = pico_pen_y, pitch = g_screen_ppitch;\r
145         unsigned short *p = (unsigned short *)g_screen_ptr;\r
146         // storyware pages are actually squished, 2:1\r
147         int h = (pico_inp_mode == 1 ? 160 : saved_line_count);\r
148         if (h < 224) y++;\r
149 \r
150         x = (x * saved_col_count * ((1ULL<<32) / 320 + 1)) >> 32;\r
151         y = (y * h               * ((1ULL<<32) / 224 + 1)) >> 32;\r
152         p += (saved_start_col+x) + (saved_start_line+y) * pitch;\r
153 \r
154         p[-pitch-1] ^= o; p[-pitch] ^= _; p[-pitch+1] ^= _; p[-pitch+2] ^= o;\r
155         p[-1]       ^= _; p[0]      ^= o; p[1]        ^= o; p[2]        ^= _;\r
156         p[pitch-1]  ^= _; p[pitch]  ^= o; p[pitch+1]  ^= o; p[pitch+2]  ^= _;\r
157         p[2*pitch-1]^= o; p[2*pitch]^= _; p[2*pitch+1]^= _; p[2*pitch+2]^= o;\r
158 }\r
159 \r
160 void pemu_finalize_frame(const char *fps, const char *notice)\r
161 {\r
162         if (PicoIn.AHW & PAHW_PICO) {\r
163                 int h = saved_line_count, w = saved_col_count;\r
164                 u16 *pd = g_screen_ptr + saved_start_line*g_screen_ppitch\r
165                                         + saved_start_col;\r
166 \r
167                 if (pico_inp_mode)\r
168                         emu_pico_overlay(pd, w, h, g_screen_ppitch);\r
169                 if (pico_inp_mode /*== 2 || overlay*/)\r
170                         draw_pico_ptr();\r
171         }\r
172         if (notice && notice[0])\r
173                 emu_osd_text16(2 + g_osd_start_x, g_osd_y, notice);\r
174         if (fps && fps[0] && (currentConfig.EmuOpt & EOPT_SHOW_FPS)) {\r
175                 const char *p;\r
176                 // avoid wrapping when fps is very high\r
177                 if (fps[5] != ' ' && (p = strchr(fps, '/')))\r
178                         fps = p;\r
179                 emu_osd_text16(g_osd_fps_x, g_osd_y, fps);\r
180         }\r
181         if ((PicoIn.AHW & PAHW_MCD) && (currentConfig.EmuOpt & EOPT_EN_CD_LEDS))\r
182                 draw_cd_leds();\r
183         // draw virtual keyboard on display\r
184         if (kbd_mode && currentConfig.keyboard == 1 && vkbd)\r
185                 vkbd_draw(vkbd);\r
186 }\r
187 \r
188 void plat_video_flip(void)\r
189 {\r
190         g_screen_ptr = vout_fbdev_flip(layer_fb);\r
191         PicoDrawSetOutBuf(g_screen_ptr, g_screen_width * 2);\r
192 \r
193         // XXX: drain OS event queue here, maybe we'll actually use it someday..\r
194         xenv_update(NULL, NULL, NULL, NULL);\r
195 }\r
196 \r
197 void plat_video_clear_buffers(void)\r
198 {\r
199         vout_fbdev_clear(layer_fb);\r
200 }\r
201 \r
202 // pnd doesn't use multiple renderers, but we have to handle this since it's\r
203 // called on 32x enable and PicoDrawSetOutFormat() sets up the 32x layers\r
204 void plat_video_toggle_renderer(int change, int is_menu)\r
205 {\r
206         if (!is_menu)\r
207                 PicoDrawSetOutFormat(PDF_RGB555, 0);\r
208 }\r
209 \r
210 void plat_video_menu_update(void)\r
211 {\r
212 }\r
213 \r
214 void plat_video_menu_enter(int is_rom_loaded)\r
215 {\r
216 }\r
217 \r
218 void plat_video_menu_begin(void)\r
219 {\r
220 }\r
221 \r
222 void plat_video_menu_end(void)\r
223 {\r
224         g_menuscreen_ptr = vout_fbdev_flip(main_fb);\r
225 }\r
226 \r
227 void plat_video_menu_leave(void)\r
228 {\r
229 }\r
230 \r
231 void plat_video_wait_vsync(void)\r
232 {\r
233         vout_fbdev_wait_vsync(main_fb);\r
234 }\r
235 \r
236 void plat_status_msg_clear(void)\r
237 {\r
238         vout_fbdev_clear_lines(layer_fb, g_osd_y, 8);\r
239 }\r
240 \r
241 void plat_status_msg_busy_next(const char *msg)\r
242 {\r
243         plat_status_msg_clear();\r
244         pemu_finalize_frame("", msg);\r
245         plat_video_flip();\r
246         emu_status_msg("");\r
247         reset_timing = 1;\r
248 }\r
249 \r
250 void plat_status_msg_busy_first(const char *msg)\r
251 {\r
252         plat_status_msg_busy_next(msg);\r
253 }\r
254 \r
255 void plat_status_msg_busy_done(void)\r
256 {\r
257 }\r
258 \r
259 void plat_update_volume(int has_changed, int is_up)\r
260 {\r
261 }\r
262 \r
263 void pemu_forced_frame(int no_scale, int do_emu)\r
264 {\r
265         doing_bg_frame = 1;\r
266         // making a copy because enabling the layer clears it's mem\r
267         emu_cmn_forced_frame(no_scale, do_emu, fb_copy);\r
268         doing_bg_frame = 0;\r
269 \r
270         g_menubg_src_ptr = fb_copy;\r
271 }\r
272 \r
273 void pemu_sound_start(void)\r
274 {\r
275         emu_sound_start();\r
276 }\r
277 \r
278 void plat_debug_cat(char *str)\r
279 {\r
280 }\r
281 \r
282 static int pnd_setup_layer_(int fd, int enabled, int x, int y, int w, int h)\r
283 {\r
284         struct omapfb_plane_info pi;\r
285         struct omapfb_mem_info mi;\r
286         int is_enabled;\r
287         int retval = 0;\r
288         int ret;\r
289 \r
290         ret = ioctl(fd, OMAPFB_QUERY_PLANE, &pi);\r
291         if (ret != 0) {\r
292                 perror("QUERY_PLANE");\r
293                 return -1;\r
294         }\r
295 \r
296         ret = ioctl(fd, OMAPFB_QUERY_MEM, &mi);\r
297         if (ret != 0) {\r
298                 perror("QUERY_MEM");\r
299                 return -1;\r
300         }\r
301 \r
302         /* must disable when changing stuff */\r
303         is_enabled = pi.enabled;\r
304         if (is_enabled) {\r
305                 pi.enabled = 0;\r
306                 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);\r
307                 if (ret != 0)\r
308                         perror("SETUP_PLANE");\r
309                 else\r
310                         is_enabled = 0;\r
311         }\r
312 \r
313         if (mi.size < LAYER_MEM_SIZE) {\r
314                 unsigned int size_old = mi.size;\r
315 \r
316                 mi.size = LAYER_MEM_SIZE;\r
317                 ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);\r
318                 if (ret != 0) {\r
319                         perror("SETUP_MEM");\r
320                         fprintf(stderr, "(requested %u, had %u)\n",\r
321                                 mi.size, size_old);\r
322                         return -1;\r
323                 }\r
324         }\r
325 \r
326         pi.pos_x = x;\r
327         pi.pos_y = y;\r
328         pi.out_width = w;\r
329         pi.out_height = h;\r
330         pi.enabled = enabled;\r
331 \r
332         ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);\r
333         if (ret == 0) {\r
334                 is_enabled = pi.enabled;\r
335         }\r
336         else {\r
337                 perror("SETUP_PLANE");\r
338                 retval = -1;\r
339         }\r
340 \r
341         plat_target_switch_layer(1, is_enabled);\r
342 \r
343         return retval;\r
344 }\r
345 \r
346 int pnd_setup_layer(int enabled, int x, int y, int w, int h)\r
347 {\r
348         // it's not allowed for the layer to be partially offscreen,\r
349         // instead it is faked by emu_video_mode_change()\r
350         if (x < 0) { w += x; x = 0; }\r
351         if (y < 0) { h += y; y = 0; }\r
352         if (x + w > 800) w = 800 - x;\r
353         if (y + h > 480) h = 480 - y;\r
354 \r
355         return pnd_setup_layer_(vout_fbdev_get_fd(layer_fb), enabled, x, y, w, h);\r
356 }\r
357 \r
358 void pnd_restore_layer_data(void)\r
359 {\r
360         short *t = (short *)fb_copy + 320*240 / 2 + 160;\r
361 \r
362         // right now this is used by menu, which wants to preview something\r
363         // so try to get something on the layer.\r
364         if ((t[0] | t[5] | t[13]) == 0)\r
365                 memset32((void *)fb_copy, 0x07000700, sizeof(fb_copy) / 4);\r
366 \r
367         memcpy(g_screen_ptr, (void *)fb_copy, 320*240*2);\r
368         plat_video_flip();\r
369 }\r
370 \r
371 void emu_video_mode_change(int start_line, int line_count, int start_col, int col_count)\r
372 {\r
373         int fb_w, fb_h = 240, fb_left = 0, fb_right = 0, fb_top = 0, fb_bottom = 0;\r
374 \r
375         if (doing_bg_frame)\r
376                 return;\r
377 \r
378         switch (currentConfig.scaling) {\r
379         case SCALE_1x1:\r
380                 g_layer_w = col_count;\r
381                 g_layer_h = fb_h;\r
382                 break;\r
383         case SCALE_2x2_3x2:\r
384                 g_layer_w = col_count * (col_count < 320 ? 3 : 2);\r
385                 g_layer_h = fb_h * 2;\r
386                 break;\r
387         case SCALE_2x2_2x2:\r
388                 g_layer_w = col_count * 2;\r
389                 g_layer_h = fb_h * 2;\r
390                 break;\r
391         case SCALE_FULLSCREEN:\r
392                 g_layer_w = 800;\r
393                 g_layer_h = 480;\r
394                 break;\r
395         case SCALE_CUSTOM:\r
396                 g_layer_x = g_layer_cx;\r
397                 g_layer_y = g_layer_cy;\r
398                 g_layer_w = g_layer_cw;\r
399                 g_layer_h = g_layer_ch;\r
400                 break;\r
401         }\r
402 \r
403         if (currentConfig.scaling != SCALE_CUSTOM) {\r
404                 // center the layer\r
405                 g_layer_x = 800 / 2 - g_layer_w / 2;\r
406                 g_layer_y = 480 / 2 - g_layer_h / 2;\r
407         }\r
408 \r
409         switch (currentConfig.scaling) {\r
410         case SCALE_FULLSCREEN:\r
411         case SCALE_CUSTOM:\r
412                 fb_top = start_line;\r
413                 fb_h = start_line + line_count;\r
414                 break;\r
415         }\r
416         fb_w = 320;\r
417         fb_left = start_col;\r
418         fb_right = 320 - (start_col + col_count);\r
419         if (currentConfig.scaling == SCALE_CUSTOM) {\r
420                 int right = g_layer_x + g_layer_w;\r
421                 int bottom = g_layer_y + g_layer_h;\r
422                 if (g_layer_x < 0)\r
423                         fb_left += -g_layer_x * col_count / g_menuscreen_w;\r
424                 if (g_layer_y < 0)\r
425                         fb_top += -g_layer_y * line_count / g_menuscreen_h;\r
426                 if (right > g_menuscreen_w)\r
427                         fb_right += (right - g_menuscreen_w) * col_count / g_menuscreen_w;\r
428                 if (bottom > g_menuscreen_h)\r
429                         fb_bottom += (bottom - g_menuscreen_h) * line_count / g_menuscreen_h;\r
430         }\r
431         fb_w -= fb_left + fb_right;\r
432         fb_h -= fb_top + fb_bottom;\r
433 \r
434         pnd_setup_layer(1, g_layer_x, g_layer_y, g_layer_w, g_layer_h);\r
435         vout_fbdev_resize(layer_fb, fb_w, fb_h, 16, fb_left, fb_right, fb_top, fb_bottom, 4, 0);\r
436         vout_fbdev_clear(layer_fb);\r
437         plat_video_flip();\r
438 \r
439         g_osd_start_x = start_col;\r
440         g_osd_fps_x = start_col + col_count - 5*8 - 2;\r
441         g_osd_y = fb_top + fb_h - 8;\r
442 \r
443         saved_start_line = start_line, saved_line_count = line_count;\r
444         saved_start_col = start_col, saved_col_count = col_count;\r
445 }\r
446 \r
447 void plat_video_loop_prepare(void)\r
448 {\r
449         // make sure there is no junk left behind the layer\r
450         memset(g_menuscreen_ptr, 0, g_menuscreen_w * g_menuscreen_h * 2);\r
451         g_menuscreen_ptr = vout_fbdev_flip(main_fb);\r
452 \r
453         PicoDrawSetOutFormat(PDF_RGB555, 0);\r
454         // emu_video_mode_change will call pnd_setup_layer()\r
455 }\r
456 \r
457 void plat_show_cursor(int on)\r
458 {\r
459 }\r
460 \r
461 int plat_grab_cursor(int on)\r
462 {\r
463         return 0;\r
464 }\r
465 \r
466 int plat_has_wm(void)\r
467 {\r
468         return 0;\r
469 }\r
470 \r
471 void pemu_loop_prep(void)\r
472 {\r
473         // dirty buffers better go now than during gameplay\r
474         fflush(stdout);\r
475         fflush(stderr);\r
476         sync();\r
477         sleep(0);\r
478 }\r
479 \r
480 void pemu_loop_end(void)\r
481 {\r
482         memset(fb_copy, 0, sizeof(fb_copy));\r
483         /* do one more frame for menu bg */\r
484         pemu_forced_frame(0, 1);\r
485 \r
486         pnd_setup_layer(0, g_layer_x, g_layer_y, g_layer_w, g_layer_h);\r
487 }\r
488 \r
489 void plat_wait_till_us(unsigned int us_to)\r
490 {\r
491         unsigned int now;\r
492         signed int diff;\r
493 \r
494         now = plat_get_ticks_us();\r
495 \r
496         // XXX: need to check NOHZ\r
497         diff = (signed int)(us_to - now);\r
498         if (diff > 10000) {\r
499                 //printf("sleep %d\n", us_to - now);\r
500                 usleep(diff * 15 / 16);\r
501                 now = plat_get_ticks_us();\r
502                 //printf(" wake %d\n", (signed)(us_to - now));\r
503         }\r
504 /*\r
505         while ((signed int)(us_to - now) > 512) {\r
506                 spend_cycles(1024);\r
507                 now = plat_get_ticks_us();\r
508         }\r
509 */\r
510 }\r
511 \r
512 void *plat_mem_get_for_drc(size_t size)\r
513 {\r
514         return NULL;\r
515 }\r
516 \r
517 int plat_parse_arg(int argc, char *argv[], int *x)\r
518 {\r
519         return 1;\r
520 }\r
521 \r
522 void plat_early_init(void)\r
523 {\r
524 }\r
525 \r
526 void plat_init(void)\r
527 {\r
528         const char *main_fb_name, *layer_fb_name;\r
529         int fd, ret, w, h;\r
530 \r
531         main_fb_name = getenv("FBDEV_MAIN");\r
532         if (main_fb_name == NULL)\r
533                 main_fb_name = "/dev/fb0";\r
534 \r
535         layer_fb_name = getenv("FBDEV_LAYER");\r
536         if (layer_fb_name == NULL)\r
537                 layer_fb_name = "/dev/fb1";\r
538 \r
539         // must set the layer up first to be able to use it\r
540         fd = open(layer_fb_name, O_RDWR);\r
541         if (fd == -1) {\r
542                 fprintf(stderr, "%s: ", layer_fb_name);\r
543                 perror("open");\r
544                 exit(1);\r
545         }\r
546 \r
547         ret = pnd_setup_layer_(fd, 0, g_layer_x, g_layer_y, g_layer_w, g_layer_h);\r
548         close(fd);\r
549         if (ret != 0) {\r
550                 fprintf(stderr, "failed to set up layer, exiting.\n");\r
551                 exit(1);\r
552         }\r
553 \r
554         xenv_init(NULL, "PicoDrive " VERSION);\r
555 \r
556         w = h = 0;\r
557         main_fb = vout_fbdev_init(main_fb_name, &w, &h, 16, 2);\r
558         if (main_fb == NULL) {\r
559                 fprintf(stderr, "couldn't init fb: %s\n", main_fb_name);\r
560                 exit(1);\r
561         }\r
562 \r
563         g_menuscreen_w = g_menuscreen_pp = w;\r
564         g_menuscreen_h = h;\r
565         g_menuscreen_ptr = vout_fbdev_flip(main_fb);\r
566 \r
567         w = 320; h = 240;\r
568         layer_fb = vout_fbdev_init(layer_fb_name, &w, &h, 16, 4);\r
569         if (layer_fb == NULL) {\r
570                 fprintf(stderr, "couldn't init fb: %s\n", layer_fb_name);\r
571                 goto fail0;\r
572         }\r
573 \r
574         if (w != g_screen_width || h != g_screen_height) {\r
575                 fprintf(stderr, "%dx%d not supported on %s\n", w, h, layer_fb_name);\r
576                 goto fail1;\r
577         }\r
578         g_screen_ptr = vout_fbdev_flip(layer_fb);\r
579 \r
580         temp_frame = calloc(g_menuscreen_w * g_menuscreen_h * 2, 1);\r
581         if (temp_frame == NULL) {\r
582                 fprintf(stderr, "OOM\n");\r
583                 goto fail1;\r
584         }\r
585         g_menubg_ptr = temp_frame;\r
586         g_menubg_src_ptr = temp_frame;\r
587 \r
588         pnd_menu_init();\r
589 \r
590         // default ROM path\r
591         strcpy(rom_fname_loaded, "/media");\r
592 \r
593         in_evdev_init(&pandora_evdev_pdata);\r
594         in_probe();\r
595         plat_target_setup_input();\r
596 \r
597         sndout_oss_frag_frames = 2;\r
598 \r
599         return;\r
600 \r
601 fail1:\r
602         vout_fbdev_finish(layer_fb);\r
603 fail0:\r
604         vout_fbdev_finish(main_fb);\r
605         exit(1);\r
606 }\r
607 \r
608 void plat_finish(void)\r
609 {\r
610         vout_fbdev_finish(main_fb);\r
611         xenv_finish();\r
612 \r
613         printf("all done\n");\r
614 }\r
615 \r