#include #include #include #include #include #include #include #include #include #include #include #include #include #include "hardware.h" #include "user_io.h" #include "spi.h" #include "cfg.h" #include "file_io.h" #include "menu.h" #include "video.h" #include "input.h" #include "support.h" #include "lib/imlib2/Imlib2.h" #define FB_SIZE (1024*1024*8/4) // 8MB #define FB_ADDR (0x20000000 + (32*1024*1024)) // 512mb + 32mb(Core's fb) /* -- [2:0] : 011=8bpp(palette) 100=16bpp 101=24bpp 110=32bpp -- [3] : 0=16bits 565 1=16bits 1555 -- [4] : 0=RGB 1=BGR (for 16/24/32 modes) -- [5] : TBD */ #define FB_FMT_565 0b00100 #define FB_FMT_1555 0b01100 #define FB_FMT_888 0b00101 #define FB_FMT_8888 0b00110 #define FB_FMT_PAL8 0b00011 #define FB_FMT_RxB 0b10000 #define FB_EN 0x8000 #define FB_DV_LBRD 3 #define FB_DV_RBRD 6 #define FB_DV_UBRD 2 #define FB_DV_BBRD 2 static volatile uint32_t *fb_base = 0; static int fb_enabled = 0; static int fb_width = 0; static int fb_width_full = 0; static int fb_height = 0; static int fb_stride = 0; static int fb_num = 0; static int menu_bg = 0; static int menu_bgn = 0; struct vmode_t { uint32_t vpar[8]; double Fpix; }; vmode_t vmodes[] = { { { 1280, 110, 40, 220, 720, 5, 5, 20 }, 74.25 }, //0 { { 1024, 24, 136, 160, 768, 3, 6, 29 }, 65 }, //1 { { 720, 16, 62, 60, 480, 9, 6, 30 }, 27 }, //2 { { 720, 12, 64, 68, 576, 5, 5, 39 }, 27 }, //3 { { 1280, 48, 112, 248, 1024, 1, 3, 38 }, 108 }, //4 { { 800, 40, 128, 88, 600, 1, 4, 23 }, 40 }, //5 { { 640, 16, 96, 48, 480, 10, 2, 33 }, 25.175 }, //6 { { 1280, 440, 40, 220, 720, 5, 5, 20 }, 74.25 }, //7 { { 1920, 88, 44, 148, 1080, 4, 5, 36 }, 148.5 }, //8 { { 1920, 528, 44, 148, 1080, 4, 5, 36 }, 148.5 }, //9 { { 1366, 70, 143, 213, 768, 3, 3, 24 }, 85.5 }, //10 { { 1024, 40, 104, 144, 600, 1, 3, 18 }, 48.96 }, //11 }; #define VMODES_NUM (sizeof(vmodes) / sizeof(vmodes[0])) vmode_t tvmodes[] = { {{ 640, 16, 96, 48, 240, 4, 4, 14 }, 12.587 }, //NTSC 15K {{ 640, 16, 96, 48, 480, 8, 4, 33 }, 25.175 }, //NTSC 31K {{ 640, 16, 96, 48, 288, 6, 4, 14 }, 12.587 }, //PAL 15K {{ 640, 16, 96, 48, 576, 2, 4, 42 }, 25.175 }, //PAL 31K }; struct vmode_custom_t { uint32_t item[32]; double Fpix; }; static vmode_custom_t v_cur = {}, v_def = {}, v_pal = {}, v_ntsc = {}; static int vmode_def = 0, vmode_pal = 0, vmode_ntsc = 0; static uint32_t getPLLdiv(uint32_t div) { if (div & 1) return 0x20000 | (((div / 2) + 1) << 8) | (div / 2); return ((div / 2) << 8) | (div / 2); } static int findPLLpar(double Fout, uint32_t *pc, uint32_t *pm, double *pko) { uint32_t c = 1; while ((Fout*c) < 400) c++; while (1) { double fvco = Fout*c; uint32_t m = (uint32_t)(fvco / 50); double ko = ((fvco / 50) - m); fvco = ko + m; fvco *= 50.f; if (ko && (ko <= 0.05f || ko >= 0.95f)) { printf("Fvco=%f, C=%d, M=%d, K=%f ", fvco, c, m, ko); if (fvco > 1500.f) { printf("-> No exact parameters found\n"); return 0; } printf("-> K is outside allowed range\n"); c++; } else { *pc = c; *pm = m; *pko = ko; return 1; } } //will never reach here return 0; } static void setPLL(double Fout, vmode_custom_t *v) { double Fpix; double fvco, ko; uint32_t m, c; printf("Calculate PLL for %.4f MHz:\n", Fout); if (!findPLLpar(Fout, &c, &m, &ko)) { c = 1; while ((Fout*c) < 400) c++; fvco = Fout*c; m = (uint32_t)(fvco / 50); ko = ((fvco / 50) - m); //Make sure K is in allowed range. if (ko <= 0.05f) { ko = 0; } else if (ko >= 0.95f) { m++; ko = 0; } } uint32_t k = ko ? (uint32_t)(ko * 4294967296) : 1; fvco = ko + m; fvco *= 50.f; Fpix = fvco / c; printf("Fvco=%f, C=%d, M=%d, K=%f(%u) -> Fpix=%f\n", fvco, c, m, ko, k, Fpix); v->item[9] = 4; v->item[10] = getPLLdiv(m); v->item[11] = 3; v->item[12] = 0x10000; v->item[13] = 5; v->item[14] = getPLLdiv(c); v->item[15] = 9; v->item[16] = 2; v->item[17] = 8; v->item[18] = 7; v->item[19] = 7; v->item[20] = k; v->Fpix = Fpix; } static char scaler_flt_cfg[1024] = { 0 }; static char new_scaler = 0; static void setScaler() { fileTYPE f = {}; static char filename[1024]; if (!spi_uio_cmd_cont(UIO_SET_FLTNUM)) { DisableIO(); return; } new_scaler = 1; spi8(scaler_flt_cfg[0]); DisableIO(); sprintf(filename, COEFF_DIR"/%s", scaler_flt_cfg + 1); if (FileOpen(&f, filename)) { //printf("Read scaler coefficients\n"); char *buf = (char*)malloc(f.size+1); if (buf) { memset(buf, 0, f.size + 1); int size; if ((size = FileReadAdv(&f, buf, f.size))) { spi_uio_cmd_cont(UIO_SET_FLTCOEF); char *end = buf + size; char *pos = buf; int phase = 0; while (pos < end) { char *st = pos; while ((pos < end) && *pos && (*pos != 10)) pos++; *pos = 0; while (*st == ' ' || *st == '\t' || *st == 13) st++; if (*st == '#' || *st == ';' || !*st) pos++; else { int c0, c1, c2, c3; int n = sscanf(st, "%d,%d,%d,%d", &c0, &c1, &c2, &c3); if (n == 4) { //printf(" phase %c-%02d: %4d,%4d,%4d,%4d\n", (phase >= 16) ? 'V' : 'H', phase % 16, c0, c1, c2, c3); //printf("%03X: %03X %03X %03X %03X;\n",phase*4, c0 & 0x1FF, c1 & 0x1FF, c2 & 0x1FF, c3 & 0x1FF); spi_w((c0 & 0x1FF) | (((phase * 4) + 0) << 9)); spi_w((c1 & 0x1FF) | (((phase * 4) + 1) << 9)); spi_w((c2 & 0x1FF) | (((phase * 4) + 2) << 9)); spi_w((c3 & 0x1FF) | (((phase * 4) + 3) << 9)); phase++; if (phase >= 32) break; } } } DisableIO(); } free(buf); } } } int video_get_scaler_flt() { return new_scaler ? scaler_flt_cfg[0] : -1; } char* video_get_scaler_coeff() { return scaler_flt_cfg + 1; } static char scaler_cfg[128] = { 0 }; void video_set_scaler_flt(int n) { scaler_flt_cfg[0] = (char)n; FileSaveConfig(scaler_cfg, &scaler_flt_cfg, sizeof(scaler_flt_cfg)); spi_uio_cmd8(UIO_SET_FLTNUM, scaler_flt_cfg[0]); spi_uio_cmd(UIO_SET_FLTCOEF); } void video_set_scaler_coeff(char *name) { strcpy(scaler_flt_cfg + 1, name); FileSaveConfig(scaler_cfg, &scaler_flt_cfg, sizeof(scaler_flt_cfg)); setScaler(); user_io_send_buttons(1); } static void loadScalerCfg() { sprintf(scaler_cfg, "%s_scaler.cfg", user_io_get_core_name_ex()); if (!FileLoadConfig(scaler_cfg, &scaler_flt_cfg, sizeof(scaler_flt_cfg) - 1) || scaler_flt_cfg[0]>4) { memset(scaler_flt_cfg, 0, sizeof(scaler_flt_cfg)); } } static char gamma_cfg[1024] = { 0 }; static char has_gamma = 0; static void setGamma() { fileTYPE f = {}; static char filename[1024]; if (!spi_uio_cmd_cont(UIO_SET_GAMMA)) { DisableIO(); return; } has_gamma = 1; spi8(0); DisableIO(); sprintf(filename, GAMMA_DIR"/%s", gamma_cfg + 1); if (FileOpen(&f, filename)) { char *buf = (char*)malloc(f.size+1); if (buf) { memset(buf, 0, f.size + 1); int size; if ((size = FileReadAdv(&f, buf, f.size))) { spi_uio_cmd_cont(UIO_SET_GAMCURV); char *end = buf + size; char *pos = buf; int index = 0; while (pos < end) { char *st = pos; while ((pos < end) && *pos && (*pos != 10)) pos++; *pos = 0; while (*st == ' ' || *st == '\t' || *st == 13) st++; if (*st == '#' || *st == ';' || !*st) pos++; else { int c0, c1, c2; int n = sscanf(st, "%d,%d,%d", &c0, &c1, &c2); if (n == 1) { c1 = c0; c2 = c0; n = 3; } if (n == 3) { spi_w((index << 8) | (c0 & 0xFF)); spi_w((index << 8) | (c1 & 0xFF)); spi_w((index << 8) | (c2 & 0xFF)); index++; if (index >= 256) break; } } } DisableIO(); spi_uio_cmd8(UIO_SET_GAMMA, gamma_cfg[0]); } free(buf); } } } int video_get_gamma_en() { return has_gamma ? gamma_cfg[0] : -1; } char* video_get_gamma_curve() { return gamma_cfg + 1; } static char gamma_cfg_path[1024] = { 0 }; void video_set_gamma_en(int n) { gamma_cfg[0] = (char)n; FileSaveConfig(gamma_cfg_path, &gamma_cfg, sizeof(gamma_cfg)); setGamma(); } void video_set_gamma_curve(char *name) { strcpy(gamma_cfg + 1, name); FileSaveConfig(gamma_cfg_path, &gamma_cfg, sizeof(gamma_cfg)); setGamma(); user_io_send_buttons(1); } static void loadGammaCfg() { sprintf(gamma_cfg_path, "%s_gamma.cfg", user_io_get_core_name_ex()); if (!FileLoadConfig(gamma_cfg_path, &gamma_cfg, sizeof(gamma_cfg) - 1) || gamma_cfg[0]>1) { memset(gamma_cfg, 0, sizeof(gamma_cfg)); } } static char fb_reset_cmd[128] = {}; static void set_video(vmode_custom_t *v, double Fpix) { loadGammaCfg(); setGamma(); loadScalerCfg(); setScaler(); v_cur = *v; vmode_custom_t v_fix = v_cur; if (cfg.direct_video) { v_fix.item[2] = FB_DV_RBRD; v_fix.item[4] = FB_DV_LBRD; v_fix.item[1] += v_cur.item[2] - v_fix.item[2]; v_fix.item[1] += v_cur.item[4] - v_fix.item[4]; v_fix.item[6] = FB_DV_BBRD; v_fix.item[8] = FB_DV_UBRD;; v_fix.item[5] += v_cur.item[6] - v_fix.item[6]; v_fix.item[5] += v_cur.item[8] - v_fix.item[8]; } printf("Send HDMI parameters:\n"); spi_uio_cmd_cont(UIO_SET_VIDEO); printf("video: "); for (int i = 1; i <= 8; i++) { spi_w(v_fix.item[i]); printf("%d(%d), ", v_cur.item[i], v_fix.item[i]); } if(Fpix) setPLL(Fpix, &v_cur); printf("\nPLL: "); for (int i = 9; i < 21; i++) { printf("0x%X, ", v_cur.item[i]); if (i & 1) spi_w(v_cur.item[i] | ((i == 9 && Fpix && cfg.vsync_adjust == 2 && !is_menu_core()) ? 0x8000 : 0)); else { spi_w(v_cur.item[i]); spi_w(v_cur.item[i] >> 16); } } printf("Fpix=%f\n", v_cur.Fpix); DisableIO(); if (cfg.fb_size < 1) cfg.fb_size = (v_cur.item[1] < 1000) ? 1 : 2; else if (cfg.fb_size == 3) cfg.fb_size = 2; else if (cfg.fb_size > 4) cfg.fb_size = 4; fb_width = v_cur.item[1] / cfg.fb_size; fb_height = v_cur.item[5] / cfg.fb_size; fb_stride = ((fb_width * 4) + 255) & ~255; fb_width_full = fb_stride / 4; if (fb_enabled) video_fb_enable(1, fb_num); sprintf(fb_reset_cmd, "echo %d %d %d %d %d >/sys/module/MiSTer_fb/parameters/mode", 8888, 1, fb_width, fb_height, fb_stride); system(fb_reset_cmd); } static int parse_custom_video_mode(char* vcfg, vmode_custom_t *v) { int khz = 0; int cnt = 0; char *orig = vcfg; while (*vcfg) { char *next; if (cnt == 9 && v->item[0] == 1) { double Fpix = khz ? strtoul(vcfg, &next, 0)/1000.f : strtod(vcfg, &next); if (vcfg == next || (Fpix < 2.f || Fpix > 300.f)) { printf("Error parsing video_mode parameter: ""%s""\n", orig); return -1; } setPLL(Fpix, v); break; } uint32_t val = strtoul(vcfg, &next, 0); if (vcfg == next || (*next != ',' && *next)) { printf("Error parsing video_mode parameter: ""%s""\n", orig); return -1; } if (!cnt && val >= 100) { v->item[cnt++] = 1; khz = 1; } if (cnt < 32) v->item[cnt] = val; if (*next == ',') next++; vcfg = next; cnt++; } if (cnt == 1) { printf("Set predefined video_mode to %d\n", v->item[0]); return v->item[0]; } if ((v->item[0] == 0 && cnt < 21) || (v->item[0] == 1 && cnt < 9)) { printf("Incorrect amount of items in video_mode parameter: %d\n", cnt); return -1; } if (v->item[0] > 1) { printf("Incorrect video_mode parameter\n"); return -1; } return -2; } static int store_custom_video_mode(char* vcfg, vmode_custom_t *v) { int ret = parse_custom_video_mode(vcfg, v); if (ret == -2) return 1; uint mode = (ret < 0) ? 0 : ret; if (mode >= VMODES_NUM) mode = 0; for (int i = 0; i < 8; i++) v->item[i + 1] = vmodes[mode].vpar[i]; setPLL(vmodes[mode].Fpix, v); return ret >= 0; } static void fb_init() { if (!fb_base) { int fd = open("/dev/mem", O_RDWR | O_SYNC); if (fd == -1) return; fb_base = (volatile uint32_t*)mmap(0, FB_SIZE * 4 * 3, PROT_READ | PROT_WRITE, MAP_SHARED, fd, FB_ADDR); if (fb_base == (void *)-1) { printf("Unable to mmap FB!\n"); fb_base = 0; } close(fd); } spi_uio_cmd16(UIO_SET_FBUF, 0); } void video_mode_load() { fb_init(); if (cfg.direct_video && cfg.vsync_adjust) { printf("Disabling vsync_adjust because of enabled direct video.\n"); cfg.vsync_adjust = 0; } if (cfg.direct_video) { int mode = cfg.menu_pal ? 2 : 0; if (cfg.forced_scandoubler) mode++; v_def.item[0] = mode; for (int i = 0; i < 8; i++) v_def.item[i + 1] = tvmodes[mode].vpar[i]; setPLL(tvmodes[mode].Fpix, &v_def); vmode_def = 1; vmode_pal = 0; vmode_ntsc = 0; } else { vmode_def = store_custom_video_mode(cfg.video_conf, &v_def); vmode_pal = store_custom_video_mode(cfg.video_conf_pal, &v_pal); vmode_ntsc = store_custom_video_mode(cfg.video_conf_ntsc, &v_ntsc); } set_video(&v_def, 0); } static int api1_5 = 0; int hasAPI1_5() { return api1_5 || is_menu_core(); } static uint32_t show_video_info(int force) { uint32_t ret = 0; static uint16_t nres = 0; spi_uio_cmd_cont(UIO_GET_VRES); uint16_t res = spi_w(0); if ((nres != res) || force) { nres = res; uint32_t width = spi_w(0) | (spi_w(0) << 16); uint32_t height = spi_w(0) | (spi_w(0) << 16); uint32_t htime = spi_w(0) | (spi_w(0) << 16); uint32_t vtime = spi_w(0) | (spi_w(0) << 16); uint32_t ptime = spi_w(0) | (spi_w(0) << 16); uint32_t vtimeh = spi_w(0) | (spi_w(0) << 16); DisableIO(); float vrate = 100000000; if (vtime) vrate /= vtime; else vrate = 0; float hrate = 100000; if (htime) hrate /= htime; else hrate = 0; float prate = width * 100; prate /= ptime; printf("\033[1;33mINFO: Video resolution: %u x %u%s, fHorz = %.1fKHz, fVert = %.1fHz, fPix = %.2fMHz\033[0m\n", width, height, (res & 0x100) ? "i" : "", hrate, vrate, prate); printf("\033[1;33mINFO: Frame time (100MHz counter): VGA = %d, HDMI = %d\033[0m\n", vtime, vtimeh); if (vtimeh) api1_5 = 1; if (hasAPI1_5() && cfg.video_info) { static char str[128], res1[16], res2[16]; float vrateh = 100000000; if (vtimeh) vrateh /= vtimeh; else vrateh = 0; sprintf(res1, "%dx%d%s", width, height, (res & 0x100) ? "i" : ""); sprintf(res2, "%dx%d", v_cur.item[1], v_cur.item[5]); sprintf(str, "%9s %6.2fKHz %4.1fHz\n" \ "\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\x81\n" \ "%9s %6.2fMHz %4.1fHz", res1, hrate, vrate, res2, v_cur.Fpix, vrateh); Info(str, cfg.video_info * 1000); } uint32_t scrh = v_cur.item[5]; if (height && scrh) { if (cfg.vscale_border) { uint32_t border = cfg.vscale_border * 2; if ((border + 100) > scrh) border = scrh - 100; scrh -= border; } if (cfg.vscale_mode) { uint32_t div = 1 << (cfg.vscale_mode - 1); uint32_t mag = (scrh*div) / height; scrh = (height * mag) / div; } if(cfg.vscale_border || cfg.vscale_mode) { printf("Set vertical scaling to : %d\n", scrh); spi_uio_cmd16(UIO_SETHEIGHT, scrh); } else { spi_uio_cmd16(UIO_SETHEIGHT, 0); } } if (vtime && vtimeh) ret = vtime; minimig_set_adjust(2); } else { DisableIO(); } return ret; } void video_mode_adjust() { uint32_t vtime = show_video_info(0); if (vtime && cfg.vsync_adjust && !is_menu_core()) { printf("\033[1;33madjust_video_mode(%u): vsync_adjust=%d", vtime, cfg.vsync_adjust); int adjust = 1; vmode_custom_t *v = &v_def; if (vmode_pal || vmode_ntsc) { if (vtime > 1800000) { if (vmode_pal) { printf(", using PAL mode"); v = &v_pal; } else { printf(", PAL mode cannot be used. Using predefined NTSC mode"); v = &v_ntsc; adjust = 0; } } else { if (vmode_ntsc) { printf(", using NTSC mode"); v = &v_ntsc; } else { printf(", NTSC mode cannot be used. Using predefined PAL mode"); v = &v_pal; adjust = 0; } } } printf(".\033[0m\n"); double Fpix = 0; if (adjust) { Fpix = 100 * (v->item[1] + v->item[2] + v->item[3] + v->item[4]) * (v->item[5] + v->item[6] + v->item[7] + v->item[8]); Fpix /= vtime; if (Fpix < 2.f || Fpix > 300.f) { printf("Estimated Fpix(%.4f MHz) is outside supported range. Canceling auto-adjust.\n", Fpix); Fpix = 0; } } set_video(v, Fpix); user_io_send_buttons(1); usleep(100000); show_video_info(1); } } void video_fb_enable(int enable, int n) { if (fb_base) { if (cfg.direct_video) set_vga_fb(enable); int res = spi_uio_cmd_cont(UIO_SET_FBUF); if (res) { if (is_menu_core() && !enable && menu_bg) { enable = 1; n = menu_bgn; } if (enable) { uint32_t fb_addr = FB_ADDR + (FB_SIZE * 4 * n) + (n ? 0 : 4096); fb_num = n; int xoff = 0, yoff = 0; if (cfg.direct_video) { xoff = v_cur.item[4] - FB_DV_LBRD; yoff = v_cur.item[8] - FB_DV_UBRD; } printf("Switch to HPS frame buffer\n"); spi_w((uint16_t)(FB_EN | FB_FMT_RxB | FB_FMT_8888)); // format, enable flag spi_w((uint16_t)fb_addr); // base address low word spi_w(fb_addr >> 16); // base address high word spi_w(fb_width); // frame width spi_w(fb_height); // frame height spi_w(xoff); // scaled left spi_w(xoff + v_cur.item[1] - 1); // scaled right spi_w(yoff); // scaled top spi_w(yoff + v_cur.item[5] - 1); // scaled bottom printf("HPS frame buffer: %dx%d, stride = %d bytes\n", fb_width, fb_height, fb_stride); if (!fb_num) { system(fb_reset_cmd); input_switch(0); } else { input_switch(1); } } else { printf("Switch to core frame buffer\n"); spi_w(0); // enable flag input_switch(1); } fb_enabled = enable; } else { printf("Core doesn't support HPS frame buffer\n"); input_switch(1); } DisableIO(); if (is_menu_core()) user_io_8bit_set_status((fb_enabled && !fb_num) ? 0x160 : 0, 0x1E0); } } int video_fb_state() { if (is_menu_core()) { return fb_enabled && !fb_num; } return fb_enabled; } static void draw_checkers() { volatile uint32_t* buf = fb_base + (FB_SIZE*menu_bgn); uint32_t col1 = 0x888888; uint32_t col2 = 0x666666; int sz = fb_width/128; int pos = 0; for (int y = 0; y < fb_height; y++) { int c1 = (y / sz) & 1; pos = y * fb_width_full; for (int x = 0; x < fb_width; x++) { int c2 = c1 ^ ((x / sz) & 1); buf[pos++] = c2 ? col2 : col1; } } } static void draw_hbars1() { volatile uint32_t* buf = fb_base + (FB_SIZE*menu_bgn); int old_base = 0; int gray = 255; int sz = fb_height/7; int stp = 0; for (int y = 0; y < fb_height; y++) { int pos = y * fb_width_full; int base_color = ((7 * y) / fb_height)+1; if (old_base != base_color) { stp = sz; old_base = base_color; } gray = 255 * stp / sz; for (int x = 0; x < fb_width; x++) { uint32_t color = 0; if (base_color & 4) color |= gray; if (base_color & 2) color |= gray << 8; if (base_color & 1) color |= gray << 16; buf[pos++] = color; } stp--; if (stp < 0) stp = 0; } } static void draw_hbars2() { volatile uint32_t* buf = fb_base + (FB_SIZE*menu_bgn); for (int y = 0; y < fb_height; y++) { int pos = y * fb_width_full; int base_color = ((14 * y) / fb_height); int inv = base_color & 1; base_color >>= 1; base_color = (inv ? base_color : 6 - base_color) + 1; for (int x = 0; x < fb_width; x++) { int gray = (256 * x) / fb_width; if (inv) gray = 255 - gray; uint32_t color = 0; if (base_color & 4) color |= gray; if (base_color & 2) color |= gray << 8; if (base_color & 1) color |= gray << 16; buf[pos++] = color; } } } static void draw_vbars1() { volatile uint32_t* buf = fb_base + (FB_SIZE*menu_bgn); int sz = fb_width / 7; int stp = 0; for (int y = 0; y < fb_height; y++) { int pos = y * fb_width_full; int old_base = 0; int gray = 255; for (int x = 0; x < fb_width; x++) { int base_color = ((7 * x) / fb_width)+1; if (old_base != base_color) { stp = sz; old_base = base_color; } gray = 255 * stp / sz; uint32_t color = 0; if (base_color & 4) color |= gray; if (base_color & 2) color |= gray << 8; if (base_color & 1) color |= gray << 16; buf[pos++] = color; stp--; if (stp < 0) stp = 0; } } } static void draw_vbars2() { volatile uint32_t* buf = fb_base + (FB_SIZE*menu_bgn); for (int y = 0; y < fb_height; y++) { int pos = y * fb_width_full; for (int x = 0; x < fb_width; x++) { int gray = ((256 * y) / fb_height); int base_color = ((14 * x) / fb_width); int inv = base_color & 1; base_color >>= 1; base_color = (inv ? base_color : 6 - base_color) + 1; if (inv) gray = 255 - gray; uint32_t color = 0; if (base_color & 4) color |= gray; if (base_color & 2) color |= gray << 8; if (base_color & 1) color |= gray << 16; buf[pos++] = color; } } } static void draw_spectrum() { volatile uint32_t* buf = fb_base + (FB_SIZE*menu_bgn); for (int y = 0; y < fb_height; y++) { int pos = y * fb_width_full; int blue = ((256 * y) / fb_height); for (int x = 0; x < fb_width; x++) { int green = ((256 * x) / fb_width) - blue / 2; int red = 255 - green - blue / 2; if (red < 0) red = 0; if (green < 0) green = 0; buf[pos++] = (red<<16) | (green<<8) | blue; } } } static void draw_black() { volatile uint32_t* buf = fb_base + (FB_SIZE*menu_bgn); for (int y = 0; y < fb_height; y++) { int pos = y * fb_width_full; for (int x = 0; x < fb_width; x++) buf[pos++] = 0; } } static uint64_t getus() { struct timeval tv; gettimeofday(&tv, NULL); return (tv.tv_sec * 10000000) + tv.tv_usec; } static void vs_wait() { int fb = open("/dev/fb0", O_RDWR); int zero = 0; uint64_t t1, t2; if (ioctl(fb, FBIO_WAITFORVSYNC, &zero) == -1) { printf("fb ioctl failed: %s\n", strerror(errno)); close(fb); return; } t1 = getus(); ioctl(fb, FBIO_WAITFORVSYNC, &zero); t2 = getus(); close(fb); printf("vs_wait(us): %llu\n", t2 - t1); } static char *get_file_fromdir(const char* dir, int num, int *count) { static char name[256+32]; name[0] = 0; if(count) *count = 0; DIR *d = opendir(getFullPath(dir)); if (d) { int cnt = 0; struct dirent *de = readdir(d); while (de) { int len = strlen(de->d_name); if (len > 4 && (!strcasecmp(de->d_name + len - 4, ".png") || !strcasecmp(de->d_name + len - 4, ".jpg"))) { if (num == cnt) break; cnt++; } de = readdir(d); } if (de) { sprintf(name, "%s/%s", dir, de->d_name); } closedir(d); if(count) *count = cnt; } return name; } static Imlib_Image load_bg() { const char* bgdir = "wallpapers"; const char* fname = "menu.png"; if (!FileExists(fname)) { fname = "menu.jpg"; if (!FileExists(fname)) fname = 0; } if (!fname && PathIsDir(bgdir)) { int rndfd = open("/dev/urandom", O_RDONLY); if (rndfd >= 0) { uint32_t rnd; read(rndfd, &rnd, sizeof(rnd)); close(rndfd); int count = 0; get_file_fromdir(bgdir, -1, &count); if (count > 0) fname = get_file_fromdir(bgdir, rnd % count, &count); } } if (fname) { Imlib_Load_Error error = IMLIB_LOAD_ERROR_NONE; Imlib_Image img = imlib_load_image_with_error_return(getFullPath(fname), &error); if (img) return img; printf("Image %s loading error %d\n", fname, error); } return NULL; } static int bg_has_picture = 0; extern uint8_t _binary_logo_png_start[], _binary_logo_png_end[]; void video_menu_bg(int n, int idle) { bg_has_picture = 0; menu_bg = n; if (n) { printf("**** BG DEBUG START ****\n"); printf("n = %d\n", n); Imlib_Load_Error error; static Imlib_Image logo = 0; if (!logo) { unlink("/tmp/logo.png"); if (FileSave("/tmp/logo.png", _binary_logo_png_start, _binary_logo_png_end - _binary_logo_png_start)) { while(1) { error = IMLIB_LOAD_ERROR_NONE; if ((logo = imlib_load_image_with_error_return("/tmp/logo.png", &error))) break; else { if (error != IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT) { printf("logo.png error = %d\n", error); break; } } vs_wait(); }; if (cfg.osd_rotate) { imlib_context_set_image(logo); imlib_image_orientate(cfg.osd_rotate == 1 ? 3 : 1); } } else { printf("Fail to save to /tmp/logo.png\n"); } unlink("/tmp/logo.png"); printf("Logo = %p\n", logo); } menu_bgn = (menu_bgn == 1) ? 2 : 1; static Imlib_Image menubg = 0; static Imlib_Image bg1 = 0, bg2 = 0; if (!bg1) bg1 = imlib_create_image_using_data(fb_width_full, fb_height, (uint32_t*)(fb_base + (FB_SIZE * 1))); if (!bg1) printf("Warning: bg1 is 0\n"); if (!bg2) bg2 = imlib_create_image_using_data(fb_width_full, fb_height, (uint32_t*)(fb_base + (FB_SIZE * 2))); if (!bg2) printf("Warning: bg2 is 0\n"); Imlib_Image *bg = (menu_bgn == 1) ? &bg1 : &bg2; //printf("*bg = %p\n", *bg); static Imlib_Image curtain = 0; if (!curtain) { curtain = imlib_create_image(fb_width, fb_height); imlib_context_set_image(curtain); imlib_image_set_has_alpha(1); uint32_t *data = imlib_image_get_data(); int sz = fb_width * fb_height; for (int i = 0; i < sz; i++) { *data++ = 0x9F000000; } } switch (n) { case 1: if (!menubg) menubg = load_bg(); if (menubg) { imlib_context_set_image(menubg); int src_w = imlib_image_get_width(); int src_h = imlib_image_get_height(); printf("menubg: src_w=%d, src_h=%d\n", src_w, src_h); if (*bg) { imlib_context_set_image(*bg); imlib_blend_image_onto_image(menubg, 0, 0, 0, //int source_x, int source_y, src_w, src_h, //int source_width, int source_height, 0, 0, //int destination_x, int destination_y, fb_width, fb_height //int destination_width, int destination_height ); bg_has_picture = 1; break; } else { printf("*bg = 0!\n"); } } draw_checkers(); break; case 2: draw_hbars1(); break; case 3: draw_hbars2(); break; case 4: draw_vbars1(); break; case 5: draw_vbars2(); break; case 6: draw_spectrum(); break; case 7: draw_black(); break; } if (logo && !idle) { imlib_context_set_image(logo); int src_w = imlib_image_get_width(); int src_h = imlib_image_get_height(); printf("logo: src_w=%d, src_h=%d\n", src_w, src_h); int dst_w, dst_h; int dst_x, dst_y; if (cfg.osd_rotate) { dst_h = fb_height / 2; dst_w = src_w * dst_h / src_h; if (cfg.osd_rotate == 1) { dst_x = 0; dst_y = fb_height - dst_h; } else { dst_x = fb_width - dst_w; dst_y = 0; } } else { dst_x = 0; dst_y = 0; dst_w = fb_width * 2 / 7; dst_h = src_h * dst_w / src_w; } if (*bg) { imlib_context_set_image(*bg); imlib_blend_image_onto_image(logo, 1, 0, 0, //int source_x, int source_y, src_w, src_h, //int source_width, int source_height, dst_x, dst_y, //int destination_x, int destination_y, dst_w, dst_h //int destination_width, int destination_height ); } else { printf("*bg = 0!\n"); } } if (curtain) { if (idle > 1 && *bg) { imlib_context_set_image(*bg); imlib_blend_image_onto_image(curtain, 1, 0, 0, //int source_x, int source_y, fb_width, fb_height, //int source_width, int source_height, 0, 0, //int destination_x, int destination_y, fb_width, fb_height //int destination_width, int destination_height ); } } else { printf("curtain = 0!\n"); } //test the fb driver vs_wait(); printf("**** BG DEBUG END ****\n"); } video_fb_enable(0); } int video_bg_has_picture() { return bg_has_picture; } int video_chvt(int num) { static int cur_vt = 0; if (num) { cur_vt = num; int fd; if ((fd = open("/dev/tty0", O_RDONLY)) >= 0) { if (ioctl(fd, VT_ACTIVATE, cur_vt)) printf("ioctl VT_ACTIVATE fails\n"); if (ioctl(fd, VT_WAITACTIVE, cur_vt)) printf("ioctl VT_WAITACTIVE fails\n"); close(fd); } } return cur_vt ? cur_vt : 1; } void video_cmd(char *cmd) { if (video_fb_state()) { int accept = 0; int fmt = 0, rb = 0, div = -1, width = -1, height = -1; uint16_t hmin, hmax, vmin, vmax; if (sscanf(cmd, "fb_cmd0 %d %d %d", &fmt, &rb, &div) == 3) { if (div >= 1 && div <= 4) { width = v_cur.item[1] / div; height = v_cur.item[5] / div; hmin = vmin = 0; hmax = v_cur.item[1] - 1; vmax = v_cur.item[5] - 1; accept = 1; } } if (sscanf(cmd, "fb_cmd2 %d %d %d", &fmt, &rb, &div) == 3) { if (div >= 1 && div <= 4) { width = v_cur.item[1] / div; height = v_cur.item[5] / div; hmin = vmin = 0; hmax = v_cur.item[1] - 1; vmax = v_cur.item[5] - 1; accept = 1; } } if (sscanf(cmd, "fb_cmd1 %d %d %d %d", &fmt, &rb, &width, &height) == 4) { if (width < 120 || width > (int)v_cur.item[1]) width = v_cur.item[1]; if (height < 120 || height > (int)v_cur.item[5]) height = v_cur.item[5]; int divx = 1; int divy = 1; if (cfg.direct_video && (v_cur.item[5] < 300)) { // TV 240P/288P while ((width*(divx + 1)) <= (int)v_cur.item[1]) divx++; while ((height*(divy + 1)) <= (int)v_cur.item[5]) divy++; } else { while ((width*(divx + 1)) <= (int)v_cur.item[1] && (height*(divx + 1)) <= (int)v_cur.item[5]) divx++; divy = divx; } hmin = (uint16_t)((v_cur.item[1] - (width * divx)) / 2); vmin = (uint16_t)((v_cur.item[5] - (height * divy)) / 2); hmax = hmin + (width * divx) - 1; vmax = vmin + (height * divy) - 1; accept = 1; } int bpp = 0; int sc_fmt = 0; if (accept) { switch (fmt) { case 8888: bpp = 4; sc_fmt = FB_FMT_8888; break; case 1555: bpp = 2; sc_fmt = FB_FMT_1555; break; case 565: bpp = 2; sc_fmt = FB_FMT_565; break; case 8: bpp = 1; sc_fmt = FB_FMT_PAL8; rb = 0; break; default: accept = 0; } } if (rb) { sc_fmt |= FB_FMT_RxB; rb = 1; } if(accept) { int stride = ((width * bpp) + 255) & ~255; printf("fb_cmd: new mode: %dx%d => %dx%d color=%d stride=%d\n", width, height, hmax - hmin + 1, vmax - vmin + 1, fmt, stride); uint32_t addr = FB_ADDR + 4096; int xoff = 0, yoff = 0; if (cfg.direct_video) { xoff = v_cur.item[4] - FB_DV_LBRD; yoff = v_cur.item[8] - FB_DV_UBRD; } spi_uio_cmd_cont(UIO_SET_FBUF); spi_w(FB_EN | sc_fmt); // format, enable flag spi_w((uint16_t)addr); // base address low word spi_w(addr >> 16); // base address high word spi_w(width); // frame width spi_w(height); // frame height spi_w(xoff + hmin); // scaled left spi_w(xoff + hmax); // scaled right spi_w(yoff + vmin); // scaled top spi_w(yoff + vmax); // scaled bottom DisableIO(); if (cmd[6] != '2') { static char cmd[256]; sprintf(cmd, "echo %d %d %d %d %d >/sys/module/MiSTer_fb/parameters/mode", fmt, rb, width, height, stride); system(cmd); } } else { printf("video_cmd: unknown command or format.\n"); } } }