diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ad6d9752..a1b3ef7a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -63,6 +63,8 @@ file( GLOB core_files rgb_to_hdmi.c rgb_to_fb.S capture_line_default_4bpp.S + capture_line_default_4bpp_double.S + capture_line_default_4bpp_subsample.S capture_line_default_8bpp.S capture_line_atom_4bpp.S capture_line_atom_8bpp.S diff --git a/src/capture_line_default_4bpp_double.S b/src/capture_line_default_4bpp_double.S new file mode 100644 index 00000000..0d41bed3 --- /dev/null +++ b/src/capture_line_default_4bpp_double.S @@ -0,0 +1,68 @@ +#include "rpi-base.h" +#include "defs.h" + +#include "macros.S" + +.text + +.global capture_line_default_4bpp_double + +.macro CAPTURE_BITS_DOUBLE + // Pixel 0 in GPIO 4.. 2 -> 7.. 4 and 3.. 0 + // Pixel 1 in GPIO 7.. 5 -> 15..12 and 11.. 8 + // Pixel 2 in GPIO 10.. 8 -> 23..20 and 19..16 + // Pixel 3 in GPIO 13..11 -> 31..28 and 27..24 + + and r10, r8, #(7 << PIXEL_BASE) + and r9, r8, #(7 << (PIXEL_BASE + 3)) + mov r10, r10, lsl #(4 - PIXEL_BASE) + orr r10, r10, r9, lsl #(12 - (PIXEL_BASE + 3)) + + and r9, r8, #(7 << (PIXEL_BASE + 6)) + and r8, r8, #(7 << (PIXEL_BASE + 9)) + orr r10, r10, r9, lsl #(20 - (PIXEL_BASE + 6)) + orr r10, r10, r8, lsl #(28 - (PIXEL_BASE + 9)) + + // Pixel double + orr r10, r10, lsr #4 +.endm + +// The capture line function is provided the following: +// r0 = pointer to current line in frame buffer +// r1 = number of 8-pixel blocks to capture (=param_chars_per_line) +// r2 = frame buffer line pitch in bytes (=param_fb_pitch) +// r3 = flags register +// r4 = GPLEV0 constant +// r5 = frame buffer height (=param_fb_height) +// r6 = scan line count modulo 10 +// +// All registers are available as scratch registers (i.e. nothing needs to be preserved) + +capture_line_default_4bpp_double: + + push {lr} + mov r6, #0 + mov r7, #0 + tst r3, #BIT_VSYNC_MARKER + ldrne r7, =0x11111111 +loop: + WAIT_FOR_PSYNC_EDGE // expects GPLEV0 in r4, result in r8 + + CAPTURE_BITS_DOUBLE // input in r8, result in r10, corrupts r9/r14 + + // Orr in the VSync indicator + + orr r10, r10, r7 + + // Line double always in Modes 0-6 regardless of interlace + // On the multi core Pi this introduces stalling artefacts +#ifndef HAS_MULTICORE + tst r3, #BIT_SCANLINES + streq r10, [r0, r2] + strne r6, [r0, r2] +#endif + str r10, [r0], #4 + subs r1, r1, #1 + bne loop + + pop {pc} diff --git a/src/capture_line_default_4bpp_subsample.S b/src/capture_line_default_4bpp_subsample.S new file mode 100644 index 00000000..eafdc369 --- /dev/null +++ b/src/capture_line_default_4bpp_subsample.S @@ -0,0 +1,83 @@ +#include "rpi-base.h" +#include "defs.h" + +#include "macros.S" + +.text + +.global capture_line_default_4bpp_subsample + +.macro CAPTURE_LOW_BITS_SUBSAMPLE + // Pixel 0 in GPIO 4.. 2 -> 7.. 4 + // Pixel 1 ignored + // Pixel 2 in GPIO 10.. 8 -> 15..12 + // Pixel 3 ignored + + and r10, r8, #(7 << PIXEL_BASE) + mov r10, r10, lsl #(4 - PIXEL_BASE) + + and r9, r8, #(7 << (PIXEL_BASE + 6)) + orr r10, r10, r9, lsl #(6 - PIXEL_BASE) + +.endm + +.macro CAPTURE_HIGH_BITS_SUBSAMPLE + // Pixel 4 in GPIO 4.. 2 -> 23..20 + // Pixel 5 ignored + // Pixel 6 in GPIO 10.. 8 -> 31..28 + // Pixel 7 ignored + + and r9, r8, #(7 << PIXEL_BASE) + orr r10, r10, r9, lsl #(20 - PIXEL_BASE) + + and r9, r8, #(7 << (PIXEL_BASE + 6)) + orr r10, r10, r9, lsl #(22 - PIXEL_BASE) + + .endm + +// The capture line function is provided the following: +// r0 = pointer to current line in frame buffer +// r1 = number of 8-pixel blocks to capture (=param_chars_per_line) +// r2 = frame buffer line pitch in bytes (=param_fb_pitch) +// r3 = flags register +// r4 = GPLEV0 constant +// r5 = frame buffer height (=param_fb_height) +// r6 = scan line count modulo 10 +// +// All registers are available as scratch registers (i.e. nothing needs to be preserved) + +capture_line_default_4bpp_subsample: + + push {lr} + mov r6, #0 + mov r7, #0 + tst r3, #BIT_VSYNC_MARKER + ldrne r7, =0x11111111 +loop: + WAIT_FOR_PSYNC_EDGE // expects GPLEV0 in r4, result in r8 + + CAPTURE_LOW_BITS_SUBSAMPLE // input in r8, result in r10, corrupts r9/r14 + + WAIT_FOR_PSYNC_EDGE // expects GPLEV0 in r4, result in r8 + + CAPTURE_HIGH_BITS_SUBSAMPLE // input in r8, result in r10, corrupts r9/r14 + + // Pixel double + orr r10, r10, lsr #4 + + // Orr in the VSync indicator + + orr r10, r10, r7 + + // Line double always in Modes 0-6 regardless of interlace + // On the multi core Pi this introduces stalling artefacts +#ifndef HAS_MULTICORE + tst r3, #BIT_SCANLINES + streq r10, [r0, r2] + strne r6, [r0, r2] +#endif + str r10, [r0], #4 + subs r1, r1, #1 + bne loop + + pop {pc} diff --git a/src/cpld_normal.c b/src/cpld_normal.c index 43abc8b0..e8da8003 100644 --- a/src/cpld_normal.c +++ b/src/cpld_normal.c @@ -5,6 +5,7 @@ #include #include "defs.h" #include "cpld.h" +#include "geometry.h" #include "osd.h" #include "logging.h" #include "rgb_to_fb.h" @@ -392,7 +393,13 @@ static void cpld_set_mode(capture_info_t *capinfo, int mode) { if (capinfo->bpp == 8) { capinfo->capture_line = capture_line_default_8bpp; } else { - capinfo->capture_line = capture_line_default_4bpp; + if (capinfo->px_sampling == PS_DOUBLE) { + capinfo->capture_line = capture_line_default_4bpp_double; + } else if (capinfo->px_sampling == PS_SUBSAMPLE) { + capinfo->capture_line = capture_line_default_4bpp_subsample; + } else { + capinfo->capture_line = capture_line_default_4bpp; + } } } } diff --git a/src/defs.h b/src/defs.h index be479fc0..99607533 100644 --- a/src/defs.h +++ b/src/defs.h @@ -127,6 +127,7 @@ typedef struct { int v_offset; // vertical offset (in lines) int ncapture; // number of fields to capture, or -1 to capture forever int (*capture_line)(); // the capture line function to use + int px_sampling; // whether to sample normally, sub-sample or pixel double } capture_info_t; typedef struct { diff --git a/src/geometry.c b/src/geometry.c index ad64c14a..146427d5 100644 --- a/src/geometry.c +++ b/src/geometry.c @@ -11,21 +11,29 @@ enum { FB_BPP, CLOCK, LINE_LEN, - CLOCK_PPM + CLOCK_PPM, + PX_SAMPLING +}; + +static const char *px_sampling_names[] = { + "Normal", + "Sub Sample", + "Pixel Double" }; static param_t params[] = { - { H_OFFSET, "H offset", 0, 59, 1 }, - { V_OFFSET, "V offset", 0, 39, 1 }, - { H_WIDTH, "H width", 1, 100, 1 }, - { V_HEIGHT, "V height", 1, 300, 1 }, - { FB_WIDTH, "FB width", 400, 800, 1 }, - { FB_HEIGHT, "FB height", 320, 600, 1 }, - { FB_BPP, "FB bits/pixel", 4, 8, 4 }, - { CLOCK, "Clock freq", 75000000, 100000000, 1 }, - { LINE_LEN, "Line length", 1000, 9999, 1 }, - { CLOCK_PPM, "Clock tolerance", 0, 100000, 1 }, - { -1, NULL, 0, 0, 0 } + { H_OFFSET, "H offset", 0, 59, 1 }, + { V_OFFSET, "V offset", 0, 39, 1 }, + { H_WIDTH, "H width", 1, 100, 1 }, + { V_HEIGHT, "V height", 1, 300, 1 }, + { FB_WIDTH, "FB width", 400, 800, 1 }, + { FB_HEIGHT, "FB height", 320, 600, 1 }, + { FB_BPP, "FB bits/pixel", 4, 8, 4 }, + { CLOCK, "Clock freq", 75000000, 100000000, 1 }, + { LINE_LEN, "Line length", 1000, 9999, 1 }, + { CLOCK_PPM, "Clock tolerance", 0, 100000, 1 }, + { PX_SAMPLING, "Pixel sampling", 0, NUM_PS-1, 1 }, + { -1, NULL, 0, 0, 0 } }; typedef struct { @@ -39,6 +47,7 @@ typedef struct { int clock; // cpld clock (in Hz) int line_len; // number of clocks per horizontal line int clock_ppm; // cpld tolerance (in ppm) + int px_sampling; // pixel sampling mode } geometry_t; static int mode7; @@ -64,26 +73,28 @@ static void update_param_range() { void geometry_init(int version) { // These are Beeb specific defaults so the geometry property can be ommitted - mode7_geometry.h_offset = 24; - mode7_geometry.v_offset = 21; - mode7_geometry.h_width = 504 / (32 / 4); - mode7_geometry.v_height = 270; - mode7_geometry.fb_width = 504; - mode7_geometry.fb_height = 540; - mode7_geometry.fb_bpp = 4; - mode7_geometry.clock = 96000000; - mode7_geometry.line_len = 96 * 64; - mode7_geometry.clock_ppm = 5000; - default_geometry.h_offset = 32; - default_geometry.v_offset = 21; - default_geometry.h_width = 672 / (32 / 4); - default_geometry.v_height = 270; - default_geometry.fb_width = 672; - default_geometry.fb_height = 540; - default_geometry.fb_bpp = 4; - default_geometry.clock = 96000000; - default_geometry.line_len = 96 * 64; - default_geometry.clock_ppm = 5000; + mode7_geometry.h_offset = 24; + mode7_geometry.v_offset = 21; + mode7_geometry.h_width = 504 / (32 / 4); + mode7_geometry.v_height = 270; + mode7_geometry.fb_width = 504; + mode7_geometry.fb_height = 540; + mode7_geometry.fb_bpp = 4; + mode7_geometry.clock = 96000000; + mode7_geometry.line_len = 96 * 64; + mode7_geometry.clock_ppm = 5000; + mode7_geometry.px_sampling = PS_NORMAL; + default_geometry.h_offset = 32; + default_geometry.v_offset = 21; + default_geometry.h_width = 672 / (32 / 4); + default_geometry.v_height = 270; + default_geometry.fb_width = 672; + default_geometry.fb_height = 540; + default_geometry.fb_bpp = 4; + default_geometry.clock = 96000000; + default_geometry.line_len = 96 * 64; + default_geometry.clock_ppm = 5000; + default_geometry.px_sampling = PS_NORMAL; // For backwards compatibility with CPLDv1 int supports_delay = (((version >> VERSION_DESIGN_BIT) & 0x0F) == 0) && (((version >> VERSION_MAJOR_BIT ) & 0x0F) >= 2); @@ -123,10 +134,19 @@ int geometry_get_value(int num) { return geometry->line_len; case CLOCK_PPM: return geometry->clock_ppm; + case PX_SAMPLING: + return geometry->px_sampling; } return -1; } +const char *geometry_get_value_string(int num) { + if (num == PX_SAMPLING) { + return px_sampling_names[geometry_get_value(num)]; + } + return NULL; +} + void geometry_set_value(int num, int value) { switch (num) { case H_OFFSET: @@ -160,6 +180,9 @@ void geometry_set_value(int num, int value) { case CLOCK_PPM: geometry->clock_ppm = value; break; + case PX_SAMPLING: + geometry->px_sampling = value; + break; } } @@ -175,6 +198,7 @@ void geometry_get_fb_params(capture_info_t *capinfo) { capinfo->width = geometry->fb_width; capinfo->height = geometry->fb_height; capinfo->bpp = geometry->fb_bpp; + capinfo->px_sampling = geometry->px_sampling; } void geometry_get_clk_params(clk_info_t *clkinfo) { diff --git a/src/geometry.h b/src/geometry.h index 68520181..29b4b448 100644 --- a/src/geometry.h +++ b/src/geometry.h @@ -4,12 +4,20 @@ #include "defs.h" #include "cpld.h" -void geometry_init(int version); -void geometry_set_mode(int mode); -int geometry_get_value(int num); -void geometry_set_value(int num, int value); -param_t *geometry_get_params(); -void geometry_get_fb_params(capture_info_t *capinfo); -void geometry_get_clk_params(clk_info_t *clkinfo); +enum { + PS_NORMAL, // Each sampled pixel is mapped to one pixel in the frame buffer + PS_SUBSAMPLE, // Even pixels are replicated, odd pixels are ignored + PS_DOUBLE, // Each sampled pixel is mapped to two pixels in the frame buffer + NUM_PS +}; + +void geometry_init(int version); +void geometry_set_mode(int mode); +int geometry_get_value(int num); +const char *geometry_get_value_string(int num); +void geometry_set_value(int num, int value); +param_t *geometry_get_params(); +void geometry_get_fb_params(capture_info_t *capinfo); +void geometry_get_clk_params(clk_info_t *clkinfo); #endif diff --git a/src/osd.c b/src/osd.c index f97a7d67..4901a91b 100644 --- a/src/osd.c +++ b/src/osd.c @@ -544,6 +544,11 @@ static const char *get_param_string(param_menu_item_t *param_item) { return nbuffer_names[value]; #endif } + } else if (type == I_GEOMETRY) { + const char *value_str = geometry_get_value_string(param->key); + if (value_str) { + return value_str; + } } if (is_boolean_param(param_item)) { return value ? "On" : "Off"; diff --git a/src/rgb_to_fb.h b/src/rgb_to_fb.h index ed9f7d27..298bf16d 100644 --- a/src/rgb_to_fb.h +++ b/src/rgb_to_fb.h @@ -23,6 +23,10 @@ extern int capture_line_atom_8bpp(); extern int capture_line_default_4bpp(); +extern int capture_line_default_4bpp_subsample(); + +extern int capture_line_default_4bpp_double(); + extern int capture_line_default_8bpp(); extern int capture_line_mode7_4bpp(); diff --git a/src/rgb_to_hdmi.c b/src/rgb_to_hdmi.c index 550ef92f..886809de 100644 --- a/src/rgb_to_hdmi.c +++ b/src/rgb_to_hdmi.c @@ -1207,6 +1207,7 @@ void rgb_to_hdmi_main() { int result; int last_mode7; + int mode_changed; int fb_size_changed; int active_size_decreased; int clk_changed; @@ -1313,6 +1314,7 @@ void rgb_to_hdmi_main() { last_mode7 = mode7; mode7 = result & BIT_MODE7 & (!m7disable); + mode_changed = (mode7 != last_mode7) || (capinfo->px_sampling != last_capinfo.px_sampling); if (active_size_decreased) { clear = BIT_CLEAR; @@ -1327,7 +1329,7 @@ void rgb_to_hdmi_main() { recalculate_hdmi_clock_line_locked_update(); } - } while (mode7 == last_mode7 && !fb_size_changed); + } while (!mode_changed && !fb_size_changed); osd_clear(); } diff --git a/src/scripts/cmdline.txt b/src/scripts/cmdline.txt index 0175567c..4aaca050 100644 --- a/src/scripts/cmdline.txt +++ b/src/scripts/cmdline.txt @@ -66,6 +66,7 @@ # - Clock 75000000 100000000 96000000 96000000 | an Electron # - Line Length 1000 9999 6144 6144 | # - Clock tolerance 0 100000 5000 5000 | +# - Pixel sampling 0 2 0 0 | # # Any number of these parameters can be specified but typically you would specify: # @@ -84,6 +85,7 @@ # - Clock: the nominal sampling clock fed to the CPLD (in Hz) # - Line Length: the length of a horizontal line (in sampling clocks) # - Clock tolerance: the maximum correctable error in the sampling clock (in ppm) +# - Pixel sampling: 0=normal, 1=subsample, 2=pixel double # # info: the default info screen # - 0 is the calibration summary @@ -188,10 +190,10 @@ sampling06=3 sampling7=0,2,2,2,2,2,2,0,8,5 info=1 palette=0 deinterlace=6 scanli #sampling=0,5,5,5,5,5,5,0 geometry=3,37,66,200,528,400,4,85909091,5472 nbuffers=2 m7disable=1 # # Here's an example that might work with an Atom (CPLDv1) (wider border) -#sampling=0,5,5,5,5,5,5,0 geometry=0,31,69,212,552,424,4,85909091,5472 nbuffers=2 m7disable=1 +#sampling=0,5,5,5,5,5,5,0 geometry=0,26,69,212,552,424,4,85909091,5472 nbuffers=2 m7disable=1 # -# Here's an example that might work with an Atom (CPLDv1) (wider border, 8x sampling clock) -#sampling=0,4,4,4,4,4,4,0,8 geometry=8,26,69,212,552,424,4,114545440,7296 nbuffers=2 m7disable=1 +# Here's an example that might work with an Atom (CPLDv1) (wider border, sub-sample pixels) +#sampling=0,5,5,5,5,5,5,0 geometry=0,26,69,212,552,424,4,85909091,5472,5000,1 nbuffers=2 m7disable=1 # # Here's an example that might work with an Atom (CPLDv2) (even wider border) #sampling=0,5,5,5,5,5,5,0 geometry=24,16,76,240,608,480,4,85909091,5472 nbuffers=2 m7disable=1