DF-4048 / symclock_trunc.c
/* * symclock_trunc.c - userland replica of transmitter_control_v1_6's deep * color math on DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_6.ulSymClock * (sys/dev/drm/amd/display/dc/bios/command_table.c:857, :869-890). * * The struct field is ULONG (32-bit, atombios.h:1559-1575 * "ULONG ulSymClock; // Symbol Clock in 10Khz"), but the deep-color ratio * code uses the 16-bit v1_5 helpers on it (copy-paste from * transmitter_control_v1_5 where the field is USHORT usSymClock): * * params.ulSymClock = cntl->pixel_clock/10; // :857 * ... * case COLOR_DEPTH_101010: * params.ulSymClock = * cpu_to_le16((le16_to_cpu(params.ulSymClock) * 30) / 24); // :873-874 * * le16_to_cpu(u32) truncates the input to 16 bits and cpu_to_le16(...) * truncates the result to 16 bits. On the little-endian x86-64 target * these are no-ops except for the truncation. Any symbol clock whose * 10KHz value exceeds 65535 (i.e. pixel_clock > 655.35 MHz) is computed * wrong; the initial assignment at :857 is also not converted at all. */ #include <stdio.h> #include <stdint.h> /* exact replicas of the kernel expressions (LE host: pure truncation) */ static uint32_t le16_to_cpu_u32(uint32_t v) { return v & 0xffffu; } static uint32_t cpu_to_le16_ret32(uint32_t v) { return v & 0xffffu; } int main(void) { /* deep-color pixel clocks, KHz, HDMI_TYPE_A (v1_6 path) */ struct { uint32_t pix_khz; int depth; int num; const char *tag; } cases[] = { { 148500, 8, 24, "1080p60 8bpc (safe zone)" }, { 594000, 10, 30, "4K60 10bpc x30/24" }, { 594000, 12, 36, "4K60 12bpc x36/24" }, { 594000, 16, 48, "4K60 16bpc x48/24" }, { 340000, 16, 48, "HDMI1.4 4K30 16bpc x48/24" }, }; unsigned i; int bad = 0; printf("== DF-4048 replica: command_table.c:869-890 le16 math on 32-bit ==\n"); printf("== ulSymClock (ULONG, 10KHz units) ==\n\n"); printf("%-28s %10s %12s %12s %8s\n", "case", "in(10KHz)", "correct", "produced", "ok?"); for (i = 0; i < sizeof(cases)/sizeof(cases[0]); i++) { uint32_t ul = cases[i].pix_khz / 10; /* :857 */ uint64_t correct = ((uint64_t)ul * cases[i].num) / 24; /* intended math */ uint32_t produced = cpu_to_le16_ret32( (le16_to_cpu_u32(ul) * cases[i].num) / 24); /* :873-874 shape */ int ok = (uint64_t)produced == correct; if (!ok) bad++; printf("%-28s %10u %12llu %12u %8s\n", cases[i].tag, ul, (unsigned long long)correct, produced, ok ? "yes" : "TRUNC"); } printf("\nexpected: all cases compute correctly; observed: %d/%zu " "truncated\n", bad, sizeof(cases)/sizeof(cases[0])); printf("(also: :857 assigns without any cpu_to_le32 conversion - " "moot on LE x86, wrong on BE)\n"); return bad ? 1 : 0; } |