kopia lustrzana https://github.com/hoglet67/RGBtoHDMI
Profile saving system
rodzic
b0209b53d8
commit
40d5491a1a
|
@ -438,6 +438,41 @@ int file_load(char *path, char *buffer, unsigned int buffer_size) {
|
||||||
return bytes_read;
|
return bytes_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int file_save(char *path, char *buffer, unsigned int buffer_size) {
|
||||||
|
FRESULT result;
|
||||||
|
FIL file;
|
||||||
|
unsigned int num_written = 0;
|
||||||
|
init_filesystem();
|
||||||
|
|
||||||
|
log_info("Saving file %s", path);
|
||||||
|
|
||||||
|
result = f_open(&file, path, FA_WRITE | FA_CREATE_ALWAYS);
|
||||||
|
if (result != FR_OK) {
|
||||||
|
log_warn("Failed to open %s (result = %d)", path, result);
|
||||||
|
close_filesystem();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = f_write(&file, buffer, buffer_size, &num_written);
|
||||||
|
|
||||||
|
if (result != FR_OK) {
|
||||||
|
log_warn("Failed to read %s (result = %d)", path, result);
|
||||||
|
close_filesystem();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = f_close(&file);
|
||||||
|
if (result != FR_OK) {
|
||||||
|
log_warn("Failed to close %s (result = %d)", path, result);
|
||||||
|
close_filesystem();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
close_filesystem();
|
||||||
|
|
||||||
|
log_info("%s writing complete", path);
|
||||||
|
return num_written;
|
||||||
|
}
|
||||||
|
|
||||||
int file_save_config(char *resolution_name, int interpolation) {
|
int file_save_config(char *resolution_name, int interpolation) {
|
||||||
FRESULT result;
|
FRESULT result;
|
||||||
char path[256];
|
char path[256];
|
||||||
|
|
|
@ -15,4 +15,5 @@ unsigned int file_read_profile(char *profile_name, char *sub_profile_name, int u
|
||||||
void scan_resolutions(char resolution_names[MAX_RESOLUTION][MAX_RESOLUTION_WIDTH], char *path, size_t *count);
|
void scan_resolutions(char resolution_names[MAX_RESOLUTION][MAX_RESOLUTION_WIDTH], char *path, size_t *count);
|
||||||
int file_save_config(char *resolution_name, int interpolation);
|
int file_save_config(char *resolution_name, int interpolation);
|
||||||
int file_load(char *path, char *buffer, unsigned int buffer_size);
|
int file_load(char *path, char *buffer, unsigned int buffer_size);
|
||||||
|
int file_save(char *path, char *buffer, unsigned int buffer_size);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -114,7 +114,9 @@ void geometry_set_mode(int mode) {
|
||||||
mode7 = mode;
|
mode7 = mode;
|
||||||
geometry = mode ? &mode7_geometry : &default_geometry;
|
geometry = mode ? &mode7_geometry : &default_geometry;
|
||||||
}
|
}
|
||||||
|
int geometry_get_mode() {
|
||||||
|
return mode7;
|
||||||
|
}
|
||||||
int geometry_get_value(int num) {
|
int geometry_get_value(int num) {
|
||||||
switch (num) {
|
switch (num) {
|
||||||
case H_OFFSET:
|
case H_OFFSET:
|
||||||
|
|
|
@ -44,6 +44,7 @@ enum {
|
||||||
|
|
||||||
void geometry_init(int version);
|
void geometry_init(int version);
|
||||||
void geometry_set_mode(int mode);
|
void geometry_set_mode(int mode);
|
||||||
|
int geometry_get_mode();
|
||||||
int geometry_get_value(int num);
|
int geometry_get_value(int num);
|
||||||
const char *geometry_get_value_string(int num);
|
const char *geometry_get_value_string(int num);
|
||||||
void geometry_set_value(int num, int value);
|
void geometry_set_value(int num, int value);
|
||||||
|
|
815
src/osd.c
815
src/osd.c
|
@ -2,7 +2,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include "defs.h"
|
#include "defs.h"
|
||||||
#include "cpld.h"
|
#include "cpld.h"
|
||||||
#include "geometry.h"
|
#include "geometry.h"
|
||||||
|
@ -79,9 +79,9 @@ static const char *palette_control_names[] = {
|
||||||
|
|
||||||
static const char *vlockmode_names[] = {
|
static const char *vlockmode_names[] = {
|
||||||
"Unlocked",
|
"Unlocked",
|
||||||
|
"Locked (Exact)",
|
||||||
"2000ppm Slow",
|
"2000ppm Slow",
|
||||||
"500ppm Slow",
|
"500ppm Slow",
|
||||||
"Locked (Exact)",
|
|
||||||
"500ppm Fast",
|
"500ppm Fast",
|
||||||
"2000ppm Fast"
|
"2000ppm Fast"
|
||||||
};
|
};
|
||||||
|
@ -107,8 +107,8 @@ static const char *nbuffer_names[] = {
|
||||||
|
|
||||||
static const char *autoswitch_names[] = {
|
static const char *autoswitch_names[] = {
|
||||||
"Off",
|
"Off",
|
||||||
"BBC Mode 7",
|
"Sub-Profile",
|
||||||
"Sub Profile"
|
"BBC Mode 7"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *scaling_names[] = {
|
static const char *scaling_names[] = {
|
||||||
|
@ -130,8 +130,8 @@ static const char *interpolation_names[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *vlockadj_names[] = {
|
static const char *vlockadj_names[] = {
|
||||||
"Narrow",
|
"-5% to +5%",
|
||||||
"Up to 165Mhz",
|
"Full Range",
|
||||||
"Up to 260Mhz"
|
"Up to 260Mhz"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -140,18 +140,19 @@ static const char *vlockadj_names[] = {
|
||||||
// =============================================================
|
// =============================================================
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
F_PROFILE,
|
F_AUTOSWITCH,
|
||||||
F_SUBPROFILE,
|
|
||||||
F_RESOLUTION,
|
F_RESOLUTION,
|
||||||
F_INTERPOLATION,
|
F_INTERPOLATION,
|
||||||
F_SCALING,
|
F_PROFILE,
|
||||||
F_DEINTERLACE,
|
F_SUBPROFILE,
|
||||||
F_PALETTE,
|
F_PALETTE,
|
||||||
F_PALETTECONTROL,
|
F_PALETTECONTROL,
|
||||||
|
F_DEINTERLACE,
|
||||||
F_SCANLINES,
|
F_SCANLINES,
|
||||||
F_SCANLINESINT,
|
F_SCANLINESINT,
|
||||||
F_VSYNCTYPE,
|
F_SCALING,
|
||||||
F_MUX,
|
F_MUX,
|
||||||
|
F_VSYNCTYPE,
|
||||||
F_VSYNC,
|
F_VSYNC,
|
||||||
F_VLOCKMODE,
|
F_VLOCKMODE,
|
||||||
F_VLOCKLINE,
|
F_VLOCKLINE,
|
||||||
|
@ -159,31 +160,30 @@ enum {
|
||||||
#ifdef MULTI_BUFFER
|
#ifdef MULTI_BUFFER
|
||||||
F_NBUFFERS,
|
F_NBUFFERS,
|
||||||
#endif
|
#endif
|
||||||
F_AUTOSWITCH,
|
|
||||||
F_DEBUG
|
F_DEBUG
|
||||||
};
|
};
|
||||||
|
|
||||||
static param_t features[] = {
|
static param_t features[] = {
|
||||||
{ F_PROFILE, "Computer Profile", 0, 0, 1 },
|
{ F_AUTOSWITCH, "Auto Switching", 0, NUM_AUTOSWITCHES - 1, 1 },
|
||||||
{ F_SUBPROFILE, "Mode Sub-Profile", 0, 0, 1 },
|
|
||||||
{ F_RESOLUTION,"Output Resolution", 0, 0, 1 },
|
{ F_RESOLUTION,"Output Resolution", 0, 0, 1 },
|
||||||
{ F_INTERPOLATION, "Interpolation", 0,NUM_INTERPOLATION - 1, 1 },
|
{ F_INTERPOLATION, "Interpolation", 0,NUM_INTERPOLATION - 1, 1 },
|
||||||
{ F_SCALING, "Scaling", 0, NUM_SCALING - 1, 1 },
|
{ F_PROFILE, "Computer Profile", 0, 0, 1 },
|
||||||
{ F_DEINTERLACE,"Mode7 Deinterlace", 0, NUM_DEINTERLACES - 1, 1 },
|
{ F_SUBPROFILE, "Sub-Profile", 0, 0, 1 },
|
||||||
{ F_PALETTE, "Palette", 0, NUM_PALETTES - 1, 1 },
|
{ F_PALETTE, "Palette", 0, NUM_PALETTES - 1, 1 },
|
||||||
{ F_PALETTECONTROL, "Palette Control", 0, NUM_CONTROLS - 1, 1 },
|
{ F_PALETTECONTROL, "Palette Control", 0, NUM_CONTROLS - 1, 1 },
|
||||||
|
{ F_DEINTERLACE,"Mode7 Deinterlace", 0, NUM_DEINTERLACES - 1, 1 },
|
||||||
{ F_SCANLINES, "Scanlines", 0, 1, 1 },
|
{ F_SCANLINES, "Scanlines", 0, 1, 1 },
|
||||||
{ F_SCANLINESINT, "Scanline Level", 0, 15, 1 },
|
{ F_SCANLINESINT, "Scanline Level", 0, 15, 1 },
|
||||||
{ F_VSYNCTYPE, "V Sync Type", 0, NUM_VSYNCTYPES - 1, 1 },
|
{ F_SCALING, "Scaling", 0, NUM_SCALING - 1, 1 },
|
||||||
{ F_MUX,"Input Mux (3 Bit)", 0, 1, 1 },
|
{ F_MUX,"Input Mux (3 Bit)", 0, 1, 1 },
|
||||||
|
{ F_VSYNCTYPE, "V Sync Type", 0, NUM_VSYNCTYPES - 1, 1 },
|
||||||
{ F_VSYNC, "V Sync Indicator", 0, 1, 1 },
|
{ F_VSYNC, "V Sync Indicator", 0, 1, 1 },
|
||||||
{ F_VLOCKMODE, "V Lock Mode", 0, 5, 1 },
|
{ F_VLOCKMODE, "V Lock Mode", 0, 5, 1 },
|
||||||
{ F_VLOCKLINE, "V Lock Line", 0, 190, 1 },
|
{ F_VLOCKLINE, "V Lock Line",10, 190, 1 },
|
||||||
{ F_VLOCKADJ, "V Lock Adjust", 0, NUM_VLOCKADJ - 2, 1 }, //-2 so disables 260 mhz for now
|
{ F_VLOCKADJ, "V Lock Adjust", 0, NUM_VLOCKADJ - 2, 1 }, //-2 so disables 260 mhz for now
|
||||||
#ifdef MULTI_BUFFER
|
#ifdef MULTI_BUFFER
|
||||||
{ F_NBUFFERS, "Num Buffers", 0, 3, 1 },
|
{ F_NBUFFERS, "Num Buffers", 0, 3, 1 },
|
||||||
#endif
|
#endif
|
||||||
{ F_AUTOSWITCH, "Auto Switching", 0, NUM_AUTOSWITCHES - 1, 1 },
|
|
||||||
{ F_DEBUG, "Debug", 0, 1, 1 },
|
{ F_DEBUG, "Debug", 0, 1, 1 },
|
||||||
{ -1, NULL, 0, 0, 0 }
|
{ -1, NULL, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
@ -199,7 +199,9 @@ typedef enum {
|
||||||
I_GEOMETRY, // Item is a "geometry" (i.e. managed by the geometry)
|
I_GEOMETRY, // Item is a "geometry" (i.e. managed by the geometry)
|
||||||
I_PARAM, // Item is a "parameter" (i.e. managed by the cpld)
|
I_PARAM, // Item is a "parameter" (i.e. managed by the cpld)
|
||||||
I_INFO, // Item is an info screen
|
I_INFO, // Item is an info screen
|
||||||
I_BACK // Item is a link back to the previous menu
|
I_BACK, // Item is a link back to the previous menu
|
||||||
|
I_SAVE, // Item is a saving profile option
|
||||||
|
I_RESTORE // Item is a restoring a profile option
|
||||||
} item_type_t;
|
} item_type_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -233,6 +235,16 @@ typedef struct {
|
||||||
char *name;
|
char *name;
|
||||||
} back_menu_item_t;
|
} back_menu_item_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
item_type_t type;
|
||||||
|
char *name;
|
||||||
|
} save_menu_item_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
item_type_t type;
|
||||||
|
char *name;
|
||||||
|
} restore_menu_item_t;
|
||||||
|
|
||||||
static void info_cal_summary(int line);
|
static void info_cal_summary(int line);
|
||||||
static void info_cal_detail(int line);
|
static void info_cal_detail(int line);
|
||||||
static void info_cal_raw(int line);
|
static void info_cal_raw(int line);
|
||||||
|
@ -245,6 +257,8 @@ static info_menu_item_t cal_raw_ref = { I_INFO, "Calibration Raw",
|
||||||
static info_menu_item_t firmware_version_ref = { I_INFO, "Firmware Version", info_firmware_version};
|
static info_menu_item_t firmware_version_ref = { I_INFO, "Firmware Version", info_firmware_version};
|
||||||
static info_menu_item_t credits_ref = { I_INFO, "Credits", info_credits};
|
static info_menu_item_t credits_ref = { I_INFO, "Credits", info_credits};
|
||||||
static back_menu_item_t back_ref = { I_BACK, "Return"};
|
static back_menu_item_t back_ref = { I_BACK, "Return"};
|
||||||
|
static save_menu_item_t save_ref = { I_SAVE, "Save Configuration"};
|
||||||
|
static restore_menu_item_t restore_ref = { I_RESTORE, "Restore Default Configuration"};
|
||||||
|
|
||||||
static menu_t info_menu = {
|
static menu_t info_menu = {
|
||||||
"Info Menu",
|
"Info Menu",
|
||||||
|
@ -299,7 +313,6 @@ static menu_t settings_menu = {
|
||||||
{
|
{
|
||||||
(base_menu_item_t *) &back_ref,
|
(base_menu_item_t *) &back_ref,
|
||||||
(base_menu_item_t *) &scaling_ref,
|
(base_menu_item_t *) &scaling_ref,
|
||||||
(base_menu_item_t *) &autoswitch_ref,
|
|
||||||
(base_menu_item_t *) &mux_ref,
|
(base_menu_item_t *) &mux_ref,
|
||||||
(base_menu_item_t *) &vsynctype_ref,
|
(base_menu_item_t *) &vsynctype_ref,
|
||||||
(base_menu_item_t *) &vsync_ref,
|
(base_menu_item_t *) &vsync_ref,
|
||||||
|
@ -410,8 +423,11 @@ static menu_t main_menu = {
|
||||||
(base_menu_item_t *) &settings_menu_ref,
|
(base_menu_item_t *) &settings_menu_ref,
|
||||||
(base_menu_item_t *) &geometry_menu_ref,
|
(base_menu_item_t *) &geometry_menu_ref,
|
||||||
(base_menu_item_t *) &sampling_menu_ref,
|
(base_menu_item_t *) &sampling_menu_ref,
|
||||||
|
(base_menu_item_t *) &save_ref,
|
||||||
|
(base_menu_item_t *) &restore_ref,
|
||||||
(base_menu_item_t *) &resolution_ref,
|
(base_menu_item_t *) &resolution_ref,
|
||||||
(base_menu_item_t *) &interpolation_ref,
|
(base_menu_item_t *) &interpolation_ref,
|
||||||
|
(base_menu_item_t *) &autoswitch_ref,
|
||||||
(base_menu_item_t *) &profile_ref,
|
(base_menu_item_t *) &profile_ref,
|
||||||
(base_menu_item_t *) &subprofile_ref,
|
(base_menu_item_t *) &subprofile_ref,
|
||||||
NULL
|
NULL
|
||||||
|
@ -485,6 +501,7 @@ static int key_capture = OSD_SW2;
|
||||||
// Whether the menu back pointer is at the start (0) or end (1) of the menu
|
// Whether the menu back pointer is at the start (0) or end (1) of the menu
|
||||||
static int return_at_end = 1;
|
static int return_at_end = 1;
|
||||||
static char config_buffer[MAX_CONFIG_BUFFER_SIZE];
|
static char config_buffer[MAX_CONFIG_BUFFER_SIZE];
|
||||||
|
static char save_buffer[MAX_BUFFER_SIZE];
|
||||||
static char default_buffer[MAX_BUFFER_SIZE];
|
static char default_buffer[MAX_BUFFER_SIZE];
|
||||||
static char main_buffer[MAX_BUFFER_SIZE];
|
static char main_buffer[MAX_BUFFER_SIZE];
|
||||||
static char sub_default_buffer[MAX_BUFFER_SIZE];
|
static char sub_default_buffer[MAX_BUFFER_SIZE];
|
||||||
|
@ -566,6 +583,7 @@ static void set_feature(int num, int value) {
|
||||||
case F_PROFILE:
|
case F_PROFILE:
|
||||||
set_profile(value);
|
set_profile(value);
|
||||||
load_profiles(value);
|
load_profiles(value);
|
||||||
|
process_profile(value);
|
||||||
set_feature(F_SUBPROFILE, 0);
|
set_feature(F_SUBPROFILE, 0);
|
||||||
break;
|
break;
|
||||||
case F_SUBPROFILE:
|
case F_SUBPROFILE:
|
||||||
|
@ -645,6 +663,10 @@ static const char *item_name(base_menu_item_t *item) {
|
||||||
return ((info_menu_item_t *)item)->name;
|
return ((info_menu_item_t *)item)->name;
|
||||||
case I_BACK:
|
case I_BACK:
|
||||||
return ((back_menu_item_t *)item)->name;
|
return ((back_menu_item_t *)item)->name;
|
||||||
|
case I_SAVE:
|
||||||
|
return ((save_menu_item_t *)item)->name;
|
||||||
|
case I_RESTORE:
|
||||||
|
return ((restore_menu_item_t *)item)->name;
|
||||||
default:
|
default:
|
||||||
// Should never hit this case
|
// Should never hit this case
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -774,9 +796,9 @@ static void info_credits(int line) {
|
||||||
|
|
||||||
static void info_cal_summary(int line) {
|
static void info_cal_summary(int line) {
|
||||||
if (clock_error_ppm > 0) {
|
if (clock_error_ppm > 0) {
|
||||||
sprintf(message, "Clk Err: %d ppm (Computer slower than Pi)", clock_error_ppm);
|
sprintf(message, "Clk Err: %d ppm (Source slower than Pi)", clock_error_ppm);
|
||||||
} else if (clock_error_ppm < 0) {
|
} else if (clock_error_ppm < 0) {
|
||||||
sprintf(message, "Clk Err: %d ppm (Computer faster than Pi)", -clock_error_ppm);
|
sprintf(message, "Clk Err: %d ppm (Source faster than Pi)", -clock_error_ppm);
|
||||||
} else {
|
} else {
|
||||||
sprintf(message, "Clk Err: %d ppm (exact match)", clock_error_ppm);
|
sprintf(message, "Clk Err: %d ppm (exact match)", clock_error_ppm);
|
||||||
}
|
}
|
||||||
|
@ -855,7 +877,7 @@ static void redraw_menu() {
|
||||||
item_ptr = menu->items;
|
item_ptr = menu->items;
|
||||||
while ((item = *item_ptr++)) {
|
while ((item = *item_ptr++)) {
|
||||||
int len = strlen(item_name(item));
|
int len = strlen(item_name(item));
|
||||||
if (len > max) {
|
if (((item)->type == I_FEATURE || (item)->type == I_GEOMETRY || (item)->type == I_PARAM) && (len > max)){
|
||||||
max = len;
|
max = len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -880,7 +902,13 @@ static void redraw_menu() {
|
||||||
*mp++ = '=';
|
*mp++ = '=';
|
||||||
*mp++ = (osd_state == PARAM) ? sel_open : sel_none;
|
*mp++ = (osd_state == PARAM) ? sel_open : sel_none;
|
||||||
strcpy(mp, get_param_string((param_menu_item_t *)item));
|
strcpy(mp, get_param_string((param_menu_item_t *)item));
|
||||||
mp += strlen(mp);
|
int param_len = strlen(mp);
|
||||||
|
for (int j=0; j < param_len; j++) {
|
||||||
|
if (*mp == '_') {
|
||||||
|
*mp = ' ';
|
||||||
|
}
|
||||||
|
mp++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*mp++ = sel_close;
|
*mp++ = sel_close;
|
||||||
*mp++ = '\0';
|
*mp++ = '\0';
|
||||||
|
@ -1212,6 +1240,389 @@ void osd_clear() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void save_profile(char *path, char *buffer, char *default_buffer, char *sub_default_buffer)
|
||||||
|
{
|
||||||
|
char *pointer = buffer;
|
||||||
|
char param_string[80];
|
||||||
|
param_t *param;
|
||||||
|
int current_mode7 = geometry_get_mode();
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (default_buffer != NULL) {
|
||||||
|
if (get_feature(F_AUTOSWITCH) == AUTOSWITCH_MODE7) {
|
||||||
|
|
||||||
|
geometry_set_mode(1);
|
||||||
|
cpld->set_mode(1);
|
||||||
|
|
||||||
|
sprintf(pointer, "sampling7=");
|
||||||
|
pointer += strlen(pointer);
|
||||||
|
i = 0;
|
||||||
|
for(;;) {
|
||||||
|
param = cpld->get_params() + i;
|
||||||
|
if (param->key < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sprintf(pointer, "%d,", cpld->get_value(param->key));
|
||||||
|
pointer += strlen(pointer);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
sprintf(pointer - 1, "\r\n");
|
||||||
|
pointer += strlen(pointer);
|
||||||
|
|
||||||
|
sprintf(pointer, "geometry7=");
|
||||||
|
pointer += strlen(pointer);
|
||||||
|
i = 0;
|
||||||
|
for(;;) {
|
||||||
|
param = geometry_get_params() + i;
|
||||||
|
if (param->key < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sprintf(pointer, "%d,", geometry_get_value(param->key));
|
||||||
|
pointer += strlen(pointer);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
sprintf(pointer - 1, "\r\n");
|
||||||
|
pointer += strlen(pointer);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
geometry_set_mode(0);
|
||||||
|
cpld->set_mode(0);
|
||||||
|
|
||||||
|
sprintf(pointer, "sampling=");
|
||||||
|
pointer += strlen(pointer);
|
||||||
|
i = 0;
|
||||||
|
for(;;) {
|
||||||
|
param = cpld->get_params() + i;
|
||||||
|
if (param->key < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sprintf(pointer, "%d,", cpld->get_value(param->key));
|
||||||
|
pointer += strlen(pointer);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
sprintf(pointer - 1, "\r\n");
|
||||||
|
pointer += strlen(pointer);
|
||||||
|
|
||||||
|
sprintf(pointer, "geometry=");
|
||||||
|
pointer += strlen(pointer);
|
||||||
|
i = 0;
|
||||||
|
for(;;) {
|
||||||
|
param = geometry_get_params() + i;
|
||||||
|
if (param->key < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sprintf(pointer, "%d,", geometry_get_value(param->key));
|
||||||
|
pointer += strlen(pointer);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
sprintf(pointer - 1, "\r\n");
|
||||||
|
pointer += strlen(pointer);
|
||||||
|
|
||||||
|
geometry_set_mode(current_mode7);
|
||||||
|
cpld->set_mode(current_mode7);
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
for(;;) {
|
||||||
|
if (features[i].key < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ((default_buffer != NULL && i != F_RESOLUTION && i != F_INTERPOLATION && i != F_PROFILE && i != F_SUBPROFILE && (i != F_AUTOSWITCH || sub_default_buffer == NULL))
|
||||||
|
|| (default_buffer == NULL && i == F_AUTOSWITCH)) {
|
||||||
|
strcpy(param_string, features[i].name);
|
||||||
|
for(int j = 0; j< strlen(param_string); j++) {
|
||||||
|
param_string[j] = tolower(param_string[j]);
|
||||||
|
if (param_string[j] == ' ') {
|
||||||
|
param_string[j] = '_';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sprintf(pointer, "%s=%d", param_string, get_feature(i));
|
||||||
|
if (strstr(default_buffer, pointer) == NULL) {
|
||||||
|
if (sub_default_buffer) {
|
||||||
|
if (strstr(sub_default_buffer, pointer) == NULL) {
|
||||||
|
log_info("Writing sub profile entry: %s", pointer);
|
||||||
|
pointer += strlen(pointer);
|
||||||
|
sprintf(pointer, "\r\n");
|
||||||
|
pointer += 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log_info("Writing profile entry: %s", pointer);
|
||||||
|
pointer += strlen(pointer);
|
||||||
|
sprintf(pointer, "\r\n");
|
||||||
|
pointer += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
file_save(path, buffer, pointer - buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_single_profile(char *buffer) {
|
||||||
|
char param_string[80];
|
||||||
|
char *prop;
|
||||||
|
int current_mode7 = geometry_get_mode();
|
||||||
|
int i;
|
||||||
|
if (buffer[0] == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int m7 = 0; m7 < 2; m7++) {
|
||||||
|
geometry_set_mode(m7);
|
||||||
|
cpld->set_mode(m7);
|
||||||
|
|
||||||
|
prop = get_prop(buffer, m7 ? "sampling7" : "sampling");
|
||||||
|
if (prop) {
|
||||||
|
char *prop2 = strtok(prop, ",");
|
||||||
|
int i = 0;
|
||||||
|
while (prop2) {
|
||||||
|
param_t *param;
|
||||||
|
param = cpld->get_params() + i;
|
||||||
|
if (param->key < 0) {
|
||||||
|
log_warn("Too many sampling sub-params, ignoring the rest");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int val = atoi(prop2);
|
||||||
|
log_debug("cpld: %s = %d", param->name, val);
|
||||||
|
cpld->set_value(param->key, val);
|
||||||
|
prop2 = strtok(NULL, ",");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prop = get_prop(buffer, m7 ? "geometry7" : "geometry");
|
||||||
|
if (prop) {
|
||||||
|
char *prop2 = strtok(prop, ",");
|
||||||
|
int i = 0;
|
||||||
|
while (prop2) {
|
||||||
|
param_t *param;
|
||||||
|
param = geometry_get_params() + i;
|
||||||
|
if (param->key < 0) {
|
||||||
|
log_warn("Too many sampling sub-params, ignoring the rest");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int val = atoi(prop2);
|
||||||
|
log_debug("geometry: %s = %d", param->name, val);
|
||||||
|
geometry_set_value(param->key, val);
|
||||||
|
prop2 = strtok(NULL, ",");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
geometry_set_mode(current_mode7);
|
||||||
|
cpld->set_mode(current_mode7);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
for(;;) {
|
||||||
|
if (features[i].key < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (i != F_RESOLUTION && i != F_INTERPOLATION && i != F_PROFILE && i != F_SUBPROFILE) {
|
||||||
|
strcpy(param_string, features[i].name);
|
||||||
|
for(int j = 0; j< strlen(param_string); j++) {
|
||||||
|
param_string[j] = tolower(param_string[j]);
|
||||||
|
if (param_string[j] == ' ') {
|
||||||
|
param_string[j] = '_';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prop = get_prop(buffer, param_string);
|
||||||
|
if (prop) {
|
||||||
|
int val = atoi(prop);
|
||||||
|
set_feature(i, val);
|
||||||
|
log_debug("profile: %s = %d",param_string, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Properties below this point are not updateable in the UI
|
||||||
|
prop = get_prop(buffer, "keymap");
|
||||||
|
if (prop) {
|
||||||
|
int i = 0;
|
||||||
|
while (*prop) {
|
||||||
|
int val = (*prop++) - '1' + OSD_SW1;
|
||||||
|
if (val >= OSD_SW1 && val <= OSD_SW3) {
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
key_enter = val;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
key_menu_up = val;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
key_menu_down = val;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
key_value_dec = val;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
key_value_inc = val;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
key_cal = val;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
key_capture = val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Implement a return property that selects whether the menu
|
||||||
|
// return links is placed at the start (0) or end (1).
|
||||||
|
prop = get_prop(buffer, "return");
|
||||||
|
if (prop) {
|
||||||
|
return_at_end = atoi(prop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable CPLDv2 specific features for CPLDv1
|
||||||
|
if (((cpld->get_version() >> VERSION_MAJOR_BIT) & 0x0F) < 2) {
|
||||||
|
features[F_DEINTERLACE].max = DEINTERLACE_MA4;
|
||||||
|
if (get_feature(F_DEINTERLACE) > features[F_DEINTERLACE].max) {
|
||||||
|
set_feature(F_DEINTERLACE, DEINTERLACE_MA1); // TODO: Decide whether this is the right fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_autoswitch_geometry(char *buffer, int index)
|
||||||
|
{
|
||||||
|
char *prop;
|
||||||
|
// Default properties
|
||||||
|
prop = get_prop(buffer, "geometry");
|
||||||
|
if (prop) {
|
||||||
|
char *prop2 = strtok(prop, ",");
|
||||||
|
int i = 0;
|
||||||
|
while (prop2) {
|
||||||
|
param_t *param;
|
||||||
|
param = geometry_get_params() + i;
|
||||||
|
if (param->key < 0) {
|
||||||
|
log_warn("Too many sampling sub-params, ignoring the rest");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int val = atoi(prop2);
|
||||||
|
if (i == CLOCK) {
|
||||||
|
autoswitch_info[index].clock = val;
|
||||||
|
log_debug("autoswitch: %s = %d", param->name, val);
|
||||||
|
}
|
||||||
|
if (i == LINE_LEN) {
|
||||||
|
autoswitch_info[index].line_len = val;
|
||||||
|
log_debug("autoswitch: %s = %d", param->name, val);
|
||||||
|
}
|
||||||
|
if (i == CLOCK_PPM) {
|
||||||
|
autoswitch_info[index].clock_ppm = val;
|
||||||
|
log_debug("autoswitch: %s = %d", param->name, val);
|
||||||
|
}
|
||||||
|
if (i == LINES_FRAME) {
|
||||||
|
autoswitch_info[index].lines_per_frame = val;
|
||||||
|
log_debug("autoswitch: %s = %d", param->name, val);
|
||||||
|
}
|
||||||
|
if (i == SYNC_TYPE) {
|
||||||
|
autoswitch_info[index].sync_type = val;
|
||||||
|
log_debug("autoswitch: %s = %d", param->name, val);
|
||||||
|
}
|
||||||
|
prop2 = strtok(NULL, ",");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double line_time = (double) autoswitch_info[index].line_len * 1000000000 / autoswitch_info[index].clock;
|
||||||
|
double window = (double) autoswitch_info[index].clock_ppm * line_time / 1000000;
|
||||||
|
autoswitch_info[index].lower_limit = (int) line_time - window;
|
||||||
|
autoswitch_info[index].upper_limit = (int) line_time + window;
|
||||||
|
log_info("Autoswitch timings %d (%s) = %d, %d, %d, %d, %d", index, sub_profile_names[index], autoswitch_info[index].lower_limit, (int) line_time,
|
||||||
|
autoswitch_info[index].upper_limit, autoswitch_info[index].lines_per_frame, autoswitch_info[index].sync_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_profile(int profile_number) {
|
||||||
|
process_single_profile(default_buffer);
|
||||||
|
if (has_sub_profiles[profile_number]) {
|
||||||
|
process_single_profile(sub_default_buffer);
|
||||||
|
} else {
|
||||||
|
process_single_profile(main_buffer);
|
||||||
|
}
|
||||||
|
// The menu's are constructed with the back link in at the start
|
||||||
|
// TODO: there are still some corner cases where
|
||||||
|
if (return_at_end) {
|
||||||
|
cycle_menu(&main_menu);
|
||||||
|
cycle_menu(&info_menu);
|
||||||
|
cycle_menu(&processing_menu);
|
||||||
|
cycle_menu(&settings_menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_sub_profile(int profile_number, int sub_profile_number) {
|
||||||
|
if (has_sub_profiles[profile_number]) {
|
||||||
|
int saved_autoswitch = get_feature(F_AUTOSWITCH); // save autoswitch so it can be disabled to manually switch sub profiles
|
||||||
|
process_single_profile(default_buffer);
|
||||||
|
process_single_profile(sub_default_buffer);
|
||||||
|
set_feature(F_AUTOSWITCH, saved_autoswitch);
|
||||||
|
process_single_profile(sub_profile_buffers[sub_profile_number]);
|
||||||
|
// The menu's are constructed with the back link in at the start
|
||||||
|
// TODO: there are still some corner cases where
|
||||||
|
if (return_at_end) {
|
||||||
|
cycle_menu(&main_menu);
|
||||||
|
cycle_menu(&info_menu);
|
||||||
|
cycle_menu(&processing_menu);
|
||||||
|
cycle_menu(&settings_menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_profiles(int profile_number) {
|
||||||
|
unsigned int bytes ;
|
||||||
|
main_buffer[0] = 0;
|
||||||
|
features[F_SUBPROFILE].max = 0;
|
||||||
|
strcpy(sub_profile_names[0], NOT_FOUND_STRING);
|
||||||
|
sub_profile_buffers[0][0] = 0;
|
||||||
|
if (has_sub_profiles[profile_number]) {
|
||||||
|
bytes = file_read_profile(profile_names[profile_number], DEFAULT_STRING, 1, sub_default_buffer, MAX_BUFFER_SIZE - 4);
|
||||||
|
if (bytes) {
|
||||||
|
size_t count = 0;
|
||||||
|
scan_sub_profiles(sub_profile_names, profile_names[profile_number], &count);
|
||||||
|
if (count) {
|
||||||
|
features[F_SUBPROFILE].max = count - 1;
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
file_read_profile(profile_names[profile_number], sub_profile_names[i], 0, sub_profile_buffers[i], MAX_BUFFER_SIZE - 4);
|
||||||
|
get_autoswitch_geometry(sub_profile_buffers[i], i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
features[F_SUBPROFILE].max = 0;
|
||||||
|
strcpy(sub_profile_names[0], NONE_STRING);
|
||||||
|
sub_profile_buffers[0][0] = 0;
|
||||||
|
if (strcmp(profile_names[profile_number], NOT_FOUND_STRING) != 0) {
|
||||||
|
file_read_profile(profile_names[profile_number], NULL, 1, main_buffer, MAX_BUFFER_SIZE - 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int sub_profiles_available(int profile_number) {
|
||||||
|
return has_sub_profiles[profile_number];
|
||||||
|
}
|
||||||
|
|
||||||
|
int autoswitch_detect(int one_line_time_ns, int lines_per_frame, int sync_type) {
|
||||||
|
if (has_sub_profiles[get_feature(F_PROFILE)]) {
|
||||||
|
log_info("Looking for autoswitch match = %d, %d, %d", one_line_time_ns, lines_per_frame, sync_type);
|
||||||
|
for (int i=0; i <= features[F_SUBPROFILE].max; i++) {
|
||||||
|
if ( one_line_time_ns > autoswitch_info[i].lower_limit
|
||||||
|
&& one_line_time_ns < autoswitch_info[i].upper_limit
|
||||||
|
&& lines_per_frame == autoswitch_info[i].lines_per_frame
|
||||||
|
&& sync_type == autoswitch_info[i].sync_type ) {
|
||||||
|
log_info("Autoswitch match: %s (%d) = %d, %d, %d, %d", sub_profile_names[i], i, autoswitch_info[i].lower_limit,
|
||||||
|
autoswitch_info[i].upper_limit, autoswitch_info[i].lines_per_frame, autoswitch_info[i].sync_type );
|
||||||
|
return (i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
void osd_set(int line, int attr, char *text) {
|
void osd_set(int line, int attr, char *text) {
|
||||||
if (!active) {
|
if (!active) {
|
||||||
active = 1;
|
active = 1;
|
||||||
|
@ -1225,7 +1636,6 @@ void osd_set(int line, int attr, char *text) {
|
||||||
}
|
}
|
||||||
strncpy(buffer + line * LINELEN, text, len);
|
strncpy(buffer + line * LINELEN, text, len);
|
||||||
osd_update((uint32_t *) (capinfo->v_adjust * capinfo->pitch + capinfo->h_adjust + capinfo->fb), capinfo->pitch);
|
osd_update((uint32_t *) (capinfo->v_adjust * capinfo->pitch + capinfo->h_adjust + capinfo->fb), capinfo->pitch);
|
||||||
// osd_update((uint32_t *) (capinfo->fb), capinfo->pitch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int osd_active() {
|
int osd_active() {
|
||||||
|
@ -1248,7 +1658,8 @@ int osd_key(int key) {
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
static int cal_count;
|
static int cal_count;
|
||||||
static int last_vsync;
|
static int last_vsync;
|
||||||
|
char path[256];
|
||||||
|
char msg[256];
|
||||||
switch (osd_state) {
|
switch (osd_state) {
|
||||||
|
|
||||||
case IDLE:
|
case IDLE:
|
||||||
|
@ -1377,6 +1788,23 @@ int osd_key(int key) {
|
||||||
redraw_menu();
|
redraw_menu();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case I_SAVE:
|
||||||
|
if (has_sub_profiles[get_feature(F_PROFILE)]) {
|
||||||
|
sprintf(path, "/Profiles/%s/Default.txt", profile_names[get_feature(F_PROFILE)]);
|
||||||
|
save_profile(path, save_buffer, NULL, NULL);
|
||||||
|
sprintf(path, "/Profiles/%s/%s.txt", profile_names[get_feature(F_PROFILE)], sub_profile_names[get_feature(F_SUBPROFILE)]);
|
||||||
|
save_profile(path, save_buffer, default_buffer, sub_default_buffer);
|
||||||
|
} else {
|
||||||
|
sprintf(path, "/Profiles/%s.txt", profile_names[get_feature(F_PROFILE)]);
|
||||||
|
save_profile(path, save_buffer, default_buffer, NULL);
|
||||||
|
}
|
||||||
|
sprintf(msg, "Saved: %s", path + 10);
|
||||||
|
set_status_message(msg);
|
||||||
|
load_profiles(get_feature(F_PROFILE));
|
||||||
|
break;
|
||||||
|
case I_RESTORE:
|
||||||
|
set_status_message("Not Yet Implemented");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else if (key == key_menu_up) {
|
} else if (key == key_menu_up) {
|
||||||
// PREVIOUS
|
// PREVIOUS
|
||||||
|
@ -1442,339 +1870,6 @@ int osd_key(int key) {
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
void get_props_sample_geometry(char *buffer)
|
|
||||||
{
|
|
||||||
char *prop;
|
|
||||||
// Initialize the CPLD sampling points
|
|
||||||
for (int p = 0; p < 2; p++) { //reads geometry twice to workaround limiting problem
|
|
||||||
for (int m7 = 0; m7 <= 1; m7++) {
|
|
||||||
char *propname;
|
|
||||||
if (m7) {
|
|
||||||
// Mode 7 properties
|
|
||||||
propname = p ? "geometry7" : "sampling7";
|
|
||||||
prop = get_prop(buffer, propname);
|
|
||||||
} else {
|
|
||||||
// Default properties
|
|
||||||
propname = p ? "geometry" : "sampling";
|
|
||||||
prop = get_prop(buffer, propname);
|
|
||||||
if (!prop) {
|
|
||||||
// All a fallback to an "06" suffix
|
|
||||||
propname = p ? "geometry06" : "sampling06";
|
|
||||||
prop = get_prop(buffer, propname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (prop) {
|
|
||||||
cpld->set_mode(m7);
|
|
||||||
geometry_set_mode(m7);
|
|
||||||
log_debug("config.txt: %s = %s", propname, prop);
|
|
||||||
char *prop2 = strtok(prop, ",");
|
|
||||||
int i = 0;
|
|
||||||
while (prop2) {
|
|
||||||
param_t *param;
|
|
||||||
if (p == 0) {
|
|
||||||
param = cpld->get_params() + i;
|
|
||||||
} else {
|
|
||||||
param = geometry_get_params() + i;
|
|
||||||
}
|
|
||||||
if (param->key < 0) {
|
|
||||||
log_warn("Too many sampling sub-params, ignoring the rest");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int val = atoi(prop2);
|
|
||||||
if (p == 0) {
|
|
||||||
log_debug("cpld: %s = %d", param->name, val);
|
|
||||||
cpld->set_value(param->key, val);
|
|
||||||
} else {
|
|
||||||
log_debug("geometry: %s = %d", param->name, val);
|
|
||||||
geometry_set_value(param->key, val);
|
|
||||||
}
|
|
||||||
prop2 = strtok(NULL, ",");
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void process_single_profile(char *buffer) {
|
|
||||||
char *prop;
|
|
||||||
if (buffer[0] != 0) {
|
|
||||||
log_debug("Processing profile: %x", buffer);
|
|
||||||
// Initialize the OSD features
|
|
||||||
prop = get_prop(buffer, "deinterlace");
|
|
||||||
if (prop) {
|
|
||||||
int val = atoi(prop);
|
|
||||||
set_feature(F_DEINTERLACE, val);
|
|
||||||
log_debug("config.txt: deinterlace = %d", val);
|
|
||||||
}
|
|
||||||
prop = get_prop(buffer, "scaling");
|
|
||||||
if (prop) {
|
|
||||||
int val = atoi(prop);
|
|
||||||
set_feature(F_SCALING, val);
|
|
||||||
log_debug("config.txt: scaling = %d", val);
|
|
||||||
}
|
|
||||||
prop = get_prop(buffer, "palette");
|
|
||||||
if (prop) {
|
|
||||||
int val = atoi(prop);
|
|
||||||
set_feature(F_PALETTE, val);
|
|
||||||
log_debug("config.txt: palette = %d", val);
|
|
||||||
}
|
|
||||||
prop = get_prop(buffer, "control");
|
|
||||||
if (prop) {
|
|
||||||
int val = atoi(prop);
|
|
||||||
set_feature(F_PALETTECONTROL, val);
|
|
||||||
log_debug("config.txt: palette ctrl = %d", val);
|
|
||||||
}
|
|
||||||
prop = get_prop(buffer, "scanlines");
|
|
||||||
if (prop) {
|
|
||||||
int val = atoi(prop);
|
|
||||||
set_feature(F_SCANLINES, val);
|
|
||||||
log_debug("config.txt: scanlines = %d", val);
|
|
||||||
}
|
|
||||||
prop = get_prop(buffer, "scanlinelevel");
|
|
||||||
if (prop) {
|
|
||||||
int val = atoi(prop);
|
|
||||||
set_feature(F_SCANLINESINT, val);
|
|
||||||
log_debug("config.txt: scanline level = %d", val);
|
|
||||||
}
|
|
||||||
prop = get_prop(buffer, "vsynctype");
|
|
||||||
if (prop) {
|
|
||||||
int val = atoi(prop);
|
|
||||||
set_feature(F_VSYNCTYPE, val);
|
|
||||||
log_debug("config.txt: vsynctype = %d", val);
|
|
||||||
}
|
|
||||||
prop = get_prop(buffer, "mux");
|
|
||||||
if (prop) {
|
|
||||||
int val = atoi(prop);
|
|
||||||
set_feature(F_MUX, val);
|
|
||||||
log_debug("config.txt: mux = %d", val);
|
|
||||||
}
|
|
||||||
prop = get_prop(buffer, "vsync");
|
|
||||||
if (prop) {
|
|
||||||
int val = atoi(prop);
|
|
||||||
set_feature(F_VSYNC, val);
|
|
||||||
log_debug("config.txt: vsync = %d", val);
|
|
||||||
}
|
|
||||||
prop = get_prop(buffer, "vlockmode");
|
|
||||||
if (prop) {
|
|
||||||
int val = atoi(prop);
|
|
||||||
set_feature(F_VLOCKMODE, val);
|
|
||||||
log_debug("config.txt: vlockmode = %d", val);
|
|
||||||
}
|
|
||||||
prop = get_prop(buffer, "vlockline");
|
|
||||||
if (prop) {
|
|
||||||
int val = atoi(prop);
|
|
||||||
set_feature(F_VLOCKLINE, val);
|
|
||||||
log_debug("config.txt: vlockline = %d", val);
|
|
||||||
}
|
|
||||||
prop = get_prop(buffer, "autoswitch");
|
|
||||||
if (prop) {
|
|
||||||
int val = atoi(prop);
|
|
||||||
set_feature(F_AUTOSWITCH, val);
|
|
||||||
log_debug("config.txt: autoswitch = %d", val);
|
|
||||||
}
|
|
||||||
#ifdef MULTI_BUFFER
|
|
||||||
prop = get_prop(buffer, "nbuffers");
|
|
||||||
if (prop) {
|
|
||||||
int val = atoi(prop);
|
|
||||||
set_feature(F_NBUFFERS, val);
|
|
||||||
log_debug("config.txt: nbuffers = %d", val);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
prop = get_prop(buffer, "debug");
|
|
||||||
if (prop) {
|
|
||||||
int val = atoi(prop);
|
|
||||||
set_feature(F_DEBUG, val);
|
|
||||||
log_debug("config.txt: debug = %d", val);
|
|
||||||
}
|
|
||||||
|
|
||||||
get_props_sample_geometry(buffer);
|
|
||||||
|
|
||||||
// Properties below this point are not updateable in the UI
|
|
||||||
prop = get_prop(buffer, "keymap");
|
|
||||||
if (prop) {
|
|
||||||
int i = 0;
|
|
||||||
while (*prop) {
|
|
||||||
int val = (*prop++) - '1' + OSD_SW1;
|
|
||||||
if (val >= OSD_SW1 && val <= OSD_SW3) {
|
|
||||||
switch (i) {
|
|
||||||
case 0:
|
|
||||||
key_enter = val;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
key_menu_up = val;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
key_menu_down = val;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
key_value_dec = val;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
key_value_inc = val;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
key_cal = val;
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
key_capture = val;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Implement a return property that selects whether the menu
|
|
||||||
// return links is placed at the start (0) or end (1).
|
|
||||||
prop = get_prop(buffer, "return");
|
|
||||||
if (prop) {
|
|
||||||
return_at_end = atoi(prop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Disable CPLDv2 specific features for CPLDv1
|
|
||||||
if (((cpld->get_version() >> VERSION_MAJOR_BIT) & 0x0F) < 2) {
|
|
||||||
features[F_DEINTERLACE].max = DEINTERLACE_MA4;
|
|
||||||
if (get_feature(F_DEINTERLACE) > features[F_DEINTERLACE].max) {
|
|
||||||
set_feature(F_DEINTERLACE, DEINTERLACE_MA1); // TODO: Decide whether this is the right fallback
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_autoswitch_geometry(char *buffer, int index)
|
|
||||||
{
|
|
||||||
char *prop;
|
|
||||||
char *propname;
|
|
||||||
// Default properties
|
|
||||||
propname = "geometry";
|
|
||||||
prop = get_prop(buffer, propname);
|
|
||||||
if (!prop) {
|
|
||||||
// All a fallback to an "06" suffix
|
|
||||||
propname = "geometry06";
|
|
||||||
prop = get_prop(buffer, propname);
|
|
||||||
}
|
|
||||||
if (prop) {
|
|
||||||
log_debug("config.txt: %s = %s", propname, prop);
|
|
||||||
char *prop2 = strtok(prop, ",");
|
|
||||||
int i = 0;
|
|
||||||
while (prop2) {
|
|
||||||
param_t *param;
|
|
||||||
param = geometry_get_params() + i;
|
|
||||||
if (param->key < 0) {
|
|
||||||
log_warn("Too many sampling sub-params, ignoring the rest");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int val = atoi(prop2);
|
|
||||||
if (i == CLOCK) {
|
|
||||||
autoswitch_info[index].clock = val;
|
|
||||||
log_debug("autoswitch: %s = %d", param->name, val);
|
|
||||||
}
|
|
||||||
if (i == LINE_LEN) {
|
|
||||||
autoswitch_info[index].line_len = val;
|
|
||||||
log_debug("autoswitch: %s = %d", param->name, val);
|
|
||||||
}
|
|
||||||
if (i == CLOCK_PPM) {
|
|
||||||
autoswitch_info[index].clock_ppm = val;
|
|
||||||
log_debug("autoswitch: %s = %d", param->name, val);
|
|
||||||
}
|
|
||||||
if (i == LINES_FRAME) {
|
|
||||||
autoswitch_info[index].lines_per_frame = val;
|
|
||||||
log_debug("autoswitch: %s = %d", param->name, val);
|
|
||||||
}
|
|
||||||
if (i == SYNC_TYPE) {
|
|
||||||
autoswitch_info[index].sync_type = val;
|
|
||||||
log_debug("autoswitch: %s = %d", param->name, val);
|
|
||||||
}
|
|
||||||
prop2 = strtok(NULL, ",");
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
double line_time = (double) autoswitch_info[index].line_len * 1000000000 / autoswitch_info[index].clock;
|
|
||||||
double window = (double) autoswitch_info[index].clock_ppm * line_time / 1000000;
|
|
||||||
autoswitch_info[index].lower_limit = (int) line_time - window;
|
|
||||||
autoswitch_info[index].upper_limit = (int) line_time + window;
|
|
||||||
log_info("Autoswitch timings %d (%s) = %d, %d, %d, %d, %d", index, sub_profile_names[index], autoswitch_info[index].lower_limit, (int) line_time,
|
|
||||||
autoswitch_info[index].upper_limit, autoswitch_info[index].lines_per_frame, autoswitch_info[index].sync_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
void process_sub_profile(int profile_number, int sub_profile_number) {
|
|
||||||
if (has_sub_profiles[profile_number]) {
|
|
||||||
//int saved_autoswitch = get_feature(F_AUTOSWITCH); // save autoswitch so it can be disabled to manually switch sub profiles
|
|
||||||
//process_single_profile(default_buffer);
|
|
||||||
//process_single_profile(sub_default_buffer);
|
|
||||||
//set_feature(F_AUTOSWITCH, saved_autoswitch);
|
|
||||||
process_single_profile(sub_profile_buffers[sub_profile_number]);
|
|
||||||
// The menu's are constructed with the back link in at the start
|
|
||||||
// TODO: there are still some corner cases where
|
|
||||||
if (return_at_end) {
|
|
||||||
cycle_menu(&main_menu);
|
|
||||||
cycle_menu(&info_menu);
|
|
||||||
cycle_menu(&processing_menu);
|
|
||||||
cycle_menu(&settings_menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void load_profiles(int profile_number) {
|
|
||||||
unsigned int bytes ;
|
|
||||||
if (default_buffer[0] != 0) {
|
|
||||||
process_single_profile(default_buffer); //set everything back to default first
|
|
||||||
}
|
|
||||||
features[F_SUBPROFILE].max = 0;
|
|
||||||
strcpy(sub_profile_names[0], NOT_FOUND_STRING);
|
|
||||||
sub_profile_buffers[0][0] = 0;
|
|
||||||
if (has_sub_profiles[profile_number]) {
|
|
||||||
bytes = file_read_profile(profile_names[profile_number], DEFAULT_STRING, 1, sub_default_buffer, MAX_BUFFER_SIZE - 4);
|
|
||||||
if (bytes) {
|
|
||||||
process_single_profile(sub_default_buffer); //set everything back to sub-default first
|
|
||||||
size_t count = 0;
|
|
||||||
scan_sub_profiles(sub_profile_names, profile_names[profile_number], &count);
|
|
||||||
if (count) {
|
|
||||||
features[F_SUBPROFILE].max = count - 1;
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
file_read_profile(profile_names[profile_number], sub_profile_names[i], 0, sub_profile_buffers[i], MAX_BUFFER_SIZE - 4);
|
|
||||||
get_autoswitch_geometry(sub_profile_buffers[i], i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
features[F_SUBPROFILE].max = 0;
|
|
||||||
strcpy(sub_profile_names[0], NONE_STRING);
|
|
||||||
sub_profile_buffers[0][0] = 0;
|
|
||||||
if (strcmp(profile_names[profile_number], NOT_FOUND_STRING) != 0) {
|
|
||||||
bytes = file_read_profile(profile_names[profile_number], NULL, 1, main_buffer, MAX_BUFFER_SIZE - 4);
|
|
||||||
if (bytes) {
|
|
||||||
process_single_profile(main_buffer); //override defaults
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The menu's are constructed with the back link in at the start
|
|
||||||
// TODO: there are still some corner cases where
|
|
||||||
if (return_at_end) {
|
|
||||||
cycle_menu(&main_menu);
|
|
||||||
cycle_menu(&info_menu);
|
|
||||||
cycle_menu(&processing_menu);
|
|
||||||
cycle_menu(&settings_menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int sub_profiles_available(int profile_number) {
|
|
||||||
return has_sub_profiles[profile_number];
|
|
||||||
}
|
|
||||||
|
|
||||||
int autoswitch_detect(int one_line_time_ns, int lines_per_frame, int sync_type) {
|
|
||||||
if (has_sub_profiles[get_feature(F_PROFILE)]) {
|
|
||||||
log_info("Looking for autoswitch match = %d, %d, %d", one_line_time_ns, lines_per_frame, sync_type);
|
|
||||||
for (int i=0; i <= features[F_SUBPROFILE].max; i++) {
|
|
||||||
if ( one_line_time_ns > autoswitch_info[i].lower_limit
|
|
||||||
&& one_line_time_ns < autoswitch_info[i].upper_limit
|
|
||||||
&& lines_per_frame == autoswitch_info[i].lines_per_frame
|
|
||||||
&& sync_type == autoswitch_info[i].sync_type ) {
|
|
||||||
log_info("Autoswitch match: %s (%d) = %d, %d, %d, %d", sub_profile_names[i], i, autoswitch_info[i].lower_limit,
|
|
||||||
autoswitch_info[i].upper_limit, autoswitch_info[i].lines_per_frame, autoswitch_info[i].sync_type );
|
|
||||||
return (i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void osd_init() {
|
void osd_init() {
|
||||||
char *prop = NULL;
|
char *prop = NULL;
|
||||||
|
|
|
@ -16,9 +16,9 @@ extern int paletteFlags;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
HDMI_ORIGINAL,
|
HDMI_ORIGINAL,
|
||||||
|
HDMI_EXACT,
|
||||||
HDMI_SLOW_2000PPM,
|
HDMI_SLOW_2000PPM,
|
||||||
HDMI_SLOW_500PPM,
|
HDMI_SLOW_500PPM,
|
||||||
HDMI_EXACT,
|
|
||||||
HDMI_FAST_500PPM,
|
HDMI_FAST_500PPM,
|
||||||
HDMI_FAST_2000PPM
|
HDMI_FAST_2000PPM
|
||||||
};
|
};
|
||||||
|
@ -81,8 +81,8 @@ enum {
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
AUTOSWITCH_OFF,
|
AUTOSWITCH_OFF,
|
||||||
AUTOSWITCH_MODE7,
|
|
||||||
AUTOSWITCH_PC,
|
AUTOSWITCH_PC,
|
||||||
|
AUTOSWITCH_MODE7,
|
||||||
NUM_AUTOSWITCHES
|
NUM_AUTOSWITCHES
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -109,8 +109,10 @@ void osd_update_fast(uint32_t *osd_base, int bytes_per_line);
|
||||||
int osd_active();
|
int osd_active();
|
||||||
int osd_key(int key);
|
int osd_key(int key);
|
||||||
void osd_update_palette();
|
void osd_update_palette();
|
||||||
|
void process_profile(int profile_number);
|
||||||
void process_sub_profile(int profile_number, int sub_profile_number);
|
void process_sub_profile(int profile_number, int sub_profile_number);
|
||||||
void load_profiles(int profile_number);
|
void load_profiles(int profile_number);
|
||||||
|
void process_single_profile(char *buffer);
|
||||||
uint32_t osd_get_palette(int index);
|
uint32_t osd_get_palette(int index);
|
||||||
int autoswitch_detect(int one_line_time_ns, int lines_per_frame, int sync_type);
|
int autoswitch_detect(int one_line_time_ns, int lines_per_frame, int sync_type);
|
||||||
int sub_profiles_available();
|
int sub_profiles_available();
|
||||||
|
|
|
@ -65,6 +65,9 @@ static double pllh_clock = 0;
|
||||||
static int genlocked = 0;
|
static int genlocked = 0;
|
||||||
static int resync_count = 0;
|
static int resync_count = 0;
|
||||||
static int target_difference = 0;
|
static int target_difference = 0;
|
||||||
|
static int source_vsync_freq_hz = 0;
|
||||||
|
static int display_vsync_freq_hz = 0;
|
||||||
|
static char status[256];
|
||||||
|
|
||||||
// =============================================================
|
// =============================================================
|
||||||
// OSD parameters
|
// OSD parameters
|
||||||
|
@ -73,15 +76,15 @@ static int profile = 0;
|
||||||
static int subprofile = 0;
|
static int subprofile = 0;
|
||||||
static int resolution = 0;
|
static int resolution = 0;
|
||||||
static char resolution_name[MAX_RESOLUTION_WIDTH];
|
static char resolution_name[MAX_RESOLUTION_WIDTH];
|
||||||
static int interpolation = 1;
|
static int interpolation = 0;
|
||||||
static int elk = 0;
|
static int elk = 0;
|
||||||
static int debug = 0;
|
static int debug = 0;
|
||||||
static int autoswitch = 1;
|
static int autoswitch = 2;
|
||||||
static int scanlines = 0;
|
static int scanlines = 0;
|
||||||
static int scanlines_intensity = 0;
|
static int scanlines_intensity = 0;
|
||||||
static int deinterlace = 6;
|
static int deinterlace = 6;
|
||||||
static int vsync = 0;
|
static int vsync = 0;
|
||||||
static int vlockmode = 3;
|
static int vlockmode = 1;
|
||||||
static int vlockline = 10;
|
static int vlockline = 10;
|
||||||
static int vlockadj = 0;
|
static int vlockadj = 0;
|
||||||
static int lines_per_frame = 0;
|
static int lines_per_frame = 0;
|
||||||
|
@ -490,13 +493,23 @@ static void recalculate_hdmi_clock(int vlockmode) { // use local vsyncmode, not
|
||||||
double error_ppm = 1e6 * (error - 1.0);
|
double error_ppm = 1e6 * (error - 1.0);
|
||||||
|
|
||||||
double f2 = pllh_clock;
|
double f2 = pllh_clock;
|
||||||
if (vlockmode > 0) {
|
if (vlockmode != HDMI_ORIGINAL) {
|
||||||
f2 /= error;
|
f2 /= error;
|
||||||
double divisor = 1000.0;
|
switch (vlockmode) {
|
||||||
if (vlockmode == HDMI_SLOW_500PPM || vlockmode == HDMI_FAST_500PPM) {
|
case HDMI_SLOW_2000PPM:
|
||||||
divisor = 2000.0; //workaround 1000PPM actually now 500PPM
|
f2 /= 1.0 + ((double) 2 / 1000);
|
||||||
|
break;
|
||||||
|
case HDMI_SLOW_500PPM:
|
||||||
|
f2 /= 1.0 + ((double) 1 / 2000);
|
||||||
|
break;
|
||||||
|
case HDMI_FAST_500PPM:
|
||||||
|
f2 /= 1.0 + ((double) -1 / 2000);
|
||||||
|
break;
|
||||||
|
case HDMI_FAST_2000PPM:
|
||||||
|
f2 /= 1.0 + ((double) -2 / 1000);
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
f2 /= 1.0 + ((double) (HDMI_EXACT - vlockmode)) / divisor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sanity check HDMI pixel clock
|
// Sanity check HDMI pixel clock
|
||||||
|
@ -529,7 +542,8 @@ static void recalculate_hdmi_clock(int vlockmode) { // use local vsyncmode, not
|
||||||
log_debug(" Vsync error: %lf ppm", error_ppm);
|
log_debug(" Vsync error: %lf ppm", error_ppm);
|
||||||
log_debug(" Original PLLH: %lf MHz", pllh_clock);
|
log_debug(" Original PLLH: %lf MHz", pllh_clock);
|
||||||
log_debug(" Target PLLH: %lf MHz", f2);
|
log_debug(" Target PLLH: %lf MHz", f2);
|
||||||
|
source_vsync_freq_hz = (int) (source_vsync_freq + 0.5);
|
||||||
|
display_vsync_freq_hz = (int) (display_vsync_freq + 0.5);
|
||||||
// Calculate the new dividers
|
// Calculate the new dividers
|
||||||
int div = (int) (f2 / 19.2);
|
int div = (int) (f2 / 19.2);
|
||||||
int fract = (int) ((double)(1<<20) * (f2 / 19.2 - (double) div));
|
int fract = (int) ((double)(1<<20) * (f2 / 19.2 - (double) div));
|
||||||
|
@ -1538,8 +1552,9 @@ void setup_profile() {
|
||||||
|
|
||||||
log_info("Window: H = %d to %d, V = %d to %d, S = %s", hsync_comparison_lo, hsync_comparison_hi, vsync_comparison_lo, vsync_comparison_hi, sync_names[capinfo->sync_type]);
|
log_info("Window: H = %d to %d, V = %d to %d, S = %s", hsync_comparison_lo, hsync_comparison_hi, vsync_comparison_lo, vsync_comparison_hi, sync_names[capinfo->sync_type]);
|
||||||
}
|
}
|
||||||
|
void set_status_message(char *msg) {
|
||||||
|
strcpy(status, msg);
|
||||||
|
}
|
||||||
|
|
||||||
void rgb_to_hdmi_main() {
|
void rgb_to_hdmi_main() {
|
||||||
|
|
||||||
|
@ -1718,11 +1733,17 @@ void rgb_to_hdmi_main() {
|
||||||
|
|
||||||
if (osd_active()) {
|
if (osd_active()) {
|
||||||
if (clk_changed || (clkinfo.lines_per_frame != last_clkinfo.lines_per_frame) || (capinfo->sync_type < last_capinfo.sync_type)) {
|
if (clk_changed || (clkinfo.lines_per_frame != last_clkinfo.lines_per_frame) || (capinfo->sync_type < last_capinfo.sync_type)) {
|
||||||
sprintf(osdline, "%dHz %dPPM %d %s", adjusted_clock, clock_error_ppm, lines_per_frame, sync_names[capinfo->detected_sync_type & SYNC_BIT_MASK]);
|
sprintf(osdline, "%dHz %dPPM %d %s %dHz", adjusted_clock, clock_error_ppm, lines_per_frame, sync_names[capinfo->detected_sync_type & SYNC_BIT_MASK], source_vsync_freq_hz);
|
||||||
osd_set(1, 0, osdline);
|
osd_set(1, 0, osdline);
|
||||||
|
} else {
|
||||||
|
if (status[0] != 0) {
|
||||||
|
osd_set(1, 0, status);
|
||||||
|
status[0] = 0;
|
||||||
} else {
|
} else {
|
||||||
if (vlock_limited && (vlockmode != HDMI_ORIGINAL) && !reboot_required) {
|
if (vlock_limited && (vlockmode != HDMI_ORIGINAL) && !reboot_required) {
|
||||||
osd_set(1, 0, "V Lock disabled - Framerate/Profile mismatch");
|
sprintf(osdline, "V Lock disabled: Src=%dHz, Disp=%dHz", source_vsync_freq_hz, display_vsync_freq_hz);
|
||||||
|
osd_set(1, 0, osdline);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,5 +43,6 @@ void action_calibrate_auto();
|
||||||
|
|
||||||
// Status
|
// Status
|
||||||
int is_genlocked();
|
int is_genlocked();
|
||||||
|
void set_status_message(char *msg);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Ładowanie…
Reference in New Issue