diff --git a/software/sys_controller/av_controller.c b/software/sys_controller/av_controller.c index ee3320d..b7c15fd 100644 --- a/software/sys_controller/av_controller.c +++ b/software/sys_controller/av_controller.c @@ -70,6 +70,7 @@ tvp_input_t target_tvp; tvp_sync_input_t target_tvp_sync; alt_u8 target_type; alt_u8 update_cur_vm; +static alt_u8 glitch_dev_ctr; // "Sync glitch filt": consecutive deviating status reads while locked // Default settings const settings_t ts_default = { @@ -369,9 +370,37 @@ status_t get_status(tvp_sync_input_t syncinput) pcnt_field = (unsigned long)sc->fe_status2.pcnt_field; hsync_width = (unsigned long)sc->fe_status2.hsync_width; - clkcnt = (totlines>>!progressive) ? pcnt_field/(totlines>>!progressive) : 0; valid_mode = (pcnt_field > 0) && check_linecnt(progressive, totlines); + // Optional glitch filter: while locked, act on a changed measurement (line count, field period, hsync width, + // activity flag or validity) only once it has deviated on 3 consecutive status reads (30 ms, up to ~105 ms if + // the vsync wait above times out on every read); until then keep the previous values. Single-shot glitches + // (e.g. 868 -> 814 -> 868 lines seen on a NeXT SoG source) otherwise trigger a mode re-program or a + // sync-loss/re-lock cycle. Counting reads rather than requiring identical candidate values guarantees that a + // real change or loss is always accepted on the third read. + if (!tc.sync_glitch_filt || !cm.sync_active) { + glitch_dev_ctr = 0; + } else { + alt_u8 deviates = !sync_active || !valid_mode || + (totlines != cm.totlines) || (progressive != cm.progressive) || + (pcnt_field < (cm.pcnt_field - PCNT_TOLERANCE)) || (pcnt_field > (cm.pcnt_field + PCNT_TOLERANCE)) || + (abs(((int)hsync_width - (int)cm.hsync_width)) > HSYNC_WIDTH_TOLERANCE); + + if (!deviates) { + glitch_dev_ctr = 0; + } else if (glitch_dev_ctr < 2) { + glitch_dev_ctr++; + sync_active = 1; + valid_mode = 1; + totlines = cm.totlines; + progressive = cm.progressive; + pcnt_field = cm.pcnt_field; + hsync_width = cm.hsync_width; + } + } + + clkcnt = (totlines>>!progressive) ? pcnt_field/(totlines>>!progressive) : 0; + // Check sync activity if (!cm.sync_active && sync_active && valid_mode) { cm.sync_active = 1; diff --git a/software/sys_controller/inc/avconfig.h b/software/sys_controller/inc/avconfig.h index 11d54dd..6798d11 100644 --- a/software/sys_controller/inc/avconfig.h +++ b/software/sys_controller/inc/avconfig.h @@ -160,6 +160,7 @@ typedef struct { alt_u8 av3_alt_rgb; avinput_t link_av; alt_u8 pc_sog_coast; + alt_u8 sync_glitch_filt; } __attribute__((packed)) avconfig_t; int set_default_profile(int update_cc); diff --git a/software/sys_controller/src/menu.c b/software/sys_controller/src/menu.c index a7dbe83..4096d44 100644 --- a/software/sys_controller/src/menu.c +++ b/software/sys_controller/src/menu.c @@ -249,6 +249,7 @@ MENU(menu_compatibility, P99_PROTECT({ \ { "Panasonic hack", OPT_AVCONFIG_SELECTION, { .sel = { &tc.panasonic_hack, OPT_WRAP, SETTING_ITEM(off_on_desc) } } }, { "Disable PR2x", OPT_AVCONFIG_SELECTION, { .sel = { &tc.hdmi_pr2x_disable, OPT_WRAP, SETTING_ITEM(off_on_desc) } } }, { "PC SoG/CS coast", OPT_AVCONFIG_SELECTION, { .sel = { &tc.pc_sog_coast, OPT_WRAP, SETTING_ITEM(off_on_desc) } } }, + { "Sync glitch filt", OPT_AVCONFIG_SELECTION, { .sel = { &tc.sync_glitch_filt, OPT_WRAP, SETTING_ITEM(off_on_desc) } } }, })) MENU(menu_audio, P99_PROTECT({ \ diff --git a/software/sys_controller/src/userdata.c b/software/sys_controller/src/userdata.c index ad43c2c..8ed6449 100644 --- a/software/sys_controller/src/userdata.c +++ b/software/sys_controller/src/userdata.c @@ -145,6 +145,7 @@ const ude_item_map ude_profile_items[] = { UDE_ITEM(61, 122, tc.hdmi_ar), UDE_ITEM(61, 122, tc.hdmi_pr2x_disable), UDE_ITEM(68, 122, tc.pc_sog_coast), + UDE_ITEM(69, 122, tc.sync_glitch_filt), }; int write_userdata(uint8_t entry) {