1 /* SPDX-License-Identifier: GPL-2.0-only */
7 #include <device/device.h>
8 #include <device/pci.h>
9 #include <device/pci_ids.h>
10 #include <device/pci_ops.h>
11 #include <console/console.h>
13 #include "intel_bios.h"
17 const char *mainboard_vbt_filename(void)
22 void *locate_vbt(size_t *vbt_size
)
30 data
= cbfs_map(mainboard_vbt_filename(), &size
);
31 if (!data
|| size
== 0) {
32 printk(BIOS_ERR
, "Could not find or load %s CBFS file\n",
33 mainboard_vbt_filename());
37 if (*(uint32_t *)data
== VBT_SIGNATURE
) {
38 printk(BIOS_INFO
, "Found a VBT of %zu bytes\n", size
);
42 printk(BIOS_ERR
, "Missing/invalid signature in VBT data file!\n");
57 /* Write ASLS PCI register and prepare SWSCI register. */
58 static void intel_gma_opregion_register(uintptr_t opregion
)
64 igd
= pcidev_on_root(0x2, 0);
65 if (!igd
|| !igd
->enabled
)
69 * Intel BIOS Specification
70 * Chapter 5.3.7 "Initialize Hardware State"
72 pci_write_config32(igd
, ASLS
, opregion
);
75 * Atom-based platforms use a combined SMI/SCI register,
76 * whereas non-Atom platforms use a separate SCI register.
78 if (CONFIG(INTEL_GMA_SWSMISCI
))
84 * Intel's Windows driver relies on this:
85 * Intel BIOS Specification
86 * Chapter 5.4 "ASL Software SCI Handler"
88 reg16
= pci_read_config16(igd
, sci_reg
);
91 pci_write_config16(igd
, sci_reg
, reg16
);
94 /* Restore ASLS register on S3 resume and prepare SWSCI. */
95 static enum cb_err
intel_gma_restore_opregion(void)
97 const igd_opregion_t
*const opregion
= cbmem_find(CBMEM_ID_IGD_OPREGION
);
99 printk(BIOS_ERR
, "GMA: Failed to find IGD OpRegion.\n");
102 /* Write ASLS PCI register and prepare SWSCI register. */
103 intel_gma_opregion_register((uintptr_t)opregion
);
107 static enum cb_err
vbt_validate(struct region_device
*rdev
)
111 if (rdev_readat(rdev
, &sig
, 0, sizeof(sig
)) != sizeof(sig
))
114 if (sig
!= VBT_SIGNATURE
)
120 static enum cb_err
locate_vbt_vbios(const u8
*vbios
, struct region_device
*rdev
)
122 const optionrom_header_t
*oprom
;
123 const optionrom_pcir_t
*pcir
;
124 struct region_device rd
;
129 // FIXME: caller should supply a region_device instead of vbios pointer
130 if (rdev_chain_mem(&rd
, vbios
, sizeof(*oprom
)))
133 if (rdev_readat(&rd
, &opromsize
, offsetof(optionrom_header_t
, size
),
134 sizeof(opromsize
)) != sizeof(opromsize
) || !opromsize
)
137 if (rdev_chain_mem(&rd
, vbios
, opromsize
* 512))
140 oprom
= rdev_mmap(&rd
, 0, sizeof(*oprom
));
144 if (!oprom
->pcir_offset
|| !oprom
->vbt_offset
) {
145 rdev_munmap(&rd
, (void *)oprom
);
149 pcir
= rdev_mmap(&rd
, oprom
->pcir_offset
, sizeof(*pcir
));
151 rdev_munmap(&rd
, (void *)oprom
);
155 printk(BIOS_DEBUG
, "GMA: %s: %x %x %x %x %x\n", __func__
,
156 oprom
->signature
, pcir
->vendor
, pcir
->classcode
[0],
157 pcir
->classcode
[1], pcir
->classcode
[2]);
159 /* Make sure we got an Intel VGA option rom */
160 if ((oprom
->signature
!= OPROM_SIGNATURE
) ||
161 (pcir
->vendor
!= PCI_VID_INTEL
) ||
162 (pcir
->signature
!= 0x52494350) ||
163 (pcir
->classcode
[0] != 0x00) ||
164 (pcir
->classcode
[1] != 0x00) ||
165 (pcir
->classcode
[2] != 0x03)) {
166 rdev_munmap(&rd
, (void *)oprom
);
167 rdev_munmap(&rd
, (void *)pcir
);
171 rdev_munmap(&rd
, (void *)pcir
);
173 /* Search for $VBT as some VBIOS are broken... */
174 offset
= oprom
->vbt_offset
;
176 ret
= rdev_chain(rdev
, &rd
, offset
,
177 (opromsize
* 512) - offset
);
179 } while (ret
== CB_SUCCESS
&& vbt_validate(rdev
) != CB_SUCCESS
);
183 if (ret
== CB_SUCCESS
&& offset
!= oprom
->vbt_offset
)
184 printk(BIOS_WARNING
, "GMA: Buggy VBIOS found\n");
185 else if (ret
!= CB_SUCCESS
)
186 printk(BIOS_ERR
, "GMA: Broken VBIOS found\n");
188 rdev_munmap(&rd
, (void *)oprom
);
192 static enum cb_err
locate_vbt_cbfs(struct region_device
*rdev
)
194 size_t vbt_data_size
;
195 void *vbt
= locate_vbt(&vbt_data_size
);
200 if (rdev_chain_mem(rdev
, vbt
, vbt_data_size
))
203 printk(BIOS_INFO
, "GMA: Found VBT in CBFS\n");
208 static enum cb_err
locate_vbt_vbios_cbfs(struct region_device
*rdev
)
211 (const u8
*)pci_rom_probe(pcidev_on_root(0x2, 0));
215 printk(BIOS_INFO
, "GMA: Found VBIOS in CBFS\n");
217 return locate_vbt_vbios(oprom
, rdev
);
221 * Try to locate VBT in possible locations and return if found.
222 * VBT can be possibly in one of 3 regions:
223 * 1. Stitched directly into CBFS region as VBT
224 * 2. Part of pci8086 option ROM within CBFS
225 * 3. part of VBIOS at location 0xC0000.
227 static enum cb_err
find_vbt_location(struct region_device
*rdev
)
229 /* Search for vbt.bin in CBFS. */
230 if (locate_vbt_cbfs(rdev
) == CB_SUCCESS
&&
231 vbt_validate(rdev
) == CB_SUCCESS
) {
232 printk(BIOS_INFO
, "GMA: Found valid VBT in CBFS\n");
235 /* Search for pci8086,XXXX.rom in CBFS. */
236 else if (locate_vbt_vbios_cbfs(rdev
) == CB_SUCCESS
&&
237 vbt_validate(rdev
) == CB_SUCCESS
) {
238 printk(BIOS_INFO
, "GMA: Found valid VBT in VBIOS\n");
242 * Try to locate Intel VBIOS at 0xc0000. It might have been placed by
243 * Native Graphics Init as fake Option ROM or when coreboot did run the
244 * VBIOS on legacy platforms.
245 * TODO: Place generated fake VBT in CBMEM and get rid of this.
247 else if (locate_vbt_vbios((u8
*)0xc0000, rdev
) == CB_SUCCESS
&&
248 vbt_validate(rdev
) == CB_SUCCESS
) {
249 printk(BIOS_INFO
, "GMA: Found valid VBT in legacy area\n");
253 printk(BIOS_ERR
, "GMA: VBT couldn't be found\n");
257 /* Function to get the IGD Opregion version */
258 static struct opregion_version
opregion_get_version(void)
260 if (CONFIG(INTEL_GMA_OPREGION_2_1
))
261 return (struct opregion_version
) { .major
= 2, .minor
= 1 };
263 return (struct opregion_version
) { .major
= 2, .minor
= 0 };
267 * Function to determine if we need to use extended VBT region to pass
268 * VBT pointer. If VBT size > 6 KiB then we need to use extended VBT
271 static inline bool is_ext_vbt_required(igd_opregion_t
*opregion
, optionrom_vbt_t
*vbt
)
273 return (vbt
->hdr_vbt_size
> sizeof(opregion
->vbt
.gvd1
));
276 /* Function to determine if the VBT uses a relative address */
277 static inline bool uses_relative_vbt_addr(opregion_header_t
*header
)
279 if (header
->opver
.major
> 2)
282 return header
->opver
.major
>= 2 && header
->opver
.minor
>= 1;
286 * Copy extended VBT at the end of opregion and fill rvda and rvds
287 * values correctly for the opregion.
289 static void opregion_add_ext_vbt(igd_opregion_t
*opregion
, uint8_t *ext_vbt
,
290 optionrom_vbt_t
*vbt
)
292 opregion_header_t
*header
= &opregion
->header
;
293 /* Copy VBT into extended VBT region (at offset 8 KiB) */
294 memcpy(ext_vbt
, vbt
, vbt
->hdr_vbt_size
);
296 /* Fill RVDA value with relative address of the opregion buffer in case of
297 IGD Opregion version 2.1+ and physical address otherwise */
299 if (uses_relative_vbt_addr(header
))
300 opregion
->mailbox3
.rvda
= sizeof(*opregion
);
302 opregion
->mailbox3
.rvda
= (uintptr_t)ext_vbt
;
304 opregion
->mailbox3
.rvds
= vbt
->hdr_vbt_size
;
307 /* Initialize IGD OpRegion, called from ACPI code and OS drivers */
308 enum cb_err
intel_gma_init_igd_opregion(void)
310 igd_opregion_t
*opregion
;
311 struct region_device rdev
;
312 optionrom_vbt_t
*vbt
= NULL
;
313 size_t opregion_size
= sizeof(igd_opregion_t
);
315 if (acpi_is_wakeup_s3())
316 return intel_gma_restore_opregion();
318 if (find_vbt_location(&rdev
) != CB_SUCCESS
)
321 vbt
= rdev_mmap_full(&rdev
);
323 printk(BIOS_ERR
, "GMA: Error mapping VBT\n");
327 if (vbt
->hdr_vbt_size
> region_device_sz(&rdev
)) {
328 printk(BIOS_ERR
, "GMA: Error mapped only a partial VBT\n");
329 rdev_munmap(&rdev
, vbt
);
333 /* Add the space for the extended VBT header even if it's not used */
334 opregion_size
+= vbt
->hdr_vbt_size
;
336 opregion
= cbmem_add(CBMEM_ID_IGD_OPREGION
, opregion_size
);
338 printk(BIOS_ERR
, "GMA: Failed to add IGD OpRegion to CBMEM.\n");
342 memset(opregion
, 0, opregion_size
);
344 memcpy(&opregion
->header
.signature
, IGD_OPREGION_SIGNATURE
,
345 sizeof(opregion
->header
.signature
));
346 memcpy(opregion
->header
.vbios_version
, vbt
->coreblock_biosbuild
,
347 ARRAY_SIZE(vbt
->coreblock_biosbuild
));
349 /* Get the opregion version information */
350 opregion
->header
.opver
= opregion_get_version();
352 /* Extended VBT support */
353 if (is_ext_vbt_required(opregion
, vbt
)) {
354 /* Place extended VBT just after opregion */
355 uint8_t *ext_vbt
= (uint8_t *)opregion
+ sizeof(*opregion
);
356 opregion_add_ext_vbt(opregion
, ext_vbt
, vbt
);
358 /* Raw VBT size which can fit in gvd1 */
359 memcpy(opregion
->vbt
.gvd1
, vbt
, vbt
->hdr_vbt_size
);
362 rdev_munmap(&rdev
, vbt
);
365 opregion
->header
.size
= sizeof(igd_opregion_t
) / 1024;
367 // FIXME We just assume we're mobile for now
368 opregion
->header
.mailboxes
= MAILBOXES_MOBILE
;
370 // TODO Initialize Mailbox 1
371 opregion
->mailbox1
.clid
= 1;
373 // TODO Initialize Mailbox 3
374 opregion
->mailbox3
.bclp
= IGD_BACKLIGHT_BRIGHTNESS
;
375 opregion
->mailbox3
.pfit
= IGD_FIELD_VALID
| IGD_PFIT_STRETCH
;
376 opregion
->mailbox3
.pcft
= 0; // should be (IMON << 1) & 0x3e
377 opregion
->mailbox3
.cblv
= IGD_FIELD_VALID
| IGD_INITIAL_BRIGHTNESS
;
378 opregion
->mailbox3
.bclm
[0] = IGD_WORD_FIELD_VALID
+ 0x0000;
379 opregion
->mailbox3
.bclm
[1] = IGD_WORD_FIELD_VALID
+ 0x0a19;
380 opregion
->mailbox3
.bclm
[2] = IGD_WORD_FIELD_VALID
+ 0x1433;
381 opregion
->mailbox3
.bclm
[3] = IGD_WORD_FIELD_VALID
+ 0x1e4c;
382 opregion
->mailbox3
.bclm
[4] = IGD_WORD_FIELD_VALID
+ 0x2866;
383 opregion
->mailbox3
.bclm
[5] = IGD_WORD_FIELD_VALID
+ 0x327f;
384 opregion
->mailbox3
.bclm
[6] = IGD_WORD_FIELD_VALID
+ 0x3c99;
385 opregion
->mailbox3
.bclm
[7] = IGD_WORD_FIELD_VALID
+ 0x46b2;
386 opregion
->mailbox3
.bclm
[8] = IGD_WORD_FIELD_VALID
+ 0x50cc;
387 opregion
->mailbox3
.bclm
[9] = IGD_WORD_FIELD_VALID
+ 0x5ae5;
388 opregion
->mailbox3
.bclm
[10] = IGD_WORD_FIELD_VALID
+ 0x64ff;
390 /* Write ASLS PCI register and prepare SWSCI register. */
391 intel_gma_opregion_register((uintptr_t)opregion
);