kopia lustrzana https://github.com/hoglet67/RGBtoHDMI
2532 wiersze
78 KiB
C
2532 wiersze
78 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <inttypes.h>
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
#include "defs.h"
|
|
#include "cpld.h"
|
|
#include "geometry.h"
|
|
#include "osd.h"
|
|
#include "logging.h"
|
|
#include "rgb_to_hdmi.h"
|
|
#include "rgb_to_fb.h"
|
|
#include "rpi-gpio.h"
|
|
|
|
// The number of frames to compute differences over
|
|
#define NUM_CAL_FRAMES 10
|
|
|
|
#define DAC_UPDATE 1
|
|
#define NO_DAC_UPDATE 0
|
|
|
|
typedef struct {
|
|
int cpld_setup_mode;
|
|
int all_offsets;
|
|
int sp_offset[NUM_OFFSETS];
|
|
int half_px_delay; // 0 = off, 1 = on, all modes
|
|
int divider; // cpld divider, 0-3 which means 6,8,3,4
|
|
int range;
|
|
int full_px_delay; // 0..15
|
|
int filter_l;
|
|
int sub_c;
|
|
int alt_r;
|
|
int edge;
|
|
int clamptype;
|
|
int mux; // 0 = direct, 1 = via the 74LS08 buffer
|
|
int rate; // 0 = normal psync rate (3 bpp), 1 = double psync rate (6 bpp), 2 = sub-sample (odd), 3=sub-sample(even)
|
|
int terminate;
|
|
int coupling;
|
|
int dac_a;
|
|
int dac_b;
|
|
int dac_c;
|
|
int dac_d;
|
|
int dac_e;
|
|
int dac_f;
|
|
int dac_g;
|
|
int dac_h;
|
|
} config_t;
|
|
|
|
// Current calibration state for mode 0..6
|
|
static config_t set1_config;
|
|
|
|
// Current calibration state for mode 7
|
|
static config_t set2_config;
|
|
|
|
// Current configuration
|
|
static config_t *config;
|
|
static int modeset;
|
|
static int frontend = 0;
|
|
|
|
// OSD message buffer
|
|
static char message[256];
|
|
|
|
// phase text buffer
|
|
static char phase_text[256];
|
|
|
|
// Per-Offset calibration metrics (i.e. errors) for mode 0..6
|
|
static int raw_metrics_set1[16][NUM_OFFSETS];
|
|
|
|
// Per-offset calibration metrics (i.e. errors) for mode 7
|
|
static int raw_metrics_set2[16][NUM_OFFSETS];
|
|
|
|
// Aggregate calibration metrics (i.e. errors summed across all offsets) for mode 0..6
|
|
static int sum_metrics_set1[16]; // Last two not used
|
|
|
|
// Aggregate calibration metrics (i.e. errors summver across all offsets) for mode 7
|
|
static int sum_metrics_set2[16];
|
|
|
|
// Error count for final calibration values for mode 0..6
|
|
static int errors_set1;
|
|
|
|
// Error count for final calibration values for mode 7
|
|
static int errors_set2;
|
|
|
|
// Error count for final calibration values for mode 0..6
|
|
static int window_errors_set1;
|
|
|
|
// Error count for final calibration values for mode 7
|
|
static int window_errors_set2;
|
|
|
|
// The CPLD version number
|
|
static int cpld_version;
|
|
|
|
// Indicates the CPLD supports the delay parameter
|
|
static int supports_delay = 0;
|
|
|
|
// Indicates the CPLD supports the rate parameter
|
|
static int supports_rate = 0; // 0 = no, 1 = 1-bit rate param, 2 = 1-bit rate param
|
|
|
|
// Indicates the CPLD supports the invert parameter
|
|
static int supports_invert = 0;
|
|
|
|
// Indicates the CPLD supports vsync on psync when version = 0
|
|
static int supports_vsync = 0;
|
|
|
|
// Indicates the CPLD supports separate vsync & hsync
|
|
static int supports_separate = 0;
|
|
|
|
// Indicates the Analog frontent interface is present
|
|
static int supports_analog = 0;
|
|
|
|
// Indicates 24 Mhz mode 7 sampling support
|
|
static int supports_odd_even = 0;
|
|
|
|
// Indicates the Analog frontent interface supports 4 level RGB
|
|
static int supports_8bit = 0;
|
|
|
|
// Indicates CPLD supports 9 & 12 bpp on BBC CPLD build
|
|
static int supports_bbc_9_12 = 0;
|
|
|
|
// Indicates CPLD supports 6/8 divider bit
|
|
static int supports_divider = 0;
|
|
|
|
// Indicates CPLD supports mux bit
|
|
static int supports_mux = 0;
|
|
|
|
// Indicates CPLD supports 4 bit offset
|
|
static int supports_single_offset = 0;
|
|
|
|
// Indicates CPLD supports sync edge
|
|
static int supports_edge = 0;
|
|
|
|
static int RGB_CPLD_detected = 0;
|
|
|
|
// invert state (not part of config)
|
|
static int invert = 0;
|
|
// =============================================================
|
|
// Param definitions for OSD
|
|
// =============================================================
|
|
|
|
enum {
|
|
// Sampling params
|
|
CPLD_SETUP_MODE,
|
|
ALL_OFFSETS,
|
|
A_OFFSET,
|
|
B_OFFSET,
|
|
C_OFFSET,
|
|
D_OFFSET,
|
|
E_OFFSET,
|
|
F_OFFSET,
|
|
HALF,
|
|
DIVIDER,
|
|
RANGE,
|
|
DELAY,
|
|
FILTER_L,
|
|
SUB_C,
|
|
ALT_R,
|
|
EDGE,
|
|
CLAMPTYPE,
|
|
MUX,
|
|
RATE,
|
|
TERMINATE,
|
|
COUPLING,
|
|
DAC_A,
|
|
DAC_B,
|
|
DAC_C,
|
|
DAC_D,
|
|
DAC_E,
|
|
DAC_F,
|
|
DAC_G,
|
|
DAC_H
|
|
};
|
|
|
|
enum {
|
|
RGB_RATE_3, //000
|
|
RGB_RATE_6, //001
|
|
RGB_RATE_9V, //011
|
|
RGB_RATE_9LO, //011
|
|
RGB_RATE_9HI, //011
|
|
RGB_RATE_12, //011
|
|
RGB_RATE_9LO_BLANKED, //011
|
|
RGB_RATE_4_LEVEL, //001
|
|
RGB_RATE_1, //010
|
|
RGB_RATE_1_VSYNC, //010
|
|
NUM_RGB_RATE
|
|
};
|
|
|
|
static const char *rate_names[] = {
|
|
"3 Bits Per Pixel",
|
|
"6 Bits Per Pixel",
|
|
"9 Bits (V Sync)",
|
|
"9 Bits (Bits 0-2)",
|
|
"9 Bits (Bits 1-3)",
|
|
"12 Bits Per Pixel",
|
|
"9 Bits 0-2 Blanked",
|
|
"6 Bits (4 Level)",
|
|
"1 Bit Per Pixel",
|
|
"1 Bit On Vsync",
|
|
};
|
|
|
|
static const char *cpld_setup_names[] = {
|
|
"Normal",
|
|
"Set Pixel H Offset"
|
|
};
|
|
|
|
int divider_lookup[] = { 6, 8, 10, 12, 14, 16, 3, 4 };
|
|
int divider_register[] = { 2, 3, 4, 5, 6, 7, 0, 1 };
|
|
|
|
static const char *divider_names[] = {
|
|
"x6",
|
|
"x8",
|
|
"x10",
|
|
"x12",
|
|
"x14",
|
|
"x16",
|
|
"x3",
|
|
"x4"
|
|
};
|
|
|
|
static const char *edge_names[] = {
|
|
"Trailing",
|
|
"Leading",
|
|
"Leading + Delay"
|
|
};
|
|
|
|
static const char *coupling_names[] = {
|
|
"DC",
|
|
"AC With Clamp"
|
|
};
|
|
|
|
static const char *m7range_names[] = {
|
|
"Auto",
|
|
"Fixed"
|
|
};
|
|
|
|
static const char *range_names[] = {
|
|
"Auto",
|
|
"90/270 Degrees"
|
|
};
|
|
|
|
static const char *volt_names[] = {
|
|
"000 (0.00v)",
|
|
"001 (0.01v)",
|
|
"002 (0.03v)",
|
|
"003 (0.04v)",
|
|
"004 (0.05v)",
|
|
"005 (0.06v)",
|
|
"006 (0.08v)",
|
|
"007 (0.09v)",
|
|
"008 (0.10v)",
|
|
"009 (0.12v)",
|
|
"010 (0.13v)",
|
|
"011 (0.14v)",
|
|
"012 (0.16v)",
|
|
"013 (0.17v)",
|
|
"014 (0.18v)",
|
|
"015 (0.19v)",
|
|
"016 (0.21v)",
|
|
"017 (0.22v)",
|
|
"018 (0.23v)",
|
|
"019 (0.25v)",
|
|
"020 (0.26v)",
|
|
"021 (0.27v)",
|
|
"022 (0.28v)",
|
|
"023 (0.30v)",
|
|
"024 (0.31v)",
|
|
"025 (0.32v)",
|
|
"026 (0.34v)",
|
|
"027 (0.35v)",
|
|
"028 (0.36v)",
|
|
"029 (0.38v)",
|
|
"030 (0.39v)",
|
|
"031 (0.40v)",
|
|
"032 (0.41v)",
|
|
"033 (0.43v)",
|
|
"034 (0.44v)",
|
|
"035 (0.45v)",
|
|
"036 (0.47v)",
|
|
"037 (0.48v)",
|
|
"038 (0.49v)",
|
|
"039 (0.50v)",
|
|
"040 (0.52v)",
|
|
"041 (0.53v)",
|
|
"042 (0.54v)",
|
|
"043 (0.56v)",
|
|
"044 (0.57v)",
|
|
"045 (0.58v)",
|
|
"046 (0.60v)",
|
|
"047 (0.61v)",
|
|
"048 (0.62v)",
|
|
"049 (0.63v)",
|
|
"050 (0.65v)",
|
|
"051 (0.66v)",
|
|
"052 (0.67v)",
|
|
"053 (0.69v)",
|
|
"054 (0.70v)",
|
|
"055 (0.71v)",
|
|
"056 (0.72v)",
|
|
"057 (0.74v)",
|
|
"058 (0.75v)",
|
|
"059 (0.76v)",
|
|
"060 (0.78v)",
|
|
"061 (0.79v)",
|
|
"062 (0.80v)",
|
|
"063 (0.82v)",
|
|
"064 (0.83v)",
|
|
"065 (0.84v)",
|
|
"066 (0.85v)",
|
|
"067 (0.87v)",
|
|
"068 (0.88v)",
|
|
"069 (0.89v)",
|
|
"070 (0.91v)",
|
|
"071 (0.92v)",
|
|
"072 (0.93v)",
|
|
"073 (0.94v)",
|
|
"074 (0.96v)",
|
|
"075 (0.97v)",
|
|
"076 (0.98v)",
|
|
"077 (1.00v)",
|
|
"078 (1.01v)",
|
|
"079 (1.02v)",
|
|
"080 (1.04v)",
|
|
"081 (1.05v)",
|
|
"082 (1.06v)",
|
|
"083 (1.07v)",
|
|
"084 (1.09v)",
|
|
"085 (1.10v)",
|
|
"086 (1.11v)",
|
|
"087 (1.13v)",
|
|
"088 (1.14v)",
|
|
"089 (1.15v)",
|
|
"090 (1.16v)",
|
|
"091 (1.18v)",
|
|
"092 (1.19v)",
|
|
"093 (1.20v)",
|
|
"094 (1.22v)",
|
|
"095 (1.23v)",
|
|
"096 (1.24v)",
|
|
"097 (1.26v)",
|
|
"098 (1.27v)",
|
|
"099 (1.28v)",
|
|
"100 (1.29v)",
|
|
"101 (1.31v)",
|
|
"102 (1.32v)",
|
|
"103 (1.33v)",
|
|
"104 (1.35v)",
|
|
"105 (1.36v)",
|
|
"106 (1.37v)",
|
|
"107 (1.38v)",
|
|
"108 (1.40v)",
|
|
"109 (1.41v)",
|
|
"110 (1.42v)",
|
|
"111 (1.44v)",
|
|
"112 (1.45v)",
|
|
"113 (1.46v)",
|
|
"114 (1.48v)",
|
|
"115 (1.49v)",
|
|
"116 (1.50v)",
|
|
"117 (1.51v)",
|
|
"118 (1.53v)",
|
|
"119 (1.54v)",
|
|
"120 (1.55v)",
|
|
"121 (1.57v)",
|
|
"122 (1.58v)",
|
|
"123 (1.59v)",
|
|
"124 (1.60v)",
|
|
"125 (1.62v)",
|
|
"126 (1.63v)",
|
|
"127 (1.64v)",
|
|
"128 (1.66v)",
|
|
"129 (1.67v)",
|
|
"130 (1.68v)",
|
|
"131 (1.70v)",
|
|
"132 (1.71v)",
|
|
"133 (1.72v)",
|
|
"134 (1.73v)",
|
|
"135 (1.75v)",
|
|
"136 (1.76v)",
|
|
"137 (1.77v)",
|
|
"138 (1.79v)",
|
|
"139 (1.80v)",
|
|
"140 (1.81v)",
|
|
"141 (1.82v)",
|
|
"142 (1.84v)",
|
|
"143 (1.85v)",
|
|
"144 (1.86v)",
|
|
"145 (1.88v)",
|
|
"146 (1.89v)",
|
|
"147 (1.90v)",
|
|
"148 (1.92v)",
|
|
"149 (1.93v)",
|
|
"150 (1.94v)",
|
|
"151 (1.95v)",
|
|
"152 (1.97v)",
|
|
"153 (1.98v)",
|
|
"154 (1.99v)",
|
|
"155 (2.01v)",
|
|
"156 (2.02v)",
|
|
"157 (2.03v)",
|
|
"158 (2.04v)",
|
|
"159 (2.06v)",
|
|
"160 (2.07v)",
|
|
"161 (2.08v)",
|
|
"162 (2.10v)",
|
|
"163 (2.11v)",
|
|
"164 (2.12v)",
|
|
"165 (2.14v)",
|
|
"166 (2.15v)",
|
|
"167 (2.16v)",
|
|
"168 (2.17v)",
|
|
"169 (2.19v)",
|
|
"170 (2.20v)",
|
|
"171 (2.21v)",
|
|
"172 (2.23v)",
|
|
"173 (2.24v)",
|
|
"174 (2.25v)",
|
|
"175 (2.26v)",
|
|
"176 (2.28v)",
|
|
"177 (2.29v)",
|
|
"178 (2.30v)",
|
|
"179 (2.32v)",
|
|
"180 (2.33v)",
|
|
"181 (2.34v)",
|
|
"182 (2.36v)",
|
|
"183 (2.37v)",
|
|
"184 (2.38v)",
|
|
"185 (2.39v)",
|
|
"186 (2.41v)",
|
|
"187 (2.42v)",
|
|
"188 (2.43v)",
|
|
"189 (2.45v)",
|
|
"190 (2.46v)",
|
|
"191 (2.47v)",
|
|
"192 (2.48v)",
|
|
"193 (2.50v)",
|
|
"194 (2.51v)",
|
|
"195 (2.52v)",
|
|
"196 (2.54v)",
|
|
"197 (2.55v)",
|
|
"198 (2.56v)",
|
|
"199 (2.58v)",
|
|
"200 (2.59v)",
|
|
"201 (2.60v)",
|
|
"202 (2.61v)",
|
|
"203 (2.63v)",
|
|
"204 (2.64v)",
|
|
"205 (2.65v)",
|
|
"206 (2.67v)",
|
|
"207 (2.68v)",
|
|
"208 (2.69v)",
|
|
"209 (2.70v)",
|
|
"210 (2.72v)",
|
|
"211 (2.73v)",
|
|
"212 (2.74v)",
|
|
"213 (2.76v)",
|
|
"214 (2.77v)",
|
|
"215 (2.78v)",
|
|
"216 (2.80v)",
|
|
"217 (2.81v)",
|
|
"218 (2.82v)",
|
|
"219 (2.83v)",
|
|
"220 (2.85v)",
|
|
"221 (2.86v)",
|
|
"222 (2.87v)",
|
|
"223 (2.89v)",
|
|
"224 (2.90v)",
|
|
"225 (2.91v)",
|
|
"226 (2.92v)",
|
|
"227 (2.94v)",
|
|
"228 (2.95v)",
|
|
"229 (2.96v)",
|
|
"230 (2.98v)",
|
|
"231 (2.99v)",
|
|
"232 (3.00v)",
|
|
"233 (3.02v)",
|
|
"234 (3.03v)",
|
|
"235 (3.04v)",
|
|
"236 (3.05v)",
|
|
"237 (3.07v)",
|
|
"238 (3.08v)",
|
|
"239 (3.09v)",
|
|
"240 (3.11v)",
|
|
"241 (3.12v)",
|
|
"242 (3.13v)",
|
|
"243 (3.14v)",
|
|
"244 (3.16v)",
|
|
"245 (3.17v)",
|
|
"246 (3.18v)",
|
|
"247 (3.20v)",
|
|
"248 (3.21v)",
|
|
"249 (3.22v)",
|
|
"250 (3.24v)",
|
|
"251 (3.25v)",
|
|
"252 (3.26v)",
|
|
"253 (3.27v)",
|
|
"254 (3.29v)",
|
|
"255 (3.30v)",
|
|
"Disabled"
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
DIVIDER_6,
|
|
DIVIDER_8,
|
|
DIVIDER_10,
|
|
DIVIDER_12,
|
|
DIVIDER_14,
|
|
DIVIDER_16,
|
|
DIVIDER_3,
|
|
DIVIDER_4
|
|
};
|
|
|
|
enum {
|
|
RANGE_AUTO,
|
|
RANGE_FIXED
|
|
};
|
|
|
|
enum {
|
|
RANGE_180,
|
|
RANGE_90,
|
|
NUM_RANGE
|
|
};
|
|
|
|
enum {
|
|
RGB_INPUT_HI,
|
|
RGB_INPUT_TERM,
|
|
NUM_RGB_TERM
|
|
};
|
|
|
|
enum {
|
|
RGB_INPUT_DC,
|
|
RGB_INPUT_AC,
|
|
NUM_RGB_COUPLING
|
|
};
|
|
|
|
enum {
|
|
CPLD_SETUP_NORMAL,
|
|
CPLD_SETUP_DELAY,
|
|
NUM_CPLD_SETUP
|
|
};
|
|
|
|
enum {
|
|
EDGE_TRAILING,
|
|
EDGE_LEADING,
|
|
EDGE_LEADING_DELAY,
|
|
NUM_EDGE
|
|
};
|
|
|
|
static param_t params[] = {
|
|
{ CPLD_SETUP_MODE, "Setup Mode", "setup_mode", 0,NUM_CPLD_SETUP-1, 1 },
|
|
{ ALL_OFFSETS, "Sampling Phase", "all_offsets", 0, 0, 1 },
|
|
{ A_OFFSET, "A Phase", "a_offset", 0, 0, 1 },
|
|
{ B_OFFSET, "B Phase", "b_offset", 0, 0, 1 },
|
|
{ C_OFFSET, "C Phase", "c_offset", 0, 0, 1 },
|
|
{ D_OFFSET, "D Phase", "d_offset", 0, 0, 1 },
|
|
{ E_OFFSET, "E Phase", "e_offset", 0, 0, 1 },
|
|
{ F_OFFSET, "F Phase", "f_offset", 0, 0, 1 },
|
|
{ HALF, "Half Pixel Shift", "half", 0, 1, 1 },
|
|
{ DIVIDER, "Clock Multiplier", "multiplier", 0, 7, 1 },
|
|
{ RANGE, "Calibration Range", "range", 0, 1, 1 },
|
|
{ DELAY, "Pixel H Offset", "delay", 0, 15, 1 },
|
|
//block of hidden YUV options for file compatibility
|
|
{ FILTER_L, "Filter Y", "l_filter", 0, 1, 1 },
|
|
{ SUB_C, "Subsample UV", "sub_c", 0, 1, 1 },
|
|
{ ALT_R, "PAL switch", "alt_r", 0, 1, 1 },
|
|
{ EDGE, "Sync Edge", "edge", 0, NUM_EDGE-1, 1 },
|
|
{ CLAMPTYPE, "Clamp Type", "clamptype", 0, 4, 1 },
|
|
//end of hidden block
|
|
{ MUX, "Sync on G/V", "input_mux", 0, 1, 1 },
|
|
{ RATE, "Sample Mode", "sample_mode", 0, NUM_RGB_RATE-1, 1 },
|
|
{ TERMINATE, "75R Termination", "termination", 0, NUM_RGB_TERM-1, 1 },
|
|
{ COUPLING, "G Coupling", "coupling", 0, NUM_RGB_COUPLING-1, 1 },
|
|
{ DAC_A, "DAC-A: G Hi", "dac_a", 0, 256, 1 },
|
|
{ DAC_B, "DAC-B: G Lo", "dac_b", 0, 256, 1 },
|
|
{ DAC_C, "DAC-C: RB Hi", "dac_c", 0, 256, 1 },
|
|
{ DAC_D, "DAC-D: RB Lo", "dac_d", 0, 256, 1 },
|
|
{ DAC_E, "DAC-E: Sync", "dac_e", 0, 256, 1 },
|
|
{ DAC_F, "DAC-F: G/V Sync", "dac_f", 0, 256, 1 },
|
|
{ DAC_G, "DAC-G: G Clamp", "dac_g", 0, 256, 1 },
|
|
{ DAC_H, "DAC-H: Unused", "dac_h", 0, 256, 1 },
|
|
{ -1, NULL, NULL, 0, 0, 1 }
|
|
};
|
|
|
|
// =============================================================
|
|
// Private methods
|
|
// =============================================================
|
|
|
|
static int get_adjusted_divider_index() {
|
|
clk_info_t clkinfo;
|
|
geometry_get_clk_params(&clkinfo);
|
|
int clock = clkinfo.clock;
|
|
int divider_index = config->divider;
|
|
if ((config->rate == RGB_RATE_1 || config->rate == RGB_RATE_1_VSYNC) && divider_index >= DIVIDER_10 && divider_index <= DIVIDER_16) {
|
|
divider_index = DIVIDER_8;
|
|
}
|
|
while (divider_lookup[divider_index] * clock > MAX_CPLD_FREQUENCY && divider_index != DIVIDER_3) {
|
|
divider_index = (divider_index - 1 ) & 7;
|
|
}
|
|
if (supports_odd_even && divider_index > DIVIDER_8 && (config->rate == RGB_RATE_6 || config->rate == RGB_RATE_9V)) { //rates don't support odd/even higher multipliers
|
|
divider_index = DIVIDER_8;
|
|
}
|
|
if (config->rate >= RGB_RATE_9V && config->rate <= RGB_RATE_9LO_BLANKED && divider_index >= DIVIDER_3) { //in 12bpp modes don't use x3 or x4 as not enough time for capture
|
|
divider_index = DIVIDER_6;
|
|
}
|
|
return divider_index;
|
|
}
|
|
|
|
static void sendDAC(int dac, int value)
|
|
{
|
|
if (value > 255) {
|
|
value = 255;
|
|
}
|
|
int old_dac = -1;
|
|
int old_value = value;
|
|
int M62364_dac = 0;
|
|
switch (dac) {
|
|
case 0:
|
|
M62364_dac = 0x08;
|
|
break;
|
|
case 1:
|
|
M62364_dac = 0x04;
|
|
break;
|
|
case 2:
|
|
M62364_dac = 0x0c;
|
|
old_dac = 0;
|
|
break;
|
|
case 3:
|
|
M62364_dac = 0x02;
|
|
old_dac = 1;
|
|
break;
|
|
case 4:
|
|
M62364_dac = 0x0a;
|
|
break;
|
|
case 5:
|
|
M62364_dac = 0x06;
|
|
old_dac = 2;
|
|
break;
|
|
case 6:
|
|
M62364_dac = 0x0e;
|
|
break;
|
|
case 7:
|
|
M62364_dac = 0x01;
|
|
old_dac = 3;
|
|
switch (config->terminate) {
|
|
default:
|
|
case RGB_INPUT_HI:
|
|
old_value = 0; //high impedance
|
|
break;
|
|
case RGB_INPUT_TERM:
|
|
old_value = 255; //termination
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (frontend == FRONTEND_ANALOG_ISSUE2_5259) {
|
|
switch (dac) {
|
|
case 6:
|
|
dac = 7;
|
|
break;
|
|
case 7:
|
|
{
|
|
dac = 6;
|
|
value = old_value;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
switch (frontend) {
|
|
case FRONTEND_ANALOG_ISSUE5:
|
|
case FRONTEND_ANALOG_ISSUE4:
|
|
case FRONTEND_ANALOG_ISSUE3_5259: // max5259
|
|
case FRONTEND_ANALOG_ISSUE2_5259:
|
|
{
|
|
if (new_DAC_detected() == 1) {
|
|
int packet = (M62364_dac << 8) | value;
|
|
//log_info("M62364 dac:%d = %02X, %03X", dac, value, packet);
|
|
RPI_SetGpioValue(STROBE_PIN, 0);
|
|
for (int i = 0; i < 12; i++) {
|
|
RPI_SetGpioValue(SP_CLKEN_PIN, 0);
|
|
RPI_SetGpioValue(SP_DATA_PIN, (packet >> 11) & 1);
|
|
delay_in_arm_cycles_cpu_adjust(500);
|
|
RPI_SetGpioValue(SP_CLKEN_PIN, 1);
|
|
delay_in_arm_cycles_cpu_adjust(500);
|
|
packet <<= 1;
|
|
}
|
|
RPI_SetGpioValue(STROBE_PIN, 1);
|
|
} else if (new_DAC_detected() == 2) {
|
|
int value_10bit = value << 2;
|
|
if (value_10bit >= 2) {
|
|
value_10bit -= 2;
|
|
}
|
|
int packet = (dac + 1) | (value_10bit << 4);
|
|
//log_info("bu2506 dac:%d = %03X, %03X", dac, value_10bit, packet);
|
|
RPI_SetGpioValue(STROBE_PIN, 0);
|
|
|
|
for (int i = 0; i < 14; i++) {
|
|
RPI_SetGpioValue(SP_CLKEN_PIN, 0);
|
|
RPI_SetGpioValue(SP_DATA_PIN, packet & 1);
|
|
delay_in_arm_cycles_cpu_adjust(500);
|
|
RPI_SetGpioValue(SP_CLKEN_PIN, 1);
|
|
delay_in_arm_cycles_cpu_adjust(500);
|
|
packet >>= 1;
|
|
}
|
|
RPI_SetGpioValue(STROBE_PIN, 1);
|
|
delay_in_arm_cycles_cpu_adjust(500);
|
|
RPI_SetGpioValue(STROBE_PIN, 0);
|
|
} else {
|
|
//log_info("Issue2/3 dac:%d = %d", dac, value);
|
|
int packet = (dac << 11) | 0x600 | value;
|
|
RPI_SetGpioValue(STROBE_PIN, 0);
|
|
for (int i = 0; i < 16; i++) {
|
|
RPI_SetGpioValue(SP_CLKEN_PIN, 0);
|
|
RPI_SetGpioValue(SP_DATA_PIN, (packet >> 15) & 1);
|
|
delay_in_arm_cycles_cpu_adjust(500);
|
|
RPI_SetGpioValue(SP_CLKEN_PIN, 1);
|
|
delay_in_arm_cycles_cpu_adjust(500);
|
|
packet <<= 1;
|
|
}
|
|
RPI_SetGpioValue(STROBE_PIN, 1);
|
|
}
|
|
|
|
RPI_SetGpioValue(SP_DATA_PIN, 0);
|
|
}
|
|
break;
|
|
|
|
case FRONTEND_ANALOG_ISSUE1_UA1: // tlc5260 or tlv5260
|
|
{
|
|
//log_info("Issue 1A dac:%d = %d", old_dac, old_value);
|
|
if (old_dac >= 0) {
|
|
int packet = old_dac << 9 | old_value;
|
|
RPI_SetGpioValue(STROBE_PIN, 1);
|
|
delay_in_arm_cycles_cpu_adjust(1000);
|
|
for (int i = 0; i < 11; i++) {
|
|
RPI_SetGpioValue(SP_CLKEN_PIN, 1);
|
|
RPI_SetGpioValue(SP_DATA_PIN, (packet >> 10) & 1);
|
|
delay_in_arm_cycles_cpu_adjust(500);
|
|
RPI_SetGpioValue(SP_CLKEN_PIN, 0);
|
|
delay_in_arm_cycles_cpu_adjust(500);
|
|
packet <<= 1;
|
|
}
|
|
RPI_SetGpioValue(STROBE_PIN, 0);
|
|
delay_in_arm_cycles_cpu_adjust(500);
|
|
RPI_SetGpioValue(STROBE_PIN, 1);
|
|
RPI_SetGpioValue(SP_DATA_PIN, 0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FRONTEND_ANALOG_ISSUE1_UB1: // dac084s085
|
|
{
|
|
if (old_dac >= 0) {
|
|
//log_info("Issue 1B dac:%d = %d", old_dac, old_value);
|
|
int packet = (old_dac << 14) | 0x1000 | (old_value << 4);
|
|
RPI_SetGpioValue(STROBE_PIN, 1);
|
|
delay_in_arm_cycles_cpu_adjust(500);
|
|
RPI_SetGpioValue(STROBE_PIN, 0);
|
|
for (int i = 0; i < 16; i++) {
|
|
RPI_SetGpioValue(SP_CLKEN_PIN, 1);
|
|
RPI_SetGpioValue(SP_DATA_PIN, (packet >> 15) & 1);
|
|
delay_in_arm_cycles_cpu_adjust(500);
|
|
RPI_SetGpioValue(SP_CLKEN_PIN, 0);
|
|
delay_in_arm_cycles_cpu_adjust(500);
|
|
packet <<= 1;
|
|
}
|
|
RPI_SetGpioValue(SP_DATA_PIN, 0);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
static void write_config(config_t *config, int dac_update) {
|
|
int sp = 0;
|
|
int scan_len;
|
|
if (supports_single_offset) {
|
|
scan_len = supports_single_offset;
|
|
int temp_offsets = config->all_offsets;
|
|
int temp_divider = divider_lookup[get_adjusted_divider_index()];
|
|
if (temp_offsets >= temp_divider) {
|
|
temp_offsets = temp_divider - 1;
|
|
}
|
|
if (config->half_px_delay == 0 && supports_single_offset == 4) {
|
|
temp_offsets = (temp_offsets + (temp_divider >> 1)) % temp_divider;
|
|
}
|
|
if (supports_edge) {
|
|
temp_offsets --; //compensate for pipelined sync edge detection
|
|
if (temp_offsets < 0) {
|
|
temp_offsets += temp_divider;
|
|
}
|
|
}
|
|
sp |= temp_offsets;
|
|
} else {
|
|
scan_len = 18;
|
|
int range = divider_lookup[get_adjusted_divider_index()];
|
|
if (supports_odd_even && range > 8) {
|
|
range >>= 1;
|
|
}
|
|
|
|
for (int i = 0; i < NUM_OFFSETS; i++) {
|
|
// Cycle the sample points taking account the pixel delay value
|
|
// (if the CPLD support this, and we are in mode 7
|
|
// delay = 0 : use ABCDEF
|
|
// delay = 1 : use BCDEFA
|
|
// delay = 2 : use CDEFAB
|
|
// etc
|
|
int offset = (supports_delay && capinfo->mode7) ? (i + config->full_px_delay) % NUM_OFFSETS : i;
|
|
sp |= (config->sp_offset[i] % range) << (offset * 3);
|
|
}
|
|
}
|
|
if (supports_single_offset != 4) {
|
|
sp |= (config->half_px_delay << scan_len);
|
|
scan_len += 1;
|
|
}
|
|
|
|
if (supports_delay) {
|
|
// We usually make use of 2 bits of delay, even in CPLDs that use more
|
|
sp |= ((config->full_px_delay & 3) << scan_len); // maintain h-offset compatibility with other CPLD versions by not using 3rd bit unless necessary
|
|
scan_len += supports_delay; // 2, 3 or 4 depending on the CPLD version
|
|
}
|
|
|
|
if (supports_edge) {
|
|
if (config->edge != EDGE_TRAILING) {
|
|
sp |= (1 << scan_len); // edge bit replaced delay(2)
|
|
}
|
|
scan_len += 1;
|
|
}
|
|
|
|
if (supports_rate) {
|
|
int temprate = 0;
|
|
if (RGB_CPLD_detected) {
|
|
switch (config->rate) {
|
|
default:
|
|
case RGB_RATE_3:
|
|
temprate = 0x00; // selects rate = 00 and rateswitch = dont care
|
|
if (config->edge > EDGE_LEADING) {
|
|
temprate = 0x04;
|
|
}
|
|
break;
|
|
case RGB_RATE_6:
|
|
temprate = 0x01; // selects rate = 01 and rateswitch = 0
|
|
break;
|
|
case RGB_RATE_4_LEVEL:
|
|
temprate = 0x01 + 0x04; // selects rate = 01 and rateswitch = 1
|
|
break;
|
|
case RGB_RATE_1:
|
|
temprate = 0x02; // selects rate = 10 and rateswitch = 0
|
|
break;
|
|
case RGB_RATE_1_VSYNC:
|
|
temprate = 0x02 + 0x04; // selects rate = 10 and rateswitch = 1
|
|
break;
|
|
case RGB_RATE_9V:
|
|
case RGB_RATE_9LO:
|
|
case RGB_RATE_9HI:
|
|
case RGB_RATE_12:
|
|
temprate = 0x03; // selects rate = 11 and rateswitch = 0
|
|
break;
|
|
case RGB_RATE_9LO_BLANKED:
|
|
temprate = 0x03 + 0x04; // selects rate = 11 and rateswitch = 1
|
|
break;
|
|
}
|
|
} else {
|
|
switch (config->rate) {
|
|
default:
|
|
case RGB_RATE_9V:
|
|
case RGB_RATE_3:
|
|
temprate = 0x00;
|
|
break;
|
|
case RGB_RATE_9LO:
|
|
case RGB_RATE_9HI:
|
|
case RGB_RATE_12:
|
|
case RGB_RATE_6:
|
|
temprate = 0x01;
|
|
break;
|
|
}
|
|
int range = divider_lookup[get_adjusted_divider_index()];
|
|
if (supports_odd_even && range > 8) { // if divider > x6 or x8 then select odd/even mode
|
|
if (config->all_offsets >= (range >> 1)) {
|
|
temprate = 0x03; //odd
|
|
} else {
|
|
temprate = 0x02; //even
|
|
}
|
|
}
|
|
//log_info ("RATE = %d", temprate);
|
|
}
|
|
|
|
sp |= (temprate << scan_len);
|
|
scan_len += supports_rate; // 1 - 3 depending on the CPLD version
|
|
}
|
|
|
|
if (supports_invert) {
|
|
sp |= (invert << scan_len);
|
|
scan_len += 1;
|
|
}
|
|
|
|
if (supports_divider) {
|
|
if (supports_divider == 3) {
|
|
int temp_divider = divider_register[get_adjusted_divider_index()];
|
|
if (config->rate == RGB_RATE_1_VSYNC || config->rate == RGB_RATE_1) {
|
|
temp_divider &= 0x03;
|
|
temp_divider |= (config->full_px_delay & 0x04); //top bit of divider used as top bit of delay in 1bpp mode (divider restricted to <= x8)
|
|
}
|
|
sp |= (temp_divider << scan_len);
|
|
} else {
|
|
sp |= (((divider_lookup[get_adjusted_divider_index()] == 8 || divider_lookup[get_adjusted_divider_index()] == 16) & 1 ) << scan_len);
|
|
}
|
|
scan_len += supports_divider;
|
|
}
|
|
|
|
if (supports_mux) {
|
|
sp |= (config->mux << scan_len);
|
|
scan_len += 1;
|
|
}
|
|
|
|
//log_info("scan = %X, %d",sp, scan_len);
|
|
|
|
for (int i = 0; i < scan_len; i++) {
|
|
RPI_SetGpioValue(SP_DATA_PIN, sp & 1);
|
|
delay_in_arm_cycles_cpu_adjust(250);
|
|
RPI_SetGpioValue(SP_CLKEN_PIN, 1);
|
|
delay_in_arm_cycles_cpu_adjust(250);
|
|
RPI_SetGpioValue(SP_CLK_PIN, 0);
|
|
delay_in_arm_cycles_cpu_adjust(250);
|
|
RPI_SetGpioValue(SP_CLK_PIN, 1);
|
|
delay_in_arm_cycles_cpu_adjust(250);
|
|
RPI_SetGpioValue(SP_CLKEN_PIN, 0);
|
|
delay_in_arm_cycles_cpu_adjust(250);
|
|
sp >>= 1;
|
|
}
|
|
|
|
if (supports_analog) {
|
|
if (dac_update) {
|
|
int dac_a = config->dac_a;
|
|
if (dac_a == 256) dac_a = 75; //approx 1v (255 = 3.3v) so a grounded input will be detected as 0 without exceeding 1.4v difference
|
|
int dac_b = config->dac_b;
|
|
if (dac_b == 256) dac_b = dac_a;
|
|
|
|
int dac_c = config->dac_c;
|
|
if (dac_c == 256) dac_c = 75; //approx 1v (255 = 3.3v) so a grounded input will be detected as 0 without exceeding 1.4v difference
|
|
int dac_d = config->dac_d;
|
|
if (dac_d == 256) dac_d = dac_c;
|
|
|
|
int dac_e = config->dac_e;
|
|
if (dac_e == 256 && config->mux != 0) dac_e = dac_a; //if sync level is disabled, track dac_a unless sync source is sync (stops spurious sync detection)
|
|
|
|
int dac_f = config->dac_f;
|
|
if (dac_f == 256 && config->mux == 0) dac_f = dac_a; //if vsync level is disabled, track dac_a unless sync source is vsync (stops spurious sync detection)
|
|
|
|
int dac_g = config->dac_g;
|
|
if (dac_g == 256) {
|
|
if (supports_8bit && config->rate == RGB_RATE_4_LEVEL) {
|
|
dac_g = dac_c;
|
|
} else {
|
|
dac_g = 0;
|
|
}
|
|
}
|
|
|
|
int dac_h = config->dac_h;
|
|
if (dac_h == 256) dac_h = dac_c;
|
|
|
|
sendDAC(0, dac_a);
|
|
sendDAC(1, dac_b);
|
|
sendDAC(2, dac_c);
|
|
sendDAC(3, dac_d);
|
|
sendDAC(5, dac_e); // DACs E and F positions swapped in menu compared to hardware
|
|
sendDAC(4, dac_f);
|
|
sendDAC(6, dac_g);
|
|
sendDAC(7, dac_h);
|
|
}
|
|
|
|
switch (config->terminate) {
|
|
default:
|
|
case RGB_INPUT_HI:
|
|
RPI_SetGpioValue(SP_CLKEN_PIN, 0); //termination
|
|
break;
|
|
case RGB_INPUT_TERM:
|
|
RPI_SetGpioValue(SP_CLKEN_PIN, 1);
|
|
break;
|
|
}
|
|
|
|
int coupling = config->coupling;
|
|
if (frontend == FRONTEND_ANALOG_ISSUE2_5259 || frontend == FRONTEND_ANALOG_ISSUE1_UA1 || frontend == FRONTEND_ANALOG_ISSUE1_UB1) {
|
|
coupling = RGB_INPUT_AC; // always ac coupling with issue 1 or 2
|
|
}
|
|
if (RGB_CPLD_detected) {
|
|
switch(config->rate) {
|
|
default:
|
|
case RGB_RATE_1:
|
|
case RGB_RATE_1_VSYNC:
|
|
case RGB_RATE_3:
|
|
case RGB_RATE_6:
|
|
RPI_SetGpioValue(SP_DATA_PIN, coupling); //ac-dc
|
|
break;
|
|
case RGB_RATE_9V:
|
|
case RGB_RATE_4_LEVEL:
|
|
RPI_SetGpioValue(SP_DATA_PIN, 0); //dc only in 4 level mode or enable RGB_RATE_9V
|
|
break;
|
|
case RGB_RATE_9LO:
|
|
case RGB_RATE_9HI:
|
|
case RGB_RATE_12:
|
|
case RGB_RATE_9LO_BLANKED:
|
|
RPI_SetGpioValue(SP_DATA_PIN, 1); //enable 12 bit mode
|
|
break;
|
|
}
|
|
} else {
|
|
switch(config->rate) {
|
|
default:
|
|
case RGB_RATE_3:
|
|
case RGB_RATE_6:
|
|
if (supports_bbc_9_12) {
|
|
RPI_SetGpioValue(SP_DATA_PIN, 0); //no ac coupling
|
|
} else {
|
|
RPI_SetGpioValue(SP_DATA_PIN, coupling);
|
|
}
|
|
break;
|
|
case RGB_RATE_9V:
|
|
case RGB_RATE_9LO:
|
|
case RGB_RATE_9HI:
|
|
case RGB_RATE_12:
|
|
RPI_SetGpioValue(SP_DATA_PIN, 1); //enable 12 bit mode
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
if (RGB_CPLD_detected) {
|
|
switch(config->rate) {
|
|
default:
|
|
case RGB_RATE_1:
|
|
case RGB_RATE_1_VSYNC:
|
|
case RGB_RATE_3:
|
|
case RGB_RATE_6:
|
|
case RGB_RATE_9V:
|
|
case RGB_RATE_4_LEVEL:
|
|
RPI_SetGpioValue(SP_DATA_PIN, 0);
|
|
break;
|
|
case RGB_RATE_9LO:
|
|
case RGB_RATE_9HI:
|
|
case RGB_RATE_12:
|
|
case RGB_RATE_9LO_BLANKED:
|
|
RPI_SetGpioValue(SP_DATA_PIN, 1);
|
|
break;
|
|
}
|
|
} else {
|
|
switch(config->rate) {
|
|
default:
|
|
case RGB_RATE_3:
|
|
case RGB_RATE_6:
|
|
RPI_SetGpioValue(SP_DATA_PIN, 0);
|
|
break;
|
|
case RGB_RATE_9V:
|
|
case RGB_RATE_9LO:
|
|
case RGB_RATE_9HI:
|
|
case RGB_RATE_12:
|
|
RPI_SetGpioValue(SP_DATA_PIN, 1); //enable 12 bit mode
|
|
break;
|
|
}
|
|
}
|
|
RPI_SetGpioValue(SP_CLKEN_PIN, 0);
|
|
}
|
|
RPI_SetGpioValue(MUX_PIN, config->mux); //only required for old CPLDs - has no effect on newer ones
|
|
}
|
|
|
|
static int osd_sp(config_t *config, int line, int metric, int window_metric) {
|
|
int range = divider_lookup[get_adjusted_divider_index()];
|
|
int offset_range = 0;
|
|
if (supports_odd_even && range > 8) {
|
|
range >>= 1;
|
|
offset_range = config->all_offsets >= range ? range : 0;
|
|
}
|
|
// Line ------
|
|
if (modeset == MODE_SET2) {
|
|
osd_set(line, 0, " Mode: Set 2");
|
|
} else {
|
|
osd_set(line, 0, " Mode: Set 1");
|
|
}
|
|
line++;
|
|
// Line ------
|
|
if (capinfo->mode7 && !RGB_CPLD_detected) {
|
|
sprintf(message, " Sampling Phases: %d %d %d %d %d %d",
|
|
config->sp_offset[0] % range + offset_range, config->sp_offset[1] % range + offset_range, config->sp_offset[2] % range + offset_range,
|
|
config->sp_offset[3] % range + offset_range, config->sp_offset[4] % range + offset_range, config->sp_offset[5] % range + offset_range);
|
|
} else {
|
|
sprintf(message, " Sampling Phase: %d", config->all_offsets);
|
|
}
|
|
osd_set(line, 0, message);
|
|
|
|
line++;
|
|
// Line ------
|
|
sprintf(message, "Half Pixel Shift: %d", config->half_px_delay);
|
|
osd_set(line, 0, message);
|
|
line++;
|
|
// Line ------
|
|
sprintf(message, "Clock Multiplier: %s", divider_names[get_adjusted_divider_index()]);
|
|
osd_set(line, 0, message);
|
|
line++;
|
|
// Line ------
|
|
if (supports_delay) {
|
|
sprintf(message, " Pixel H Offset: %d", config->full_px_delay);
|
|
osd_set(line, 0, message);
|
|
line++;
|
|
}
|
|
// Line ------
|
|
if (supports_rate) {
|
|
char *rate_string;
|
|
rate_string = (char *) rate_names[config->rate];
|
|
sprintf(message, " Sample Mode: %s", rate_string);
|
|
osd_set(line, 0, message);
|
|
line++;
|
|
}
|
|
// Line ------
|
|
if (window_metric < 0) {
|
|
sprintf(message, " Error Window: unknown");
|
|
} else {
|
|
sprintf(message, " Error Window: %d", window_metric);
|
|
}
|
|
osd_set(line, 0, message);
|
|
line++;
|
|
// Line ------
|
|
if (metric < 0) {
|
|
sprintf(message, " Errors: unknown");
|
|
} else {
|
|
sprintf(message, " Errors: %d", metric);
|
|
}
|
|
osd_set(line, 0, message);
|
|
|
|
return(line);
|
|
}
|
|
|
|
static void log_sp(config_t *config) {
|
|
int range = divider_lookup[get_adjusted_divider_index()];
|
|
int offset_range = 0;
|
|
if (supports_odd_even && range > 8) {
|
|
range >>= 1;
|
|
offset_range = config->all_offsets >= range ? range : 0;
|
|
}
|
|
char *mp = message;
|
|
mp += sprintf(mp, "phases = %d %d %d %d %d %d; half = %d",
|
|
config->sp_offset[0] % range + offset_range, config->sp_offset[1] % range + offset_range, config->sp_offset[2] % range + offset_range,
|
|
config->sp_offset[3] % range + offset_range, config->sp_offset[4] % range + offset_range, config->sp_offset[5] % range + offset_range,
|
|
config->half_px_delay);
|
|
if (supports_delay) {
|
|
mp += sprintf(mp, "; pix h offset = %d", config->full_px_delay);
|
|
}
|
|
|
|
if (supports_divider) {
|
|
mp += sprintf(mp, "; mult = %d", get_adjusted_divider_index());
|
|
}
|
|
|
|
if (supports_rate) {
|
|
mp += sprintf(mp, "; rate = %d", config->rate);
|
|
}
|
|
if (supports_invert) {
|
|
mp += sprintf(mp, "; inv = %d", invert);
|
|
}
|
|
log_info("%s", message);
|
|
}
|
|
|
|
// =============================================================
|
|
// Public methods
|
|
// =============================================================
|
|
|
|
static void cpld_init(int version) {
|
|
// hide YUV lines included for file compatibility
|
|
params[FILTER_L].hidden = 1;
|
|
params[SUB_C].hidden = 1;
|
|
params[ALT_R].hidden = 1;
|
|
params[EDGE].hidden = 1;
|
|
params[CLAMPTYPE].hidden = 1;
|
|
params[DIVIDER].max = 1;
|
|
if (eight_bit_detected()) {
|
|
supports_8bit = 1;
|
|
}
|
|
|
|
cpld_version = version;
|
|
// Setup default frame buffer params
|
|
//
|
|
// Nominal width should be 640x512 or 480x504, but making this a bit larger deals with two problems:
|
|
// 1. Slight differences in the horizontal placement in the different screen modes
|
|
// 2. Slight differences in the vertical placement due to *TV settings
|
|
|
|
// Extract just the major version number
|
|
int major = (cpld_version >> VERSION_MAJOR_BIT) & 0x0F;
|
|
int minor = (cpld_version >> VERSION_MINOR_BIT) & 0x0F;
|
|
// Optional delay parameter
|
|
// CPLDv2 and beyond supports the delay parameter, and starts sampling earlier
|
|
if (major >= 6) {
|
|
supports_delay = 2; // 2 bits of delay
|
|
} else if (major >= 2) {
|
|
supports_delay = 4; // 4 bits of delay
|
|
} else {
|
|
supports_delay = 0;
|
|
params[DELAY].hidden = 1;
|
|
}
|
|
|
|
// Optional rate parameter
|
|
// CPLDv3 supports a 1-bit rate, CPLDv4 and beyond supports a 2-bit rate
|
|
if (major >= 4) {
|
|
supports_rate = 2;
|
|
params[RATE].max = RGB_RATE_6;
|
|
supports_odd_even = 1;
|
|
params[DIVIDER].max = 7;
|
|
} else if (major >= 3) {
|
|
supports_rate = 1;
|
|
params[RATE].max = RGB_RATE_6;
|
|
} else {
|
|
supports_rate = 0;
|
|
params[RATE].hidden = 1;
|
|
}
|
|
// Optional invert parameter
|
|
// CPLDv5 and beyond support an invertion of sync
|
|
if (major >= 5) {
|
|
supports_invert = 1;
|
|
}
|
|
if (major >= 6) {
|
|
supports_vsync = 1;
|
|
}
|
|
|
|
//*******************************************************************************************************************************
|
|
if ((major >= 6 && minor >= 4) || major >= 7) {
|
|
supports_separate = 1;
|
|
}
|
|
|
|
if (major == 6 && minor >= 7) {
|
|
supports_invert = 0; //BBC cpld replaces invert with divider in chain
|
|
supports_vsync = 0;
|
|
supports_separate = 0;
|
|
supports_divider = 1;
|
|
}
|
|
|
|
if (major == 7 && minor >= 6) { //BBC cpld
|
|
supports_invert = 0; //BBC cpld replaces invert with divider in chain
|
|
supports_vsync = 0;
|
|
supports_separate = 0;
|
|
supports_divider = 1;
|
|
params[MUX].hidden = 1;
|
|
if (minor >= 8) {
|
|
if (supports_8bit && !supports_analog) {
|
|
params[RATE].max = RGB_RATE_12;
|
|
}
|
|
supports_bbc_9_12 = 1;
|
|
}
|
|
}
|
|
|
|
if (major >= 8) { // V8 CPLD branch is deprecated
|
|
RGB_CPLD_detected = 1;
|
|
params[DIVIDER].max = 1;
|
|
params[RATE].max = RGB_RATE_4_LEVEL;
|
|
supports_odd_even = 0;
|
|
supports_divider = 1;
|
|
supports_mux = 1;
|
|
}
|
|
|
|
if (major == 9) {
|
|
RGB_CPLD_detected = 1;
|
|
params[RATE].max = RGB_RATE_12;
|
|
params[DIVIDER].max = 1;
|
|
supports_odd_even = 0;
|
|
supports_mux = 1;
|
|
supports_single_offset = 3;
|
|
supports_rate = 3;
|
|
supports_delay = 3;
|
|
supports_divider = 2;
|
|
}
|
|
|
|
if (major == 9 && minor >= 2) {
|
|
params[DIVIDER].max = 7;
|
|
supports_divider = 3;
|
|
supports_single_offset = 4;
|
|
}
|
|
|
|
if (major == 9 && minor >= 4) {
|
|
params[RATE].max = RGB_RATE_1_VSYNC;
|
|
supports_delay = 2;
|
|
supports_edge = 1;
|
|
params[EDGE].hidden = 0;
|
|
}
|
|
|
|
//*******************************************************************************************************************************
|
|
|
|
// Hide analog frontend parameters.
|
|
if (!supports_analog) {
|
|
params[TERMINATE].hidden = 1;
|
|
params[COUPLING].hidden = 1;
|
|
params[DAC_A].hidden = 1;
|
|
params[DAC_B].hidden = 1;
|
|
params[DAC_C].hidden = 1;
|
|
params[DAC_D].hidden = 1;
|
|
params[DAC_E].hidden = 1;
|
|
params[DAC_F].hidden = 1;
|
|
params[DAC_G].hidden = 1;
|
|
}
|
|
|
|
params[DAC_H].hidden = 1;
|
|
|
|
if (major > 2) {
|
|
geometry_hide_pixel_sampling();
|
|
}
|
|
|
|
for (int i = 0; i < NUM_OFFSETS; i++) {
|
|
set1_config.sp_offset[i] = 2;
|
|
set2_config.sp_offset[i] = 5;
|
|
}
|
|
set1_config.half_px_delay = 0;
|
|
set2_config.half_px_delay = 0;
|
|
set1_config.divider = 0;
|
|
set2_config.divider = 1;
|
|
set1_config.mux = 0;
|
|
set2_config.mux = 0;
|
|
set1_config.full_px_delay = 5; // Correct for the master
|
|
set2_config.full_px_delay = 8; // Correct for the master
|
|
set1_config.rate = 0;
|
|
set2_config.rate = 0;
|
|
config = &set1_config;
|
|
for (int i = 0; i < 8; i++) {
|
|
sum_metrics_set1[i] = -1;
|
|
sum_metrics_set2[i] = -1;
|
|
for (int j = 0; j < NUM_OFFSETS; j++) {
|
|
raw_metrics_set1[i][j] = -1;
|
|
raw_metrics_set2[i][j] = -1;
|
|
}
|
|
}
|
|
errors_set1 = -1;
|
|
errors_set2 = -1;
|
|
window_errors_set1 = -1;
|
|
window_errors_set2 = -1;
|
|
config->cpld_setup_mode = 0;
|
|
}
|
|
|
|
static int cpld_get_version() {
|
|
return cpld_version;
|
|
}
|
|
|
|
static void update_param_range() {
|
|
int max;
|
|
// Set the range of the offset params based on cpld divider
|
|
max = divider_lookup[get_adjusted_divider_index()] - 1;
|
|
params[ALL_OFFSETS].max = max;
|
|
if (config->all_offsets > max) {
|
|
config->all_offsets = max;
|
|
}
|
|
params[A_OFFSET].max = max;
|
|
params[B_OFFSET].max = max;
|
|
params[C_OFFSET].max = max;
|
|
params[D_OFFSET].max = max;
|
|
params[E_OFFSET].max = max;
|
|
params[F_OFFSET].max = max;
|
|
for (int i = 0; i < NUM_OFFSETS; i++) {
|
|
if (config->sp_offset[i] > max) {
|
|
config->sp_offset[i] = max;
|
|
}
|
|
}
|
|
// Finally, make surethe hardware is consistent with the current value of divider
|
|
// Divider = 0 gives 6 clocks per pixel
|
|
// Divider = 1 gives 8 clocks per pixel
|
|
|
|
if (supports_divider) {
|
|
RPI_SetGpioValue(MODE7_PIN, modeset == MODE_SET2);
|
|
} else {
|
|
RPI_SetGpioValue(MODE7_PIN, (max == 7 || max == 15));
|
|
}
|
|
}
|
|
|
|
static void cpld_set_mode(int mode) {
|
|
modeset = mode;
|
|
if (modeset == MODE_SET1) {
|
|
config = &set1_config;
|
|
} else {
|
|
config = &set2_config;
|
|
}
|
|
write_config(config, DAC_UPDATE);
|
|
// Update the OSD param ranges based on the new config
|
|
update_param_range();
|
|
}
|
|
|
|
static void cpld_set_vsync_psync(int state) { //state = 1 is psync (version = 1), state = 0 is vsync (version = 0, mux = 1)
|
|
if (supports_analog) {
|
|
int temp_mux = config->mux;
|
|
if (state == 0) {
|
|
config->mux = 1;
|
|
}
|
|
write_config(config, NO_DAC_UPDATE);
|
|
config->mux = temp_mux;
|
|
}
|
|
RPI_SetGpioValue(VERSION_PIN, state);
|
|
}
|
|
|
|
static int cpld_analyse(int selected_sync_state, int analyse) {
|
|
if (supports_invert) {
|
|
if (invert ^ (selected_sync_state & SYNC_BIT_HSYNC_INVERTED)) {
|
|
invert ^= SYNC_BIT_HSYNC_INVERTED;
|
|
if (invert) {
|
|
log_info("Analyze Csync: polarity changed to inverted");
|
|
} else {
|
|
log_info("Analyze Csync: polarity changed to non-inverted");
|
|
}
|
|
} else {
|
|
if (invert) {
|
|
log_info("Analyze Csync: polarity unchanged (inverted)");
|
|
} else {
|
|
log_info("Analyze Csync: polarity unchanged (non-inverted)");
|
|
}
|
|
}
|
|
write_config(config, DAC_UPDATE);
|
|
int polarity = selected_sync_state;
|
|
if (analyse) {
|
|
polarity = analyse_sync();
|
|
//log_info("Raw polarity = %x %d %d %d %d", polarity, hsync_comparison_lo, hsync_comparison_hi, vsync_comparison_lo, vsync_comparison_hi);
|
|
if (selected_sync_state & SYNC_BIT_COMPOSITE_SYNC) {
|
|
polarity &= SYNC_BIT_HSYNC_INVERTED;
|
|
polarity |= SYNC_BIT_COMPOSITE_SYNC;
|
|
}
|
|
}
|
|
polarity ^= invert;
|
|
if (supports_separate == 0) {
|
|
polarity ^= ((polarity & SYNC_BIT_VSYNC_INVERTED) ? SYNC_BIT_HSYNC_INVERTED : 0);
|
|
polarity |= SYNC_BIT_MIXED_SYNC;
|
|
}
|
|
if (supports_vsync) {
|
|
return (polarity);
|
|
} else {
|
|
return ((polarity & SYNC_BIT_HSYNC_INVERTED) | SYNC_BIT_COMPOSITE_SYNC | SYNC_BIT_MIXED_SYNC);
|
|
}
|
|
} else {
|
|
return (SYNC_BIT_COMPOSITE_SYNC | SYNC_BIT_MIXED_SYNC);
|
|
}
|
|
}
|
|
|
|
static void cpld_update_capture_info(capture_info_t *capinfo) {
|
|
// Update the capture info stucture, if one was passed in
|
|
if (capinfo) {
|
|
// Update the sample width
|
|
|
|
switch(config->rate) {
|
|
default:
|
|
case RGB_RATE_1:
|
|
case RGB_RATE_1_VSYNC:
|
|
capinfo->sample_width = SAMPLE_WIDTH_1;
|
|
break;
|
|
case RGB_RATE_3:
|
|
capinfo->sample_width = SAMPLE_WIDTH_3;
|
|
break;
|
|
case RGB_RATE_6:
|
|
case RGB_RATE_4_LEVEL:
|
|
capinfo->sample_width = SAMPLE_WIDTH_6;
|
|
break;
|
|
case RGB_RATE_9V:
|
|
case RGB_RATE_9HI:
|
|
capinfo->sample_width = SAMPLE_WIDTH_9HI;
|
|
break;
|
|
case RGB_RATE_9LO:
|
|
case RGB_RATE_9LO_BLANKED:
|
|
capinfo->sample_width = SAMPLE_WIDTH_9LO;
|
|
break;
|
|
case RGB_RATE_12:
|
|
capinfo->sample_width = SAMPLE_WIDTH_12;
|
|
break;
|
|
}
|
|
|
|
// Update the line capture function
|
|
switch (capinfo->sample_width) {
|
|
case SAMPLE_WIDTH_1 :
|
|
capinfo->capture_line = capture_line_normal_1bpp_table;
|
|
break;
|
|
case SAMPLE_WIDTH_3:
|
|
switch (capinfo->px_sampling) {
|
|
case PS_NORMAL:
|
|
capinfo->capture_line = capture_line_normal_3bpp_table;
|
|
break;
|
|
case PS_NORMAL_O:
|
|
capinfo->capture_line = capture_line_odd_3bpp_table;
|
|
break;
|
|
case PS_NORMAL_E:
|
|
capinfo->capture_line = capture_line_even_3bpp_table;
|
|
break;
|
|
case PS_HALF_O:
|
|
capinfo->capture_line = capture_line_half_odd_3bpp_table;
|
|
break;
|
|
case PS_HALF_E:
|
|
capinfo->capture_line = capture_line_half_even_3bpp_table;
|
|
break;
|
|
}
|
|
break;
|
|
case SAMPLE_WIDTH_6 :
|
|
capinfo->capture_line = capture_line_normal_6bpp_table;
|
|
break;
|
|
case SAMPLE_WIDTH_9LO :
|
|
capinfo->capture_line = capture_line_normal_9bpplo_table;
|
|
break;
|
|
case SAMPLE_WIDTH_9HI :
|
|
capinfo->capture_line = capture_line_normal_9bpphi_table;
|
|
break;
|
|
case SAMPLE_WIDTH_12 :
|
|
capinfo->capture_line = capture_line_normal_12bpp_table;
|
|
break;
|
|
}
|
|
}
|
|
write_config(config, DAC_UPDATE);
|
|
}
|
|
|
|
static param_t *cpld_get_params() {
|
|
int hide_offsets = RGB_CPLD_detected || !capinfo->mode7;
|
|
params[A_OFFSET].hidden = hide_offsets;
|
|
params[B_OFFSET].hidden = hide_offsets;
|
|
params[C_OFFSET].hidden = hide_offsets;
|
|
params[D_OFFSET].hidden = hide_offsets;
|
|
params[E_OFFSET].hidden = hide_offsets;
|
|
params[F_OFFSET].hidden = hide_offsets;
|
|
return params;
|
|
}
|
|
|
|
static int cpld_get_value(int num) {
|
|
switch (num) {
|
|
|
|
case CPLD_SETUP_MODE:
|
|
return config->cpld_setup_mode;
|
|
case ALL_OFFSETS:
|
|
return config->all_offsets;
|
|
case A_OFFSET:
|
|
return config->sp_offset[0];
|
|
case B_OFFSET:
|
|
return config->sp_offset[1];
|
|
case C_OFFSET:
|
|
return config->sp_offset[2];
|
|
case D_OFFSET:
|
|
return config->sp_offset[3];
|
|
case E_OFFSET:
|
|
return config->sp_offset[4];
|
|
case F_OFFSET:
|
|
return config->sp_offset[5];
|
|
case HALF:
|
|
return config->half_px_delay;
|
|
case DIVIDER:
|
|
return config->divider;
|
|
case RANGE:
|
|
return config->range;
|
|
case DELAY:
|
|
return config->full_px_delay;
|
|
case RATE:
|
|
return config->rate;
|
|
case MUX:
|
|
return config->mux;
|
|
case DAC_A:
|
|
return config->dac_a;
|
|
case DAC_B:
|
|
return config->dac_b;
|
|
case DAC_C:
|
|
return config->dac_c;
|
|
case DAC_D:
|
|
return config->dac_d;
|
|
case DAC_E:
|
|
return config->dac_e;
|
|
case DAC_F:
|
|
return config->dac_f;
|
|
case DAC_G:
|
|
return config->dac_g;
|
|
case DAC_H:
|
|
return config->dac_h;
|
|
case TERMINATE:
|
|
return config->terminate;
|
|
case COUPLING:
|
|
return config->coupling;
|
|
|
|
case FILTER_L:
|
|
return config->filter_l;
|
|
case SUB_C:
|
|
return config->sub_c;
|
|
case ALT_R:
|
|
return config->alt_r;
|
|
case EDGE:
|
|
return config->edge;
|
|
case CLAMPTYPE:
|
|
return config->clamptype;
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static const char *cpld_get_value_string(int num) {
|
|
if (num == EDGE) {
|
|
return edge_names[config->edge];
|
|
}
|
|
if (num == RATE) {
|
|
return rate_names[config->rate];
|
|
}
|
|
if (num == CPLD_SETUP_MODE) {
|
|
return cpld_setup_names[config->cpld_setup_mode];
|
|
}
|
|
if (num == DIVIDER) {
|
|
return divider_names[config->divider];
|
|
}
|
|
if (num == COUPLING) {
|
|
return coupling_names[config->coupling];
|
|
}
|
|
if (num == RANGE) {
|
|
if (capinfo->mode7) {
|
|
return m7range_names[config->range];
|
|
} else {
|
|
return range_names[config->range];
|
|
}
|
|
}
|
|
if (num >= DAC_A && num <= DAC_H) {
|
|
return volt_names[cpld_get_value(num)];
|
|
}
|
|
if (num >= ALL_OFFSETS && num <= F_OFFSET) {
|
|
sprintf(phase_text, "%d (%d Degrees)", cpld_get_value(num), cpld_get_value(num) * 360 / divider_lookup[get_adjusted_divider_index()]);
|
|
return phase_text;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int offset_fixup(int value) {
|
|
int range = divider_lookup[get_adjusted_divider_index()];
|
|
if (supports_odd_even && range > 8) {
|
|
range >>= 1;
|
|
int odd_even_offset = config->all_offsets - (config->all_offsets % range);
|
|
return (value % range) + odd_even_offset;
|
|
} else {
|
|
return value;
|
|
}
|
|
}
|
|
|
|
static void cpld_set_value(int num, int value) {
|
|
if (value < params[num].min) {
|
|
value = params[num].min;
|
|
}
|
|
if (value > params[num].max && (num < ALL_OFFSETS || num > F_OFFSET)) { //don't clip offsets because the max value could change after the values are written when loading a new profile if the divider is different
|
|
value = params[num].max;
|
|
}
|
|
switch (num) {
|
|
case CPLD_SETUP_MODE:
|
|
config->cpld_setup_mode = value;
|
|
set_setup_mode(value);
|
|
break;
|
|
case ALL_OFFSETS:
|
|
config->all_offsets = value;
|
|
config->sp_offset[0] = offset_fixup(value);
|
|
config->sp_offset[1] = offset_fixup(value);
|
|
config->sp_offset[2] = offset_fixup(value);
|
|
config->sp_offset[3] = offset_fixup(value);
|
|
config->sp_offset[4] = offset_fixup(value);
|
|
config->sp_offset[5] = offset_fixup(value);
|
|
break;
|
|
case A_OFFSET:
|
|
config->sp_offset[0] = offset_fixup(value);
|
|
break;
|
|
case B_OFFSET:
|
|
config->sp_offset[1] = offset_fixup(value);
|
|
break;
|
|
case C_OFFSET:
|
|
config->sp_offset[2] = offset_fixup(value);
|
|
break;
|
|
case D_OFFSET:
|
|
config->sp_offset[3] = offset_fixup(value);
|
|
break;
|
|
case E_OFFSET:
|
|
config->sp_offset[4] = offset_fixup(value);
|
|
break;
|
|
case F_OFFSET:
|
|
config->sp_offset[5] = offset_fixup(value);
|
|
break;
|
|
case HALF:
|
|
config->half_px_delay = value;
|
|
break;
|
|
case DIVIDER:
|
|
if (supports_odd_even) {
|
|
if (value > config->divider) {
|
|
if (value == DIVIDER_10) {
|
|
value = DIVIDER_12;
|
|
} else if (value == DIVIDER_14) {
|
|
value = DIVIDER_16;
|
|
} else if (value == DIVIDER_3) {
|
|
value = DIVIDER_6;
|
|
} else if (value == DIVIDER_4) {
|
|
value = DIVIDER_16;
|
|
}
|
|
} else {
|
|
if (value == DIVIDER_10) {
|
|
value = DIVIDER_8;
|
|
} else if (value == DIVIDER_14) {
|
|
value = DIVIDER_12;
|
|
} else if (value == DIVIDER_3) {
|
|
value = DIVIDER_16;
|
|
} else if (value == DIVIDER_4) {
|
|
value = DIVIDER_6;
|
|
}
|
|
}
|
|
}
|
|
config->divider = value;
|
|
update_param_range();
|
|
int actual_value = get_adjusted_divider_index();
|
|
if (actual_value != value) {
|
|
char msg[64];
|
|
if (supports_odd_even && config->rate != RGB_RATE_3 && config->rate != RGB_RATE_9LO && config->rate != RGB_RATE_9HI && config->rate != RGB_RATE_12){
|
|
sprintf(msg, "1 Bit, 6 Bit & 9 Bit(V) Limited to %s", divider_names[actual_value]);
|
|
} else if (config->rate >= RGB_RATE_9V && config->rate <= RGB_RATE_9LO_BLANKED && value >= 6) {
|
|
sprintf(msg, "Can't use %s with 9 or 12 BPP, using %s", divider_names[value], divider_names[actual_value]);
|
|
} else if ((config->rate == RGB_RATE_1 || config->rate == RGB_RATE_1_VSYNC) && actual_value == DIVIDER_8 && value != DIVIDER_8) {
|
|
sprintf(msg, "1 Bit Per Pixel: Limited to x8");
|
|
} else {
|
|
sprintf(msg, "Clock > %dMHz: Limited to %s", MAX_CPLD_FREQUENCY/1000000, divider_names[actual_value]);
|
|
}
|
|
set_status_message(msg);
|
|
}
|
|
break;
|
|
case RANGE:
|
|
config->range = value;
|
|
break;
|
|
case DELAY:
|
|
config->full_px_delay = value;
|
|
break;
|
|
case RATE:
|
|
if (!supports_8bit || supports_analog) {
|
|
if (value > config->rate) {
|
|
if (value == RGB_RATE_9V) {
|
|
value = RGB_RATE_4_LEVEL;
|
|
}
|
|
} else {
|
|
if (value == RGB_RATE_9LO_BLANKED) {
|
|
value = RGB_RATE_6;
|
|
}
|
|
}
|
|
}
|
|
config->rate = value;
|
|
if (supports_analog) {
|
|
if (supports_8bit) {
|
|
if (RGB_CPLD_detected && config->rate == RGB_RATE_4_LEVEL) {
|
|
params[DAC_H].hidden = 0;
|
|
params[COUPLING].hidden = 1;
|
|
params[DAC_F].label = "DAC-F: G Mid";
|
|
params[DAC_G].label = "DAC-G: R Mid";
|
|
params[DAC_H].label = "DAC-H: B Mid";
|
|
} else {
|
|
params[DAC_H].hidden = 1;
|
|
params[COUPLING].hidden = 0;
|
|
params[DAC_F].label = "DAC-F: G V Sync";
|
|
params[DAC_G].label = "DAC-G: G Clamp";
|
|
params[DAC_H].label = "DAC-H: Unused";
|
|
}
|
|
}
|
|
if (!supports_bbc_9_12 && (config->rate == RGB_RATE_1 || config->rate == RGB_RATE_1_VSYNC || config->rate == RGB_RATE_3 || config->rate == RGB_RATE_6)) {
|
|
params[COUPLING].hidden = 0;
|
|
} else {
|
|
params[COUPLING].hidden = 1;
|
|
}
|
|
}
|
|
osd_refresh();
|
|
break;
|
|
case MUX:
|
|
config->mux = value;
|
|
break;
|
|
case DAC_A:
|
|
config->dac_a = value;
|
|
break;
|
|
case DAC_B:
|
|
config->dac_b = value;
|
|
break;
|
|
case DAC_C:
|
|
config->dac_c = value;
|
|
break;
|
|
case DAC_D:
|
|
config->dac_d = value;
|
|
break;
|
|
case DAC_E:
|
|
config->dac_e = value;
|
|
break;
|
|
case DAC_F:
|
|
config->dac_f = value;
|
|
break;
|
|
case DAC_G:
|
|
config->dac_g = value;
|
|
break;
|
|
case DAC_H:
|
|
config->dac_h = value;
|
|
break;
|
|
case TERMINATE:
|
|
config->terminate = value;
|
|
break;
|
|
case COUPLING:
|
|
config->coupling = value;
|
|
break;
|
|
|
|
case FILTER_L:
|
|
config->filter_l = value;
|
|
break;
|
|
case SUB_C:
|
|
config->sub_c = value;
|
|
break;
|
|
case ALT_R:
|
|
config->alt_r = value;
|
|
break;
|
|
case EDGE:
|
|
config->edge = value;
|
|
if (value > EDGE_LEADING && config->rate != RGB_RATE_3) {
|
|
set_status_message("Delay Only In 3BPP Mode");
|
|
}
|
|
break;
|
|
case CLAMPTYPE:
|
|
config->clamptype = value;
|
|
break;
|
|
|
|
}
|
|
write_config(config, DAC_UPDATE);
|
|
}
|
|
|
|
|
|
static void cpld_calibrate_sub(capture_info_t *capinfo, int elk, int (*raw_metrics)[16][NUM_OFFSETS], int (*sum_metrics)[16], int *errors, int *window_errors) {
|
|
int min_i = 0;
|
|
int metric; // this is a point value (at one sample offset)
|
|
int min_metric;
|
|
int win_metric; // this is a windowed value (over three sample offsets)
|
|
int min_win_metric;
|
|
int *by_sample_metrics;
|
|
int range; // 0..5 in Modes 0..6, 0..7 in Mode 7
|
|
int oddeven = 0;
|
|
int offset_range = 0;
|
|
char msg[256];
|
|
int msgptr = 0;
|
|
|
|
range = divider_lookup[get_adjusted_divider_index()];
|
|
if (supports_odd_even && range > 8) {
|
|
oddeven = 1;
|
|
range >>= 1;
|
|
offset_range = config->all_offsets >= range ? range : 0;
|
|
}
|
|
|
|
// Measure the error metrics at all possible offset values
|
|
min_metric = INT_MAX;
|
|
if (!oddeven) { // if mode 7 cpld using odd/even then let caller set config->half_px_delay as it is actually a quarter pixel delay
|
|
config->half_px_delay = 0;
|
|
}
|
|
int temp_delay;
|
|
if (capinfo->mode7) { //dummy calls to alignment to detect bbc or non bbc profile
|
|
temp_delay = analyze_mode7_alignment(capinfo);
|
|
} else {
|
|
temp_delay = analyze_default_alignment(capinfo);
|
|
}
|
|
if (temp_delay >= 0) { //if >=0 then bbc profile
|
|
config->full_px_delay = 0; //so set offset to 0 for alignment calibration
|
|
}
|
|
msgptr = 0;
|
|
msgptr += sprintf(msg, "INFO: ");
|
|
for (int i = 0; i < NUM_OFFSETS; i++) {
|
|
msgptr += sprintf(msg + msgptr ,"%7c", 'A' + i);
|
|
}
|
|
sprintf(msg + msgptr, " total");
|
|
log_info(msg);
|
|
for (int value = 0; value < range; value++) {
|
|
for (int i = 0; i < NUM_OFFSETS; i++) {
|
|
config->sp_offset[i] = value;
|
|
}
|
|
config->all_offsets = config->sp_offset[0] + offset_range;
|
|
write_config(config, DAC_UPDATE);
|
|
by_sample_metrics = diff_N_frames_by_sample(capinfo, NUM_CAL_FRAMES, elk);
|
|
metric = 0;
|
|
msgptr = 0;
|
|
msgptr += sprintf(msg + msgptr, "INFO: value = %d: metrics = ", value + offset_range);
|
|
for (int i = 0; i < NUM_OFFSETS; i++) {
|
|
(*raw_metrics)[value][i] = by_sample_metrics[i];
|
|
metric += by_sample_metrics[i];
|
|
msgptr += sprintf(msg + msgptr, "%7d", by_sample_metrics[i]);
|
|
}
|
|
msgptr += sprintf(msg + msgptr, "%8d", metric);
|
|
log_info(msg);
|
|
(*sum_metrics)[value] = metric;
|
|
osd_sp(config, 2, metric, -1);
|
|
if (capinfo->bpp == 16) {
|
|
unsigned int flags = extra_flags() | BIT_CALIBRATE | (2 << OFFSET_NBUFFERS);
|
|
|
|
rgb_to_fb(capinfo, flags); //restore OSD
|
|
delay_in_arm_cycles_cpu_adjust(1000000000);
|
|
}
|
|
if (metric < min_metric) {
|
|
min_metric = metric;
|
|
}
|
|
}
|
|
|
|
min_win_metric = INT_MAX;
|
|
//first seatch for noisiest sample phase
|
|
int max_metric = 0;
|
|
int max_i = 0;
|
|
for (int i = 0; i < range; i++) {
|
|
if ((*sum_metrics)[i] > max_metric) {
|
|
max_metric = (*sum_metrics)[i];
|
|
max_i = i;
|
|
}
|
|
}
|
|
if (config->range == RANGE_180 || capinfo->mode7) { // always use 180 degrees in mode 7 as no option to switch to 90
|
|
min_i = (max_i + (range >> 1)) % range; //180 degrees from worst
|
|
min_win_metric = (*sum_metrics)[(min_i - 1 + range) % range] + (*sum_metrics)[min_i] + (*sum_metrics)[(min_i + 1 + range) % range]; // is this a good sample point?
|
|
} else {
|
|
int min_i_90 = (max_i + (range >> 2)) % range; //90 degrees from worst
|
|
int min_win_metric_90_left = (*sum_metrics)[(min_i_90 - 1 + range) % range] + (*sum_metrics)[min_i_90];
|
|
int min_win_metric_90_right = (*sum_metrics)[min_i_90] + (*sum_metrics)[(min_i_90 + 1 + range) % range];
|
|
int min_i_270 = (max_i + (range >> 1) + (range >> 2)) % range; //270 degrees from worst
|
|
int min_win_metric_270_left = (*sum_metrics)[(min_i_270 - 1 + range) % range] + (*sum_metrics)[min_i_270];
|
|
int min_win_metric_270_right = (*sum_metrics)[min_i_270] + (*sum_metrics)[(min_i_270 + 1 + range) % range];
|
|
|
|
if ((min_win_metric_90_left + min_win_metric_90_right) == 0) { // is there a good 3 sample phase window at 90?
|
|
min_i = min_i_90;
|
|
min_win_metric = 0;
|
|
}else if ((min_win_metric_270_left + min_win_metric_270_right) == 0) { // is there a good 3 sample phase window at 270?
|
|
min_i = min_i_270;
|
|
min_win_metric = 0;
|
|
} else if (min_win_metric_90_left == 0 || min_win_metric_90_right == 0) { // is there a good 2 sample phase window at 90?
|
|
min_i = min_i_90;
|
|
min_win_metric = 0;
|
|
} else if (min_win_metric_270_left == 0 || min_win_metric_270_right == 0) { // is there a good 2 sample phase window at 270?
|
|
min_i = min_i_270;
|
|
min_win_metric = 0;
|
|
}
|
|
}
|
|
|
|
if (min_win_metric != 0) { // if 90 / 180 / 270 approach didn't find a zero sample window, scan whole range for best window
|
|
// Use a 3 sample window to find the minimum and maximum
|
|
min_win_metric = INT_MAX;
|
|
for (int i = 0; i < range; i++) {
|
|
int left = (i - 1 + range) % range;
|
|
int right = (i + 1 + range) % range;
|
|
win_metric = (*sum_metrics)[left] + (*sum_metrics)[i] + (*sum_metrics)[right];
|
|
if ((*sum_metrics)[i] == min_metric) {
|
|
if (win_metric < min_win_metric) {
|
|
min_win_metric = win_metric;
|
|
min_i = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the min metric is at the limit, make use of the half pixel delay
|
|
if (!RGB_CPLD_detected && capinfo->mode7 && range >= 6 && min_metric > 0 && (min_i <= 1 || min_i >= (range - 2))) {
|
|
if (!oddeven && range > 4) { //do not select half if odd/even rate as it is actually a quarter pixel shift
|
|
log_info("Enabling half pixel delay for metric %d, range = %d", min_i, range);
|
|
config->half_px_delay = 1;
|
|
min_i = (min_i + (range >> 1)) % range;
|
|
log_info("Adjusted metric = %d", min_i);
|
|
// Swap the metrics as well
|
|
for (int i = 0; i < (range >> 1); i++) {
|
|
for (int j = 0; j < NUM_OFFSETS; j++) {
|
|
int tmp = (*raw_metrics)[i][j];
|
|
(*raw_metrics)[i][j] = (*raw_metrics)[(i + (range >> 1)) % range][j];
|
|
(*raw_metrics)[(i + (range >> 1)) % range][j] = tmp;
|
|
}
|
|
}
|
|
for (int i = 0; i < (range >> 1); i++) {
|
|
int tmp = (*sum_metrics)[i];
|
|
(*sum_metrics)[i] = (*sum_metrics)[(i + (range >> 1)) % range];
|
|
(*sum_metrics)[(i + (range >> 1)) % range] = tmp;
|
|
}
|
|
}
|
|
}
|
|
|
|
// In all modes, start with the min metric
|
|
for (int i = 0; i < NUM_OFFSETS; i++) {
|
|
config->sp_offset[i] = min_i;
|
|
}
|
|
|
|
// If the metric is non zero, there is scope for further optimization in mode7
|
|
if (!RGB_CPLD_detected && capinfo->mode7 && min_metric > 0) {
|
|
log_info("Optimizing calibration");
|
|
for (int i = 0; i < NUM_OFFSETS; i++) {
|
|
// Start with current value of the sample point i
|
|
int value = config->sp_offset[i];
|
|
// Look up the current metric for this value
|
|
int ref = (*raw_metrics)[value][i];
|
|
// Loop up the metric if we decrease this by one
|
|
int left = INT_MAX;
|
|
if (value > 0) {
|
|
left = (*raw_metrics)[value - 1][i];
|
|
}
|
|
// Look up the metric if we increase this by one
|
|
int right = INT_MAX;
|
|
if (value < (range - 1)) {
|
|
right = (*raw_metrics)[value + 1][i];
|
|
}
|
|
// Make the actual decision
|
|
if (left < right && left < ref) {
|
|
config->sp_offset[i]--;
|
|
} else if (right < left && right < ref) {
|
|
config->sp_offset[i]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < NUM_OFFSETS; i++) {
|
|
config->sp_offset[i] = config->sp_offset[i] + offset_range;
|
|
}
|
|
config->all_offsets = config->sp_offset[0];
|
|
write_config(config, DAC_UPDATE);
|
|
*errors = diff_N_frames(capinfo, NUM_CAL_FRAMES, elk);
|
|
*window_errors = min_win_metric;
|
|
|
|
osd_sp(config, 2, *errors, *window_errors);
|
|
log_sp(config);
|
|
log_info("Calibration pass complete, retested errors = %d, window errors = %d", *errors, *window_errors);
|
|
}
|
|
|
|
static void cpld_calibrate(capture_info_t *capinfo, int elk) {
|
|
int (*raw_metrics)[16][NUM_OFFSETS];
|
|
int (*sum_metrics)[16];
|
|
int *errors;
|
|
int *window_errors;
|
|
config_t min_config;
|
|
int min_raw_metrics[16][NUM_OFFSETS];
|
|
int min_sum_metrics[16];
|
|
int min_errors = 0x70000000;
|
|
int min_window_errors = 0x70000000;
|
|
int old_full_px_delay;
|
|
|
|
if (modeset == MODE_SET2) {
|
|
log_info("Calibrating mode: Set 2");
|
|
raw_metrics = &raw_metrics_set2;
|
|
sum_metrics = &sum_metrics_set2;
|
|
errors = &errors_set2;
|
|
window_errors = &window_errors_set2;
|
|
} else {
|
|
log_info("Calibrating mode: Set 1");
|
|
raw_metrics = &raw_metrics_set1;
|
|
sum_metrics = &sum_metrics_set1;
|
|
errors = &errors_set1;
|
|
window_errors = &window_errors_set1;
|
|
}
|
|
old_full_px_delay = config->full_px_delay;
|
|
int multiplier = divider_lookup[get_adjusted_divider_index()];
|
|
if (supports_odd_even) { // odd even modes in BBC CPLD only
|
|
if (capinfo->mode7 && config->range == RANGE_AUTO && multiplier != 16) { //don't auto range if multiplier set to x16
|
|
log_info("Auto range: First setting to x8 multiplier in mode 7");
|
|
cpld_set_value(DIVIDER, DIVIDER_8);
|
|
calibrate_sampling_clock(0);
|
|
multiplier = divider_lookup[get_adjusted_divider_index()];
|
|
}
|
|
if (multiplier <= 8) {
|
|
cpld_calibrate_sub(capinfo, elk, raw_metrics, sum_metrics, errors, window_errors);
|
|
if (capinfo->mode7 && config->range == RANGE_AUTO && (*errors != 0 || *window_errors != 0)) {
|
|
log_info("Auto range: trying to increase to x12 multiplier in mode 7");
|
|
cpld_set_value(DIVIDER, DIVIDER_12);
|
|
calibrate_sampling_clock(0);
|
|
multiplier = divider_lookup[get_adjusted_divider_index()];
|
|
}
|
|
}
|
|
if (multiplier > 8) {
|
|
int range = multiplier >> 1;
|
|
for (int i = 0; i < 4; i++) {
|
|
switch (i){
|
|
case 0:
|
|
config->all_offsets = 0;
|
|
config->half_px_delay = 0;
|
|
break;
|
|
case 1:
|
|
config->all_offsets = range;
|
|
config->half_px_delay = 0;
|
|
break;
|
|
case 2:
|
|
config->all_offsets = 0;
|
|
config->half_px_delay = 1;
|
|
break;
|
|
case 3:
|
|
config->all_offsets = range;
|
|
config->half_px_delay = 1;
|
|
break;
|
|
}
|
|
cpld_calibrate_sub(capinfo, elk, raw_metrics, sum_metrics, errors, window_errors);
|
|
int all_offsets = config->all_offsets % range;
|
|
if (*errors < min_errors || (*errors == min_errors && *window_errors <= min_window_errors && all_offsets > 0 && all_offsets < (range - 1))) {
|
|
min_errors = *errors;
|
|
min_window_errors = *window_errors;
|
|
memcpy(&min_config, config, sizeof min_config);
|
|
memcpy(&min_raw_metrics, raw_metrics, sizeof min_raw_metrics);
|
|
memcpy(&min_sum_metrics, sum_metrics, sizeof min_sum_metrics);
|
|
log_info("Current minimum: Phase = %d, Half = %d", config->all_offsets, config->half_px_delay);
|
|
}
|
|
}
|
|
*errors = min_errors;
|
|
*window_errors = min_window_errors;
|
|
memcpy(config, &min_config, sizeof min_config);
|
|
memcpy(raw_metrics, &min_raw_metrics, sizeof min_raw_metrics);
|
|
memcpy(sum_metrics, &min_sum_metrics, sizeof min_sum_metrics);
|
|
write_config(config, DAC_UPDATE);
|
|
}
|
|
} else {
|
|
cpld_calibrate_sub(capinfo, elk, raw_metrics, sum_metrics, errors, window_errors);
|
|
}
|
|
unsigned int flags = extra_flags() | BIT_CALIBRATE | (2 << OFFSET_NBUFFERS);
|
|
|
|
rgb_to_fb(capinfo, flags); //restore OSD
|
|
// Determine mode 7 alignment
|
|
if (supports_delay) {
|
|
signed int new_full_px_delay;
|
|
if (capinfo->mode7) {
|
|
new_full_px_delay = analyze_mode7_alignment(capinfo);
|
|
} else {
|
|
new_full_px_delay = analyze_default_alignment(capinfo);
|
|
}
|
|
if (new_full_px_delay >= 0) { // if negative then not in a bbc autoswitch profile so don't auto update delay
|
|
log_info("Characters aligned to word boundaries");
|
|
config->full_px_delay = (int) new_full_px_delay;
|
|
} else {
|
|
log_info("Not a BBC display: Delay not auto adjusted");
|
|
config->full_px_delay = old_full_px_delay;
|
|
}
|
|
write_config(config, DAC_UPDATE);
|
|
}
|
|
osd_sp(config, 2, *errors, *window_errors);
|
|
log_sp(config);
|
|
rgb_to_fb(capinfo, flags); //restore OSD
|
|
log_info("Calibration complete, errors = %d", *errors);
|
|
delay_in_arm_cycles_cpu_adjust(1000000000);
|
|
}
|
|
|
|
static int cpld_show_cal_summary(int line) {
|
|
return osd_sp(config, line, modeset == MODE_SET2 ? errors_set2 : errors_set1, modeset == MODE_SET2 ? window_errors_set2 : window_errors_set1);
|
|
}
|
|
|
|
static void cpld_show_cal_details(int line) {
|
|
int *sum_metrics = modeset == MODE_SET2 ? sum_metrics_set2 : sum_metrics_set1;
|
|
int range = (*sum_metrics < 0) ? 0 : divider_lookup[get_adjusted_divider_index()];
|
|
int offset_range = 0;
|
|
if (range == 0) {
|
|
sprintf(message, "No calibration data for this mode");
|
|
osd_set(line, 0, message);
|
|
} else {
|
|
if (supports_odd_even && range > 8) {
|
|
range >>= 1;
|
|
offset_range = config->all_offsets >= range ? range : 0;
|
|
}
|
|
for (int value = 0; value < range; value++) {
|
|
sprintf(message, "Phase %2d: Errors = %6d", value + offset_range, sum_metrics[value]);
|
|
osd_set(line + value, 0, message);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void cpld_show_cal_raw(int line) {
|
|
int (*raw_metrics)[16][NUM_OFFSETS] = modeset == MODE_SET2 ? &raw_metrics_set2 : &raw_metrics_set1;
|
|
int range = ((*raw_metrics)[0][0] < 0) ? 0 : divider_lookup[get_adjusted_divider_index()];
|
|
int offset_range = 0;
|
|
if (range == 0) {
|
|
sprintf(message, "No calibration data for this mode");
|
|
osd_set(line, 0, message);
|
|
} else {
|
|
if (supports_odd_even && range > 8) {
|
|
range >>= 1;
|
|
offset_range = config->all_offsets >= range ? range : 0;
|
|
}
|
|
for (int value = 0; value < range; value++) {
|
|
char *mp = message;
|
|
mp += sprintf(mp, "%2d:", value + offset_range);
|
|
for (int i = 0; i < NUM_OFFSETS; i++) {
|
|
mp += sprintf(mp, "%6d", (*raw_metrics)[value][i]);
|
|
}
|
|
osd_set(line + value, 0, message);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int cpld_old_firmware_support() {
|
|
int firmware = 0;
|
|
if (((cpld_version >> VERSION_MAJOR_BIT ) & 0x0F) <= 1) {
|
|
firmware |= BIT_NORMAL_FIRMWARE_V1;
|
|
}
|
|
if (((cpld_version >> VERSION_MAJOR_BIT ) & 0x0F) == 2) {
|
|
firmware |= BIT_NORMAL_FIRMWARE_V2;
|
|
}
|
|
return firmware;
|
|
}
|
|
|
|
static int cpld_get_divider() {
|
|
return divider_lookup[get_adjusted_divider_index()];
|
|
}
|
|
|
|
static int cpld_get_delay() {
|
|
if (config->rate == RGB_RATE_1 || config->rate == RGB_RATE_1_VSYNC) {
|
|
return cpld_get_value(DELAY) & 0x08; // mask out lower 3 bits of delay
|
|
} else {
|
|
return cpld_get_value(DELAY) & 0x0c; // mask out lower 2 bits of delay
|
|
}
|
|
}
|
|
|
|
static int cpld_get_sync_edge() {
|
|
if (config->edge != EDGE_TRAILING) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void cpld_set_frontend(int value) {
|
|
}
|
|
|
|
// =============================================================
|
|
// BBC Driver Specific
|
|
// =============================================================
|
|
|
|
static void cpld_init_bbc(int version) {
|
|
supports_analog = 0;
|
|
cpld_init(version);
|
|
params[MUX].label = "Input Mux";
|
|
}
|
|
|
|
static int cpld_frontend_info_bbc() {
|
|
return FRONTEND_TTL_3BIT | FRONTEND_TTL_3BIT << 16;
|
|
}
|
|
|
|
cpld_t cpld_bbc = {
|
|
.name = "3-12_BIT_BBC",
|
|
.nameBBC = "3-12_BIT_BBC",
|
|
.nameRGB = "3-12_BIT_BBC",
|
|
.nameYUV = "3-12_BIT_BBC",
|
|
.nameprefix = "BBC",
|
|
.nameBBCprefix = "BBC",
|
|
.nameRGBprefix = "BBC",
|
|
.nameYUVprefix = "BBC",
|
|
.default_profile = "Acorn/BBC_Micro",
|
|
.init = cpld_init_bbc,
|
|
.get_version = cpld_get_version,
|
|
.calibrate = cpld_calibrate,
|
|
.set_mode = cpld_set_mode,
|
|
.set_vsync_psync = cpld_set_vsync_psync,
|
|
.analyse = cpld_analyse,
|
|
.old_firmware_support = cpld_old_firmware_support,
|
|
.frontend_info = cpld_frontend_info_bbc,
|
|
.set_frontend = cpld_set_frontend,
|
|
.get_divider = cpld_get_divider,
|
|
.get_delay = cpld_get_delay,
|
|
.get_sync_edge = cpld_get_sync_edge,
|
|
.update_capture_info = cpld_update_capture_info,
|
|
.get_params = cpld_get_params,
|
|
.get_value = cpld_get_value,
|
|
.get_value_string = cpld_get_value_string,
|
|
.set_value = cpld_set_value,
|
|
.show_cal_summary = cpld_show_cal_summary,
|
|
.show_cal_details = cpld_show_cal_details,
|
|
.show_cal_raw = cpld_show_cal_raw
|
|
};
|
|
|
|
cpld_t cpld_bbcv10v20 = {
|
|
.name = "Legacy_3_BIT",
|
|
.nameBBC = "Legacy_3_BIT",
|
|
.nameRGB = "Legacy_3_BIT",
|
|
.nameYUV = "Legacy_3_BIT",
|
|
.nameprefix = "BBC",
|
|
.nameBBCprefix = "BBC",
|
|
.nameRGBprefix = "BBC",
|
|
.nameYUVprefix = "BBC",
|
|
.default_profile = "Acorn/BBC_Micro_v10-v20",
|
|
.init = cpld_init_bbc,
|
|
.get_version = cpld_get_version,
|
|
.calibrate = cpld_calibrate,
|
|
.set_mode = cpld_set_mode,
|
|
.set_vsync_psync = cpld_set_vsync_psync,
|
|
.analyse = cpld_analyse,
|
|
.old_firmware_support = cpld_old_firmware_support,
|
|
.frontend_info = cpld_frontend_info_bbc,
|
|
.set_frontend = cpld_set_frontend,
|
|
.get_divider = cpld_get_divider,
|
|
.get_delay = cpld_get_delay,
|
|
.get_sync_edge = cpld_get_sync_edge,
|
|
.update_capture_info = cpld_update_capture_info,
|
|
.get_params = cpld_get_params,
|
|
.get_value = cpld_get_value,
|
|
.get_value_string = cpld_get_value_string,
|
|
.set_value = cpld_set_value,
|
|
.show_cal_summary = cpld_show_cal_summary,
|
|
.show_cal_details = cpld_show_cal_details,
|
|
.show_cal_raw = cpld_show_cal_raw
|
|
};
|
|
|
|
cpld_t cpld_bbcv21v23 = {
|
|
.name = "Legacy_3_BIT",
|
|
.nameBBC = "Legacy_3_BIT",
|
|
.nameRGB = "Legacy_3_BIT",
|
|
.nameYUV = "Legacy_3_BIT",
|
|
.nameprefix = "BBC",
|
|
.nameBBCprefix = "BBC",
|
|
.nameRGBprefix = "BBC",
|
|
.nameYUVprefix = "BBC",
|
|
.default_profile = "Acorn/BBC_Micro_v21-v23",
|
|
.init = cpld_init_bbc,
|
|
.get_version = cpld_get_version,
|
|
.calibrate = cpld_calibrate,
|
|
.set_mode = cpld_set_mode,
|
|
.set_vsync_psync = cpld_set_vsync_psync,
|
|
.analyse = cpld_analyse,
|
|
.old_firmware_support = cpld_old_firmware_support,
|
|
.frontend_info = cpld_frontend_info_bbc,
|
|
.set_frontend = cpld_set_frontend,
|
|
.get_divider = cpld_get_divider,
|
|
.get_delay = cpld_get_delay,
|
|
.get_sync_edge = cpld_get_sync_edge,
|
|
.update_capture_info = cpld_update_capture_info,
|
|
.get_params = cpld_get_params,
|
|
.get_value = cpld_get_value,
|
|
.get_value_string = cpld_get_value_string,
|
|
.set_value = cpld_set_value,
|
|
.show_cal_summary = cpld_show_cal_summary,
|
|
.show_cal_details = cpld_show_cal_details,
|
|
.show_cal_raw = cpld_show_cal_raw
|
|
};
|
|
|
|
cpld_t cpld_bbcv24 = {
|
|
.name = "Legacy_3_BIT",
|
|
.nameBBC = "Legacy_3_BIT",
|
|
.nameRGB = "Legacy_3_BIT",
|
|
.nameYUV = "Legacy_3_BIT",
|
|
.nameprefix = "BBC",
|
|
.nameBBCprefix = "BBC",
|
|
.nameRGBprefix = "BBC",
|
|
.nameYUVprefix = "BBC",
|
|
.default_profile = "Acorn/BBC_Micro_v24",
|
|
.init = cpld_init_bbc,
|
|
.get_version = cpld_get_version,
|
|
.calibrate = cpld_calibrate,
|
|
.set_mode = cpld_set_mode,
|
|
.set_vsync_psync = cpld_set_vsync_psync,
|
|
.analyse = cpld_analyse,
|
|
.old_firmware_support = cpld_old_firmware_support,
|
|
.frontend_info = cpld_frontend_info_bbc,
|
|
.set_frontend = cpld_set_frontend,
|
|
.get_divider = cpld_get_divider,
|
|
.get_delay = cpld_get_delay,
|
|
.get_sync_edge = cpld_get_sync_edge,
|
|
.update_capture_info = cpld_update_capture_info,
|
|
.get_params = cpld_get_params,
|
|
.get_value = cpld_get_value,
|
|
.get_value_string = cpld_get_value_string,
|
|
.set_value = cpld_set_value,
|
|
.show_cal_summary = cpld_show_cal_summary,
|
|
.show_cal_details = cpld_show_cal_details,
|
|
.show_cal_raw = cpld_show_cal_raw
|
|
};
|
|
|
|
cpld_t cpld_bbcv30v62 = {
|
|
.name = "Legacy_3_BIT",
|
|
.nameBBC = "Legacy_3_BIT",
|
|
.nameRGB = "Legacy_3_BIT",
|
|
.nameYUV = "Legacy_3_BIT",
|
|
.nameprefix = "BBC",
|
|
.nameBBCprefix = "BBC",
|
|
.nameRGBprefix = "BBC",
|
|
.nameYUVprefix = "BBC",
|
|
.default_profile = "Acorn/BBC_Micro_v30-v62",
|
|
.init = cpld_init_bbc,
|
|
.get_version = cpld_get_version,
|
|
.calibrate = cpld_calibrate,
|
|
.set_mode = cpld_set_mode,
|
|
.set_vsync_psync = cpld_set_vsync_psync,
|
|
.analyse = cpld_analyse,
|
|
.old_firmware_support = cpld_old_firmware_support,
|
|
.frontend_info = cpld_frontend_info_bbc,
|
|
.set_frontend = cpld_set_frontend,
|
|
.get_divider = cpld_get_divider,
|
|
.get_delay = cpld_get_delay,
|
|
.get_sync_edge = cpld_get_sync_edge,
|
|
.update_capture_info = cpld_update_capture_info,
|
|
.get_params = cpld_get_params,
|
|
.get_value = cpld_get_value,
|
|
.get_value_string = cpld_get_value_string,
|
|
.set_value = cpld_set_value,
|
|
.show_cal_summary = cpld_show_cal_summary,
|
|
.show_cal_details = cpld_show_cal_details,
|
|
.show_cal_raw = cpld_show_cal_raw
|
|
};
|
|
|
|
|
|
// =============================================================
|
|
// RGB_TTL Driver Specific
|
|
// =============================================================
|
|
|
|
static void cpld_init_rgb_ttl(int version) {
|
|
supports_analog = 0;
|
|
cpld_init(version);
|
|
}
|
|
|
|
static int cpld_frontend_info_rgb_ttl() {
|
|
return FRONTEND_TTL_6_8BIT | FRONTEND_TTL_6_8BIT << 16;
|
|
}
|
|
|
|
cpld_t cpld_rgb_ttl = {
|
|
.name = "6-12_BIT_RGB",
|
|
.nameBBC = "3-12_BIT_BBC",
|
|
.nameRGB = "6-12_BIT_RGB",
|
|
.nameYUV = "6-12_BIT_YUV",
|
|
.nameprefix = "RGB",
|
|
.nameBBCprefix = "BBC",
|
|
.nameRGBprefix = "RGB",
|
|
.nameYUVprefix = "YUV",
|
|
.default_profile = "Acorn/Electron",
|
|
.init = cpld_init_rgb_ttl,
|
|
.get_version = cpld_get_version,
|
|
.calibrate = cpld_calibrate,
|
|
.set_mode = cpld_set_mode,
|
|
.set_vsync_psync = cpld_set_vsync_psync,
|
|
.analyse = cpld_analyse,
|
|
.old_firmware_support = cpld_old_firmware_support,
|
|
.frontend_info = cpld_frontend_info_rgb_ttl,
|
|
.set_frontend = cpld_set_frontend,
|
|
.get_divider = cpld_get_divider,
|
|
.get_delay = cpld_get_delay,
|
|
.get_sync_edge = cpld_get_sync_edge,
|
|
.update_capture_info = cpld_update_capture_info,
|
|
.get_params = cpld_get_params,
|
|
.get_value = cpld_get_value,
|
|
.get_value_string = cpld_get_value_string,
|
|
.set_value = cpld_set_value,
|
|
.show_cal_summary = cpld_show_cal_summary,
|
|
.show_cal_details = cpld_show_cal_details,
|
|
.show_cal_raw = cpld_show_cal_raw
|
|
};
|
|
|
|
cpld_t cpld_rgb_ttl_24mhz = {
|
|
.name = "3-12_BIT_BBC",
|
|
.nameBBC = "3-12_BIT_BBC",
|
|
.nameRGB = "6-12_BIT_RGB",
|
|
.nameYUV = "6-12_BIT_YUV",
|
|
.nameprefix = "BBC",
|
|
.nameBBCprefix = "BBC",
|
|
.nameRGBprefix = "RGB",
|
|
.nameYUVprefix = "YUV",
|
|
.default_profile = "Acorn/BBC_Micro",
|
|
.init = cpld_init_rgb_ttl,
|
|
.get_version = cpld_get_version,
|
|
.calibrate = cpld_calibrate,
|
|
.set_mode = cpld_set_mode,
|
|
.set_vsync_psync = cpld_set_vsync_psync,
|
|
.analyse = cpld_analyse,
|
|
.old_firmware_support = cpld_old_firmware_support,
|
|
.frontend_info = cpld_frontend_info_rgb_ttl,
|
|
.set_frontend = cpld_set_frontend,
|
|
.get_divider = cpld_get_divider,
|
|
.get_delay = cpld_get_delay,
|
|
.get_sync_edge = cpld_get_sync_edge,
|
|
.update_capture_info = cpld_update_capture_info,
|
|
.get_params = cpld_get_params,
|
|
.get_value = cpld_get_value,
|
|
.get_value_string = cpld_get_value_string,
|
|
.set_value = cpld_set_value,
|
|
.show_cal_summary = cpld_show_cal_summary,
|
|
.show_cal_details = cpld_show_cal_details,
|
|
.show_cal_raw = cpld_show_cal_raw
|
|
};
|
|
|
|
// =============================================================
|
|
// RGB_Analog Driver Specific
|
|
// =============================================================
|
|
|
|
static void cpld_init_rgb_analog(int version) {
|
|
supports_analog = 1;
|
|
cpld_init(version);
|
|
}
|
|
|
|
static int cpld_frontend_info_rgb_analog() {
|
|
if (new_DAC_detected() == 1) {
|
|
return FRONTEND_ANALOG_ISSUE4 | FRONTEND_ANALOG_ISSUE4 << 16;
|
|
} else if (new_DAC_detected() == 2) {
|
|
return FRONTEND_ANALOG_ISSUE5 | FRONTEND_ANALOG_ISSUE5 << 16;
|
|
} else {
|
|
return FRONTEND_ANALOG_ISSUE3_5259 | FRONTEND_ANALOG_ISSUE1_UB1 << 16;
|
|
}
|
|
}
|
|
|
|
static void cpld_set_frontend_rgb_analog(int value) {
|
|
frontend = value;
|
|
write_config(config, DAC_UPDATE);
|
|
}
|
|
|
|
cpld_t cpld_rgb_analog = {
|
|
.name = "6-12_BIT_RGB_Analog",
|
|
.nameBBC = "3-12_BIT_BBC_Analog",
|
|
.nameRGB = "6-12_BIT_RGB_Analog",
|
|
.nameYUV = "6-12_BIT_YUV_Analog",
|
|
.nameprefix = "RGB",
|
|
.nameBBCprefix = "BBC",
|
|
.nameRGBprefix = "RGB",
|
|
.nameYUVprefix = "YUV",
|
|
.default_profile = "Amstrad/Amstrad_CPC",
|
|
.init = cpld_init_rgb_analog,
|
|
.get_version = cpld_get_version,
|
|
.calibrate = cpld_calibrate,
|
|
.set_mode = cpld_set_mode,
|
|
.set_vsync_psync = cpld_set_vsync_psync,
|
|
.analyse = cpld_analyse,
|
|
.old_firmware_support = cpld_old_firmware_support,
|
|
.frontend_info = cpld_frontend_info_rgb_analog,
|
|
.set_frontend = cpld_set_frontend_rgb_analog,
|
|
.get_divider = cpld_get_divider,
|
|
.get_delay = cpld_get_delay,
|
|
.get_sync_edge = cpld_get_sync_edge,
|
|
.update_capture_info = cpld_update_capture_info,
|
|
.get_params = cpld_get_params,
|
|
.get_value = cpld_get_value,
|
|
.get_value_string = cpld_get_value_string,
|
|
.set_value = cpld_set_value,
|
|
.show_cal_summary = cpld_show_cal_summary,
|
|
.show_cal_details = cpld_show_cal_details,
|
|
.show_cal_raw = cpld_show_cal_raw
|
|
};
|
|
|
|
|
|
cpld_t cpld_rgb_analog_24mhz = {
|
|
.name = "3-12_BIT_BBC_Analog",
|
|
.nameBBC = "3-12_BIT_BBC_Analog",
|
|
.nameRGB = "6-12_BIT_RGB_Analog",
|
|
.nameYUV = "6-12_BIT_YUV_Analog",
|
|
.nameprefix = "BBC",
|
|
.nameBBCprefix = "BBC",
|
|
.nameRGBprefix = "RGB",
|
|
.nameYUVprefix = "YUV",
|
|
.default_profile = "Acorn/BBC_Micro",
|
|
.init = cpld_init_rgb_analog,
|
|
.get_version = cpld_get_version,
|
|
.calibrate = cpld_calibrate,
|
|
.set_mode = cpld_set_mode,
|
|
.set_vsync_psync = cpld_set_vsync_psync,
|
|
.analyse = cpld_analyse,
|
|
.old_firmware_support = cpld_old_firmware_support,
|
|
.frontend_info = cpld_frontend_info_rgb_analog,
|
|
.set_frontend = cpld_set_frontend_rgb_analog,
|
|
.get_divider = cpld_get_divider,
|
|
.get_delay = cpld_get_delay,
|
|
.get_sync_edge = cpld_get_sync_edge,
|
|
.update_capture_info = cpld_update_capture_info,
|
|
.get_params = cpld_get_params,
|
|
.get_value = cpld_get_value,
|
|
.get_value_string = cpld_get_value_string,
|
|
.set_value = cpld_set_value,
|
|
.show_cal_summary = cpld_show_cal_summary,
|
|
.show_cal_details = cpld_show_cal_details,
|
|
.show_cal_raw = cpld_show_cal_raw
|
|
}; |