integrate M-HT's neon scalers
[fceu.git] / drivers / pandora / pandora.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7 #include <sys/ioctl.h>
8 #include <unistd.h>
9 #include <linux/input.h>
10 #include <linux/omapfb.h>
11
12 #include "../common/platform.h"
13 #include "../common/input.h"
14 #include "../common/settings.h"
15 #include "../common/main.h"
16 #include "../common/args.h"
17 #include "../../video.h"
18 #include "../arm/asmutils.h"
19 #include "../arm/neon_scale2x.h"
20 #include "../arm/neon_eagle2x.h"
21 #include "../libpicofe/input.h"
22 #include "../libpicofe/plat.h"
23 #include "../libpicofe/menu.h"
24 #include "../libpicofe/linux/in_evdev.h"
25 #include "../libpicofe/linux/fbdev.h"
26 #include "../libpicofe/linux/xenv.h"
27
28 static int g_layer_x, g_layer_y, g_layer_w, g_layer_h;
29 static struct vout_fbdev *main_fb, *layer_fb;
30 static void *layer_buf;
31 static int bounce_buf[320 * 241 / 4];
32 static unsigned short pal[256];
33 static unsigned int pal2[256]; // for neon_*2x
34
35 enum scaling {
36         SCALING_1X = 0,
37         SCALING_PROPORTIONAL,
38         SCALING_4_3,
39         SCALING_FULLSCREEN,
40 };
41
42 enum sw_filter {
43         SWFILTER_NONE = 0,
44         SWFILTER_SCALE2X,
45         SWFILTER_EAGLE2X,
46 };
47
48 static const struct in_default_bind in_evdev_defbinds[] = {
49   { KEY_UP,       IN_BINDTYPE_PLAYER12, NKEYB_UP },
50   { KEY_DOWN,     IN_BINDTYPE_PLAYER12, NKEYB_DOWN },
51   { KEY_LEFT,     IN_BINDTYPE_PLAYER12, NKEYB_LEFT },
52   { KEY_RIGHT,    IN_BINDTYPE_PLAYER12, NKEYB_RIGHT },
53   { KEY_PAGEDOWN, IN_BINDTYPE_PLAYER12, NKEYB_B },
54   { KEY_END,      IN_BINDTYPE_PLAYER12, NKEYB_A },
55   { KEY_HOME,     IN_BINDTYPE_PLAYER12, NKEYB_B_TURBO },
56   { KEY_PAGEUP,   IN_BINDTYPE_PLAYER12, NKEYB_A_TURBO },
57   { KEY_LEFTCTRL, IN_BINDTYPE_PLAYER12, NKEYB_SELECT },
58   { KEY_LEFTALT,  IN_BINDTYPE_PLAYER12, NKEYB_START },
59   { KEY_1,        IN_BINDTYPE_EMU, EACTB_SAVE_STATE },
60   { KEY_2,        IN_BINDTYPE_EMU, EACTB_LOAD_STATE },
61   { KEY_3,        IN_BINDTYPE_EMU, EACTB_PREV_SLOT },
62   { KEY_4,        IN_BINDTYPE_EMU, EACTB_NEXT_SLOT },
63   { KEY_5,        IN_BINDTYPE_EMU, EACTB_FDS_INSERT },
64   { KEY_6,        IN_BINDTYPE_EMU, EACTB_FDS_SELECT },
65   { KEY_7,        IN_BINDTYPE_EMU, EACTB_INSERT_COIN },
66   { KEY_SPACE,    IN_BINDTYPE_EMU, EACTB_ENTER_MENU },
67   { 0, 0, 0 }
68 };
69
70 static int omap_setup_layer_(int fd, int enabled, int x, int y, int w, int h)
71 {
72         struct omapfb_plane_info pi = { 0, };
73         struct omapfb_mem_info mi = { 0, };
74         int ret;
75
76         ret = ioctl(fd, OMAPFB_QUERY_PLANE, &pi);
77         if (ret != 0) {
78                 perror("QUERY_PLANE");
79                 return -1;
80         }
81
82         ret = ioctl(fd, OMAPFB_QUERY_MEM, &mi);
83         if (ret != 0) {
84                 perror("QUERY_MEM");
85                 return -1;
86         }
87
88         /* must disable when changing stuff */
89         if (pi.enabled) {
90                 pi.enabled = 0;
91                 ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
92                 if (ret != 0)
93                         perror("SETUP_PLANE");
94         }
95
96         if (mi.size < 256*2*240*2*3) {
97                 mi.size = 256*2*240*2*3;
98                 ret = ioctl(fd, OMAPFB_SETUP_MEM, &mi);
99                 if (ret != 0) {
100                         perror("SETUP_MEM");
101                         return -1;
102                 }
103         }
104
105         pi.pos_x = x;
106         pi.pos_y = y;
107         pi.out_width = w;
108         pi.out_height = h;
109         pi.enabled = enabled;
110
111         ret = ioctl(fd, OMAPFB_SETUP_PLANE, &pi);
112         if (ret != 0) {
113                 perror("SETUP_PLANE");
114                 return -1;
115         }
116
117         return 0;
118 }
119
120 static int omap_enable_layer(int enabled)
121 {
122         return omap_setup_layer_(vout_fbdev_get_fd(layer_fb), enabled,
123                 g_layer_x, g_layer_y, g_layer_w, g_layer_h);
124 }
125
126 void platform_init(void)
127 {
128         const char *main_fb_name, *layer_fb_name;
129         int fd, ret, w, h;
130
131         memset(&Settings, 0, sizeof(Settings));
132         Settings.frameskip = 0;
133         Settings.sound_rate = 44100;
134         Settings.turbo_rate_add = (8*2 << 24) / 60 + 1; // 8Hz turbofire
135         Settings.gamma = 100;
136         Settings.sstate_confirm = 1;
137         Settings.scaling = SCALING_4_3;
138
139         main_fb_name = getenv("FBDEV_MAIN");
140         if (main_fb_name == NULL)
141                 main_fb_name = "/dev/fb0";
142
143         layer_fb_name = getenv("FBDEV_LAYER");
144         if (layer_fb_name == NULL)
145                 layer_fb_name = "/dev/fb1";
146
147         // must set the layer up first to be able to use it
148         fd = open(layer_fb_name, O_RDWR);
149         if (fd == -1) {
150                 fprintf(stderr, "%s: ", layer_fb_name);
151                 perror("open");
152                 exit(1);
153         }
154
155         g_layer_x = 80, g_layer_y = 0;
156         g_layer_w = 640, g_layer_h = 480;
157
158         ret = omap_setup_layer_(fd, 0, g_layer_x, g_layer_y, g_layer_w, g_layer_h);
159         close(fd);
160         if (ret != 0) {
161                 fprintf(stderr, "failed to set up layer, exiting.\n");
162                 exit(1);
163         }
164
165         xenv_init(NULL, "fceu");
166
167         w = h = 0;
168         main_fb = vout_fbdev_init(main_fb_name, &w, &h, 16, 2);
169         if (main_fb == NULL) {
170                 fprintf(stderr, "couldn't init fb: %s\n", main_fb_name);
171                 exit(1);
172         }
173
174         g_menuscreen_w = w;
175         g_menuscreen_h = h;
176         g_menuscreen_ptr = vout_fbdev_flip(main_fb);
177
178         w = 256*2;
179         h = 240*2;
180         layer_fb = vout_fbdev_init(layer_fb_name, &w, &h, 16, 3);
181         if (layer_fb == NULL) {
182                 fprintf(stderr, "couldn't init fb: %s\n", layer_fb_name);
183                 goto fail0;
184         }
185         layer_buf = vout_fbdev_resize(layer_fb, 256, 240, 16,
186                 0, 0, 0, 0, 3);
187         if (layer_buf == NULL) {
188                 fprintf(stderr, "couldn't get layer buf\n");
189                 goto fail1;
190         }
191
192         plat_target_init();
193
194         XBuf = (void *)bounce_buf;
195
196         return;
197
198 fail1:
199         vout_fbdev_finish(layer_fb);
200 fail0:
201         vout_fbdev_finish(main_fb);
202         xenv_finish();
203         exit(1);
204 }
205
206 void platform_late_init(void)
207 {
208         in_evdev_init(in_evdev_defbinds);
209         in_probe();
210         plat_target_setup_input();
211 }
212
213 /* video */
214 void CleanSurface(void)
215 {
216         memset(bounce_buf, 0, sizeof(bounce_buf));
217         vout_fbdev_clear(layer_fb);
218 }
219
220 void KillVideo(void)
221 {
222 }
223
224 int InitVideo(void)
225 {
226         CleanSurface();
227
228         srendline = 0;
229         erendline = 239;
230
231         return 1;
232 }
233
234 // 16: rrrr rggg gg0b bbbb
235 void FCEUD_SetPalette(uint8 index, uint8 r, uint8 g, uint8 b)
236 {
237         unsigned int v = ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | (b >> 3);
238         pal[index] = v;
239         pal2[index] = v;
240 }
241
242 void FCEUD_GetPalette(uint8 index, uint8 *r, uint8 *g, uint8 *b)
243 {
244         unsigned int v = pal[index];
245         *r = (v >> 8) & 0xf8;
246         *g = (v >> 3) & 0xfc;
247         *b = v << 3;
248 }
249
250 void BlitPrepare(int skip)
251 {
252         unsigned char *s;
253         unsigned short *d;
254         int *p, i;
255
256         if (skip)
257                 return;
258
259         if (eoptions & EO_CLIPSIDES)
260         {
261                 int i, *p = bounce_buf + 32/4;
262                 for (i = 240; i; i--, p += 320/4)
263                 {
264                         p[0] = p[1] = p[62] = p[63] = 0;
265                 }
266         }
267
268         p = bounce_buf + 32/4;
269         if (srendline > 0)
270                 for (i = srendline; i > 0; i--, p += 320/4)
271                         memset(p, 0, 256);
272         if (erendline < 239)
273         {
274                 p = bounce_buf + erendline*320/4 + 32/4;
275                 for (i = 239-erendline; i > 0; i--, p += 320/4)
276                         memset(p, 0, 256);
277         }
278
279         /* this is called before throttle, so we blit here too */
280         s = (unsigned char *)bounce_buf + 32;
281         d = (unsigned short *)layer_buf;
282
283         switch (Settings.sw_filter)
284         {
285         case SWFILTER_SCALE2X:
286                 neon_scale2x_8_16(s, d, pal2, 256, 320, 256*2*2, 240);
287                 break;
288         case SWFILTER_EAGLE2X:
289                 neon_eagle2x_8_16(s, d, pal2, 256, 320, 256*2*2, 240);
290                 break;
291         case SWFILTER_NONE:
292         default:
293                 for (i = 0; i < 239; i++, d += 256, s += 320)
294                         do_clut(d, s, pal, 256);
295                 break;
296         }
297         
298         layer_buf = vout_fbdev_flip(layer_fb);
299 }
300
301 void BlitScreen(int skip)
302 {
303 }
304
305 /* throttle */
306 extern uint8 PAL;
307 extern int FSkip;
308 static struct timeval tv_expect;
309 static int skip_count = 0;
310 static int us_interval, us_interval1024;
311 #define MAX_LAG_FRAMES 3
312
313 void RefreshThrottleFPS(void)
314 {
315         skip_count = 0;
316         us_interval = PAL ? 20000 : 16667;
317         us_interval1024 = PAL ? 20000*1024 : 17066667;
318
319         vout_fbdev_wait_vsync(layer_fb);
320         gettimeofday(&tv_expect, 0);
321         tv_expect.tv_usec *= 1024;
322 }
323
324 void SpeedThrottle(void)
325 {
326         struct timeval now;
327         int diff;
328
329         gettimeofday(&now, 0);
330
331         // usec*1024 units to prevent drifting
332         tv_expect.tv_usec += us_interval1024;
333         if (tv_expect.tv_usec > 1000000 * 1024) {
334                 tv_expect.tv_usec -= 1000000 * 1024;
335                 tv_expect.tv_sec++;
336         }
337
338         diff = (tv_expect.tv_sec - now.tv_sec) * 1000000 +
339                 (tv_expect.tv_usec >> 10) - now.tv_usec;
340
341         if (Settings.frameskip >= 0)
342         {
343                 if (skip_count >= Settings.frameskip)
344                         skip_count = 0;
345                 else {
346                         skip_count++;
347                         FSkip = 1;
348                 }
349         }
350         else 
351         {
352                 FSkip = diff < -us_interval;
353         }
354
355         if (diff > MAX_LAG_FRAMES * us_interval
356             || diff < -MAX_LAG_FRAMES * us_interval)
357         {
358                 //printf("reset %d\n", diff);
359                 RefreshThrottleFPS();
360                 return;
361         }
362         else if (diff > us_interval)
363         {
364                 usleep(diff - us_interval);
365         }
366 }
367
368 /* called just before emulation */
369 void platform_apply_config(void)
370 {
371         static int old_layer_w, old_layer_h;
372         int fb_w = 256, fb_h = 240;
373         float mult;
374
375         if (Settings.sw_filter == SWFILTER_SCALE2X
376             || Settings.sw_filter == SWFILTER_EAGLE2X)
377         {
378                 fb_w *= 2;
379                 fb_h *= 2;
380         }
381
382         if (fb_w != old_layer_w || fb_h != old_layer_h)
383         {
384                 layer_buf = vout_fbdev_resize(layer_fb, fb_w, fb_h, 16,
385                                 0, 0, 0, 0, 3);
386                 if (layer_buf == NULL) {
387                         fprintf(stderr, "fatal: mode change %dx%x -> %dx%d failed\n",
388                                 old_layer_w, old_layer_h, fb_w, fb_h);
389                         exit(1);
390                 }
391
392                 old_layer_w = fb_w;
393                 old_layer_h = fb_h;
394         }
395
396         switch (Settings.scaling)
397         {
398         case SCALING_1X:
399                 g_layer_w = fb_w;
400                 g_layer_h = fb_h;
401                 break;
402         case SCALING_PROPORTIONAL:
403                 // assumes screen width > height
404                 mult = (float)g_menuscreen_h / fb_h;
405                 g_layer_w = (int)(fb_w * mult);
406                 g_layer_h = g_menuscreen_h;
407                 break;
408         case SCALING_FULLSCREEN:
409                 g_layer_w = g_menuscreen_w;
410                 g_layer_h = g_menuscreen_h;
411                 break;
412         case SCALING_4_3:
413         default:
414                 g_layer_w = g_menuscreen_h * 4 / 3;
415                 g_layer_h = g_menuscreen_h;
416                 break;
417         }
418         if (g_layer_w > g_menuscreen_w)
419                 g_layer_w = g_menuscreen_w;
420         if (g_layer_h > g_menuscreen_h)
421                 g_layer_h = g_menuscreen_h;
422         g_layer_x = g_menuscreen_w / 2 - g_layer_w / 2;
423         g_layer_y = g_menuscreen_h / 2 - g_layer_h / 2;
424
425         plat_target_set_filter(Settings.hw_filter);
426         plat_target_set_lcdrate(PAL);
427
428         // adjust since we run at 60Hz, and real NES doesn't
429         FCEUI_Sound(Settings.sound_rate + Settings.sound_rate / 980);
430
431         omap_enable_layer(1);
432 }
433
434 void platform_set_volume(int val)
435 {
436 }
437
438 void plat_video_menu_enter(int is_rom_loaded)
439 {
440         unsigned short *d = g_menubg_src_ptr;
441         unsigned char *s, *sr = (void *)bounce_buf;
442         int u, v = 240 / 2;
443         int x, y, w;
444
445         omap_enable_layer(0);
446
447         if (!fceugi)
448                 return;
449
450         for (y = 0; y < g_menuscreen_h; y++)
451         {
452                 s = sr + v * 320 + 32;
453                 u = 256 / 2;
454                 for (x = 0; x < g_menuscreen_w; )
455                 {
456                         w = g_menuscreen_w - x;
457                         if (w > 256 - u)
458                                 w = 256 - u;
459                         do_clut(d + x, s + u, pal, w);
460                         u = (u + w) & 255;
461                         x += w;
462                 }
463                 d += x;
464                 v++;
465                 if (v > erendlinev[PAL])
466                         v = srendlinev[PAL];
467         }
468 }
469
470 void plat_video_menu_begin(void)
471 {
472 }
473
474 void plat_video_menu_end(void)
475 {
476         g_menuscreen_ptr = vout_fbdev_flip(main_fb);
477 }
478
479 void plat_video_menu_leave(void)
480 {
481         memset(g_menuscreen_ptr, 0, g_menuscreen_w * g_menuscreen_h * 2);
482         g_menuscreen_ptr = vout_fbdev_flip(main_fb);
483         // layer enabled in platform_apply_config()
484 }
485
486 char *DriverUsage="";
487
488 ARGPSTRUCT DriverArgs[]={
489          {0,0,0,0}
490 };
491
492 void DoDriverArgs(void)
493 {
494 }
495
496 void GetBaseDirectory(char *BaseDirectory)
497 {
498         strcpy(BaseDirectory, "fceultra");
499 }
500
501 void platform_finish(void)
502 {
503         omap_enable_layer(0);
504         vout_fbdev_finish(layer_fb);
505         vout_fbdev_finish(main_fb);
506         xenv_finish();
507 }