diff --git a/Makefile b/Makefile
index c8b7e1a..de8048b 100644
--- a/Makefile
+++ b/Makefile
@@ -46,7 +46,7 @@ DEP = $(C_SRC:.c=.c.d) $(CPP_SRC:.cpp=.cpp.d)
DFLAGS = $(INCLUDE) -D_7ZIP_ST -DPACKAGE_VERSION=\"1.3.3\" -DFLAC_API_EXPORTS -DFLAC__HAS_OGG=0 -DHAVE_LROUND -DHAVE_STDINT_H -DHAVE_STDLIB_H -DHAVE_SYS_PARAM_H -DENABLE_64_BIT_WORDS=0 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE -DVDATE=\"`date +"%y%m%d"`\"
CFLAGS = $(DFLAGS) -Wall -Wextra -Wno-strict-aliasing -Wno-stringop-overflow -Wno-stringop-truncation -Wno-format-truncation -Wno-psabi -Wno-restrict -c -O3
-LFLAGS = -lc -lstdc++ -lm -lrt $(IMLIB2_LIB) -Llib/bluetooth -lbluetooth
+LFLAGS = -lc -lstdc++ -lm -lrt $(IMLIB2_LIB) -Llib/bluetooth -lbluetooth -lpthread
ifeq ($(PROFILING),1)
DFLAGS += -DPROFILING
diff --git a/MiSTer.vcxproj b/MiSTer.vcxproj
index 74b14cc..b157e17 100644
--- a/MiSTer.vcxproj
+++ b/MiSTer.vcxproj
@@ -73,13 +73,16 @@
+
+
+
@@ -142,13 +145,16 @@
+
+
+
diff --git a/MiSTer.vcxproj.filters b/MiSTer.vcxproj.filters
index c3e2751..7046df0 100644
--- a/MiSTer.vcxproj.filters
+++ b/MiSTer.vcxproj.filters
@@ -223,6 +223,15 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
@@ -432,5 +441,14 @@
Header Files
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
\ No newline at end of file
diff --git a/file_io.cpp b/file_io.cpp
index 464b4e1..7351734 100644
--- a/file_io.cpp
+++ b/file_io.cpp
@@ -1105,7 +1105,7 @@ void FindStorage(void)
device = 0;
cfg_parse();
device = saveddev;
- video_mode_load();
+ video_init();
user_io_send_buttons(1);
printf("Waiting for USB...\n");
diff --git a/fpga_io.cpp b/fpga_io.cpp
index 7045679..51c4811 100644
--- a/fpga_io.cpp
+++ b/fpga_io.cpp
@@ -15,6 +15,7 @@
#include "osd.h"
#include "menu.h"
#include "shmem.h"
+#include "offload.h"
#include "fpga_base_addr_ac5.h"
#include "fpga_manager.h"
@@ -617,6 +618,8 @@ void app_restart(const char *path, const char *xml)
input_switch(0);
input_uinp_destroy();
+ offload_stop();
+
char *appname = getappname();
printf("restarting the %s\n", appname);
execl(appname, appname, path, xml, NULL);
diff --git a/main.cpp b/main.cpp
index f9109d2..99c03eb 100644
--- a/main.cpp
+++ b/main.cpp
@@ -32,6 +32,7 @@ along with this program. If not, see .
#include "fpga_io.h"
#include "scheduler.h"
#include "osd.h"
+#include "offload.h"
const char *version = "$VER:" VDATE;
@@ -45,6 +46,8 @@ int main(int argc, char *argv[])
CPU_SET(1, &set);
sched_setaffinity(0, sizeof(set), &set);
+ offload_start();
+
fpga_io_init();
DISKLED_OFF;
diff --git a/offload.cpp b/offload.cpp
new file mode 100644
index 0000000..e25397d
--- /dev/null
+++ b/offload.cpp
@@ -0,0 +1,122 @@
+#include "offload.h"
+#include "profiling.h"
+#include
+#include
+#include
+#include
+
+static constexpr uint32_t QUEUE_SIZE = 8;
+
+static pthread_t s_thread_handle;
+static pthread_cond_t s_cond_work, s_cond_available;
+static pthread_mutex_t s_queue_lock;
+
+struct Work
+{
+ std::function handler;
+};
+
+static Work s_queue[QUEUE_SIZE];
+static uint32_t s_queue_head, s_queue_tail;
+static bool s_quit;
+
+static void *worker_thread(void *)
+{
+ while (true)
+ {
+ Work *current_work = nullptr;
+ // Wait for work
+ pthread_mutex_lock(&s_queue_lock);
+ if (s_queue_head == s_queue_tail)
+ {
+ // queue empty and quit flag set, exit
+ if (s_quit)
+ {
+ pthread_mutex_unlock(&s_queue_lock);
+ break;
+ }
+
+ // wait for work signal
+ pthread_cond_wait(&s_cond_work, &s_queue_lock);
+
+ // quit flag was set and queue still empty, quit
+ if (s_quit && (s_queue_head == s_queue_tail))
+ {
+ pthread_mutex_unlock(&s_queue_lock);
+ break;
+ }
+ }
+
+ // get work
+ current_work = &s_queue[s_queue_tail % QUEUE_SIZE];
+ pthread_mutex_unlock(&s_queue_lock);
+
+ // execute
+ current_work->handler();
+ current_work->handler = nullptr;
+
+ // lock and move tail forward
+ pthread_mutex_lock(&s_queue_lock);
+ s_queue_tail++;
+ pthread_cond_signal(&s_cond_available);
+ pthread_mutex_unlock(&s_queue_lock);
+ }
+ return (void *)0;
+}
+
+void offload_start()
+{
+ pthread_cond_init(&s_cond_available, nullptr);
+ pthread_cond_init(&s_cond_work, nullptr);
+ pthread_mutex_init(&s_queue_lock, nullptr);
+
+ s_queue_head = s_queue_tail = 0;
+ s_quit = false;
+
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+
+ // Set affinity to core #0 since main runs on core #1
+ cpu_set_t set;
+ CPU_ZERO(&set);
+ CPU_SET(0, &set);
+ pthread_attr_setaffinity_np(&attr, sizeof(set), &set);
+
+ pthread_create(&s_thread_handle, &attr, worker_thread, nullptr);
+}
+
+void offload_stop()
+{
+ pthread_mutex_lock(&s_queue_lock);
+
+ s_quit = true;
+ pthread_cond_signal(&s_cond_work);
+
+ pthread_mutex_unlock(&s_queue_lock);
+
+ printf("Waiting for offloaded work to finish...");
+ pthread_join(s_thread_handle, nullptr);
+ printf("Done\n");
+}
+
+void offload_add_work(std::function handler)
+{
+ PROFILE_FUNCTION();
+
+ pthread_mutex_lock(&s_queue_lock);
+
+ if ((s_queue_head - s_queue_tail) == QUEUE_SIZE)
+ {
+ pthread_cond_wait(&s_cond_available, &s_queue_lock);
+ }
+
+ Work *work = &s_queue[s_queue_head % QUEUE_SIZE];
+ work->handler = handler;
+
+ s_queue_head++;
+
+ pthread_cond_signal(&s_cond_work);
+
+ pthread_mutex_unlock(&s_queue_lock);
+}
\ No newline at end of file
diff --git a/offload.h b/offload.h
new file mode 100644
index 0000000..a31ec04
--- /dev/null
+++ b/offload.h
@@ -0,0 +1,12 @@
+#ifndef OFFLOAD_H
+#define OFFLOAD_H
+
+#include
+#include
+
+void offload_start();
+void offload_stop();
+
+void offload_add_work(std::function work);
+
+#endif
\ No newline at end of file
diff --git a/user_io.cpp b/user_io.cpp
index 34f0aca..d6e075a 100644
--- a/user_io.cpp
+++ b/user_io.cpp
@@ -1322,7 +1322,7 @@ void user_io_init(const char *path, const char *xml)
bootcore_init(xml ? xml : path);
}
- video_mode_load();
+ video_init();
if (strlen(cfg.font)) LoadFont(cfg.font);
load_volume();
diff --git a/video.cpp b/video.cpp
index 4642412..f15ed15 100644
--- a/video.cpp
+++ b/video.cpp
@@ -24,9 +24,11 @@
#include "smbus.h"
#include "str_util.h"
#include "profiling.h"
+#include "offload.h"
#include "support.h"
#include "lib/imlib2/Imlib2.h"
+#include "lib/md5/md5.h"
#define FB_SIZE (1920*1080)
#define FB_ADDR (0x20000000 + (32*1024*1024)) // 512mb + 32mb(Core's fb)
@@ -91,6 +93,7 @@ static vrr_cap_t vrr_modes[3] = {
static uint8_t last_vrr_mode = 0xFF;
static float last_vrr_rate = 0.0f;
+static uint32_t last_vrr_vfp = 0;
static uint8_t edid[256] = {};
struct vmode_t
@@ -173,6 +176,10 @@ struct vmode_custom_t
static_assert(sizeof(vmode_custom_param_t) == sizeof(vmode_custom_t::item));
+// Static fwd decl
+static void video_fb_config();
+static void video_calculate_cvt(int horiz_pixels, int vert_pixels, float refresh_rate, int reduced_blanking, vmode_custom_t *vmode);
+
static vmode_custom_t v_cur = {}, v_def = {}, v_pal = {}, v_ntsc = {};
static int vmode_def = 0, vmode_pal = 0, vmode_ntsc = 0;
@@ -190,8 +197,6 @@ static bool supports_vrr()
return video_version != 0;
}
-static void video_calculate_cvt(int horiz_pixels, int vert_pixels, float refresh_rate, int reduced_blanking, vmode_custom_t *vmode);
-
static uint32_t getPLLdiv(uint32_t div)
{
if (div & 1) return 0x20000 | (((div / 2) + 1) << 8) | (div / 2);
@@ -306,13 +311,25 @@ struct FilterPhase
static constexpr int N_PHASES = 256;
+struct VideoFilterDigest
+{
+ VideoFilterDigest() { memset(md5, 0, sizeof(md5)); }
+ bool operator!=(const VideoFilterDigest& other) { return memcmp(md5, other.md5, sizeof(md5)) != 0; }
+ bool operator==(const VideoFilterDigest& other) { return memcmp(md5, other.md5, sizeof(md5)) == 0; }
+
+ unsigned char md5[16];
+};
+
struct VideoFilter
{
bool is_adaptive;
FilterPhase phases[N_PHASES];
FilterPhase adaptive_phases[N_PHASES];
+ VideoFilterDigest digest;
};
+static VideoFilter scaler_flt_data[3];
+
static bool scale_phases(FilterPhase out_phases[N_PHASES], FilterPhase *in_phases, int in_count)
{
if (!in_count)
@@ -340,12 +357,16 @@ static bool scale_phases(FilterPhase out_phases[N_PHASES], FilterPhase *in_phase
static bool read_video_filter(int type, VideoFilter *out)
{
+ PROFILE_FUNCTION();
+
fileTextReader reader = {};
FilterPhase phases[512];
int count = 0;
bool is_adaptive = false;
int scale = 2;
+ memset(out, 0, sizeof(VideoFilter));
+
static char filename[1024];
snprintf(filename, sizeof(filename), COEFF_DIR"/%s", scaler_flt[type].filename);
@@ -385,29 +406,50 @@ static bool read_video_filter(int type, VideoFilter *out)
is_adaptive ? count / 2 : count,
is_adaptive ? "true" : "false" );
+ bool valid = false;
if (is_adaptive)
{
out->is_adaptive = true;
- bool valid = scale_phases(out->phases, phases, count / 2);
+ valid = scale_phases(out->phases, phases, count / 2);
valid = valid && scale_phases(out->adaptive_phases, phases + (count / 2), count / 2);
- return valid;
}
else if (count == 32 && !is_adaptive) // legacy
{
out->is_adaptive = false;
- return scale_phases(out->phases, phases, 16);
+ valid = scale_phases(out->phases, phases, 16);
}
else if (!is_adaptive)
{
out->is_adaptive = false;
- return scale_phases(out->phases, phases, count);
+ valid = scale_phases(out->phases, phases, count);
+ }
+ else
+ {
+ // Make a default NN filter in case of error
+ out->is_adaptive = false;
+ FilterPhase nn_phases[2] =
+ {
+ { .t = { 0, 256, 0, 0 } },
+ { .t = { 0, 0, 256, 0 } }
+ };
+ scale_phases(out->phases, nn_phases, 2);
+ valid = false;
}
- return false;
+ MD5Context ctx;
+ MD5Init(&ctx);
+ MD5Update(&ctx, (unsigned char *)&out->is_adaptive, sizeof(VideoFilter::is_adaptive));
+ MD5Update(&ctx, (unsigned char *)out->phases, sizeof(VideoFilter::phases));
+ MD5Update(&ctx, (unsigned char *)out->adaptive_phases, sizeof(VideoFilter::adaptive_phases));
+ MD5Final(out->digest.md5, &ctx);
+
+ return valid;
}
static void send_phases_legacy(int addr, const FilterPhase phases[N_PHASES])
{
+ PROFILE_FUNCTION();
+
for (int idx = 0; idx < N_PHASES; idx += 16)
{
const FilterPhase *p = &phases[idx];
@@ -421,6 +463,8 @@ static void send_phases_legacy(int addr, const FilterPhase phases[N_PHASES])
static void send_phases(int addr, const FilterPhase phases[N_PHASES], bool full_precision)
{
+ PROFILE_FUNCTION();
+
const int skip = full_precision ? 1 : 4;
const int shift = full_precision ? 0 : 1;
@@ -437,6 +481,8 @@ static void send_phases(int addr, const FilterPhase phases[N_PHASES], bool full_
}
}
+static VideoFilterDigest horiz_filter_digest, vert_filter_digest;
+
static void send_video_filters(const VideoFilter *horiz, const VideoFilter *vert, int ver)
{
PROFILE_FUNCTION();
@@ -445,25 +491,28 @@ static void send_video_filters(const VideoFilter *horiz, const VideoFilter *vert
const bool full_precision = (ver & 0x4) != 0;
+ const bool send_horiz = horiz_filter_digest != horiz->digest;
+ const bool send_vert = vert_filter_digest != vert->digest;
+
switch( ver & 0x3 )
{
case 1:
- send_phases_legacy(0, horiz->phases);
- send_phases_legacy(64, vert->phases);
+ if (send_horiz) send_phases_legacy(0, horiz->phases);
+ if (send_vert) send_phases_legacy(64, vert->phases);
break;
case 2:
- send_phases(0, horiz->phases, full_precision);
- send_phases(1, vert->phases, full_precision);
+ if (send_horiz) send_phases(0, horiz->phases, full_precision);
+ if (send_vert) send_phases(1, vert->phases, full_precision);
break;
case 3:
- send_phases(0, horiz->phases, full_precision);
- send_phases(1, vert->phases, full_precision);
+ if (send_horiz) send_phases(0, horiz->phases, full_precision);
+ if (send_vert) send_phases(1, vert->phases, full_precision);
- if (horiz->is_adaptive)
+ if (horiz->is_adaptive && send_horiz)
{
send_phases(2, horiz->adaptive_phases, full_precision);
}
- else if (vert->is_adaptive)
+ else if (vert->is_adaptive && send_vert)
{
send_phases(3, vert->adaptive_phases, full_precision);
}
@@ -472,6 +521,9 @@ static void send_video_filters(const VideoFilter *horiz, const VideoFilter *vert
break;
}
+ horiz_filter_digest = horiz->digest;
+ vert_filter_digest = vert->digest;
+
DisableIO();
}
@@ -494,29 +546,13 @@ static void set_vfilter(int force)
spi8(scaler_flt[0].mode);
DisableIO();
- VideoFilter horiz, vert;
+ int vert_flt;
+ if (current_video_info.interlaced) vert_flt = VFILTER_HORZ;
+ else if ((flt_flags & 0x30) && scaler_flt[VFILTER_SCAN].mode) vert_flt = VFILTER_SCAN;
+ else if (scaler_flt[VFILTER_VERT].mode) vert_flt = VFILTER_VERT;
+ else vert_flt = VFILTER_HORZ;
- //horizontal filter
- bool valid = read_video_filter(VFILTER_HORZ, &horiz);
- if (valid)
- {
- //vertical/scanlines filter
- int vert_flt;
- if (current_video_info.interlaced) vert_flt = VFILTER_HORZ;
- else if ((flt_flags & 0x30) && scaler_flt[VFILTER_SCAN].mode) vert_flt = VFILTER_SCAN;
- else if (scaler_flt[VFILTER_VERT].mode) vert_flt = VFILTER_VERT;
- else vert_flt = VFILTER_HORZ;
-
- if (!read_video_filter(vert_flt, &vert))
- {
- vert = horiz;
- valid = true;
- }
-
- send_video_filters(&horiz, &vert, flt_flags & 0xF);
- }
-
- if (!valid) spi_uio_cmd8(UIO_SET_FLTNUM, 0);
+ send_video_filters(&scaler_flt_data[VFILTER_HORZ], &scaler_flt_data[vert_flt], flt_flags & 0xF);
}
static void setScaler()
@@ -572,6 +608,7 @@ void video_set_scaler_coeff(int type, const char *name)
{
strcpy(scaler_flt[type].filename, name);
FileSaveConfig(scaler_cfg, &scaler_flt, sizeof(scaler_flt));
+ read_video_filter(type, &scaler_flt_data[type]);
setScaler();
user_io_send_buttons(1);
}
@@ -605,12 +642,12 @@ static void loadScalerCfg()
scaler_flt[VFILTER_SCAN].mode = 1;
}
- VideoFilter null;
- if (!read_video_filter(VFILTER_HORZ, &null)) memset(&scaler_flt[VFILTER_HORZ], 0, sizeof(scaler_flt[VFILTER_HORZ]));
- if (!read_video_filter(VFILTER_VERT, &null)) memset(&scaler_flt[VFILTER_VERT], 0, sizeof(scaler_flt[VFILTER_VERT]));
- if (!read_video_filter(VFILTER_SCAN, &null)) memset(&scaler_flt[VFILTER_SCAN], 0, sizeof(scaler_flt[VFILTER_SCAN]));
+ if (!read_video_filter(VFILTER_HORZ, &scaler_flt_data[VFILTER_HORZ])) memset(&scaler_flt[VFILTER_HORZ], 0, sizeof(scaler_flt[VFILTER_HORZ]));
+ if (!read_video_filter(VFILTER_VERT, &scaler_flt_data[VFILTER_VERT])) memset(&scaler_flt[VFILTER_VERT], 0, sizeof(scaler_flt[VFILTER_VERT]));
+ if (!read_video_filter(VFILTER_SCAN, &scaler_flt_data[VFILTER_SCAN])) memset(&scaler_flt[VFILTER_SCAN], 0, sizeof(scaler_flt[VFILTER_SCAN]));
}
+static char active_gamma_cfg[1024] = { 0 };
static char gamma_cfg[1024] = { 0 };
static char has_gamma = 0;
@@ -618,6 +655,8 @@ static void setGamma()
{
PROFILE_FUNCTION();
+ if (!memcmp(active_gamma_cfg, gamma_cfg, sizeof(gamma_cfg))) return;
+
fileTextReader reader = {};
static char filename[1024];
@@ -630,6 +669,7 @@ static void setGamma()
has_gamma = 1;
spi8(0);
DisableIO();
+
snprintf(filename, sizeof(filename), GAMMA_DIR"/%s", gamma_cfg + 1);
if (FileOpenTextReader(&reader, filename))
@@ -662,6 +702,7 @@ static void setGamma()
DisableIO();
spi_uio_cmd8(UIO_SET_GAMMA, gamma_cfg[0]);
}
+ memcpy(active_gamma_cfg, gamma_cfg, sizeof(gamma_cfg));
}
int video_get_gamma_en()
@@ -1026,22 +1067,9 @@ static void hdmi_config_set_spare(bool val)
}
}
-static void hdmi_config()
+static void hdmi_config_init()
{
- PROFILE_FUNCTION();
-
int ypbpr = cfg.ypbpr && cfg.direct_video;
- const uint8_t vic_mode = (uint8_t)v_cur.param.vic;
- uint8_t pr_flags;
-
- if (cfg.direct_video && is_menu()) pr_flags = 0; // automatic pixel repetition
- else if (v_cur.param.pr != 0) pr_flags = 0b01001000; // manual pixel repetition with 2x clock
- else pr_flags = 0b01000000; // manual pixel repetition
-
- uint8_t sync_invert = 0;
- if (v_cur.param.hpol == 0) sync_invert |= 1 << 5;
- if (v_cur.param.vpol == 0) sync_invert |= 1 << 6;
-
// address, value
uint8_t init_data[] = {
@@ -1078,7 +1106,7 @@ static void hdmi_config()
// DDR Input Edge falling [1]=0 (not using DDR atm).
// Output Colour Space RGB [0]=0.
- 0x17, (uint8_t)(0b00000010 | sync_invert), // Aspect ratio 16:9 [1]=1, 4:3 [1]=0
+ 0x17, 0b01100010, // Aspect ratio 16:9 [1]=1, 4:3 [1]=0, invert sync polarity
0x18, (uint8_t)(ypbpr ? 0x86 : (cfg.hdmi_limited & 1) ? 0x8D : (cfg.hdmi_limited & 2) ? 0x8E : 0x00), // CSC Scaling Factors and Coefficients for RGB Full->Limited.
0x19, (uint8_t)(ypbpr ? 0xDF : (cfg.hdmi_limited & 1) ? 0xBC : 0xFE), // Taken from table in ADV7513 Programming Guide.
@@ -1107,7 +1135,7 @@ static void hdmi_config()
0x2E, (uint8_t)(ypbpr ? 0x07 : 0x01),
0x2F, (uint8_t)(ypbpr ? 0xE7 : 0x00),
- 0x3B, pr_flags,
+ 0x3B, 0x0, // Automatic pixel repetition and VIC detection
0x48, 0b00001000, // [6]=0 Normal bus order!
@@ -1132,8 +1160,6 @@ static void hdmi_config()
| ((ypbpr || cfg.hdmi_limited) ? 0b0100 : 0b1000)), // [3:2] RGB Quantization range
// [1:0] Non-Uniform Scaled: 00 - None. 01 - Horiz. 10 - Vert. 11 - Both.
- 0x3C, vic_mode, // VIC
-
0x59, (uint8_t)(((ypbpr || cfg.hdmi_limited) ? 0x00 : 0x40) // [7:6] [YQ1 YQ0] YCC Quantization Range: b00 = Limited Range, b01 = Full Range
| (cfg.hdmi_game_mode ? 0x30 : 0x00)), // [5:4] IT Content Type b11 = Game, b00 = Graphics/None
// [3:0] Pixel Repetition Fields b0000 = No Repetition
@@ -1225,6 +1251,55 @@ static void hdmi_config()
}
}
+static uint8_t last_sync_invert = 0xff;
+static uint8_t last_pr_flags = 0xff;
+static uint8_t last_vic_mode = 0xff;
+
+static void hdmi_config_set_mode(vmode_custom_t *vm)
+{
+ PROFILE_FUNCTION();
+
+ const uint8_t vic_mode = (uint8_t)vm->param.vic;
+ uint8_t pr_flags;
+
+ if (cfg.direct_video && is_menu()) pr_flags = 0; // automatic pixel repetition
+ else if (vm->param.pr != 0) pr_flags = 0b01001000; // manual pixel repetition with 2x clock
+ else pr_flags = 0b01000000; // manual pixel repetition
+
+ uint8_t sync_invert = 0;
+ if (vm->param.hpol == 0) sync_invert |= 1 << 5;
+ if (vm->param.vpol == 0) sync_invert |= 1 << 6;
+
+ if (last_sync_invert == sync_invert && last_pr_flags == pr_flags && last_vic_mode == vic_mode) return;
+
+ // address, value
+ uint8_t init_data[] = {
+ 0x17, (uint8_t)(0b00000010 | sync_invert), // Aspect ratio 16:9 [1]=1, 4:3 [1]=0
+ 0x3B, pr_flags,
+ 0x3C, vic_mode, // VIC
+ };
+
+ int fd = i2c_open(0x39, 0);
+ if (fd >= 0)
+ {
+ for (uint i = 0; i < sizeof(init_data); i += 2)
+ {
+ int res = i2c_smbus_write_byte_data(fd, init_data[i], init_data[i + 1]);
+ if (res < 0) printf("i2c: write error (%02X %02X): %d\n", init_data[i], init_data[i + 1], res);
+ }
+
+ i2c_close(fd);
+ }
+ else
+ {
+ printf("*** ADV7513 not found on i2c bus! HDMI won't be available!\n");
+ }
+
+ last_pr_flags = pr_flags;
+ last_sync_invert = sync_invert;
+ last_vic_mode = vic_mode;
+}
+
static void edid_parse_cea_ext(uint8_t *cea)
{
uint8_t *data_block_end = cea + cea[2];
@@ -1319,8 +1394,6 @@ static int is_edid_valid()
static int get_active_edid()
{
- hdmi_config(); // required to get EDID
-
int fd = i2c_open(0x39, 0);
if (fd < 0)
{
@@ -1494,15 +1567,21 @@ static void set_vrr_mode()
if (cfg.vrr_mode == 0)
{
- hdmi_config_set_spd(0);
- hdmi_config_set_spare(0);
+ if (last_vrr_mode != 0)
+ {
+ hdmi_config_set_spd(0);
+ hdmi_config_set_spare(0);
+ }
+ last_vrr_mode = 0;
return;
}
if (current_video_info.vtimeh) vrateh /= current_video_info.vtimeh; else vrateh = 0;
if (cfg.vrr_vesa_framerate) vrateh = cfg.vrr_vesa_framerate;
- if (last_vrr_mode == cfg.vrr_mode && last_vrr_rate == vrateh) return;
+ if ((last_vrr_mode == cfg.vrr_mode) &&
+ (last_vrr_rate == vrateh) &&
+ (last_vrr_vfp == v_cur.param.vfp || cfg.vrr_mode != VRR_VESA)) return;
if (!is_edid_valid())
{
@@ -1651,19 +1730,16 @@ static void set_vrr_mode()
}
last_vrr_mode = cfg.vrr_mode;
last_vrr_rate = vrateh;
+ last_vrr_vfp = v_cur.param.vfp;
if (!supports_vrr() || cfg.vsync_adjust) use_vrr = 0;
}
-static char fb_reset_cmd[128] = {};
-static void set_video(vmode_custom_t *v, double Fpix)
+static void video_set_mode(vmode_custom_t *v, double Fpix)
{
PROFILE_FUNCTION();
- loadGammaCfg();
setGamma();
-
- loadScalerCfg();
setScaler();
v_cur = *v;
@@ -1758,39 +1834,10 @@ static void set_video(vmode_custom_t *v, double Fpix)
printf("Fpix=%f\n", v_cur.Fpix);
DisableIO();
- hdmi_config();
+ hdmi_config_set_mode(&v_cur);
- int fb_scale = cfg.fb_size;
+ video_fb_config();
- if (fb_scale <= 1)
- {
- if (((v_cur.item[1] * v_cur.item[5]) > FB_SIZE))
- fb_scale = 2;
- else
- fb_scale = 1;
- }
- else if (fb_scale == 3) fb_scale = 2;
- else if (fb_scale > 4) fb_scale = 4;
-
- const int fb_scale_x = fb_scale;
- const int fb_scale_y = v_cur.param.pr == 0 ? fb_scale : fb_scale * 2;
-
- fb_width = v_cur.item[1] / fb_scale_x;
- fb_height = v_cur.item[5] / fb_scale_y;
-
- brd_x = cfg.vscale_border / fb_scale_x;
- brd_y = cfg.vscale_border / fb_scale_y;
-
- if (fb_enabled) video_fb_enable(1, fb_num);
-
- {
- PROFILE_SCOPE("Write MiSTer_fb");
-
- sprintf(fb_reset_cmd, "echo %d %d %d %d %d >/sys/module/MiSTer_fb/parameters/mode", 8888, 1, fb_width, fb_height, fb_width * 4);
- system(fb_reset_cmd);
- }
-
- loadShadowMaskCfg();
setShadowMask();
}
@@ -1910,9 +1957,8 @@ static void fb_init()
spi_uio_cmd16(UIO_SET_FBUF, 0);
}
-void video_mode_load()
+static void video_mode_load()
{
- fb_init();
if (cfg.direct_video && cfg.vsync_adjust)
{
printf("Disabling vsync_adjust because of enabled direct video.\n");
@@ -1949,9 +1995,22 @@ void video_mode_load()
vmode_ntsc = store_custom_video_mode(cfg.video_conf_ntsc, &v_ntsc);
}
}
- set_video(&v_def, 0);
}
+void video_init()
+{
+ fb_init();
+ hdmi_config_init();
+ video_mode_load();
+
+ loadGammaCfg();
+ loadScalerCfg();
+ loadShadowMaskCfg();
+
+ video_set_mode(&v_def, 0);
+}
+
+
static int api1_5 = 0;
int hasAPI1_5()
{
@@ -2274,6 +2333,12 @@ void video_mode_adjust()
{
current_video_info = video_info;
+ show_video_info(&video_info, &v_cur);
+ }
+ force = false;
+
+ if (vid_changed && !is_menu())
+ {
if (cfg_has_video_sections())
{
cfg_parse();
@@ -2281,58 +2346,81 @@ void video_mode_adjust()
user_io_send_buttons(1);
}
- show_video_info(&video_info, &v_cur);
- video_scaling_adjust(&video_info, &v_cur);
- }
- force = false;
-
- if (vid_changed && !is_menu() && (cfg.vsync_adjust || cfg.vscale_mode >= 4))
- {
- const uint32_t vtime = video_info.vtime;
-
- printf("\033[1;33madjust_video_mode(%u): vsync_adjust=%d vscale_mode=%d.\033[0m\n", vtime, cfg.vsync_adjust, cfg.vscale_mode);
-
- vmode_custom_t new_mode;
- bool adjust = video_mode_select(vtime, &new_mode);
-
- video_resolution_adjust(&video_info, &new_mode);
-
- vmode_custom_t *v = &new_mode;
- double Fpix = 0;
- if (adjust)
+ if ((cfg.vsync_adjust || cfg.vscale_mode >= 4))
{
- 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)
+ const uint32_t vtime = video_info.vtime;
+
+ printf("\033[1;33madjust_video_mode(%u): vsync_adjust=%d vscale_mode=%d.\033[0m\n", vtime, cfg.vsync_adjust, cfg.vscale_mode);
+
+ vmode_custom_t new_mode;
+ bool adjust = video_mode_select(vtime, &new_mode);
+
+ video_resolution_adjust(&video_info, &new_mode);
+
+ vmode_custom_t *v = &new_mode;
+ double Fpix = 0;
+ if (adjust)
{
- printf("Estimated Fpix(%.4f MHz) is outside supported range. Canceling auto-adjust.\n", Fpix);
- Fpix = 0;
+ 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;
+ }
+
+ float hz = 100000000.0f / vtime;
+ if (cfg.refresh_min && hz < cfg.refresh_min)
+ {
+ printf("Estimated frame rate (%f Hz) is less than REFRESH_MIN(%f Hz). Canceling auto-adjust.\n", hz, cfg.refresh_min);
+ Fpix = 0;
+ }
+
+ if (cfg.refresh_max && hz > cfg.refresh_max)
+ {
+ printf("Estimated frame rate (%f Hz) is more than REFRESH_MAX(%f Hz). Canceling auto-adjust.\n", hz, cfg.refresh_max);
+ Fpix = 0;
+ }
}
- float hz = 100000000.0f / vtime;
- if (cfg.refresh_min && hz < cfg.refresh_min)
- {
- printf("Estimated frame rate (%f Hz) is less than REFRESH_MIN(%f Hz). Canceling auto-adjust.\n", hz, cfg.refresh_min);
- Fpix = 0;
- }
-
- if (cfg.refresh_max && hz > cfg.refresh_max)
- {
- printf("Estimated frame rate (%f Hz) is more than REFRESH_MAX(%f Hz). Canceling auto-adjust.\n", hz, cfg.refresh_max);
- Fpix = 0;
- }
+ video_set_mode(v, Fpix);
+ user_io_send_buttons(1);
+ force = true;
+ }
+ else if (cfg_has_video_sections()) // if we have video sections but aren't updating the resolution for other reasons, then do it here
+ {
+ video_set_mode(&v_def, 0);
+ user_io_send_buttons(1);
+ force = true;
+ }
+ else
+ {
+ set_vfilter(1); // force update filters in case interlacing changed
}
- set_video(v, Fpix);
- user_io_send_buttons(1);
- force = true;
+ video_scaling_adjust(&video_info, &v_cur);
}
else
{
- set_vfilter(0);
+ set_vfilter(0); // update filters if flags have changed
}
}
+static void fb_write_module_params()
+{
+ int width = fb_width;
+ int height = fb_height;
+ offload_add_work([=]
+ {
+ FILE *fp = fopen("/sys/module/MiSTer_fb/parameters/mode", "wt");
+ if (fp)
+ {
+ fprintf(fp, "%d %d %d %d %d\n", 8888, 1, width, height, width * 4);
+ fclose(fp);
+ }
+ });
+}
+
void video_fb_enable(int enable, int n)
{
PROFILE_FUNCTION();
@@ -2375,7 +2463,7 @@ void video_fb_enable(int enable, int n)
//printf("Linux frame buffer: %dx%d, stride = %d bytes\n", fb_width, fb_height, fb_width * 4);
if (!fb_num)
{
- system(fb_reset_cmd);
+ fb_write_module_params();
input_switch(0);
}
else
@@ -2414,6 +2502,37 @@ int video_fb_state()
return fb_enabled;
}
+
+static void video_fb_config()
+{
+ PROFILE_FUNCTION();
+
+ int fb_scale = cfg.fb_size;
+
+ if (fb_scale <= 1)
+ {
+ if (((v_cur.item[1] * v_cur.item[5]) > FB_SIZE))
+ fb_scale = 2;
+ else
+ fb_scale = 1;
+ }
+ else if (fb_scale == 3) fb_scale = 2;
+ else if (fb_scale > 4) fb_scale = 4;
+
+ const int fb_scale_x = fb_scale;
+ const int fb_scale_y = v_cur.param.pr == 0 ? fb_scale : fb_scale * 2;
+
+ fb_width = v_cur.item[1] / fb_scale_x;
+ fb_height = v_cur.item[5] / fb_scale_y;
+
+ brd_x = cfg.vscale_border / fb_scale_x;
+ brd_y = cfg.vscale_border / fb_scale_y;
+
+ if (fb_enabled) video_fb_enable(1, fb_num);
+
+ fb_write_module_params();
+}
+
static void draw_checkers()
{
volatile uint32_t* buf = fb_base + (FB_SIZE*menu_bgn);
diff --git a/video.h b/video.h
index 5172b2f..1aaf883 100644
--- a/video.h
+++ b/video.h
@@ -25,6 +25,8 @@ struct VideoInfo
bool rotated;
};
+void video_init();
+
int video_get_scaler_flt(int type);
void video_set_scaler_flt(int type, int n);
char* video_get_scaler_coeff(int type, int only_name = 1);
@@ -41,7 +43,6 @@ char* video_get_shadow_mask(int only_name = 1);
void video_set_shadow_mask(const char *name);
void video_loadPreset(char *name);
-void video_mode_load();
void video_mode_adjust();
int hasAPI1_5();