DF-4450 / psr2_reg_demo.c
/* * DF-4450 demo: EDP_PSR2_CTL field spill in hsw_activate_psr2() * Mirrors the kernel math from sys/dev/drm/i915/intel_psr.c:410-443 and the * register field definitions from sys/dev/drm/i915/i915_reg.h:4206-4222. * Build: cc -O2 -o psr2_reg_demo psr2_reg_demo.c */ #include <stdio.h> #include <stdint.h> /* i915_reg.h:4206-4222 (verbatim values) */ #define EDP_PSR2_ENABLE (1u << 31) #define EDP_SU_TRACK_ENABLE (1u << 30) #define EDP_PSR2_TP2_TIME_500us (0u << 8) #define EDP_PSR2_TP2_TIME_100us (1u << 8) #define EDP_PSR2_TP2_TIME_2500us (2u << 8) #define EDP_PSR2_TP2_TIME_MASK (3u << 8) #define EDP_PSR2_FRAME_BEFORE_SU_SHIFT 4 #define EDP_PSR2_FRAME_BEFORE_SU_MASK (0xfu << 4) #define EDP_PSR2_FRAME_BEFORE_SU(a) ((a) << 4) #define EDP_PSR2_IDLE_FRAME_MASK 0xfu #define EDP_PSR2_IDLE_FRAME_SHIFT 0 /* include/drm/drm_dp_helper.h:800 (verbatim) */ #define DP_MAX_RESYNC_FRAME_COUNT_MASK 0xfu /* int max(a,b) as in the kernel */ static int kmax(int a, int b) { return a > b ? a : b; } /* * Exact reproduction of hsw_activate_psr2() value construction * (intel_psr.c:418-441) for vbt.psr.idle_frames = 0 (common) and * vbt.psr.tp2_tp3_wakeup_time_us > 500 ("else" -> 2500us branch). * sink_sync_latency is the masked DPCD byte from * intel_dp_get_sink_sync_latency() (intel_psr.c:205-215). */ static uint32_t encode(unsigned sink_sync_latency) { int idle_frames = kmax(6, 0 /* vbt.psr.idle_frames */); uint32_t val; idle_frames = kmax(idle_frames, sink_sync_latency + 1); /* :420 */ val = idle_frames << EDP_PSR2_IDLE_FRAME_SHIFT; /* :421 */ val |= EDP_PSR2_ENABLE | EDP_SU_TRACK_ENABLE; /* :426 */ val |= EDP_PSR2_FRAME_BEFORE_SU(sink_sync_latency + 1); /* :430 */ val |= EDP_PSR2_TP2_TIME_2500us; /* :440 */ return val; } static const char *tp2_name(unsigned tp2) { switch (tp2) { case 0: return "500us"; case 1: return "100us"; case 2: return "2500us"; case 3: return "50us"; } return "?"; } static void decode(const char *tag, uint32_t val) { unsigned idle = val & EDP_PSR2_IDLE_FRAME_MASK; unsigned fbsu = (val & EDP_PSR2_FRAME_BEFORE_SU_MASK) >> EDP_PSR2_FRAME_BEFORE_SU_SHIFT; unsigned tp2 = (val & EDP_PSR2_TP2_TIME_MASK) >> 8; printf("%-34s EDP_PSR2_CTL=0x%08x -> IDLE_FRAME=%u FRAME_BEFORE_SU=%u TP2_TIME=%s\n", tag, val, idle, fbsu, tp2_name(tp2)); } int main(void) { printf("DF-4450: hsw_activate_psr2() encoding, sink_sync_latency from DPCD\n"); printf("(DP_MAX_RESYNC_FRAME_COUNT_MASK=0x%x; HW fields are 4 bits wide)\n\n", DP_MAX_RESYNC_FRAME_COUNT_MASK); decode("latency= 7 (typical)", encode(7)); decode("latency=11", encode(11)); decode("latency=14 (max legal)", encode(14)); decode("latency=15 (SPILLS!)", encode(15)); printf("\nWith sink_sync_latency=15 the kernel computes idle_frames=16 and\n" "FRAME_BEFORE_SU(16):\n" " * 16 << 0 spills bit 4 into FRAME_BEFORE_SU (idle reads back 0,\n" " below the HW minimum of 6 the code tries to enforce)\n" " * 16 << 4 = 0x100 spills bit 8 into TP2_TIME; OR'ed with the\n" " requested 2500us encoding (0x200) the field becomes 0x300 = 50us\n" " -> three independent PSR2 timing fields corrupted in one write\n"); return 0; } |