1 /* 2 * Copyright 2012-2014, Artem Falcon <lomka@gero.in> 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <string.h> 8 9 #include <KernelExport.h> 10 11 #include "intel_extreme.h" 12 13 14 #define TRACE_BIOS 15 #ifdef TRACE_BIOS 16 # define TRACE(x) dprintf x 17 #else 18 # define TRACE(x) ; 19 #endif 20 21 22 static const off_t kVbtPointer = 0x1A; 23 24 25 struct vbt_header { 26 uint8 signature[20]; 27 uint16 version; 28 uint16 header_size; 29 uint16 vbt_size; 30 uint8 vbt_checksum; 31 uint8 reserved0; 32 uint32 bdb_offset; 33 uint32 aim_offset[4]; 34 } __attribute__((packed)); 35 36 37 struct bdb_header { 38 uint8 signature[16]; 39 uint16 version; 40 uint16 header_size; 41 uint16 bdb_size; 42 } __attribute__((packed)); 43 44 45 // FIXME the struct definition for the bdb_header is not complete, so we rely 46 // on direct access with hardcoded offsets to get the timings out of it. 47 #define _PIXEL_CLOCK(x) (x[0] + (x[1] << 8)) * 10000 48 #define _H_ACTIVE(x) (x[2] + ((x[4] & 0xF0) << 4)) 49 #define _H_BLANK(x) (x[3] + ((x[4] & 0x0F) << 8)) 50 #define _H_SYNC_OFF(x) (x[8] + ((x[11] & 0xC0) << 2)) 51 #define _H_SYNC_WIDTH(x) (x[9] + ((x[11] & 0x30) << 4)) 52 #define _V_ACTIVE(x) (x[5] + ((x[7] & 0xF0) << 4)) 53 #define _V_BLANK(x) (x[6] + ((x[7] & 0x0F) << 8)) 54 #define _V_SYNC_OFF(x) ((x[10] >> 4) + ((x[11] & 0x0C) << 2)) 55 #define _V_SYNC_WIDTH(x) ((x[10] & 0x0F) + ((x[11] & 0x03) << 4)) 56 57 58 struct lvds_bdb1 { 59 uint8 id; 60 uint16 size; 61 uint8 panel_type; 62 uint8 reserved0; 63 uint16 caps; 64 } __attribute__((packed)); 65 66 67 struct lvds_bdb2_entry { 68 uint16 lfp_info_offset; 69 uint8 lfp_info_size; 70 uint16 lfp_edid_dtd_offset; 71 uint8 lfp_edid_dtd_size; 72 uint16 lfp_edid_pid_offset; 73 uint8 lfp_edid_pid_size; 74 } __attribute__((packed)); 75 76 77 struct lvds_bdb2 { 78 uint8 id; 79 uint16 size; 80 uint8 table_size; /* unapproved */ 81 struct lvds_bdb2_entry panels[16]; 82 } __attribute__((packed)); 83 84 85 struct lvds_bdb2_lfp_info { 86 uint16 x_res; 87 uint16 y_res; 88 uint32 lvds_reg; 89 uint32 lvds_reg_val; 90 uint32 pp_on_reg; 91 uint32 pp_on_reg_val; 92 uint32 pp_off_reg; 93 uint32 pp_off_reg_val; 94 uint32 pp_cycle_reg; 95 uint32 pp_cycle_reg_val; 96 uint32 pfit_reg; 97 uint32 pfit_reg_val; 98 uint16 terminator; 99 } __attribute__((packed)); 100 101 102 static struct vbios { 103 area_id area; 104 uint8* memory; 105 display_mode* shared_info; 106 struct { 107 uint16 hsync_start; 108 uint16 hsync_end; 109 uint16 hsync_total; 110 uint16 vsync_start; 111 uint16 vsync_end; 112 uint16 vsync_total; 113 } timings_common; 114 115 uint16_t ReadWord(off_t address) 116 { 117 return memory[address] | memory[address + 1] << 8; 118 } 119 } vbios; 120 121 122 /* TODO: move code to accelerant, if possible */ 123 124 /*! This is reimplementation, Haiku uses BIOS call and gets most current panel 125 info, we're, otherwise, digging in VBIOS memory and parsing VBT tables to 126 get native panel timings. This will allow to get non-updated, 127 PROM-programmed timings info when compensation mode is off on your machine. 128 */ 129 static bool 130 get_bios(void) 131 { 132 static const uint64_t kVBIOSAddress = 0xc0000; 133 static const int kVBIOSSize = 64 * 1024; 134 // FIXME: is this the same for all cards? 135 136 /* !!!DANGER!!!: mapping of BIOS using legacy location for now, 137 hence, if panel mode will be set using info from VBT, it will 138 be taken from primary card's VBIOS */ 139 vbios.area = map_physical_memory("VBIOS mapping", kVBIOSAddress, 140 kVBIOSSize, B_ANY_KERNEL_ADDRESS, B_READ_AREA, (void**)&vbios.memory); 141 142 if (vbios.area < 0) 143 return false; 144 145 TRACE((DEVICE_NAME ": mapping VBIOS: 0x%" B_PRIx64 " -> %p\n", 146 kVBIOSAddress, vbios.memory)); 147 148 int vbtOffset = vbios.ReadWord(kVbtPointer); 149 if (vbtOffset >= kVBIOSSize) { 150 TRACE((DEVICE_NAME": bad VBT offset : 0x%x\n", vbtOffset)); 151 delete_area(vbios.area); 152 return false; 153 } 154 155 struct vbt_header* vbt = (struct vbt_header*)(vbios.memory + vbtOffset); 156 if (memcmp(vbt->signature, "$VBT", 4) != 0) { 157 TRACE((DEVICE_NAME": bad VBT signature: %20s\n", vbt->signature)); 158 delete_area(vbios.area); 159 return false; 160 } 161 162 return true; 163 } 164 165 166 static bool 167 feed_shared_info(uint8* data) 168 { 169 bool bogus = false; 170 171 /* handle bogus h/vtotal values, if got such */ 172 if (vbios.timings_common.hsync_end > vbios.timings_common.hsync_total) { 173 vbios.timings_common.hsync_total = vbios.timings_common.hsync_end + 1; 174 bogus = true; 175 TRACE((DEVICE_NAME": got bogus htotal. Fixing\n")); 176 } 177 if (vbios.timings_common.vsync_end > vbios.timings_common.vsync_total) { 178 vbios.timings_common.vsync_total = vbios.timings_common.vsync_end + 1; 179 bogus = true; 180 TRACE((DEVICE_NAME": got bogus vtotal. Fixing\n")); 181 } 182 183 if (bogus) { 184 TRACE((DEVICE_NAME": adjusted LFP modeline: x%d Hz,\t" 185 "%d %d %d %d %d %d %d %d\n", 186 _PIXEL_CLOCK(data) / ((_H_ACTIVE(data) + _H_BLANK(data)) 187 * (_V_ACTIVE(data) + _V_BLANK(data))), 188 _H_ACTIVE(data), vbios.timings_common.hsync_start, 189 vbios.timings_common.hsync_end, vbios.timings_common.hsync_total, 190 _V_ACTIVE(data), vbios.timings_common.vsync_start, 191 vbios.timings_common.vsync_end, vbios.timings_common.vsync_total)); 192 } 193 194 /* TODO: add retrieved info to edid info struct, not fixed mode struct */ 195 196 /* struct display_timing is not packed, so we have to set elements 197 individually */ 198 vbios.shared_info->timing.pixel_clock = _PIXEL_CLOCK(data) / 1000; 199 vbios.shared_info->timing.h_display = vbios.shared_info->virtual_width 200 = _H_ACTIVE(data); 201 vbios.shared_info->timing.h_sync_start = vbios.timings_common.hsync_start; 202 vbios.shared_info->timing.h_sync_end = vbios.timings_common.hsync_end; 203 vbios.shared_info->timing.h_total = vbios.timings_common.hsync_total; 204 vbios.shared_info->timing.v_display = vbios.shared_info->virtual_height 205 = _V_ACTIVE(data); 206 vbios.shared_info->timing.v_sync_start = vbios.timings_common.vsync_start; 207 vbios.shared_info->timing.v_sync_end = vbios.timings_common.vsync_end; 208 vbios.shared_info->timing.v_total = vbios.timings_common.vsync_total; 209 210 delete_area(vbios.area); 211 return true; 212 } 213 214 215 bool 216 get_lvds_mode_from_bios(display_mode* sharedInfo) 217 { 218 if (!get_bios()) 219 return false; 220 221 int vbtOffset = vbios.ReadWord(kVbtPointer); 222 struct vbt_header* vbt = (struct vbt_header*)(vbios.memory + vbtOffset); 223 int bdbOffset = vbtOffset + vbt->bdb_offset; 224 225 struct bdb_header* bdb = (struct bdb_header*)(vbios.memory + bdbOffset); 226 if (memcmp(bdb->signature, "BIOS_DATA_BLOCK ", 16) != 0) { 227 TRACE((DEVICE_NAME": bad BDB signature\n")); 228 delete_area(vbios.area); 229 } 230 231 TRACE((DEVICE_NAME": parsing BDB blocks\n")); 232 int blockSize; 233 int panelType = -1; 234 235 for (int bdbBlockOffset = bdb->header_size; bdbBlockOffset < bdb->bdb_size; 236 bdbBlockOffset += blockSize) { 237 int start = bdbOffset + bdbBlockOffset; 238 239 int id = vbios.memory[start]; 240 blockSize = vbios.ReadWord(start + 1) + 3; 241 // TRACE((DEVICE_NAME": found BDB block type %d\n", id)); 242 switch (id) { 243 case 40: // FIXME magic numbers 244 { 245 struct lvds_bdb1 *lvds1; 246 lvds1 = (struct lvds_bdb1 *)(vbios.memory + start); 247 panelType = lvds1->panel_type; 248 break; 249 } 250 case 41: 251 { 252 if (panelType == -1) 253 break; 254 struct lvds_bdb2 *lvds2; 255 struct lvds_bdb2_lfp_info *lvds2_lfp_info; 256 257 lvds2 = (struct lvds_bdb2 *)(vbios.memory + start); 258 lvds2_lfp_info = (struct lvds_bdb2_lfp_info *) 259 (vbios.memory + bdbOffset 260 + lvds2->panels[panelType].lfp_info_offset); 261 /* found bad one terminator */ 262 if (lvds2_lfp_info->terminator != 0xffff) { 263 delete_area(vbios.area); 264 return false; 265 } 266 uint8_t* timing_data = vbios.memory + bdbOffset 267 + lvds2->panels[panelType].lfp_edid_dtd_offset; 268 TRACE((DEVICE_NAME": found LFP of size %d x %d " 269 "in BIOS VBT tables\n", 270 lvds2_lfp_info->x_res, lvds2_lfp_info->y_res)); 271 272 vbios.timings_common.hsync_start = _H_ACTIVE(timing_data) 273 + _H_SYNC_OFF(timing_data); 274 vbios.timings_common.hsync_end 275 = vbios.timings_common.hsync_start 276 + _H_SYNC_WIDTH(timing_data); 277 vbios.timings_common.hsync_total = _H_ACTIVE(timing_data) 278 + _H_BLANK(timing_data); 279 vbios.timings_common.vsync_start = _V_ACTIVE(timing_data) 280 + _V_SYNC_OFF(timing_data); 281 vbios.timings_common.vsync_end 282 = vbios.timings_common.vsync_start 283 + _V_SYNC_WIDTH(timing_data); 284 vbios.timings_common.vsync_total = _V_ACTIVE(timing_data) 285 + _V_BLANK(timing_data); 286 287 vbios.shared_info = sharedInfo; 288 return feed_shared_info(timing_data); 289 } 290 } 291 } 292 293 delete_area(vbios.area); 294 return true; 295 } 296