DF-4947 / harness_mask.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | /* * harness_mask.c โ unit-level proof of the DF-4947 mask-vs-index defect in * hw_translate_dcn10.c id_to_offset (sys/dev/drm/amd/display/dc/gpio/dcn10/). * * id_to_offset() below is a VERBATIM copy of the GPIO_ID_DDC_DATA and * GPIO_ID_DDC_CLOCK branches (hw_translate_dcn10.c:210-274). For * en = GPIO_DDC_LINE_I2C_PAD it pairs REG(DC_GPIO_I2CPAD_A) with the * DDC6DATA mask 0x100 โ but DC_GPIO_I2CPAD_A's SDA field is bit 1 (0x2) per * dcn_1_0_sh_mask.h:41219, and the factory's own ddc_i2c_data_regs entry * (ddc_regs.h:69-71) uses that SDA_A_MASK for this very pin. The two tables * disagree: the translate table would drive the wrong GPIO bit. * * The sole in-tree consumer of id_to_offset is dal_gpio_get_pin_info * (gpio_base.c:140-147), which currently has no callers โ the defect is * latent (Info severity), but deterministic to demonstrate. */ #include <stdio.h> #include <stdint.h> #include <stdbool.h> #include <string.h> enum gpio_id { GPIO_ID_DDC_DATA, GPIO_ID_DDC_CLOCK, }; struct gpio_pin_info { uint32_t offset, offset_y, offset_en, offset_mask; uint32_t mask, mask_y, mask_en, mask_mask; }; enum gpio_ddc_line { GPIO_DDC_LINE_DDC1 = 0, GPIO_DDC_LINE_DDC2, GPIO_DDC_LINE_DDC3, GPIO_DDC_LINE_DDC4, GPIO_DDC_LINE_DDC5, GPIO_DDC_LINE_DDC6, GPIO_DDC_LINE_DDC_VGA, GPIO_DDC_LINE_VIP_PAD, GPIO_DDC_LINE_I2C_PAD = GPIO_DDC_LINE_VIP_PAD, GPIO_DDC_LINE_COUNT }; #define BASE_INNER(seg) DCE_BASE__INST0_SEG ## seg #define BASE(seg) BASE_INNER(seg) #define REG(reg_name) BASE(mm ## reg_name ## _BASE_IDX) + mm ## reg_name #define DCE_BASE__INST0_SEG2 0x34C0 #define mmDC_GPIO_DDC1_A 0x28d1 #define mmDC_GPIO_DDC1_A_BASE_IDX 2 #define mmDC_GPIO_DDC2_A 0x28d5 #define mmDC_GPIO_DDC2_A_BASE_IDX 2 #define mmDC_GPIO_DDC3_A 0x28d9 #define mmDC_GPIO_DDC3_A_BASE_IDX 2 #define mmDC_GPIO_DDC4_A 0x28dd #define mmDC_GPIO_DDC4_A_BASE_IDX 2 #define mmDC_GPIO_DDC5_A 0x28e1 #define mmDC_GPIO_DDC5_A_BASE_IDX 2 #define mmDC_GPIO_DDC6_A 0x28e5 #define mmDC_GPIO_DDC6_A_BASE_IDX 2 #define mmDC_GPIO_DDCVGA_A 0x28e9 #define mmDC_GPIO_DDCVGA_A_BASE_IDX 2 #define mmDC_GPIO_I2CPAD_A 0x2901 #define mmDC_GPIO_I2CPAD_A_BASE_IDX 2 #define DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK 0x00000100L #define DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK 0x00000001L /* dcn_1_0_sh_mask.h:41216-41219 โ the real I2C pad field positions */ #define DC_GPIO_I2CPAD_A__DC_GPIO_SCL_A_MASK 0x00000001L #define DC_GPIO_I2CPAD_A__DC_GPIO_SDA_A_MASK 0x00000002L /* dcn_1_0_sh_mask.h:40628 etc โ DDC1-6/VGA DATA fields all sit at bit 8 */ #define DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK 0x00000100L #define DC_GPIO_DDCVGA_A__DC_GPIO_DDCVGADATA_A_MASK 0x00000100L static bool id_to_offset_ddc(enum gpio_id id, uint32_t en, struct gpio_pin_info *info) { switch (id) { /* ---- VERBATIM hw_translate_dcn10.c:210-242 ---- */ case GPIO_ID_DDC_DATA: info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK; switch (en) { case GPIO_DDC_LINE_DDC1: info->offset = REG(DC_GPIO_DDC1_A); break; case GPIO_DDC_LINE_DDC2: info->offset = REG(DC_GPIO_DDC2_A); break; case GPIO_DDC_LINE_DDC3: info->offset = REG(DC_GPIO_DDC3_A); break; case GPIO_DDC_LINE_DDC4: info->offset = REG(DC_GPIO_DDC4_A); break; case GPIO_DDC_LINE_DDC5: info->offset = REG(DC_GPIO_DDC5_A); break; case GPIO_DDC_LINE_DDC6: info->offset = REG(DC_GPIO_DDC6_A); break; case GPIO_DDC_LINE_DDC_VGA: info->offset = REG(DC_GPIO_DDCVGA_A); break; case GPIO_DDC_LINE_I2C_PAD: info->offset = REG(DC_GPIO_I2CPAD_A); break; default: return false; } return true; /* ---- VERBATIM hw_translate_dcn10.c:243-274 ---- */ case GPIO_ID_DDC_CLOCK: info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK; switch (en) { case GPIO_DDC_LINE_DDC1: info->offset = REG(DC_GPIO_DDC1_A); break; case GPIO_DDC_LINE_DDC2: info->offset = REG(DC_GPIO_DDC2_A); break; case GPIO_DDC_LINE_DDC3: info->offset = REG(DC_GPIO_DDC3_A); break; case GPIO_DDC_LINE_DDC4: info->offset = REG(DC_GPIO_DDC4_A); break; case GPIO_DDC_LINE_DDC5: info->offset = REG(DC_GPIO_DDC5_A); break; case GPIO_DDC_LINE_DDC6: info->offset = REG(DC_GPIO_DDC6_A); break; case GPIO_DDC_LINE_DDC_VGA: info->offset = REG(DC_GPIO_DDCVGA_A); break; case GPIO_DDC_LINE_I2C_PAD: info->offset = REG(DC_GPIO_I2CPAD_A); break; default: return false; } return true; } return false; } static void show(const char *tag, enum gpio_id id, uint32_t en, uint32_t true_mask) { struct gpio_pin_info info; bool r; memset(&info, 0, sizeof(info)); r = id_to_offset_ddc(id, en, &info); printf("%-34s = %-5s offset=0x%04x mask=0x%-4x real field mask=0x%-4x %s\n", tag, r ? "true" : "false", info.offset, info.mask, true_mask, (r && info.mask == true_mask) ? "OK" : "WRONG BIT (DF-4947)"); } int main(void) { int bad = 0; printf("== id_to_offset DDC mask-vs-register-field audit (DF-4947) ==\n"); printf("DDC1-6/VGA DATA/CLK fields are laid out identically to DDC6\n"); printf("(bit8/bit0), so the hardwired DDC6 masks are correct there.\n"); printf("DC_GPIO_I2CPAD_A is NOT: SDA=bit1(0x2), SCL=bit0(0x1).\n\n"); show("id_to_offset(DDC_DATA, DDC1)", GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK); show("id_to_offset(DDC_DATA, DDC_VGA)", GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC_VGA, DC_GPIO_DDCVGA_A__DC_GPIO_DDCVGADATA_A_MASK); show("id_to_offset(DDC_CLOCK, I2C_PAD)", GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_I2C_PAD, DC_GPIO_I2CPAD_A__DC_GPIO_SCL_A_MASK); show("id_to_offset(DDC_DATA, I2C_PAD)", GPIO_ID_DDC_DATA, GPIO_DDC_LINE_I2C_PAD, DC_GPIO_I2CPAD_A__DC_GPIO_SDA_A_MASK); /* count the wrong one */ { struct gpio_pin_info info; memset(&info, 0, sizeof(info)); if (id_to_offset_ddc(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_I2C_PAD, &info) && info.mask != DC_GPIO_I2CPAD_A__DC_GPIO_SDA_A_MASK) bad++; } printf("\n%d wrong-mask lines (DDC_DATA+I2C_PAD is the DF-4947 defect)\n", bad); return (bad == 1) ? 0 : 1; } |