platform ps2, handle audio similar to psp
[picodrive.git] / platform / common / plat_sdl.c
CommitLineData
636d5f25 1/*
2 * PicoDrive
3 * (C) notaz, 2013
7bf552b5 4 * (C) irixxxx, 2020-2024
636d5f25 5 *
6 * This work is licensed under the terms of MAME license.
7 * See COPYING file in the top-level directory.
8 */
9
10#include <stdio.h>
11
12#include "../libpicofe/input.h"
758abbeb 13#include "../libpicofe/plat.h"
636d5f25 14#include "../libpicofe/plat_sdl.h"
15#include "../libpicofe/in_sdl.h"
16#include "../libpicofe/gl.h"
17#include "emu.h"
18#include "menu_pico.h"
19#include "input_pico.h"
b437951a 20#include "plat_sdl.h"
636d5f25 21#include "version.h"
22
57c5a5e5 23#include <pico/pico_int.h>
74e770b1 24
636d5f25 25static void *shadow_fb;
f8aaa200 26static int shadow_size;
d5d17782 27static struct area { int w, h; } area;
636d5f25 28
c12b126f 29static struct in_pdata in_sdl_platform_data;
4e3551a5 30
089f516d 31static int sound_rates[] = { 8000, 11025, 16000, 22050, 32000, 44100, 53000, -1 };
32struct plat_target plat_target = { .sound_rates = sound_rates };
58fc34b1 33
34#if defined __MIYOO__
35const char *plat_device = "miyoo";
36#elif defined __GCW0__
37const char *plat_device = "gcw0";
38#elif defined __RETROFW__
39const char *plat_device = "retrofw";
40#elif defined __DINGUX__
41const char *plat_device = "dingux";
42#else
43const char *plat_device = "";
44#endif
45
46int plat_parse_arg(int argc, char *argv[], int *x)
47{
48#if defined __OPENDINGUX__
49 if (*plat_device == '\0' && strcasecmp(argv[*x], "-device") == 0) {
50 plat_device = argv[++(*x)];
51 return 0;
52 }
53#endif
54 return 1;
55}
56
57void plat_early_init(void)
58{
59}
60
61int plat_target_init(void)
62{
63#if defined __ODBETA__
64 if (*plat_device == '\0') {
65 /* ODbeta should always have a device tree, get the model info from there */
66 FILE *f = fopen("/proc/device-tree/compatible", "r");
67 if (f) {
68 char buf[10];
69 int c = fread(buf, 1, sizeof(buf), f);
70 if (strncmp(buf, "gcw,", 4) == 0)
71 plat_device = "gcw0";
72 }
73 }
74#endif
75 return 0;
76}
77
78void plat_target_finish(void)
79{
80}
81
636d5f25 82/* YUV stuff */
83static int yuv_ry[32], yuv_gy[32], yuv_by[32];
84static unsigned char yuv_u[32 * 2], yuv_v[32 * 2];
f740428b 85static unsigned char yuv_y[256];
f821bb70 86static struct uyvy { uint32_t y:8; uint32_t vyu:24; } yuv_uyvy[65536];
636d5f25 87
88void bgr_to_uyvy_init(void)
89{
90 int i, v;
91
92 /* init yuv converter:
93 y0 = (int)((0.299f * r0) + (0.587f * g0) + (0.114f * b0));
94 y1 = (int)((0.299f * r1) + (0.587f * g1) + (0.114f * b1));
95 u = (int)(8 * 0.565f * (b0 - y0)) + 128;
96 v = (int)(8 * 0.713f * (r0 - y0)) + 128;
97 */
98 for (i = 0; i < 32; i++) {
99 yuv_ry[i] = (int)(0.299f * i * 65536.0f + 0.5f);
100 yuv_gy[i] = (int)(0.587f * i * 65536.0f + 0.5f);
101 yuv_by[i] = (int)(0.114f * i * 65536.0f + 0.5f);
102 }
103 for (i = -32; i < 32; i++) {
104 v = (int)(8 * 0.565f * i) + 128;
105 if (v < 0)
106 v = 0;
107 if (v > 255)
108 v = 255;
109 yuv_u[i + 32] = v;
110 v = (int)(8 * 0.713f * i) + 128;
111 if (v < 0)
112 v = 0;
113 if (v > 255)
114 v = 255;
115 yuv_v[i + 32] = v;
116 }
32feba74 117 // valid Y range seems to be 16..235
118 for (i = 0; i < 256; i++) {
119 yuv_y[i] = 16 + 219 * i / 32;
120 }
f740428b 121 // everything combined into one large array for speed
122 for (i = 0; i < 65536; i++) {
123 int r = (i >> 11) & 0x1f, g = (i >> 6) & 0x1f, b = (i >> 0) & 0x1f;
124 int y = (yuv_ry[r] + yuv_gy[g] + yuv_by[b]) >> 16;
125 yuv_uyvy[i].y = yuv_y[y];
57c5a5e5 126#if CPU_IS_LE
f740428b 127 yuv_uyvy[i].vyu = (yuv_v[r-y + 32] << 16) | (yuv_y[y] << 8) | yuv_u[b-y + 32];
57c5a5e5 128#else
129 yuv_uyvy[i].vyu = (yuv_v[b-y + 32] << 16) | (yuv_y[y] << 8) | yuv_u[r-y + 32];
130#endif
f740428b 131 }
636d5f25 132}
133
f8aaa200 134void rgb565_to_uyvy(void *d, const void *s, int w, int h, int pitch, int dpitch, int x2)
636d5f25 135{
09cab6d2 136 uint32_t *dst = d;
137 const uint16_t *src = s;
d5d17782 138 int i;
636d5f25 139
d5d17782 140 if (x2) while (h--) {
f8aaa200 141 for (i = w; i >= 4; src += 4, dst += 4, i -= 4)
d5d17782 142 {
143 struct uyvy *uyvy0 = yuv_uyvy + src[0], *uyvy1 = yuv_uyvy + src[1];
144 struct uyvy *uyvy2 = yuv_uyvy + src[2], *uyvy3 = yuv_uyvy + src[3];
57c5a5e5 145#if CPU_IS_LE
d5d17782 146 dst[0] = (uyvy0->y << 24) | uyvy0->vyu;
147 dst[1] = (uyvy1->y << 24) | uyvy1->vyu;
148 dst[2] = (uyvy2->y << 24) | uyvy2->vyu;
149 dst[3] = (uyvy3->y << 24) | uyvy3->vyu;
57c5a5e5 150#else
d5d17782 151 dst[0] = uyvy0->y | (uyvy0->vyu << 8);
152 dst[1] = uyvy1->y | (uyvy1->vyu << 8);
153 dst[2] = uyvy2->y | (uyvy2->vyu << 8);
154 dst[3] = uyvy3->y | (uyvy3->vyu << 8);
57c5a5e5 155#endif
d5d17782 156 }
f8aaa200 157 src += pitch - (w-i);
158 dst += (dpitch - 2*(w-i))/2;
d5d17782 159 } else while (h--) {
f8aaa200 160 for (i = w; i >= 4; src += 4, dst += 2, i -= 4)
d5d17782 161 {
162 struct uyvy *uyvy0 = yuv_uyvy + src[0], *uyvy1 = yuv_uyvy + src[1];
163 struct uyvy *uyvy2 = yuv_uyvy + src[2], *uyvy3 = yuv_uyvy + src[3];
57c5a5e5 164#if CPU_IS_LE
d5d17782 165 dst[0] = (uyvy1->y << 24) | uyvy0->vyu;
166 dst[1] = (uyvy3->y << 24) | uyvy2->vyu;
57c5a5e5 167#else
d5d17782 168 dst[0] = uyvy1->y | (uyvy0->vyu << 8);
169 dst[1] = uyvy3->y | (uyvy2->vyu << 8);
57c5a5e5 170#endif
d5d17782 171 }
f8aaa200 172 src += pitch - (w-i);
173 dst += (dpitch - (w-i))/2;
636d5f25 174 }
175}
176
69b7b264 177static int clear_buf_cnt, clear_stat_cnt;
178
f8aaa200 179static void resize_buffers(void)
180{
181 // make sure the shadow buffers are big enough in case of resize
182 if (shadow_size < g_menuscreen_w * g_menuscreen_h * 2) {
183 shadow_size = g_menuscreen_w * g_menuscreen_h * 2;
184 shadow_fb = realloc(shadow_fb, shadow_size);
185 g_menubg_ptr = realloc(g_menubg_ptr, shadow_size);
186 }
187}
188
d5d17782 189void plat_video_set_size(int w, int h)
190{
191 if (area.w != w || area.h != h) {
c989b8fe 192 area = (struct area) { w, h };
d5d17782 193 if (plat_sdl_change_video_mode(w, h, 0) < 0) {
194 // failed, revert to original resolution
c989b8fe 195 area = (struct area) { g_screen_width,g_screen_height };
d5d17782 196 plat_sdl_change_video_mode(g_screen_width, g_screen_height, 0);
d5d17782 197 }
198 if (!plat_sdl_overlay && !plat_sdl_gl_active) {
f8aaa200 199 g_screen_width = plat_sdl_screen->w;
200 g_screen_height = plat_sdl_screen->h;
201 g_screen_ppitch = plat_sdl_screen->pitch/2;
202 g_screen_ptr = plat_sdl_screen->pixels;
203 } else {
d5d17782 204 g_screen_width = w;
205 g_screen_height = h;
206 g_screen_ppitch = w;
d5d17782 207 }
208 }
209}
210
5aa57006 211void plat_video_set_shadow(int w, int h)
212{
213 g_screen_width = w;
214 g_screen_height = h;
215 g_screen_ppitch = w;
216 g_screen_ptr = shadow_fb;
217}
218
636d5f25 219void plat_video_flip(void)
220{
f8aaa200 221 resize_buffers();
222
636d5f25 223 if (plat_sdl_overlay != NULL) {
224 SDL_Rect dstrect =
225 { 0, 0, plat_sdl_screen->w, plat_sdl_screen->h };
636d5f25 226 SDL_LockYUVOverlay(plat_sdl_overlay);
f8aaa200 227 if (area.w <= plat_sdl_overlay->w && area.h <= plat_sdl_overlay->h)
228 rgb565_to_uyvy(plat_sdl_overlay->pixels[0], shadow_fb,
229 area.w, area.h, g_screen_ppitch,
230 plat_sdl_overlay->pitches[0]/2,
231 plat_sdl_overlay->w >= 2*area.w);
636d5f25 232 SDL_UnlockYUVOverlay(plat_sdl_overlay);
233 SDL_DisplayYUVOverlay(plat_sdl_overlay, &dstrect);
234 }
235 else if (plat_sdl_gl_active) {
9cdfc191 236 gl_flip(shadow_fb, g_screen_ppitch, g_screen_height);
636d5f25 237 }
238 else {
758abbeb 239 if (SDL_MUSTLOCK(plat_sdl_screen)) {
eb7ce29e 240 SDL_UnlockSurface(plat_sdl_screen);
758abbeb 241 SDL_Flip(plat_sdl_screen);
242 SDL_LockSurface(plat_sdl_screen);
243 } else
244 SDL_Flip(plat_sdl_screen);
f8aaa200 245 g_screen_ppitch = plat_sdl_screen->pitch/2;
758abbeb 246 g_screen_ptr = plat_sdl_screen->pixels;
247 plat_video_set_buffer(g_screen_ptr);
69b7b264 248 if (clear_buf_cnt) {
a1ef15a7 249 memset(g_screen_ptr, 0, plat_sdl_screen->pitch*plat_sdl_screen->h);
69b7b264 250 clear_buf_cnt--;
251 }
252 }
f8aaa200 253
254 // for overlay/gl modes buffer ptr may change on resize
255 if ((plat_sdl_overlay || plat_sdl_gl_active) &&
256 (g_screen_ptr != shadow_fb || g_screen_ppitch != g_screen_width)) {
257 g_screen_ppitch = g_screen_width;
258 g_screen_ptr = shadow_fb;
259 plat_video_set_buffer(g_screen_ptr);
260 }
69b7b264 261 if (clear_stat_cnt) {
262 unsigned short *d = (unsigned short *)g_screen_ptr + g_screen_ppitch * g_screen_height;
263 int l = g_screen_ppitch * 8;
264 memset((int *)(d - l), 0, l * 2);
265 clear_stat_cnt--;
636d5f25 266 }
267}
268
269void plat_video_wait_vsync(void)
270{
271}
272
69b7b264 273void plat_video_clear_status(void)
274{
275 clear_stat_cnt = 3; // do it thrice in case of triple buffering
276}
277
278void plat_video_clear_buffers(void)
279{
f8aaa200 280 if (plat_sdl_overlay || plat_sdl_gl_active)
74bd7040 281 memset(shadow_fb, 0, g_menuscreen_w * g_menuscreen_h * 2);
69b7b264 282 else {
a1ef15a7 283 memset(g_screen_ptr, 0, plat_sdl_screen->pitch*plat_sdl_screen->h);
69b7b264 284 clear_buf_cnt = 3; // do it thrice in case of triple buffering
285 }
286}
287
636d5f25 288void plat_video_menu_enter(int is_rom_loaded)
289{
758abbeb 290 if (SDL_MUSTLOCK(plat_sdl_screen))
291 SDL_UnlockSurface(plat_sdl_screen);
636d5f25 292}
293
294void plat_video_menu_begin(void)
295{
23e47196 296 plat_sdl_change_video_mode(g_menuscreen_w, g_menuscreen_h, 1);
f8aaa200 297 resize_buffers();
298 if (plat_sdl_overlay || plat_sdl_gl_active) {
299 g_menuscreen_pp = g_menuscreen_w;
636d5f25 300 g_menuscreen_ptr = shadow_fb;
301 }
302 else {
eb7ce29e
PC
303 if (SDL_MUSTLOCK(plat_sdl_screen))
304 SDL_LockSurface(plat_sdl_screen);
f8aaa200 305 g_menuscreen_pp = plat_sdl_screen->pitch / 2;
636d5f25 306 g_menuscreen_ptr = plat_sdl_screen->pixels;
307 }
308}
309
310void plat_video_menu_end(void)
311{
312 if (plat_sdl_overlay != NULL) {
313 SDL_Rect dstrect =
314 { 0, 0, plat_sdl_screen->w, plat_sdl_screen->h };
315
316 SDL_LockYUVOverlay(plat_sdl_overlay);
f8aaa200 317 if (g_menuscreen_w <= plat_sdl_overlay->w && g_menuscreen_h <= plat_sdl_overlay->h)
318 rgb565_to_uyvy(plat_sdl_overlay->pixels[0], shadow_fb,
319 g_menuscreen_w, g_menuscreen_h, g_menuscreen_pp,
320 plat_sdl_overlay->pitches[0]/2,
321 plat_sdl_overlay->w >= 2 * g_menuscreen_w);
636d5f25 322 SDL_UnlockYUVOverlay(plat_sdl_overlay);
323
324 SDL_DisplayYUVOverlay(plat_sdl_overlay, &dstrect);
325 }
326 else if (plat_sdl_gl_active) {
9cdfc191 327 gl_flip(g_menuscreen_ptr, g_menuscreen_pp, g_menuscreen_h);
636d5f25 328 }
329 else {
eb7ce29e
PC
330 if (SDL_MUSTLOCK(plat_sdl_screen))
331 SDL_UnlockSurface(plat_sdl_screen);
636d5f25 332 SDL_Flip(plat_sdl_screen);
333 }
334 g_menuscreen_ptr = NULL;
636d5f25 335}
336
337void plat_video_menu_leave(void)
338{
339}
340
341void plat_video_loop_prepare(void)
342{
d5d17782 343 // take over any new vout settings
f8aaa200 344 plat_sdl_change_video_mode(0, 0, 0);
345 area.w = g_menuscreen_w, area.h = g_menuscreen_h;
346 resize_buffers();
347
ce79590a 348 // switch over to scaled output if available, but keep the aspect ratio
f8aaa200 349 if (plat_sdl_overlay || plat_sdl_gl_active) {
5aa57006 350 if (g_menuscreen_w * 240 >= g_menuscreen_h * 320) {
351 g_screen_width = (240 * g_menuscreen_w/g_menuscreen_h) & ~1;
352 g_screen_height= 240;
353 } else {
354 g_screen_width = 320;
355 g_screen_height= (320 * g_menuscreen_h/g_menuscreen_w) & ~1;
356 }
832faed3 357 g_screen_ppitch = g_screen_width;
b053cb20 358 g_screen_ptr = shadow_fb;
636d5f25 359 }
360 else {
f8aaa200 361 g_screen_width = plat_sdl_screen->w;
362 g_screen_height = plat_sdl_screen->h;
363 g_screen_ppitch = plat_sdl_screen->pitch/2;
b053cb20 364 if (SDL_MUSTLOCK(plat_sdl_screen))
365 SDL_LockSurface(plat_sdl_screen);
366 g_screen_ptr = plat_sdl_screen->pixels;
636d5f25 367 }
f8aaa200 368
d5d17782 369 plat_video_set_size(g_screen_width, g_screen_height);
f8aaa200 370 plat_video_set_buffer(g_screen_ptr);
636d5f25 371}
372
f8aaa200 373static void plat_sdl_resize(int w, int h)
374{
375 // take over new settings
5aa57006 376 if (plat_sdl_screen->w != area.w || plat_sdl_screen->h != area.h) {
83093596 377#if defined(__OPENDINGUX__)
0e7c5311 378 if (currentConfig.vscaling != EOPT_SCALE_HW &&
379 plat_sdl_screen->w == 320 &&
380 plat_sdl_screen->h == 480) {
381 g_menuscreen_h = 240;
382 g_menuscreen_w = 320;
383
384 } else
385#endif
386 {
387 g_menuscreen_h = plat_sdl_screen->h;
388 g_menuscreen_w = plat_sdl_screen->w;
389 }
390 resize_buffers();
5aa57006 391 rendstatus_old = -1;
392 }
f8aaa200 393}
394
bec84f92 395static void plat_sdl_quit(void)
396{
397 // for now..
398 exit(1);
399}
400
636d5f25 401void plat_init(void)
402{
636d5f25 403 int ret;
404
405 ret = plat_sdl_init();
406 if (ret != 0)
407 exit(1);
f2554438 408#if defined(__OPENDINGUX__)
758abbeb 409 // opendingux on JZ47x0 may falsely report a HW overlay, fix to window
410 plat_target.vout_method = 0;
411#endif
636d5f25 412
bec84f92 413 plat_sdl_quit_cb = plat_sdl_quit;
f8aaa200 414 plat_sdl_resize_cb = plat_sdl_resize;
bec84f92 415
f8aaa200 416 SDL_ShowCursor(0);
fc11dd05 417 SDL_WM_SetCaption("PicoDrive " VERSION, NULL);
636d5f25 418
9cdfc191 419 g_menuscreen_pp = g_menuscreen_w;
636d5f25 420 g_menuscreen_ptr = NULL;
421
422 shadow_size = g_menuscreen_w * g_menuscreen_h * 2;
423 if (shadow_size < 320 * 480 * 2)
424 shadow_size = 320 * 480 * 2;
425
7a7265ee 426 shadow_fb = calloc(1, shadow_size);
2d2e57b2 427 g_menubg_ptr = calloc(1, shadow_size);
636d5f25 428 if (shadow_fb == NULL || g_menubg_ptr == NULL) {
429 fprintf(stderr, "OOM\n");
430 exit(1);
431 }
432
433 g_screen_width = 320;
434 g_screen_height = 240;
9cdfc191 435 g_screen_ppitch = 320;
636d5f25 436 g_screen_ptr = shadow_fb;
437
58fc34b1 438 plat_target_setup_input();
c12b126f 439 in_sdl_platform_data.defbinds = in_sdl_defbinds,
b437951a 440 in_sdl_platform_data.kmap_size = in_sdl_key_map_sz,
58fc34b1 441 in_sdl_platform_data.key_map = in_sdl_key_map,
b437951a 442 in_sdl_platform_data.jmap_size = in_sdl_joy_map_sz,
58fc34b1 443 in_sdl_platform_data.joy_map = in_sdl_joy_map,
444 in_sdl_platform_data.key_names = in_sdl_key_names,
4e3551a5 445 in_sdl_init(&in_sdl_platform_data, plat_sdl_event_handler);
636d5f25 446 in_probe();
447
448 bgr_to_uyvy_init();
85174a6d 449 linux_menu_init();
636d5f25 450}
451
452void plat_finish(void)
453{
454 free(shadow_fb);
455 shadow_fb = NULL;
456 free(g_menubg_ptr);
457 g_menubg_ptr = NULL;
458 plat_sdl_finish();
459}