update video mode selection and processing

Updated mode detection to be based on new TVP7002 frontend, and integrated Pure-LM functionality and processing from OSSC Pro.
This commit is contained in:
marqs 2023-03-11 12:45:19 +02:00
parent 86c3f744ed
commit 0839e118ad
7 changed files with 1054 additions and 383 deletions

View File

@ -1,5 +1,5 @@
//
// Copyright (C) 2015-2022 Markus Hiienkari <mhiienka@niksula.hut.fi>
// Copyright (C) 2015-2023 Markus Hiienkari <mhiienka@niksula.hut.fi>
//
// This file is part of Open Source Scan Converter project.
//
@ -33,7 +33,6 @@
#include "sdcard.h"
#include "menu.h"
#include "avconfig.h"
#include "sysconfig.h"
#include "firmware.h"
#include "userdata.h"
#include "it6613.h"
@ -43,12 +42,9 @@
#include "sd_io.h"
#include "sys/alt_timestamp.h"
#define STABLE_THOLD 1
#define MIN_LINES_PROGRESSIVE 200
#define MIN_LINES_INTERLACED 400
#define SYNC_LOCK_THOLD 3
#define SYNC_LOSS_THOLD -5
#define STATUS_TIMEOUT 100000
#define STATUS_TIMEOUT_US 25000
#define PCNT_TOLERANCE 50
@ -57,7 +53,7 @@ alt_u32 sys_ctrl;
// Current mode
avmode_t cm;
extern mode_data_t video_modes[];
extern mode_data_t video_modes_plm[];
extern ypbpr_to_rgb_csc_t csc_coeffs[];
extern alt_u16 rc_keymap[REMOTE_MAX_KEYS];
extern alt_u16 rc_keymap_default[REMOTE_MAX_KEYS];
@ -66,11 +62,11 @@ extern alt_u32 btn_code, btn_code_prev;
extern alt_u8 remote_rpt, remote_rpt_prev;
extern avconfig_t tc, tc_default;
extern alt_u8 vm_sel;
extern char target_profile_name[PROFILE_NAME_LEN+1];
tvp_input_t target_tvp;
tvp_sync_input_t target_tvp_sync;
alt_u8 target_type;
alt_u8 stable_frames;
alt_u8 update_cur_vm;
alt_u8 profile_sel, profile_sel_menu, input_profiles[AV_LAST], lt_sel, def_input, profile_link, lcd_bl_timeout;
@ -84,9 +80,13 @@ avinput_t target_input;
alt_u8 pcm1862_active;
alt_u32 pclk_out;
uint8_t sl_def_iv_x, sl_def_iv_y;
alt_u32 read_it2(alt_u32 regaddr);
mode_data_t vmode_in, vmode_out;
vm_proc_config_t vm_conf;
// Manually (see cyiv-51005.pdf) or automatically (MIF/HEX from PLL megafunction) generated config may not
// provide fully correct scan chain data (e.g. mismatches in C3) and lead to incorrect PLL configuration.
// To get correct scan chain data, do the following:
@ -100,9 +100,12 @@ alt_u32 read_it2(alt_u32 regaddr);
// 8. Compare your MIF/HEX to the captured scan chain and update it accordingly
// 9. Dump the updated scan chain data to an array like below (last 16 bits are 0)
// 10. PLL can be then reconfigured with custom pll_reconfig as shown in program_mode()
const pll_config_t pll_configs[] = { {{0x0d806000, 0x00402010, 0x08040220, 0x00004022, 0x00000000}}, // 1x, 1x (default)
{{0x0dc06000, 0x00783c11, 0x070180e0, 0x0000180e, 0x00000000}}, // 2x, 5x
{{0x0d806000, 0x00301804, 0x02014060, 0x00001406, 0x00000000}} }; // 3x, 4x
const pll_config_t pll_configs[] = { {{0x0d806000, 0x00402010, 0x08800020, 0x00080002, 0x00000000}}, // 1x (default)
{{0x0d806000, 0x00402008, 0x04800020, 0x00080002, 0x00000000}}, // 2x (~20-40MHz)
{{0x0d806000, 0x00441c07, 0x02800020, 0x00080002, 0x00000000}}, // 3x (~20-40MHz)
{{0x0d806000, 0x00402004, 0x02800020, 0x00080002, 0x00000000}}, // 4x (~20-40MHz)
{{0x0d806000, 0x00441c05, 0x01800020, 0x00080002, 0x00000000}}, // 5x (~20-40MHz)
{{0x0e406000, 0x00281407, 0x02800020, 0x00080002, 0x00000000}} }; // 2x (~75MHz)
volatile sc_regs *sc = (volatile sc_regs*)SC_CONFIG_0_BASE;
volatile osd_regs *osd = (volatile osd_regs*)OSD_GENERATOR_0_BASE;
@ -150,7 +153,7 @@ inline void SetupAudio(tx_mode_t mode)
EnableAudioInfoFrame(FALSE, NULL);
if (mode != TX_DVI) {
EnableAudioOutput4OSSC(pclk_out, tc.audio_dw_sampl, tc.audio_swap_lr);
EnableAudioOutput4OSSC(cm.pclk_o_hz, tc.audio_dw_sampl, tc.audio_swap_lr);
HDMITX_SetAudioInfoFrame((BYTE)tc.audio_dw_sampl);
#ifdef DEBUG
Switch_HDMITX_Bank(1);
@ -177,7 +180,7 @@ inline void TX_enable(tx_mode_t mode)
EnableVideoOutput(cm.hdmitx_pclk_level ? PCLK_HIGH : PCLK_MEDIUM, COLOR_RGB444, (mode == TX_HDMI_YCBCR444) ? COLOR_YUV444 : COLOR_RGB444, (mode != TX_DVI));
if (mode != TX_DVI) {
HDMITX_SetAVIInfoFrame(cm.hdmitx_vic, (mode == TX_HDMI_RGB) ? F_MODE_RGB444 : F_MODE_YUV444, 0, 0, tc.hdmi_itc, cm.hdmitx_pixr_ifr);
HDMITX_SetAVIInfoFrame(vmode_out.vic, (mode == TX_HDMI_RGB) ? F_MODE_RGB444 : F_MODE_YUV444, 0, 0, tc.hdmi_itc, cm.hdmitx_pixr_ifr);
cm.cc.hdmi_itc = tc.hdmi_itc;
}
@ -227,8 +230,7 @@ void pll_reconfigure(alt_u8 id)
void set_lpf(alt_u8 lpf)
{
alt_u32 pclk;
pclk = (TVP_EXTCLK_HZ/cm.clkcnt)*video_modes[cm.id].h_total;
printf("PCLK_in: %luHz\n", pclk);
pclk = estimate_dotclk(&vmode_in, (TVP_EXTCLK_HZ/cm.clkcnt));
//Auto
if (lpf == 0) {
@ -247,7 +249,6 @@ void set_lpf(alt_u8 lpf)
ths_set_lpf(THS_LPF_16MHZ);
break;
case VIDEO_SDTV:
case VIDEO_LDTV:
default:
ths_set_lpf(THS_LPF_9MHZ);
break;
@ -283,102 +284,69 @@ inline int check_linecnt(alt_u8 progressive, alt_u32 totlines) {
return (totlines >= MIN_LINES_INTERLACED);
}
void set_sampler_phase(uint8_t sampler_phase) {
uint32_t sample_rng_x1000;
uint8_t tvp_phase;
vmode_in.sampler_phase = sampler_phase;
if (vm_conf.h_skip == 0) {
vm_conf.h_sample_sel = 0;
tvp_phase = sampler_phase;
} else {
sample_rng_x1000 = 360000 / (vm_conf.h_skip+1);
vm_conf.h_sample_sel = (sampler_phase*11250)/sample_rng_x1000;
tvp_phase = ((((sampler_phase*11250) % sample_rng_x1000)*32)/sample_rng_x1000);
}
if (vm_conf.h_skip > 0)
printf("Sample sel: %u/%u\n", (vm_conf.h_sample_sel+1), (vm_conf.h_skip+1));
tvp_set_hpll_phase(tvp_phase);
}
// Check if input video status / target configuration has changed
status_t get_status(tvp_sync_input_t syncinput)
{
alt_u32 data1, data2;
alt_u32 totlines, clkcnt, pcnt_frame;
alt_u8 progressive;
//alt_u8 refclk;
alt_u8 sync_active;
alt_u8 vsyncmode;
alt_u16 totlines_tvp;
alt_u16 h_samplerate;
status_t status;
static alt_8 act_ctr;
alt_u32 ctr;
int valid_linecnt;
alt_u8 h_mult;
status = NO_CHANGE;
alt_u8 progressive, sync_active, valid_linecnt;
status_t status = NO_CHANGE;
alt_timestamp_type start_ts = alt_timestamp();
// Wait until vsync active (avoid noise coupled to I2C bus on earlier prototypes)
for (ctr=0; ctr<STATUS_TIMEOUT; ctr++) {
if (sc->sc_status.vsync_flag) {
//printf("ctrval %u\n", ctr);
while (alt_timestamp() < start_ts + STATUS_TIMEOUT_US*(TIMER_0_FREQ/1000000)) {
if (IORD_ALTERA_AVALON_PIO_DATA(PIO_1_BASE) & VSYNC_FLAG_MASK)
break;
}
}
sync_active = tvp_check_sync(syncinput);
vsyncmode = cm.sync_active ? sc->sc_status.fpga_vsyncgen : 0;
//sync_active = tvp_check_sync(syncinput);
sync_active = sc->fe_status.sync_active;
// Read sync information from TVP7002 status registers
data1 = tvp_readreg(TVP_LINECNT1);
data2 = tvp_readreg(TVP_LINECNT2);
/*totlines = ((data2 & 0x0f) << 8) | data1;
progressive = !!(data2 & (1<<5));*/
totlines = sc->sc_status.vmax;
progressive = !sc->sc_status.interlace_flag;
cm.macrovis = !!(data2 & (1<<6));
data1 = tvp_readreg(TVP_CLKCNT1);
data2 = tvp_readreg(TVP_CLKCNT2);
//clkcnt = ((data2 & 0x0f) << 8) | data1;
pcnt_frame = (unsigned long)sc->sc_status2.pcnt_frame;
// Read sync information from TVP7002 frontend
totlines = sc->fe_status.vtotal;
progressive = !sc->fe_status.interlace_flag;
pcnt_frame = (unsigned long)sc->fe_status2.pcnt_frame;
clkcnt = pcnt_frame/(totlines>>!progressive);
// Read how many lines TVP7002 outputs in reality (valid only if output enabled)
/*totlines_tvp = sc->sc_status.vmax_tvp+1;
valid_linecnt = check_linecnt(progressive, totlines);
// NOTE: "progressive" may not have correct value if H-PLL is not locked (!cm.sync_active)
if ((vsyncmode == 0x2) || (!cm.sync_active && (totlines < MIN_LINES_INTERLACED))) {
progressive = 1;
} else if (vsyncmode == 0x1) {
progressive = 0;
totlines = totlines_tvp; //compensate skipped vsync
}*/
//valid_linecnt = check_linecnt(progressive, totlines);
valid_linecnt = 1;
// TVP7002 may randomly report "no sync" (especially with arcade boards),
// thus disable output only after N consecutive "no sync"-events
// Check sync activity
if (!cm.sync_active && sync_active && valid_linecnt) {
printf("Sync up in %d...\n", SYNC_LOCK_THOLD-act_ctr);
if (act_ctr >= SYNC_LOCK_THOLD) {
act_ctr = 0;
cm.sync_active = 1;
status = ACTIVITY_CHANGE;
} else {
act_ctr++;
}
cm.sync_active = 1;
status = ACTIVITY_CHANGE;
} else if (cm.sync_active && (!sync_active || !valid_linecnt)) {
printf("Sync down in %d...\n", act_ctr-SYNC_LOSS_THOLD);
if (act_ctr <= SYNC_LOSS_THOLD) {
act_ctr = 0;
cm.sync_active = 0;
status = ACTIVITY_CHANGE;
} else {
act_ctr--;
}
} else {
act_ctr = 0;
cm.sync_active = 0;
status = ACTIVITY_CHANGE;
}
if (valid_linecnt) {
// Line count reported in TVP7002 status registers is sometimes +-1 line off and may alternate with correct value. Ignore these events
if ((totlines != cm.totlines) ||
(progressive != cm.progressive) ||
(pcnt_frame < (cm.pcnt_frame - PCNT_TOLERANCE)) ||
(pcnt_frame > (cm.pcnt_frame + PCNT_TOLERANCE))) {
printf("totlines: %lu (cur) / %lu (prev), pcnt_frame: %lu (cur) / %lu (prev). VSM: %u\n", totlines, cm.totlines, pcnt_frame, cm.pcnt_frame, vsyncmode);
/*if (!cm.sync_active)
act_ctr = 0;*/
stable_frames = 0;
} else if (stable_frames != STABLE_THOLD) {
stable_frames++;
if (stable_frames == STABLE_THOLD)
status = (status < MODE_CHANGE) ? MODE_CHANGE : status;
printf("totlines: %lu (cur) / %lu (prev), pcnt_frame: %lu (cur) / %lu (prev)\n", totlines, cm.totlines, pcnt_frame, cm.pcnt_frame);
status = (status < MODE_CHANGE) ? MODE_CHANGE : status;
}
if ((tc.pm_240p != cm.cc.pm_240p) ||
@ -391,28 +359,23 @@ status_t get_status(tvp_sync_input_t syncinput)
(tc.l4_mode != cm.cc.l4_mode) ||
(tc.l5_mode != cm.cc.l5_mode) ||
(tc.l5_fmt != cm.cc.l5_fmt) ||
(tc.tvp_hpll2x != cm.cc.tvp_hpll2x) ||
(tc.upsample2x != cm.cc.upsample2x) ||
(tc.vga_ilace_fix != cm.cc.vga_ilace_fix) ||
(tc.ar_256col != cm.cc.ar_256col) ||
(tc.default_vic != cm.cc.default_vic) ||
(tc.clamp_offset != cm.cc.clamp_offset))
status = (status < MODE_CHANGE) ? MODE_CHANGE : status;
if ((tc.s480p_mode != cm.cc.s480p_mode) && (video_modes[cm.id].v_total == 525))
if ((tc.s480p_mode != cm.cc.s480p_mode) && (vmode_in.timings.v_total == 525))
status = (status < MODE_CHANGE) ? MODE_CHANGE : status;
if ((tc.s400p_mode != cm.cc.s400p_mode) && (video_modes[cm.id].v_total == 449))
if ((tc.s400p_mode != cm.cc.s400p_mode) && (vmode_in.timings.v_total == 449))
status = (status < MODE_CHANGE) ? MODE_CHANGE : status;
if (cm.pll_config != pll_reconfig->pll_config_status.c_config_id)
if ((vm_conf.si_pclk_mult > 1) && (pll_reconfig->pll_config_status.c_config_id != 5) && (vm_conf.si_pclk_mult-1 != pll_reconfig->pll_config_status.c_config_id))
status = (status < MODE_CHANGE) ? MODE_CHANGE : status;
if (update_cur_vm) {
cm.h_mult_total = (video_modes[cm.id].h_total*cm.sample_mult) + ((cm.sample_mult*video_modes[cm.id].h_total_adj*5 + 50) / 100);
tvp_setup_hpll(cm.h_mult_total, totlines, cm.cc.tvp_hpll2x && (video_modes[cm.id].flags & MODE_PLLDIVBY2));
cm.sample_sel = tvp_set_hpll_phase(video_modes[cm.id].sampler_phase, cm.sample_mult);
status = (status < SC_CONFIG_CHANGE) ? SC_CONFIG_CHANGE : status;
}
if (update_cur_vm)
status = (status < MODE_CHANGE) ? MODE_CHANGE : status;
cm.totlines = totlines;
cm.clkcnt = clkcnt;
@ -425,18 +388,17 @@ status_t get_status(tvp_sync_input_t syncinput)
(tc.sl_hybr_str != cm.cc.sl_hybr_str) ||
(tc.sl_method != cm.cc.sl_method) ||
(tc.sl_str != cm.cc.sl_str) ||
(tc.sl_cust_iv_x != cm.cc.sl_cust_iv_x) ||
(tc.sl_cust_iv_y != cm.cc.sl_cust_iv_y) ||
memcmp(tc.sl_cust_l_str, cm.cc.sl_cust_l_str, 5) ||
memcmp(tc.sl_cust_c_str, cm.cc.sl_cust_c_str, 6) ||
(tc.sl_altern != cm.cc.sl_altern) ||
(tc.sl_altiv != cm.cc.sl_altiv) ||
(tc.sl_id != cm.cc.sl_id) ||
(tc.h_mask != cm.cc.h_mask) ||
(tc.v_mask != cm.cc.v_mask) ||
(tc.mask_br != cm.cc.mask_br) ||
(tc.mask_color != cm.cc.mask_color) ||
(tc.ar_256col != cm.cc.ar_256col) ||
(tc.reverse_lpf != cm.cc.reverse_lpf) ||
(tc.panasonic_hack != cm.cc.panasonic_hack))
(tc.reverse_lpf != cm.cc.reverse_lpf))
status = (status < SC_CONFIG_CHANGE) ? SC_CONFIG_CHANGE : status;
if (tc.sync_vth != cm.cc.sync_vth)
@ -479,6 +441,12 @@ status_t get_status(tvp_sync_input_t syncinput)
if (pcm1862_active && (tc.audio_gain != cm.cc.audio_gain))
pcm_set_gain(tc.audio_gain-AUDIO_GAIN_0DB);
if (pcm1862_active && (tc.audio_mono != cm.cc.audio_mono)) {
DisableAudioOutput();
pcm_set_stereo_mode(tc.audio_mono);
SetupAudio(cm.cc.tx_mode);
}
#endif
cm.cc = tc;
@ -487,289 +455,244 @@ status_t get_status(tvp_sync_input_t syncinput)
return status;
}
void update_sc_config()
void update_sc_config(mode_data_t *vm_in, mode_data_t *vm_out, vm_proc_config_t *vm_conf, avconfig_t *avconfig)
{
int i;
hv_config_reg hv_in_config = {.data=0x00000000};
hv_config2_reg hv_in_config2 = {.data=0x00000000};
hv_config3_reg hv_in_config3 = {.data=0x00000000};
hv_config_reg hv_out_config = {.data=0x00000000};
hv_config2_reg hv_out_config2 = {.data=0x00000000};
hv_config3_reg hv_out_config3 = {.data=0x00000000};
xy_config_reg xy_out_config = {.data=0x00000000};
xy_config2_reg xy_out_config2 = {.data=0x00000000};
misc_config_reg misc_config = {.data=0x00000000};
sl_config_reg sl_config = {.data=0x00000000};
sl_config2_reg sl_config2 = {.data=0x00000000};
sl_config3_reg sl_config3 = {.data=0x00000000};
alt_u8 sl_no_altern = 0;
alt_u8 sl_l_overlay = 0, sl_c_overlay = 0;
alt_u32 sl_l_str_arr = 0, sl_c_str_arr = 0;
// Set input params
hv_in_config.h_total = vm_in->timings.h_total;
hv_in_config.h_active = vm_in->timings.h_active;
hv_in_config.h_synclen = vm_in->timings.h_synclen;
hv_in_config2.h_backporch = vm_in->timings.h_backporch;
hv_in_config2.v_active = vm_in->timings.v_active;
hv_in_config3.v_synclen = vm_in->timings.v_synclen;
hv_in_config3.v_backporch = vm_in->timings.v_backporch;
hv_in_config2.interlaced = vm_in->timings.interlaced;
hv_in_config3.v_startline = vm_in->timings.v_synclen+vm_in->timings.v_backporch+12;
hv_in_config3.h_skip = vm_conf->h_skip;
hv_in_config3.h_sample_sel = vm_conf->h_sample_sel;
alt_u8 h_opt_scale = cm.sample_mult;
alt_u16 h_opt_startoffs = 0;
alt_u16 h_synclen = video_modes[cm.id].h_synclen;
alt_u16 h_border, h_mask;
alt_u16 v_active = video_modes[cm.id].v_active;
alt_u16 v_backporch = video_modes[cm.id].v_backporch;
// Set output params
hv_out_config.h_total = vm_out->timings.h_total;
hv_out_config.h_active = vm_out->timings.h_active;
hv_out_config.h_synclen = vm_out->timings.h_synclen;
hv_out_config2.h_backporch = vm_out->timings.h_backporch;
hv_out_config2.v_total = vm_out->timings.v_total;
hv_out_config2.v_active = vm_out->timings.v_active;
hv_out_config3.v_synclen = vm_out->timings.v_synclen;
hv_out_config3.v_backporch = vm_out->timings.v_backporch;
hv_out_config2.interlaced = vm_out->timings.interlaced;
hv_out_config3.v_startline = vm_conf->framesync_line;
int i;
xy_out_config.x_size = vm_conf->x_size;
xy_out_config.y_size = vm_conf->y_size;
xy_out_config.y_offset = vm_conf->y_offset;
xy_out_config2.x_offset = vm_conf->x_offset;
xy_out_config2.x_start_lb = vm_conf->x_start_lb;
xy_out_config2.y_start_lb = vm_conf->y_start_lb;
xy_out_config2.x_rpt = vm_conf->x_rpt;
xy_out_config2.y_rpt = vm_conf->y_rpt;
// construct default scanline overlay
if ((cm.cc.sl_type == 0) || (cm.cc.sl_type == 2)) {
if (cm.cc.sl_altiv) {
sl_l_overlay = 1<<(cm.cc.sl_id);
misc_config.mask_br = avconfig->mask_br;
misc_config.mask_color = avconfig->mask_color;
misc_config.reverse_lpf = avconfig->reverse_lpf;
misc_config.lm_deint_mode = 0;
misc_config.nir_even_offset = 0;
misc_config.ypbpr_cs = (avconfig->ypbpr_cs == 0) ? ((vm_in->type & VIDEO_HDTV) ? 1 : 0) : avconfig->ypbpr_cs-1;
misc_config.vip_enable = 0;
misc_config.bfi_enable = 0;
misc_config.bfi_str = 0;
// set default/custom scanline interval
sl_def_iv_y = (vm_conf->y_rpt > 0) ? vm_conf->y_rpt : 1;
sl_def_iv_x = (vm_conf->x_rpt > 0) ? vm_conf->x_rpt : sl_def_iv_y;
sl_config3.sl_iv_x = ((avconfig->sl_type == 3) && (avconfig->sl_cust_iv_x)) ? avconfig->sl_cust_iv_x : sl_def_iv_x;
sl_config3.sl_iv_y = ((avconfig->sl_type == 3) && (avconfig->sl_cust_iv_y)) ? avconfig->sl_cust_iv_y : sl_def_iv_y;
// construct custom/default scanline overlay
for (i=0; i<6; i++) {
if (avconfig->sl_type == 3) {
sl_config.sl_l_str_arr |= ((avconfig->sl_cust_l_str[i]-1)&0xf)<<(4*i);
sl_config.sl_l_overlay |= (avconfig->sl_cust_l_str[i]!=0)<<i;
} else {
switch (cm.fpga_vmultmode) {
case FPGA_V_MULTMODE_3X:
sl_l_overlay = 1<<(2*(cm.cc.sl_id));
break;
case FPGA_V_MULTMODE_4X:
sl_l_overlay = 3<<(2*(cm.cc.sl_id));
break;
case FPGA_V_MULTMODE_5X:
sl_l_overlay = 3<<(3*(cm.cc.sl_id));
break;
default: //1x, 2x
sl_l_overlay = 1<<(cm.cc.sl_id);
break;
sl_config.sl_l_str_arr |= avconfig->sl_str<<(4*i);
if ((i==5) && ((avconfig->sl_type == 0) || (avconfig->sl_type == 2))) {
sl_config.sl_l_overlay = (1<<((sl_config3.sl_iv_y+1)/2))-1;
if (avconfig->sl_id)
sl_config.sl_l_overlay <<= (sl_config3.sl_iv_y+2)/2;
}
}
}
if ((cm.cc.sl_type == 1) || (cm.cc.sl_type == 2)) {
if (cm.sample_mult <= 4)
sl_c_overlay = (1<<((cm.sample_mult-1)*(cm.cc.sl_id)));
else
sl_c_overlay = (3<<((cm.sample_mult-2)*(cm.cc.sl_id)));
}
// construct custom scanline overlay and strength arrays
for (i=0; i<5; i++) {
if (cm.cc.sl_type == 3) {
sl_l_str_arr |= ((cm.cc.sl_cust_l_str[i]-1)&0xf)<<(4*i);
sl_l_overlay |= (cm.cc.sl_cust_l_str[i]!=0)<<i;
for (i=0; i<10; i++) {
if (avconfig->sl_type == 3) {
if (i<8)
sl_config2.sl_c_str_arr_l |= ((avconfig->sl_cust_c_str[i]-1)&0xf)<<(4*i);
else
sl_config3.sl_c_str_arr_h |= ((avconfig->sl_cust_c_str[i]-1)&0xf)<<(4*(i-8));
sl_config3.sl_c_overlay |= (avconfig->sl_cust_c_str[i]!=0)<<i;
} else {
sl_l_str_arr |= cm.cc.sl_str<<(4*i);
if (i<8)
sl_config2.sl_c_str_arr_l |= avconfig->sl_str<<(4*i);
else
sl_config3.sl_c_str_arr_h |= avconfig->sl_str<<(4*(i-8));
if ((i==9) && ((avconfig->sl_type == 1) || (avconfig->sl_type == 2)))
sl_config3.sl_c_overlay = (1<<((sl_config3.sl_iv_x+1)/2))-1;
}
}
for (i=0; i<6; i++) {
if (cm.cc.sl_type == 3) {
sl_c_str_arr |= ((cm.cc.sl_cust_c_str[i]-1)&0xf)<<(4*i);
sl_c_overlay |= (cm.cc.sl_cust_c_str[i]!=0)<<i;
} else {
sl_c_str_arr |= cm.cc.sl_str<<(4*i);
}
sl_config.sl_method = avconfig->sl_method;
sl_config.sl_altern = avconfig->sl_altern;
// disable scanlines if configured so
if (((avconfig->sl_mode == 1) && (!vm_conf->y_rpt)) || (avconfig->sl_mode == 0)) {
sl_config.sl_l_overlay = 0;
sl_config3.sl_c_overlay = 0;
}
// enable/disable alternating scanlines
if ((video_modes[cm.id].flags & MODE_INTERLACED) && (cm.fpga_vmultmode))
sl_no_altern = !cm.cc.sl_altern;
// oevrride scanline mode
if (cm.cc.sl_mode == 1) { //auto
//disable scanlines
if ((cm.fpga_vmultmode==0) || (video_modes[cm.id].group == GROUP_480P)) {
sl_l_overlay = 0;
sl_c_overlay = 0;
}
} else if (cm.cc.sl_mode == 0) { //off
//disable scanlines
sl_l_overlay = 0;
sl_c_overlay = 0;
}
switch (cm.target_lm) {
case MODE_L2_240x360:
h_opt_scale = 4;
break;
case MODE_L2_256_COL:
h_opt_scale = 6-2*cm.cc.ar_256col;
break;
case MODE_L3_320_COL:
h_opt_scale = 3;
break;
case MODE_L3_256_COL:
h_opt_scale = 4-cm.cc.ar_256col;
break;
case MODE_L3_240x360:
h_opt_scale = 6;
break;
case MODE_L4_256_COL:
h_opt_scale = 5-cm.cc.ar_256col;
break;
case MODE_L5_256_COL:
h_opt_scale = 6-cm.cc.ar_256col;
break;
default:
break;
}
if (cm.target_lm >= MODE_L5_GEN_4_3 && cm.cc.l5_fmt == L5FMT_1920x1080) {
v_active -= 24;
v_backporch += 12;
}
// CEA-770.3 HDTV modes use tri-level syncs which have twice the width of bi-level syncs of corresponding CEA-861 modes
if (target_type == VIDEO_HDTV)
h_synclen *= 2;
// 1920x* modes need short hsync
if (h_synclen > cm.hsync_cut)
h_synclen -= cm.hsync_cut;
else
h_synclen = 1;
h_border = (((cm.sample_mult-h_opt_scale)*video_modes[cm.id].h_active)/2);
h_mask = h_border + h_opt_scale*cm.cc.h_mask;
h_opt_startoffs = h_border + (cm.sample_mult-h_opt_scale)*(h_synclen+(alt_u16)video_modes[cm.id].h_backporch);
printf("h_border: %u, h_opt_startoffs: %u\n", h_border, h_opt_startoffs);
misc_config.h_multmode = cm.fpga_hmultmode;
//misc_config.h_l5fmt = (cm.cc.l5_fmt!=L5FMT_1600x1200);
misc_config.h_l3_240x360 = (cm.target_lm==MODE_L3_240x360);
hv_in_config.h_total = video_modes[cm.id].h_total;
hv_in_config.h_synclen = (cm.sample_mult*h_synclen);
hv_in_config2.h_backporch = (cm.sample_mult*(alt_u16)video_modes[cm.id].h_backporch);
hv_in_config.h_active = (cm.sample_mult*video_modes[cm.id].h_active);
//h_config2.h_mask = h_mask;
misc_config.h_opt_scale = h_opt_scale;
hv_in_config3.h_sample_sel = cm.sample_sel;
hv_in_config3.h_skip = cm.sample_mult;
misc_config.h_opt_startoff = h_opt_startoffs;
misc_config.v_multmode = cm.fpga_vmultmode;
//v_config.v_mask = cm.cc.v_mask;
hv_in_config3.v_synclen = video_modes[cm.id].v_synclen;
hv_in_config3.v_backporch = v_backporch;
hv_in_config2.v_active = v_active;
//hv_in_config2.v_total = v_total;
misc_config.rev_lpf_str = cm.cc.reverse_lpf;
misc_config.mask_br = cm.cc.mask_br;
misc_config.mask_color = cm.cc.mask_color;
misc_config.panasonic_hack = cm.cc.panasonic_hack;
sl_config.sl_l_str_arr = sl_l_str_arr;
sl_config.sl_l_overlay = sl_l_overlay;
sl_config.sl_hybr_str = cm.cc.sl_hybr_str;
sl_config.sl_method = cm.cc.sl_method;
sl_config.sl_no_altern = sl_no_altern;
sl_config2.sl_c_str_arr = sl_c_str_arr;
sl_config2.sl_c_overlay = sl_c_overlay;
sl_config2.sl_altiv = cm.cc.sl_altiv;
sc->hv_in_config = hv_in_config;
sc->hv_in_config2 = hv_in_config2;
sc->hv_in_config3 = hv_in_config3;
sc->hv_out_config = hv_out_config;
sc->hv_out_config2 = hv_out_config2;
sc->hv_out_config3 = hv_out_config3;
sc->xy_out_config = xy_out_config;
sc->xy_out_config2 = xy_out_config2;
sc->misc_config = misc_config;
sc->sl_config = sl_config;
sc->sl_config2 = sl_config2;
sc->sl_config3 = sl_config3;
}
// Configure TVP7002 and scan converter logic based on the video mode
void program_mode()
{
alt_u8 h_syncinlen, v_syncinlen, hdmitx_pclk_level, osd_x_size, osd_y_size;
alt_u32 h_hz, v_hz_x100, h_synclen_px;
alt_u8 h_syncinlen, v_syncinlen, macrovis, hdmitx_pclk_level, osd_x_size, osd_y_size;
alt_u32 h_hz, h_synclen_px, pclk_i_hz, dotclk_hz, pll_h_total;
// Mark as stable (needed after sync up to avoid unnecessary mode switch)
stable_frames = STABLE_THOLD;
memset(&vmode_in, 0, sizeof(mode_data_t));
cm.tx_pixelrep = cm.hdmitx_pixr_ifr = 0;
if ((cm.clkcnt != 0) && (cm.totlines != 0)) { //prevent div by 0
h_hz = TVP_EXTCLK_HZ/cm.clkcnt;
v_hz_x100 = (100*TVP_EXTCLK_HZ)/cm.pcnt_frame;
} else {
h_hz = 15700;
v_hz_x100 = 6000;
}
vmode_in.timings.v_hz_x100 = (100*27000000UL)/cm.pcnt_frame;
h_hz = (100*27000000UL)/((100*cm.pcnt_frame*(1+!cm.progressive))/cm.totlines);
printf("\nLines: %u %c\n", (unsigned)cm.totlines, cm.progressive ? 'p' : 'i');
printf("Clocks per line: %u : HS %u.%.3u kHz VS %u.%.2u Hz\n", (unsigned)cm.clkcnt, (unsigned)(h_hz/1000), (unsigned)(h_hz%1000), (unsigned)(v_hz_x100/100), (unsigned)(v_hz_x100%100));
printf("Clocks per line: %u\n", (unsigned)cm.clkcnt);
h_syncinlen = tvp_readreg(TVP_HSINWIDTH);
v_syncinlen = tvp_readreg(TVP_VSINWIDTH);
printf("Hswidth: %u Vswidth: %u Macrovision: %u\n", (unsigned)h_syncinlen, (unsigned)(v_syncinlen & 0x1F), (unsigned)cm.macrovis);
macrovis = !!(tvp_readreg(TVP_LINECNT2) & (1<<6));
printf("Hswidth: %u Vswidth: %u Macrovision: %u\n", (unsigned)h_syncinlen, (unsigned)(v_syncinlen & 0x1F), (unsigned)macrovis);
vmode_in.timings.h_synclen = h_syncinlen;
vmode_in.timings.v_total = cm.totlines;
vmode_in.timings.interlaced = !cm.progressive;
sniprintf(row1, LCD_ROW_LEN+1, "%s %u-%c", avinput_str[cm.avinput], (unsigned)cm.totlines, cm.progressive ? 'p' : 'i');
sniprintf(row2, LCD_ROW_LEN+1, "%u.%.2ukHz %u.%.2uHz", (unsigned)(h_hz/1000), (unsigned)((h_hz%1000)/10), (unsigned)(v_hz_x100/100), (unsigned)(v_hz_x100%100));
sniprintf(row2, LCD_ROW_LEN+1, "%u.%.2ukHz %u.%.2uHz", (unsigned)(h_hz/1000), (unsigned)((h_hz%1000)/10), (unsigned)(vmode_in.timings.v_hz_x100/100), (unsigned)(vmode_in.timings.v_hz_x100%100));
ui_disp_status(1);
//printf ("Get mode id with %u %u %f\n", totlines, progressive, hz);
cm.id = get_mode_id(cm.totlines, cm.progressive, v_hz_x100/100, h_syncinlen);
cm.id = get_pure_lm_mode(&cm.cc, &vmode_in, &vmode_out, &vm_conf);
if (cm.id == -1) {
printf ("Error: no suitable mode found, defaulting to 240p\n");
cm.id = 4;
printf ("ERROR: no suitable mode preset found\n");
vm_conf.si_pclk_mult = 0;
return;
}
vm_sel = cm.id;
cm.h_mult_total = (video_modes[cm.id].h_total*cm.sample_mult) + ((cm.sample_mult*video_modes[cm.id].h_total_adj*5 + 50) / 100);
// Double TVP7002 PLL sampling rate when possible to minimize jitter
while (1) {
pll_h_total = (vm_conf.h_skip+1) * vmode_in.timings.h_total + (((vm_conf.h_skip+1) * vmode_in.timings.h_total_adj * 5 + 50) / 100);
pclk_i_hz = h_hz * pll_h_total;
if ((pclk_i_hz < 25000000UL) && ((vm_conf.si_pclk_mult % 2) == 0)) {
vm_conf.h_skip = 2*(vm_conf.h_skip+1)-1;
vm_conf.si_pclk_mult /= 2;
} else {
break;
}
}
// Tweak infoframe pixel repetition indicator if passing thru horizontally multiplied mode
if ((vm_conf.y_rpt == 0) && (vm_conf.h_skip > 0))
cm.hdmitx_pixr_ifr = vm_conf.h_skip;
dotclk_hz = estimate_dotclk(&vmode_in, h_hz);
cm.pclk_o_hz = calculate_pclk(pclk_i_hz, &vmode_out, &vm_conf);
printf("H: %lu.%.2lukHz V: %u.%.2uHz\n", (h_hz+5)/1000, ((h_hz+5)%1000)/10, (vmode_in.timings.v_hz_x100/100), (vmode_in.timings.v_hz_x100%100));
printf("Estimated source dot clock: %lu.%.2luMHz\n", (dotclk_hz+5000)/1000000, ((dotclk_hz+5000)%1000000)/10000);
printf("PCLK_IN: %luHz PCLK_OUT: %luHz\n", pclk_i_hz, cm.pclk_o_hz);
// Trilevel sync is used with HDTV modes using composite sync
if (video_modes[cm.id].type & VIDEO_HDTV)
if (video_modes_plm[cm.id].type & VIDEO_HDTV)
target_type = (target_tvp_sync <= TVP_SOG3) ? VIDEO_HDTV : VIDEO_PC;
else
target_type = video_modes[cm.id].type;
target_type = video_modes_plm[cm.id].type;
h_synclen_px = ((alt_u32)h_syncinlen * (alt_u32)cm.h_mult_total) / cm.clkcnt;
h_synclen_px = ((alt_u32)h_syncinlen * pll_h_total) / cm.clkcnt;
printf("Mode %s selected - hsync width: %upx\n", video_modes[cm.id].name, (unsigned)h_synclen_px);
printf("Mode %s selected - hsync width: %upx\n", video_modes_plm[cm.id].name, (unsigned)h_synclen_px);
tvp_source_setup(target_type,
cm.h_mult_total,
pll_h_total,
cm.clkcnt,
cm.cc.tvp_hpll2x && (video_modes[cm.id].flags & MODE_PLLDIVBY2),
0,
(alt_u8)h_synclen_px,
(alt_8)(cm.cc.clamp_offset-SIGNED_NUMVAL_ZERO));
set_lpf(cm.cc.video_lpf);
set_csc(cm.cc.ypbpr_cs);
cm.sample_sel = tvp_set_hpll_phase(video_modes[cm.id].sampler_phase, cm.sample_mult);
pll_reconfig->pll_config_status.reset = (cm.fpga_vmultmode == FPGA_V_MULTMODE_1X);
set_sampler_phase(video_modes_plm[cm.id].sampler_phase);
switch (cm.fpga_vmultmode) {
case FPGA_V_MULTMODE_2X:
case FPGA_V_MULTMODE_5X:
cm.pll_config = PLL_CONFIG_2X_5X;
break;
case FPGA_V_MULTMODE_3X:
case FPGA_V_MULTMODE_4X:
cm.pll_config = PLL_CONFIG_3X_4X;
break;
default:
break;
}
pll_reconfigure(cm.pll_config);
pll_reconfig->pll_config_status.reset = (vm_conf.si_pclk_mult <= 1);
if (cm.fpga_vmultmode == FPGA_V_MULTMODE_1X) {
osd_y_size = (video_modes[cm.id].v_active > 700) ? 1 : 0;
osd_x_size = osd_y_size + !!(video_modes[cm.id].flags & MODE_INTERLACED);
if (vm_conf.si_pclk_mult > 1) {
if ((vm_conf.si_pclk_mult == 2) && (pclk_i_hz > 50000000UL))
pll_reconfigure(5);
else
pll_reconfigure(vm_conf.si_pclk_mult-1);
sys_ctrl &= ~PLL_BYPASS;
} else {
osd_x_size = 1 - cm.tx_pixelrep + (cm.fpga_hmultmode == FPGA_H_MULTMODE_OPTIMIZED_1X) + (cm.fpga_vmultmode > FPGA_V_MULTMODE_3X);
osd_y_size = 0;
sys_ctrl |= PLL_BYPASS;
}
osd->osd_config.x_size = osd_x_size;
osd->osd_config.y_size = osd_y_size;
IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, sys_ctrl);
update_sc_config();
update_osd_size(&vmode_out);
update_sc_config(&vmode_in, &vmode_out, &vm_conf, &cm.cc);
TX_SetPixelRepetition(cm.tx_pixelrep, ((cm.cc.tx_mode!=TX_DVI) && (cm.tx_pixelrep == cm.hdmitx_pixr_ifr)) ? 1 : 0);
pclk_out = (TVP_EXTCLK_HZ/cm.clkcnt)*cm.h_mult_total*(cm.fpga_vmultmode+1);
pclk_out *= 1+cm.tx_pixelrep;
if (cm.fpga_hmultmode == FPGA_H_MULTMODE_OPTIMIZED_1X)
pclk_out /= 2;
else if (cm.fpga_hmultmode == FPGA_H_MULTMODE_ASPECTFIX)
pclk_out = (pclk_out*4)/3;
printf("PCLK_out: %luHz\n", pclk_out);
if (pclk_out > 85000000)
if (cm.pclk_o_hz > 85000000)
hdmitx_pclk_level = 1;
else if (pclk_out < 75000000)
else if (cm.pclk_o_hz < 75000000)
hdmitx_pclk_level = 0;
else
hdmitx_pclk_level = cm.hdmitx_pclk_level;
printf("PCLK level: %u, PR: %u, IPR: %u, ITC: %u\n", hdmitx_pclk_level, cm.tx_pixelrep, cm.hdmitx_pixr_ifr, cm.cc.hdmi_itc);
// Full TX initialization increases mode switch delay, use only when necessary
if (cm.cc.full_tx_setup || (cm.hdmitx_pclk_level != hdmitx_pclk_level)) {
cm.hdmitx_pclk_level = hdmitx_pclk_level;
TX_enable(cm.cc.tx_mode);
} else if (cm.cc.tx_mode!=TX_DVI) {
HDMITX_SetAVIInfoFrame(cm.hdmitx_vic, (cm.cc.tx_mode == TX_HDMI_RGB) ? F_MODE_RGB444 : F_MODE_YUV444, 0, 0, cm.cc.hdmi_itc, cm.hdmitx_pixr_ifr);
HDMITX_SetAVIInfoFrame(vmode_out.vic, (cm.cc.tx_mode == TX_HDMI_RGB) ? F_MODE_RGB444 : F_MODE_YUV444, 0, 0, cm.cc.hdmi_itc, cm.hdmitx_pixr_ifr);
#ifdef ENABLE_AUDIO
#ifdef MANUAL_CTS
SetupAudio(cm.cc.tx_mode);
@ -885,21 +808,18 @@ int init_hw()
memcpy(&cm.cc, &tc_default, sizeof(avconfig_t));
memcpy(rc_keymap, rc_keymap_default, sizeof(rc_keymap));
// Init menu
init_menu();
// Load initconfig and profile
read_userdata(INIT_CONFIG_SLOT, 0);
read_userdata(profile_sel, 0);
// Setup OSD
osd->osd_config.x_size = 0;
osd->osd_config.y_size = 0;
osd->osd_config.x_offset = 3;
osd->osd_config.y_offset = 3;
osd->osd_config.enable = !!osd_enable;
osd->osd_config.status_timeout = osd_status_timeout;
osd->osd_config.border_color = 1;
// Setup test pattern
get_vmode(VMODE_480p, &vmode_in, &vmode_out, &vm_conf);
update_sc_config(&vmode_in, &vmode_out, &vm_conf, &cm.cc);
// init always in HDMI mode (fixes yellow screen bug)
cm.hdmitx_vic = HDMI_480p60;
TX_enable(TX_HDMI_RGB);
// Setup remote keymap
@ -909,6 +829,39 @@ int init_hw()
return 0;
}
void print_vm_stats() {
int row = 0;
if (!menu_active) {
memset((void*)osd->osd_array.data, 0, sizeof(osd_char_array));
read_userdata(profile_sel, 1);
sniprintf((char*)osd->osd_array.data[row][0], OSD_CHAR_COLS, "Mode preset:");
sniprintf((char*)osd->osd_array.data[row][1], OSD_CHAR_COLS, "%s", vmode_out.name);
sniprintf((char*)osd->osd_array.data[++row][0], OSD_CHAR_COLS, "Refresh rate:");
sniprintf((char*)osd->osd_array.data[row][1], OSD_CHAR_COLS, "%u.%.2uHz", vmode_out.timings.v_hz_x100/100, vmode_out.timings.v_hz_x100%100);
sniprintf((char*)osd->osd_array.data[++row][0], OSD_CHAR_COLS, "H/V synclen:");
sniprintf((char*)osd->osd_array.data[row][1], OSD_CHAR_COLS, "%-5u %-5u", vmode_out.timings.h_synclen, vmode_out.timings.v_synclen);
sniprintf((char*)osd->osd_array.data[++row][0], OSD_CHAR_COLS, "H/V backporch:");
sniprintf((char*)osd->osd_array.data[row][1], OSD_CHAR_COLS, "%-5u %-5u", vmode_out.timings.h_backporch, vmode_out.timings.v_backporch);
sniprintf((char*)osd->osd_array.data[++row][0], OSD_CHAR_COLS, "H/V active:");
sniprintf((char*)osd->osd_array.data[row][1], OSD_CHAR_COLS, "%-5u %-5u", vmode_out.timings.h_active, vmode_out.timings.v_active);
sniprintf((char*)osd->osd_array.data[++row][0], OSD_CHAR_COLS, "H/V total:");
sniprintf((char*)osd->osd_array.data[row][1], OSD_CHAR_COLS, "%-5u %-5u", vmode_out.timings.h_total, vmode_out.timings.v_total);
row++;
sniprintf((char*)osd->osd_array.data[++row][0], OSD_CHAR_COLS, "Profile:");
sniprintf((char*)osd->osd_array.data[row][1], OSD_CHAR_COLS, "%u: %s", profile_sel, (target_profile_name[0] == 0) ? "<empty>" : target_profile_name);
sniprintf((char*)osd->osd_array.data[++row][0], OSD_CHAR_COLS, "Firmware:");
sniprintf((char*)osd->osd_array.data[row][1], OSD_CHAR_COLS, "%u.%.2u" FW_SUFFIX1 FW_SUFFIX2 " @ " __DATE__, FW_VER_MAJOR, FW_VER_MINOR);
osd->osd_config.status_refresh = 1;
osd->osd_row_color.mask = 0;
osd->osd_sec_enable[0].mask = (1<<(row+1))-1;
osd->osd_sec_enable[1].mask = (1<<(row+1))-1;
}
}
int latency_test() {
lt_status_reg lt_status;
alt_u16 latency_ms_x100, stb_ms_x100;
@ -970,18 +923,6 @@ int latency_test() {
return 0;
}
// Enable chip outputs
void enable_outputs()
{
// enable TVP output
tvp_enable_output();
// program video mode
program_mode();
// enable and unmute TX
TX_enable(tc.tx_mode);
}
int main()
{
ths_input_t target_ths = 0;
@ -1006,7 +947,7 @@ int main()
printf("### DIY VIDEO DIGITIZER / SCANCONVERTER INIT OK ###\n\n");
sniprintf(row1, LCD_ROW_LEN+1, "OSSC fw. %u.%.2u" FW_SUFFIX1 FW_SUFFIX2, FW_VER_MAJOR, FW_VER_MINOR);
#ifndef DEBUG
strncpy(row2, "2014-2022 marqs", LCD_ROW_LEN+1);
strncpy(row2, "2014-2023 marqs", LCD_ROW_LEN+1);
#else
strncpy(row2, "** DEBUG BUILD *", LCD_ROW_LEN+1);
#endif
@ -1166,7 +1107,7 @@ int main()
cm.avinput = target_input;
cm.sync_active = 0;
ths_source_sel(target_ths, (cm.cc.video_lpf > 1) ? (VIDEO_LPF_MAX-cm.cc.video_lpf) : THS_LPF_BYPASS);
tvp_disable_output();
tvp_powerdown();
#ifdef ENABLE_AUDIO
DisableAudioOutput();
if (pcm1862_active)
@ -1193,7 +1134,7 @@ int main()
// Check here to enable regardless of input
if (tc.tx_mode != cm.cc.tx_mode) {
HDMITX_SetAVIInfoFrame(cm.hdmitx_vic, F_MODE_RGB444, 0, 0, 0, 0);
HDMITX_SetAVIInfoFrame(vmode_out.vic, F_MODE_RGB444, 0, 0, 0, 0);
TX_enable(tc.tx_mode);
cm.cc.tx_mode = tc.tx_mode;
cm.clkcnt = 0; //TODO: proper invalidate
@ -1201,7 +1142,7 @@ int main()
if ((tc.tx_mode != TX_DVI) && (tc.hdmi_itc != cm.cc.hdmi_itc)) {
//EnableAVIInfoFrame(FALSE, NULL);
printf("setting ITC to %d\n", tc.hdmi_itc);
HDMITX_SetAVIInfoFrame(cm.hdmitx_vic, (tc.tx_mode == TX_HDMI_RGB) ? F_MODE_RGB444 : F_MODE_YUV444, 0, 0, tc.hdmi_itc, cm.hdmitx_pixr_ifr);
HDMITX_SetAVIInfoFrame(vmode_out.vic, (tc.tx_mode == TX_HDMI_RGB) ? F_MODE_RGB444 : F_MODE_YUV444, 0, 0, tc.hdmi_itc, cm.hdmitx_pixr_ifr);
cm.cc.hdmi_itc = tc.hdmi_itc;
}
if (tc.av3_alt_rgb != cm.cc.av3_alt_rgb) {
@ -1227,11 +1168,15 @@ int main()
printf("Sync up\n");
sys_ctrl |= VIDGEN_OFF;
IOWR_ALTERA_AVALON_PIO_DATA(PIO_0_BASE, sys_ctrl);
enable_outputs();
tvp_powerup();
program_mode();
#ifdef ENABLE_AUDIO
SetupAudio(cm.cc.tx_mode);
#endif
} else {
printf("Sync lost\n");
cm.clkcnt = 0; //TODO: proper invalidate
tvp_disable_output();
tvp_powerdown();
//ths_source_sel(THS_STANDBY, 0);
strncpy(row1, avinput_str[cm.avinput], LCD_ROW_LEN+1);
strncpy(row2, " NO SYNC", LCD_ROW_LEN+1);
@ -1250,7 +1195,7 @@ int main()
case SC_CONFIG_CHANGE:
if (cm.sync_active) {
printf("Scanconverter config change\n");
update_sc_config();
update_sc_config(&vmode_in, &vmode_out, &vm_conf, &cm.cc);
}
break;
default:

View File

@ -1,5 +1,5 @@
//
// Copyright (C) 2015-2019 Markus Hiienkari <mhiienka@niksula.hut.fi>
// Copyright (C) 2015-2023 Markus Hiienkari <mhiienka@niksula.hut.fi>
//
// This file is part of Open Source Scan Converter project.
//
@ -27,10 +27,11 @@
#include "pll_reconfig_regs.h"
// sys_ctrl bits
#define VSYNC_I_TYPE (1<<18)
#define LT_ACTIVE (1<<15)
#define LT_ARMED (1<<14)
#define LT_MODE_OFFS 12
#define VSYNC_I_TYPE (1<<10)
#define PLL_BYPASS (1<<9)
#define REMOTE_EVENT (1<<8)
#define SD_SPI_SS_N (1<<7)
#define LCD_CS_N (1<<6)
@ -40,37 +41,18 @@
#define VIDGEN_OFF (1<<1)
#define AV_RESET_N (1<<0)
#define LT_CTRL_MASK 0xf000
#define LT_CTRL_MASK 0xf000
// HDMI_TX definitions
#define HDMITX_MODE_MASK 0x00040000
#define PLL_ACTIVECLK_MASK 0x00080000
#define TX_PIXELREP_DISABLE 0
#define TX_PIXELREP_2X 1
#define TX_PIXELREP_4X 3
// FPGA macros
#define FPGA_V_MULTMODE_1X 0
#define FPGA_V_MULTMODE_2X 1
#define FPGA_V_MULTMODE_3X 2
#define FPGA_V_MULTMODE_4X 3
#define FPGA_V_MULTMODE_5X 4
#define FPGA_H_MULTMODE_FULLWIDTH 0
#define FPGA_H_MULTMODE_ASPECTFIX 1
#define FPGA_H_MULTMODE_OPTIMIZED 2
#define FPGA_H_MULTMODE_OPTIMIZED_1X 3
#define VSYNC_FLAG_MASK 0x00100000
#define AUTO_OFF 0
#define AUTO_CURRENT_INPUT 1
#define AUTO_MAX_COUNT 100
#define AUTO_MAX_COUNT 100
#define AUTO_CURRENT_MAX_COUNT 6
#define PLL_CONFIG_VG 0
#define PLL_CONFIG_2X_5X 1
#define PLL_CONFIG_3X_4X 2
// In reverse order of importance
typedef enum {
@ -97,22 +79,13 @@ typedef struct {
alt_u32 pcnt_frame;
alt_u32 clkcnt;
alt_u8 progressive;
alt_u8 macrovis;
alt_8 id;
alt_u8 sync_active;
alt_u8 fpga_vmultmode;
alt_u8 fpga_hmultmode;
alt_u8 tx_pixelrep;
alt_u8 hdmitx_pixr_ifr;
alt_u8 hdmitx_pclk_level;
HDMI_Video_Type hdmitx_vic;
alt_u8 sample_mult;
alt_u8 sample_sel;
alt_u8 hsync_cut;
alt_u16 h_mult_total;
mode_flags target_lm;
alt_u32 pclk_o_hz;
avinput_t avinput;
alt_u8 pll_config;
// Current configuration
avconfig_t cc;
} avmode_t;
@ -123,6 +96,7 @@ void ui_disp_status(alt_u8 refresh_osd_timer);
int load_profile();
int save_profile();
void print_vm_stats();
int latency_test();
#endif

View File

@ -1,5 +1,5 @@
//
// Copyright (C) 2015-2019 Markus Hiienkari <mhiienka@niksula.hut.fi>
// Copyright (C) 2015-2023 Markus Hiienkari <mhiienka@niksula.hut.fi>
//
// This file is part of Open Source Scan Converter project.
//
@ -26,7 +26,6 @@
#define DEFAULT_ON 1
extern mode_data_t video_modes[], video_modes_default[];
extern alt_u8 update_cur_vm;
// Target configuration
@ -39,7 +38,6 @@ const avconfig_t tc_default = {
.pm_384p = 1,
.pm_480i = 1,
.pm_1080i = 1,
.tvp_hpll2x = DEFAULT_ON,
.sync_vth = DEFAULT_SYNC_VTH,
.linelen_tol = DEFAULT_LINELEN_TOL,
.vsync_thold = DEFAULT_VSYNC_THOLD,
@ -74,7 +72,7 @@ int set_default_avconfig()
tc.tx_mode = (IORD_ALTERA_AVALON_PIO_DATA(PIO_1_BASE) & HDMITX_MODE_MASK) ? TX_DVI : TX_HDMI_RGB;
#endif
memcpy(video_modes, video_modes_default, VIDEO_MODES_SIZE);
set_default_vm_table();
update_cur_vm = 1;
return 0;

View File

@ -1,5 +1,5 @@
//
// Copyright (C) 2015-2019 Markus Hiienkari <mhiienka@niksula.hut.fi>
// Copyright (C) 2015-2023 Markus Hiienkari <mhiienka@niksula.hut.fi>
//
// This file is part of Open Source Scan Converter project.
//
@ -21,7 +21,7 @@
#define AVCONFIG_H_
#include "alt_types.h"
#include "tvp7002.h"
#include "sysconfig.h"
#define SIGNED_NUMVAL_ZERO 128
@ -72,18 +72,28 @@ typedef enum {
AV_LAST = 10
} avinput_t;
typedef struct {
alt_u8 r_f_off;
alt_u8 g_f_off;
alt_u8 b_f_off;
alt_u8 r_f_gain;
alt_u8 g_f_gain;
alt_u8 b_f_gain;
alt_u8 c_gain;
} __attribute__((packed)) color_setup_t;
typedef struct {
alt_u8 sl_mode;
alt_u8 sl_type;
alt_u8 sl_hybr_str;
alt_u8 sl_method;
alt_u8 sl_altern;
alt_u8 sl_altiv;
alt_u8 sl_str;
alt_u8 sl_id;
alt_u8 sl_cust_l_str[5];
alt_u8 sl_cust_c_str[6];
alt_u8 linemult_target;
alt_u8 sl_cust_iv_x;
alt_u8 sl_cust_iv_y;
alt_u8 l2_mode;
alt_u8 l3_mode;
alt_u8 l4_mode;
@ -103,7 +113,6 @@ typedef struct {
alt_u8 hdmi_itc;
alt_u8 s480p_mode;
alt_u8 s400p_mode;
alt_u8 tvp_hpll2x;
alt_u8 upsample2x;
alt_u8 ypbpr_cs;
alt_u8 sync_vth;
@ -115,13 +124,12 @@ typedef struct {
alt_u8 pre_coast;
alt_u8 post_coast;
alt_u8 full_tx_setup;
alt_u8 vga_ilace_fix;
alt_u8 av3_alt_rgb;
alt_u8 panasonic_hack;
alt_u8 reverse_lpf;
alt_u8 audio_dw_sampl;
alt_u8 audio_swap_lr;
alt_u8 audio_gain;
alt_u8 audio_mono;
alt_u8 default_vic;
alt_u8 clamp_offset;
alt_u8 alc_h_filter;

View File

@ -0,0 +1,489 @@
//
// Copyright (C) 2015-2023 Markus Hiienkari <mhiienka@niksula.hut.fi>
//
// This file is part of Open Source Scan Converter project.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include "system.h"
#include "video_modes.h"
#include "av_controller.h"
#include "avconfig.h"
#define VM_OUT_YMULT (vm_conf->y_rpt+1)
#define VM_OUT_XMULT (vm_conf->x_rpt+1)
#define VM_OUT_PCLKMULT (((vm_conf->x_rpt+1)*(vm_conf->y_rpt+1))/(vm_conf->h_skip+1))
#include "video_modes_list.c"
const int num_video_modes_plm = sizeof(video_modes_plm_default)/sizeof(mode_data_t);
mode_data_t video_modes_plm[sizeof(video_modes_plm_default)/sizeof(mode_data_t)];
void set_default_vm_table() {
memcpy(video_modes_plm, video_modes_plm_default, sizeof(video_modes_plm_default));
}
void vmode_hv_mult(mode_data_t *vmode, uint8_t h_mult, uint8_t v_mult) {
uint32_t val, bp_extra;
val = vmode->timings.h_synclen * h_mult;
if (val > H_SYNCLEN_MAX) {
vmode->timings.h_synclen = H_SYNCLEN_MAX;
bp_extra = val - vmode->timings.h_synclen;
} else {
vmode->timings.h_synclen = val;
bp_extra = 0;
}
val = (vmode->timings.h_backporch * h_mult) + bp_extra;
if (val > H_BPORCH_MAX)
vmode->timings.h_backporch = H_BPORCH_MAX;
else
vmode->timings.h_backporch = val;
val = vmode->timings.h_active * h_mult;
if (val > H_ACTIVE_MAX)
vmode->timings.h_active = H_ACTIVE_MAX;
else
vmode->timings.h_active = val;
vmode->timings.h_total = h_mult * vmode->timings.h_total + ((h_mult * vmode->timings.h_total_adj * 5 + 50) / 100);
val = vmode->timings.v_synclen * v_mult;
if (val > V_SYNCLEN_MAX) {
vmode->timings.v_synclen = V_SYNCLEN_MAX;
bp_extra = val - vmode->timings.v_synclen;
} else {
vmode->timings.v_synclen = val;
bp_extra = 0;
}
val = (vmode->timings.v_backporch * v_mult) + bp_extra;
if (val > V_BPORCH_MAX)
vmode->timings.v_backporch = V_BPORCH_MAX;
else
vmode->timings.v_backporch = val;
val = vmode->timings.v_active * v_mult;
if (val > V_ACTIVE_MAX)
vmode->timings.v_active = V_ACTIVE_MAX;
else
vmode->timings.v_active = val;
if (vmode->timings.interlaced && ((v_mult % 2) == 0)) {
vmode->timings.interlaced = 0;
vmode->timings.v_total *= (v_mult / 2);
} else {
vmode->timings.v_total *= v_mult;
}
}
uint32_t estimate_dotclk(mode_data_t *vm_in, uint32_t h_hz) {
if ((vm_in->type & VIDEO_SDTV) ||
(vm_in->type & VIDEO_EDTV))
{
return h_hz * 858;
} else {
return vm_in->timings.h_total * h_hz;
}
}
uint32_t calculate_pclk(uint32_t src_clk_hz, mode_data_t *vm_out, vm_proc_config_t *vm_conf) {
uint32_t pclk_hz;
if (vm_conf->si_pclk_mult > 0) {
pclk_hz = vm_conf->si_pclk_mult*src_clk_hz;
} else if (vm_conf->si_pclk_mult < 0) {
pclk_hz = src_clk_hz/((-1)*vm_conf->si_pclk_mult+1);
} else {
// Round to kHz but maximize accuracy without using 64-bit division
pclk_hz = (((uint32_t)((((uint64_t)vm_out->timings.h_total*vm_out->timings.v_total*vm_out->timings.v_hz_x100)>>vm_out->timings.interlaced)/8)+6250)/12500)*1000;
// Switch to integer mult if possible
if (!vm_conf->framelock) {
if ((pclk_hz >= src_clk_hz) && (pclk_hz % src_clk_hz == 0))
vm_conf->si_pclk_mult = (pclk_hz/src_clk_hz);
else if ((pclk_hz < src_clk_hz) && (src_clk_hz % pclk_hz == 0))
vm_conf->si_pclk_mult = (-1)*((src_clk_hz/pclk_hz)-1);
}
}
return pclk_hz;
}
int get_pure_lm_mode(avconfig_t *cc, mode_data_t *vm_in, mode_data_t *vm_out, vm_proc_config_t *vm_conf)
{
int i, diff_lines, diff_v_hz_x100, mindiff_id=0, mindiff_lines=1000, mindiff_v_hz_x100=10000;
mode_data_t *mode_preset;
mode_flags valid_lm[] = { MODE_PT, (MODE_L2 | (MODE_L2<<cc->l2_mode)), (MODE_L3_GEN_16_9<<cc->l3_mode), (MODE_L4_GEN_4_3<<cc->l4_mode), (MODE_L5_GEN_4_3<<cc->l5_mode) };
mode_flags target_lm, mindiff_lm;
uint8_t pt_only = 0;
uint8_t upsample2x = cc->upsample2x;
// one for each video_group
uint8_t* group_ptr[] = { &pt_only, &cc->pm_240p, &cc->pm_240p, &cc->pm_384p, &cc->pm_480i, &cc->pm_480i, &cc->pm_480p, &cc->pm_480p, &pt_only, &cc->pm_1080i, &pt_only };
for (i=0; i<num_video_modes_plm; i++) {
mode_preset = &video_modes_plm[i];
switch (mode_preset->group) {
case GROUP_384P:
//fixed Line2x/3x mode for 240x360p/400p
valid_lm[2] = MODE_L3_GEN_16_9;
valid_lm[3] = MODE_L2_240x360;
valid_lm[4] = MODE_L3_240x360;
if ((!vm_in->timings.h_total) && (mode_preset->timings.v_total == 449)) {
if (!strncmp(mode_preset->name, "720x400_70", 10)) {
if (cc->s400p_mode == 0)
continue;
} else if (!strncmp(mode_preset->name, "640x400_70", 10)) {
if (cc->s400p_mode == 1)
continue;
}
}
break;
case GROUP_480I:
case GROUP_576I:
//fixed Line3x/4x mode for 480i
valid_lm[2] = MODE_L3_GEN_16_9;
valid_lm[3] = MODE_L4_GEN_4_3;
break;
case GROUP_480P:
if (mode_preset->vic == HDMI_480p60) {
switch (cc->s480p_mode) {
case 0: // Auto
if (vm_in->timings.h_synclen > 82)
continue;
break;
case 1: // DTV 480p
break;
default:
continue;
}
} else if (mode_preset->vic == HDMI_640x480p60) {
switch (cc->s480p_mode) {
case 0: // Auto
case 2: // VESA 640x480@60
break;
default:
continue;
}
}
break;
default:
break;
}
target_lm = valid_lm[*group_ptr[mode_preset->group]];
if ((target_lm & mode_preset->flags) &&
(vm_in->timings.interlaced == mode_preset->timings.interlaced))
{
diff_lines = abs(vm_in->timings.v_total - mode_preset->timings.v_total);
diff_v_hz_x100 = abs(vm_in->timings.v_hz_x100 - mode_preset->timings.v_hz_x100);
if ((diff_lines < mindiff_lines) || ((mode_preset->group >= GROUP_720P) && (diff_lines == mindiff_lines) && (diff_v_hz_x100 < mindiff_v_hz_x100))) {
mindiff_id = i;
mindiff_lines = diff_lines;
mindiff_v_hz_x100 = diff_v_hz_x100;
mindiff_lm = target_lm;
} else if ((mindiff_lines <= 2) && (diff_lines > mindiff_lines)) {
// Break out if suitable mode already found
break;
}
}
}
if (mindiff_lines >= 110)
return -1;
mode_preset = &video_modes_plm[mindiff_id];
vm_in->timings.h_active = mode_preset->timings.h_active;
vm_in->timings.v_active = mode_preset->timings.v_active;
vm_in->timings.h_synclen = mode_preset->timings.h_synclen;
vm_in->timings.v_synclen = mode_preset->timings.v_synclen;
vm_in->timings.h_backporch = mode_preset->timings.h_backporch;
vm_in->timings.v_backporch = mode_preset->timings.v_backporch;
vm_in->timings.h_total = mode_preset->timings.h_total;
vm_in->timings.h_total_adj = mode_preset->timings.h_total_adj;
vm_in->sampler_phase = mode_preset->sampler_phase;
vm_in->type = mode_preset->type;
vm_in->group = mode_preset->group;
vm_in->vic = mode_preset->vic;
strncpy(vm_in->name, mode_preset->name, 10);
memcpy(vm_out, vm_in, sizeof(mode_data_t));
vm_out->vic = HDMI_Unknown;
memset(vm_conf, 0, sizeof(vm_proc_config_t));
vm_conf->si_pclk_mult = 1;
mindiff_lm &= mode_preset->flags; //ensure L2 mode uniqueness
switch (mindiff_lm) {
case MODE_PT:
vm_out->vic = vm_in->vic;
// multiply horizontal resolution if necessary to fulfill min. 25MHz TMDS clock requirement. Tweak infoframe pixel repetition indicator later to make sink treat it as original resolution.
while ((((vm_out->timings.v_hz_x100*vm_out->timings.v_total)/100)*vm_out->timings.h_total*(vm_conf->h_skip+1))>>vm_out->timings.interlaced < 25000000UL) {
vm_conf->x_rpt = vm_conf->h_skip = 2*(vm_conf->h_skip+1)-1;
}
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
break;
case MODE_L2:
vm_conf->y_rpt = 1;
// Upsample / pixel-repeat horizontal resolution of 384p/480p/960i modes
if ((mode_preset->group == GROUP_384P) || (mode_preset->group == GROUP_480P) || (mode_preset->group == GROUP_576P) || ((mode_preset->group == GROUP_1080I) && (mode_preset->timings.h_total < 1200))) {
if (upsample2x) {
vmode_hv_mult(vm_in, 2, 1);
vmode_hv_mult(vm_out, 2, VM_OUT_YMULT);
} else {
vm_conf->x_rpt = 1;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
}
} else {
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
}
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
break;
case MODE_L2_512_COL:
vm_conf->y_rpt = 1;
vm_conf->x_rpt = vm_conf->h_skip = 1;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
break;
case MODE_L2_384_COL:
case MODE_L2_320_COL:
vm_conf->y_rpt = 1;
vm_conf->x_rpt = vm_conf->h_skip = 1;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
break;
case MODE_L2_256_COL:
vm_conf->y_rpt = 1;
vm_conf->x_rpt = vm_conf->h_skip = 2;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
vm_conf->x_rpt -= cc->ar_256col;
break;
case MODE_L2_240x360:
vm_conf->y_rpt = 1;
vm_conf->x_rpt = vm_conf->h_skip = 4;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
break;
case MODE_L3_GEN_16_9:
vm_conf->y_rpt = 2;
// Upsample / pixel-repeat horizontal resolution of 480i mode
if ((mode_preset->group == GROUP_480I) || (mode_preset->group == GROUP_576I)) {
if (upsample2x) {
vmode_hv_mult(vm_in, 2, 1);
vmode_hv_mult(vm_out, 2, VM_OUT_YMULT);
} else {
vm_conf->x_rpt = 1;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
}
} else {
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
}
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
break;
case MODE_L3_GEN_4_3:
vm_conf->y_rpt = 2;
vm_conf->x_size = vm_out->timings.h_active;
vm_out->timings.h_synclen /= 3;
vm_out->timings.h_backporch /= 3;
vm_out->timings.h_active /= 3;
vm_conf->x_offset = vm_out->timings.h_active/2;
vm_out->timings.h_total /= 3;
vm_out->timings.h_total_adj = 0;
vmode_hv_mult(vm_out, 4, VM_OUT_YMULT);
vm_conf->si_pclk_mult = 4;
break;
case MODE_L3_512_COL:
vm_conf->y_rpt = 2;
vm_conf->x_rpt = vm_conf->h_skip = 1;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
break;
case MODE_L3_384_COL:
vm_conf->y_rpt = 2;
vm_conf->x_rpt = vm_conf->h_skip = 2;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
break;
case MODE_L3_320_COL:
vm_conf->y_rpt = 2;
vm_conf->x_rpt = vm_conf->h_skip = 3;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
vm_conf->x_rpt = 2;
break;
case MODE_L3_256_COL:
vm_conf->y_rpt = 2;
vm_conf->x_rpt = vm_conf->h_skip = 4;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
vm_conf->x_rpt = cc->ar_256col ? 2 : 3;
break;
case MODE_L3_240x360:
vm_conf->y_rpt = 2;
vm_conf->x_rpt = vm_conf->h_skip = 6;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
//cm.hsync_cut = 13;
break;
case MODE_L4_GEN_4_3:
vm_conf->y_rpt = 3;
// Upsample / pixel-repeat horizontal resolution of 480i mode
if ((mode_preset->group == GROUP_480I) || (mode_preset->group == GROUP_576I)) {
if (upsample2x) {
vmode_hv_mult(vm_in, 2, 1);
vmode_hv_mult(vm_out, 2, VM_OUT_YMULT);
} else {
vm_conf->x_rpt = 1;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
}
} else {
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
}
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
break;
case MODE_L4_512_COL:
vm_conf->y_rpt = 3;
vm_conf->x_rpt = vm_conf->h_skip = 1;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
break;
case MODE_L4_384_COL:
vm_conf->y_rpt = 3;
vm_conf->x_rpt = vm_conf->h_skip = 2;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
break;
case MODE_L4_320_COL:
vm_conf->y_rpt = 3;
vm_conf->x_rpt = vm_conf->h_skip = 3;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
break;
case MODE_L4_256_COL:
vm_conf->y_rpt = 3;
vm_conf->x_rpt = vm_conf->h_skip = 4;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
vm_conf->x_rpt -= cc->ar_256col;
break;
case MODE_L5_GEN_4_3:
vm_conf->y_rpt = 4;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
break;
case MODE_L5_512_COL:
vm_conf->y_rpt = 4;
vm_conf->x_rpt = vm_conf->h_skip = 2;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
//cm.hsync_cut = 40;
break;
case MODE_L5_384_COL:
vm_conf->y_rpt = 4;
vm_conf->x_rpt = vm_conf->h_skip = 3;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
//cm.hsync_cut = 30;
break;
case MODE_L5_320_COL:
vm_conf->y_rpt = 4;
vm_conf->x_rpt = vm_conf->h_skip = 4;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
//cm.hsync_cut = 24;
break;
case MODE_L5_256_COL:
vm_conf->y_rpt = 4;
vm_conf->x_rpt = vm_conf->h_skip = 5;
vmode_hv_mult(vm_out, VM_OUT_XMULT, VM_OUT_YMULT);
vm_conf->si_pclk_mult = VM_OUT_PCLKMULT;
vm_conf->x_rpt -= cc->ar_256col;
//cm.hsync_cut = 20;
break;
default:
printf("WARNING: invalid mindiff_lm\n");
return -1;
break;
}
sniprintf(vm_out->name, 10, "%s x%u", vm_in->name, vm_conf->y_rpt+1);
if (vm_conf->x_size == 0)
vm_conf->x_size = vm_in->timings.h_active*(vm_conf->x_rpt+1);
if (vm_conf->y_size == 0)
vm_conf->y_size = vm_out->timings.v_active;
vm_conf->x_offset = ((vm_out->timings.h_active-vm_conf->x_size)/2);
vm_conf->x_start_lb = (vm_conf->x_offset >= 0) ? 0 : (-vm_conf->x_offset / (vm_conf->x_rpt+1));
// Line5x format
if (vm_conf->y_rpt == 4) {
// adjust output width to 1920
if (cc->l5_fmt != 1) {
vm_conf->x_offset = (1920-vm_conf->x_size)/2;
vm_out->timings.h_synclen = (vm_out->timings.h_total - 1920)/4;
vm_out->timings.h_backporch = (vm_out->timings.h_total - 1920)/2;
vm_out->timings.h_active = 1920;
}
// adjust output height to 1080
if (cc->l5_fmt == 0) {
vm_conf->y_start_lb = (vm_out->timings.v_active-1080)/10;
vm_out->timings.v_backporch += 5*vm_conf->y_start_lb;
vm_out->timings.v_active = 1080;
}
}
#ifdef LM_EMIF_EXTRA_DELAY
vm_conf->framesync_line = ((vm_out->timings.v_total>>vm_out->timings.interlaced)-(1+vm_out->timings.interlaced)*(vm_conf->y_rpt+1));
#else
//vm_conf->framesync_line = vm_in->timings.interlaced ? ((vm_out->timings.v_total>>vm_out->timings.interlaced)-(vm_conf->y_rpt+1)) : 0;
vm_conf->framesync_line = 0;
#endif
vm_conf->framelock = 1;
if (vm_out->vic == HDMI_Unknown)
vm_out->vic = cc->default_vic;
return mindiff_id;
}
int get_vmode(vmode_t vmode_id, mode_data_t *vm_in, mode_data_t *vm_out, vm_proc_config_t *vm_conf)
{
memset(vm_conf, 0, sizeof(vm_proc_config_t));
memset(vm_in, 0, sizeof(mode_data_t));
memcpy(vm_out, &video_modes_plm[vmode_id], sizeof(mode_data_t));
return 0;
}

View File

@ -0,0 +1,173 @@
//
// Copyright (C) 2015-2023 Markus Hiienkari <mhiienka@niksula.hut.fi>
//
// This file is part of Open Source Scan Converter project.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef VIDEO_MODES_H_
#define VIDEO_MODES_H_
#include <stdint.h>
#include "sysconfig.h"
#include "avconfig.h"
#include "it6613_sys.h"
#define DEF_PHASE 0x10
#define H_TOTAL_MIN 300
#define H_TOTAL_MAX 2800
#define H_TOTAL_ADJ_MAX 19
#define H_SYNCLEN_MIN 10
#define H_SYNCLEN_MAX 255
#define H_BPORCH_MIN 0
#define H_BPORCH_MAX 511
#define H_ACTIVE_MIN 200
#define H_ACTIVE_MAX 2560
#define H_ACTIVE_SMP_MAX 2048
#define V_SYNCLEN_MIN 1
#define V_SYNCLEN_MAX 15
#define V_BPORCH_MIN 0
#define V_BPORCH_MAX 511
#define V_ACTIVE_MIN 160
#define V_ACTIVE_MAX 1440
typedef enum {
FORMAT_RGBS = 0,
FORMAT_RGBHV = 1,
FORMAT_RGsB = 2,
FORMAT_YPbPr = 3
} video_format;
typedef enum {
VIDEO_SDTV = (1<<0),
VIDEO_EDTV = (1<<1),
VIDEO_HDTV = (1<<2),
VIDEO_PC = (1<<3),
} video_type;
typedef enum {
GROUP_NONE = 0,
GROUP_240P = 1,
GROUP_288P = 2,
GROUP_384P = 3,
GROUP_480I = 4,
GROUP_576I = 5,
GROUP_480P = 6,
GROUP_576P = 7,
GROUP_720P = 8,
GROUP_1080I = 9,
GROUP_1080P = 10,
} video_group;
typedef enum {
MODE_INTERLACED = (1<<0), //deprecated
MODE_CRT = (1<<1),
//at least one of the flags below must be set for each P-LM mode
MODE_PT = (1<<2),
MODE_L2 = (1<<3),
MODE_L2_512_COL = (1<<4),
MODE_L2_384_COL = (1<<5),
MODE_L2_320_COL = (1<<6),
MODE_L2_256_COL = (1<<7),
MODE_L2_240x360 = (1<<8),
MODE_L3_GEN_16_9 = (1<<9),
MODE_L3_GEN_4_3 = (1<<10),
MODE_L3_512_COL = (1<<11),
MODE_L3_384_COL = (1<<12),
MODE_L3_320_COL = (1<<13),
MODE_L3_256_COL = (1<<14),
MODE_L3_240x360 = (1<<15),
MODE_L4_GEN_4_3 = (1<<16),
MODE_L4_512_COL = (1<<17),
MODE_L4_384_COL = (1<<18),
MODE_L4_320_COL = (1<<19),
MODE_L4_256_COL = (1<<20),
MODE_L5_GEN_4_3 = (1<<21),
MODE_L5_512_COL = (1<<22),
MODE_L5_384_COL = (1<<23),
MODE_L5_320_COL = (1<<24),
MODE_L5_256_COL = (1<<25),
} mode_flags;
typedef enum {
VMODE_480p = 24,
} vmode_t;
typedef struct {
uint16_t h_active;
uint16_t v_active;
uint16_t v_hz_x100;
uint16_t h_total;
uint8_t h_total_adj;
uint16_t v_total;
uint16_t h_backporch;
uint16_t v_backporch;
uint16_t h_synclen;
uint8_t v_synclen;
uint8_t interlaced;
} sync_timings_t;
typedef struct {
uint8_t h;
uint8_t v;
} aspect_ratio_t;
typedef enum {
TX_1X = 0,
TX_2X = 1,
TX_4X = 2
} HDMI_pixelrep_t;
typedef struct {
char name[10];
HDMI_Video_Type vic;
sync_timings_t timings;
uint8_t sampler_phase;
aspect_ratio_t ar;
video_type type;
video_group group;
mode_flags flags;
} mode_data_t;
typedef struct {
int8_t x_rpt;
int8_t y_rpt;
uint8_t h_skip;
uint8_t h_sample_sel;
int16_t x_offset;
int16_t y_offset;
uint16_t x_size;
uint16_t y_size;
uint16_t framesync_line;
uint8_t x_start_lb;
int8_t y_start_lb;
uint8_t framelock;
// for generation from 27MHz clock
int8_t si_pclk_mult;
} vm_proc_config_t;
void set_default_vm_table();
uint32_t estimate_dotclk(mode_data_t *vm_in, uint32_t h_hz);
uint32_t calculate_pclk(uint32_t src_clk_hz, mode_data_t *vm_out, vm_proc_config_t *vm_conf);
int get_pure_lm_mode(avconfig_t *cc, mode_data_t *vm_in, mode_data_t *vm_out, vm_proc_config_t *vm_conf);
int get_vmode(vmode_t vmode_id, mode_data_t *vm_in, mode_data_t *vm_out, vm_proc_config_t *vm_conf);
#endif /* VIDEO_MODES_H_ */

View File

@ -0,0 +1,84 @@
//
// Copyright (C) 2020-2023 Markus Hiienkari <mhiienka@niksula.hut.fi>
//
// This file is part of Open Source Scan Converter project.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
/* Pure LM modes */
#ifdef VM_STATIC_INCLUDE
static
#endif
const mode_data_t video_modes_plm_default[] = {
/* 240p modes */
{ "1600x240", HDMI_Unknown, {1600, 240, 6000, 2046, 0, 262, 202, 15, 150, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_240P, (MODE_L5_GEN_4_3), },
{ "1280x240", HDMI_Unknown, {1280, 240, 6000, 1560, 0, 262, 170, 15, 72, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_240P, (MODE_L3_GEN_16_9 | MODE_L4_GEN_4_3), },
{ "960x240", HDMI_Unknown, { 960, 240, 6000, 1170, 0, 262, 128, 15, 54, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_240P, (MODE_L3_GEN_4_3), },
{ "512x240", HDMI_Unknown, { 512, 240, 6000, 682, 0, 262, 77, 14, 50, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_240P, (MODE_L2_512_COL | MODE_L3_512_COL | MODE_L4_512_COL | MODE_L5_512_COL), },
{ "384x240", HDMI_Unknown, { 384, 240, 6000, 512, 0, 262, 59, 14, 37, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_240P, (MODE_L2_384_COL | MODE_L3_384_COL | MODE_L4_384_COL | MODE_L5_384_COL), },
{ "320x240", HDMI_Unknown, { 320, 240, 6000, 426, 0, 262, 49, 14, 31, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_240P, (MODE_L2_320_COL | MODE_L3_320_COL | MODE_L4_320_COL | MODE_L5_320_COL), },
{ "256x240", HDMI_Unknown, { 256, 240, 6000, 341, 0, 262, 39, 14, 25, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_240P, (MODE_L2_256_COL | MODE_L3_256_COL | MODE_L4_256_COL | MODE_L5_256_COL), },
{ "240p", HDMI_240p60, { 720, 240, 6005, 858, 0, 262, 57, 15, 62, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_240P, (MODE_PT | MODE_L2), },
/* 288p modes */
{ "1600x240L", HDMI_Unknown, {1600, 240, 5000, 2046, 0, 312, 202, 43, 150, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_288P, (MODE_L5_GEN_4_3), },
{ "1280x288", HDMI_Unknown, {1280, 288, 5000, 1560, 0, 312, 170, 19, 72, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_288P, (MODE_L3_GEN_16_9 | MODE_L4_GEN_4_3), },
{ "960x288", HDMI_Unknown, { 960, 288, 5000, 1170, 0, 312, 128, 19, 54, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_288P, (MODE_L3_GEN_4_3), },
{ "512x240LB", HDMI_Unknown, { 512, 240, 5000, 682, 0, 312, 77, 41, 50, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_288P, (MODE_L2_512_COL | MODE_L3_512_COL | MODE_L4_512_COL | MODE_L5_512_COL), },
{ "384x240LB", HDMI_Unknown, { 384, 240, 5000, 512, 0, 312, 59, 41, 37, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_288P, (MODE_L2_384_COL | MODE_L3_384_COL | MODE_L4_384_COL | MODE_L5_384_COL), },
{ "320x240LB", HDMI_Unknown, { 320, 240, 5000, 426, 0, 312, 49, 41, 31, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_288P, (MODE_L2_320_COL | MODE_L3_320_COL | MODE_L4_320_COL | MODE_L5_320_COL), },
{ "256x240LB", HDMI_Unknown, { 256, 240, 5000, 341, 0, 312, 39, 41, 25, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_288P, (MODE_L2_256_COL | MODE_L3_256_COL | MODE_L4_256_COL | MODE_L5_256_COL), },
{ "288p", HDMI_288p50, { 720, 288, 5008, 864, 0, 312, 69, 19, 63, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_288P, (MODE_PT | MODE_L2), },
/* 360p: GBI */
{ "480x360", HDMI_Unknown, { 480, 360, 6000, 600, 0, 375, 63, 10, 38, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_EDTV, GROUP_384P, (MODE_PT | MODE_L2), },
{ "240x360", HDMI_Unknown, { 256, 360, 6000, 300, 0, 375, 24, 10, 18, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_EDTV, GROUP_384P, (MODE_L2_240x360 | MODE_L3_240x360), },
/* 384p: Sega Model 2 */
{ "384p", HDMI_Unknown, { 496, 384, 5500, 640, 0, 423, 50, 29, 62, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_EDTV, GROUP_384P, (MODE_PT | MODE_L2), },
/* 400p line3x */
{ "1600x400", HDMI_Unknown, {1600, 400, 7000, 2000, 0, 449, 120, 34, 240, 2, 0}, DEF_PHASE, { 4, 3}, VIDEO_PC, GROUP_384P, (MODE_L3_GEN_16_9), },
/* 720x400@70Hz, VGA Mode 3+/7+ */
{ "720x400_70", HDMI_Unknown, { 720, 400, 7000, 900, 0, 449, 64, 34, 96, 2, 0}, DEF_PHASE, { 4, 3}, VIDEO_PC, GROUP_384P, (MODE_PT | MODE_L2), },
/* 640x400@70Hz, VGA Mode 13h */
{ "640x400_70", HDMI_Unknown, { 640, 400, 7000, 800, 0, 449, 48, 34, 96, 2, 0}, DEF_PHASE, { 4, 3}, VIDEO_PC, GROUP_384P, (MODE_PT | MODE_L2), },
/* 384p: X68k @ 24kHz */
{ "640x384", HDMI_Unknown, { 640, 384, 5500, 800, 0, 492, 48, 63, 96, 2, 0}, DEF_PHASE, { 4, 3}, VIDEO_PC, GROUP_384P, (MODE_PT | MODE_L2), },
/* ~525-line modes */
{ "480i", HDMI_480i60, { 720, 240, 5994, 858, 0, 525, 57, 15, 62, 3, 1}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_480I, (MODE_PT | MODE_L2 | MODE_L3_GEN_16_9 | MODE_L4_GEN_4_3), },
{ "480p", HDMI_480p60, { 720, 480, 5994, 858, 0, 525, 60, 30, 62, 6, 0}, DEF_PHASE, { 4, 3}, VIDEO_EDTV, GROUP_480P, (MODE_PT | MODE_L2), },
{ "640x480_60", HDMI_640x480p60, { 640, 480, 6000, 800, 0, 525, 48, 33, 96, 2, 0}, DEF_PHASE, { 4, 3}, VIDEO_PC, GROUP_480P, (MODE_PT | MODE_L2), },
/* 480p PSP in-game */ \
{ "480x272", HDMI_480p60_16x9, { 480, 272, 6000, 858, 0, 525, 177,134, 62, 6, 0}, DEF_PHASE, {16, 9}, VIDEO_EDTV, GROUP_480P, (MODE_PT | MODE_L2) }, \
/* X68k @ 31kHz */
{ "640x512", HDMI_Unknown, { 640, 512, 6000, 800, 0, 568, 48, 34, 96, 6, 0}, DEF_PHASE, { 4, 3}, VIDEO_PC, GROUP_480P, (MODE_PT | MODE_L2), },
/* ~625-line modes */
{ "576i", HDMI_576i50, { 720, 288, 5000, 864, 0, 625, 69, 19, 63, 3, 1}, DEF_PHASE, { 4, 3}, VIDEO_SDTV, GROUP_576I, (MODE_PT | MODE_L2 | MODE_L3_GEN_16_9 | MODE_L4_GEN_4_3), },
{ "576p", HDMI_576p50, { 720, 576, 5000, 864, 0, 625, 68, 39, 64, 5, 0}, DEF_PHASE, { 4, 3}, VIDEO_EDTV, GROUP_576P, (MODE_PT | MODE_L2), },
{ "800x600_60", HDMI_Unknown, { 800, 600, 6000, 1056, 0, 628, 88, 23, 128, 4, 0}, DEF_PHASE, { 4, 3}, VIDEO_PC, GROUP_NONE, MODE_PT, },
/* CEA 720p modes */
{ "720p_50", HDMI_720p50, {1280, 720, 5000, 1980, 0, 750, 220, 20, 40, 5, 0}, DEF_PHASE, {16, 9}, (VIDEO_HDTV | VIDEO_PC), GROUP_720P, MODE_PT, },
{ "720p_60", HDMI_720p60, {1280, 720, 6000, 1650, 0, 750, 220, 20, 40, 5, 0}, DEF_PHASE, {16, 9}, (VIDEO_HDTV | VIDEO_PC), GROUP_720P, MODE_PT, },
/* VESA XGA,1280x960 and SXGA modes */
{ "1024x768", HDMI_Unknown, {1024, 768, 6000, 1344, 0, 806, 160, 29, 136, 6, 0}, DEF_PHASE, { 4, 3}, VIDEO_PC, GROUP_NONE, MODE_PT, },
{ "1280x960", HDMI_Unknown, {1280, 960, 6000, 1800, 0, 1000, 312, 36, 112, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_PC, GROUP_NONE, MODE_PT, },
{ "1280x1024", HDMI_Unknown, {1280, 1024, 6000, 1688, 0, 1066, 248, 38, 112, 3, 0}, DEF_PHASE, { 5, 4}, VIDEO_PC, GROUP_NONE, MODE_PT, },
/* PS2 GSM 960i mode */
{ "640x960i", HDMI_Unknown, { 640, 480, 6000, 800, 0, 1050, 48, 33, 96, 2, 1}, DEF_PHASE, { 4, 3}, VIDEO_EDTV, GROUP_1080I, (MODE_PT | MODE_L2), },
/* CEA 1080i/p modes */
{ "1080i_50", HDMI_1080i50, {1920, 540, 5000, 2640, 0, 1125, 148, 15, 44, 5, 1}, DEF_PHASE, {16, 9}, (VIDEO_HDTV | VIDEO_PC), GROUP_1080I, (MODE_PT | MODE_L2), },
{ "1080i_60", HDMI_1080i60, {1920, 540, 6000, 2200, 0, 1125, 148, 15, 44, 5, 1}, DEF_PHASE, {16, 9}, (VIDEO_HDTV | VIDEO_PC), GROUP_1080I, (MODE_PT | MODE_L2), },
{ "1080p_50", HDMI_1080p50, {1920, 1080, 5000, 2640, 0, 1125, 148, 36, 44, 5, 0}, DEF_PHASE, {16, 9}, (VIDEO_HDTV | VIDEO_PC), GROUP_1080P, MODE_PT, },
{ "1080p_60", HDMI_1080p60, {1920, 1080, 6000, 2200, 0, 1125, 148, 36, 44, 5, 0}, DEF_PHASE, {16, 9}, (VIDEO_HDTV | VIDEO_PC), GROUP_1080P, MODE_PT, },
/* VESA UXGA mode */
{ "1600x1200", HDMI_Unknown, {1600, 1200, 6000, 2160, 0, 1250, 304, 46, 192, 3, 0}, DEF_PHASE, { 4, 3}, VIDEO_PC, GROUP_NONE, MODE_PT, },
};