+// FIXME: pass real dimensions to blitters
+static void mlc_flip(void *src, int bpp, int stride)
+{
+ static int old_bpp;
+
+ // only pass pal to host if it's dirty
+ if (bpp <= 8 && mmsp2.v.dirty_pal) {
+ if (mmsp2.v.dirty_pal == DIRTY_PAL_MMSP2)
+ host_video_update_pal32(mmsp2.mlc_stl_pallt_d32);
+ else
+ host_video_update_pal16(mmsp2.mlcpalette);
+ mmsp2.v.dirty_pal = 0;
+ }
+
+ if (bpp != old_bpp) {
+ host_video_change_bpp(bpp);
+ old_bpp = bpp;
+ }
+
+ switch (bpp) {
+ case 4:
+ host_video_blit4(src, 320, 240, stride);
+ break;
+
+ case 8:
+ host_video_blit8(src, 320, 240, stride);
+ break;
+
+ case 16:
+ host_video_blit16(src, 320, 240, stride);
+ break;
+
+ case 24:
+ // TODO
+ break;
+ }
+}
+
+#define ts_add_nsec(ts, ns) { \
+ ts.tv_nsec += ns; \
+ if (ts.tv_nsec >= 1000000000) { \
+ ts.tv_sec++; \
+ ts.tv_nsec -= 1000000000; \
+ } \
+}
+
+static int fb_sync_thread_paused;
+
+static void *fb_sync_thread(void *arg)
+{
+ int invalid_fb_addr = 1;
+ int manual_refresh = 0;
+ int frame_counter = 0;
+ struct timespec ts;
+ int ret, wait_ret;
+
+ //ret = pthread_setschedprio(pthread_self(), -1);
+ //log("pthread_setschedprio %d\n", ret);
+ //ret = setpriority(PRIO_PROCESS, 0, -1);
+ //log("setpriority %d\n", ret);
+
+ ret = clock_gettime(CLOCK_REALTIME, &ts);
+ if (ret != 0) {
+ perror(PFX "clock_gettime");
+ exit(1);
+ }
+
+ while (1) {
+ u8 *gp2x_fb, *gp2x_fb_end;
+
+ ret = pthread_mutex_lock(&fb_mutex);
+ wait_ret = pthread_cond_timedwait(&fb_cond, &fb_mutex, &ts);
+ ret |= pthread_mutex_unlock(&fb_mutex);
+
+ if (ret != 0) {
+ err("fb_thread: mutex error: %d\n", ret);
+ sleep(1);
+ goto check_keys;
+ }
+ if (wait_ret != 0 && wait_ret != ETIMEDOUT) {
+ err("fb_thread: cond error: %d\n", wait_ret);
+ sleep(1);
+ goto check_keys;
+ }
+ if (fb_sync_thread_paused) {
+ ts_add_nsec(ts, 100000000);
+ goto check_keys;
+ }
+
+ if (wait_ret != ETIMEDOUT) {
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts_add_nsec(ts, 50000000);
+ manual_refresh++;
+ if (manual_refresh == 2)
+ dbg("fb_thread: switch to manual refresh\n");
+ } else {
+ ts_add_nsec(ts, 16666667);
+ if (manual_refresh > 1)
+ dbg("fb_thread: switch to auto refresh\n");
+ manual_refresh = 0;
+ }
+
+ gp2x_fb = uppermem_lookup(mmsp2.mlc_stl_adr, &gp2x_fb_end);
+ if (gp2x_fb == NULL || gp2x_fb + 320*240 * mmsp2.v.bpp / 8 > gp2x_fb_end) {
+ if (!invalid_fb_addr) {
+ err("fb_thread: %08x is out of range\n", mmsp2.mlc_stl_adr);
+ invalid_fb_addr = 1;
+ }
+ continue;
+ }
+
+ invalid_fb_addr = 0;
+ mlc_flip(gp2x_fb, mmsp2.v.bpp, mmsp2.v.stride);
+
+ frame_counter++;
+ if (frame_counter & 0x0f)
+ continue;
+
+check_keys:
+ // this is to check for kill key, in case main thread hung
+ // or something else went wrong.
+ pollux.btn_state = host_read_btns();
+ }
+}
+
+static void fb_thread_pause(void)
+{
+ fb_sync_thread_paused = 1;
+ // wait until it finishes last refresh
+ // that it might be doing now
+ usleep(10000);
+}
+
+static void fb_thread_resume(void)
+{
+ fb_sync_thread_paused = 0;
+}
+
+static u32 xread32_io_cmn(u32 a, u32 *handled)
+{
+ u32 d = 0;
+
+ *handled = 1;
+ switch (a) {
+ // Wiz stuff
+ case 0x402c: // MLCVSTRIDE0
+ case 0x4060: // MLCVSTRIDE1
+ d = pollux.v.stride;
+ break;
+ case 0x4038: // MLCADDRESS0
+ case 0x406c: // MLCADDRESS1
+ d = pollux.mlc_stl_adr;
+ break;
+ // wiz_lib reads:
+ // ???? ???? YXBA DURiLe ???? VdVuMS LR?? ????
+ // | GPIOC[31:16] | GPIOB[31:16] |
+ case 0xa058: // GPIOBPAD
+ d = (pollux.btn_state >> 1) & 0x0100;
+ d |= (pollux.btn_state << 1) & 0x0200;
+ d |= (pollux.btn_state >> 3) & 0x0080;
+ d |= (pollux.btn_state >> 5) & 0x0040;
+ d |= (pollux.btn_state >> 6) & 0x0c00;
+ d <<= 16;
+ d = ~d;
+ break;
+ case 0xa098: // GPIOCPAD
+ pollux.btn_state = host_read_btns();
+ d = (pollux.btn_state >> 8) & 0x00f0;
+ d |= (pollux.btn_state >> 1) & 0x0008;
+ d |= (pollux.btn_state << 2) & 0x0004;
+ d |= (pollux.btn_state >> 5) & 0x0002;
+ d |= (pollux.btn_state >> 2) & 0x0001;
+ d <<= 16;
+ d = ~d;
+ break;
+ default:
+ *handled = 0;
+ break;
+ }
+
+ return d;
+}
+