/* * Native HUB75 refresh driver for WalnutPi ZeroW / Allwinner H618. * * GPIO register layout and physical-pin mapping are derived from WalnutPi's * MIT-licensed gpioc project, commit 220a46267469cc9cb1cbc35cd72f35eb1699a6e8: * https://github.com/walnutpi/gpioc * * This project keeps the upstream MIT license in THIRD_PARTY_NOTICES.md. */ #ifndef WALNUTPI_H618_HUB75_H #define WALNUTPI_H618_HUB75_H #include #include #ifdef __cplusplus extern "C" { #endif #define HUB75_WIDTH 64 #define HUB75_HEIGHT 64 #define HUB75_SCAN_ROWS 32 #define HUB75_PWM_BITS 7 #define HUB75_INTERNAL_PWM_BITS 11 #define HUB75_PWM_PLANE_OFFSET (HUB75_INTERNAL_PWM_BITS - HUB75_PWM_BITS) #define HUB75_SCANS_PER_FRAME 1 #define HUB75_FRAME_BYTES (HUB75_WIDTH * HUB75_HEIGHT * 3) #define HUB75_BITPLANE_WORDS (HUB75_SCAN_ROWS * HUB75_PWM_BITS * HUB75_WIDTH) #define HUB75_HARDWARE_MAPPING "walnutpi-pi-bank-pwm-oe-v2" #define HUB75_OE_TIMING_BACKEND "h618-pwm4" #define HUB75_OE_PWM_TICK_NS 125u typedef struct hub75_context hub75_context; typedef struct hub75_config { int brightness; int refresh_rate_hz; int cpu_affinity; int realtime_priority; int use_dev_mem; int fake_pwm5_mode; int fake_pwm4_stuck; } hub75_config; typedef struct hub75_stats { double actual_refresh_rate_hz; double panel_scan_rate_hz; uint64_t completed_frames; uint64_t completed_scans; uint64_t deadline_misses; uint64_t buffer_swaps; uint64_t submitted_frames; uint64_t oe_pulse_faults; uint64_t oe_forced_blanks; uint64_t max_programmed_oe_ns; int cpu_affinity; int cpu_affinity_active; int realtime_priority; int realtime_priority_active; int memory_locked; int running; int brightness; int refresh_rate_hz; int scans_per_frame; char governor[32]; char affinity_error[128]; char realtime_error[128]; char memory_lock_error[128]; char oe_timing_backend[32]; char oe_timing_error[256]; char safeoff_error[256]; char last_error[256]; } hub75_stats; hub75_context *hub75_create(const hub75_config *config, char *error, size_t error_size); int hub75_start(hub75_context *context); int hub75_submit_rgb(hub75_context *context, const uint8_t *rgb, size_t length); int hub75_set_brightness(hub75_context *context, int brightness); int hub75_set_refresh_rate(hub75_context *context, int refresh_rate_hz); int hub75_clear(hub75_context *context); int hub75_get_stats(hub75_context *context, hub75_stats *stats); void hub75_destroy(hub75_context *context); const char *hub75_hardware_mapping(void); int hub75_force_safeoff(int use_dev_mem, char *error, size_t error_size); /* Pure conversion hook used by tests; output words contain PI0..PI5 colors. */ int hub75_build_bitplanes( const uint8_t *rgb, size_t length, uint16_t *output, size_t output_words ); /* Raspberry-Pi-compatible CIE1931 brightness mapping into 11-bit color. */ int hub75_build_bitplanes_for_brightness( const uint8_t *rgb, size_t length, int brightness, uint16_t *output, size_t output_words ); /* Pure 8-bit RGB channel to 11-bit CIE1931 mapping hook. */ uint16_t hub75_map_channel_for_brightness(uint8_t value, int brightness); /* Pure scan-count hook used by native tests and the benchmark. */ int hub75_scans_per_frame_for_brightness(int brightness); /* Fixed binary OE weights matching internal bitplanes 4..10. */ uint32_t hub75_oe_ticks_for_plane( uint64_t scan_slot_ns, int brightness, unsigned plane, uint64_t *remainder ); typedef struct hub75_fake_registers { uint32_t mux0; uint32_t mux1; uint32_t data; uint32_t pwm_pccr45; uint32_t pwm_per; uint32_t pwm_pcr4; uint32_t pwm_ppr4; uint32_t pwm_pcr5; uint32_t pwm_ppr5; } hub75_fake_registers; int hub75_get_fake_registers( hub75_context *context, hub75_fake_registers *registers ); #ifdef __cplusplus } #endif #endif