blob: cc68ab9336474aa8f005c7d65ef54e9222063fb5 [file] [log] [blame]
Patrick Georgi02363b52020-05-05 20:48:50 +02001/* This file is part of the coreboot project. */
Patrick Georgiac959032020-05-05 22:49:26 +02002/* SPDX-License-Identifier: GPL-2.0-only */
Patrick Rudolphfa470422017-06-20 17:49:53 +02003
Furquan Shaikh76cedd22020-05-02 10:24:23 -07004#include <acpi/acpi.h>
Matt DeVillierebe08e02017-07-14 13:28:42 -05005#include <types.h>
6#include <string.h>
Matt DeVillier53e41952017-06-27 13:07:43 -05007#include <cbfs.h>
Patrick Rudolphfa470422017-06-20 17:49:53 +02008#include <device/device.h>
9#include <device/pci.h>
10#include <device/pci_ids.h>
11#include <device/pci_ops.h>
Patrick Rudolphbac23032017-06-30 15:18:23 +020012#include <console/console.h>
13#include <cbmem.h>
Matt DeVillierebe08e02017-07-14 13:28:42 -050014#include "intel_bios.h"
Patrick Rudolphfa470422017-06-20 17:49:53 +020015#include "opregion.h"
16
Patrick Georgi4a3956d2018-05-03 19:15:13 +020017__weak
18const char *mainboard_vbt_filename(void)
19{
20 return "vbt.bin";
21}
22
23static char vbt_data[8 * KiB];
Aaron Durbin44f80652018-05-11 11:43:52 -060024static size_t vbt_data_sz;
Patrick Georgi4a3956d2018-05-03 19:15:13 +020025
26void *locate_vbt(size_t *vbt_size)
27{
28 uint32_t vbtsig = 0;
29
Aaron Durbin44f80652018-05-11 11:43:52 -060030 if (vbt_data_sz != 0) {
31 if (vbt_size)
32 *vbt_size = vbt_data_sz;
Patrick Georgi4a3956d2018-05-03 19:15:13 +020033 return (void *)vbt_data;
Aaron Durbin44f80652018-05-11 11:43:52 -060034 }
Patrick Georgi4a3956d2018-05-03 19:15:13 +020035
36 const char *filename = mainboard_vbt_filename();
37
38 size_t file_size = cbfs_boot_load_file(filename,
39 vbt_data, sizeof(vbt_data), CBFS_TYPE_RAW);
40
41 if (file_size == 0)
42 return NULL;
43
44 if (vbt_size)
45 *vbt_size = file_size;
46
47 memcpy(&vbtsig, vbt_data, sizeof(vbtsig));
48 if (vbtsig != VBT_SIGNATURE) {
49 printk(BIOS_ERR, "Missing/invalid signature in VBT data file!\n");
50 return NULL;
51 }
52
53 printk(BIOS_INFO, "Found a VBT of %zu bytes after decompression\n",
54 file_size);
Aaron Durbin44f80652018-05-11 11:43:52 -060055 vbt_data_sz = file_size;
Patrick Georgi4a3956d2018-05-03 19:15:13 +020056
57 return (void *)vbt_data;
58}
59
Patrick Rudolphfa470422017-06-20 17:49:53 +020060/* Write ASLS PCI register and prepare SWSCI register. */
61void intel_gma_opregion_register(uintptr_t opregion)
62{
Elyes HAOUAS263076c2018-05-02 21:54:59 +020063 struct device *igd;
Patrick Rudolphfa470422017-06-20 17:49:53 +020064 u16 reg16;
Matt DeVillier681ef512018-02-11 01:17:01 -060065 u16 sci_reg;
Patrick Rudolphfa470422017-06-20 17:49:53 +020066
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030067 igd = pcidev_on_root(0x2, 0);
Patrick Rudolphfa470422017-06-20 17:49:53 +020068 if (!igd || !igd->enabled)
69 return;
70
71 /*
72 * Intel BIOS Specification
73 * Chapter 5.3.7 "Initialize Hardware State"
74 */
75 pci_write_config32(igd, ASLS, opregion);
76
77 /*
Matt DeVillier681ef512018-02-11 01:17:01 -060078 * Atom-based platforms use a combined SMI/SCI register,
79 * whereas non-Atom platforms use a separate SCI register.
80 */
Julius Wernercd49cce2019-03-05 16:53:33 -080081 if (CONFIG(INTEL_GMA_SWSMISCI))
Matt DeVillier681ef512018-02-11 01:17:01 -060082 sci_reg = SWSMISCI;
83 else
84 sci_reg = SWSCI;
85
86 /*
Patrick Rudolphfa470422017-06-20 17:49:53 +020087 * Intel's Windows driver relies on this:
88 * Intel BIOS Specification
89 * Chapter 5.4 "ASL Software SCI Handler"
90 */
Matt DeVillier681ef512018-02-11 01:17:01 -060091 reg16 = pci_read_config16(igd, sci_reg);
Patrick Rudolphfa470422017-06-20 17:49:53 +020092 reg16 &= ~GSSCIE;
93 reg16 |= SMISCISEL;
Matt DeVillier681ef512018-02-11 01:17:01 -060094 pci_write_config16(igd, sci_reg, reg16);
Patrick Rudolphfa470422017-06-20 17:49:53 +020095}
Patrick Rudolphbac23032017-06-30 15:18:23 +020096
97/* Restore ASLS register on S3 resume and prepare SWSCI. */
98void intel_gma_restore_opregion(void)
99{
100 if (acpi_is_wakeup_s3()) {
101 const void *const gnvs = cbmem_find(CBMEM_ID_ACPI_GNVS);
102 uintptr_t aslb;
103
104 if (gnvs && (aslb = gma_get_gnvs_aslb(gnvs)))
105 intel_gma_opregion_register(aslb);
106 else
107 printk(BIOS_ERR, "Error: GNVS or ASLB not set.\n");
108 }
109}
Matt DeVillierebe08e02017-07-14 13:28:42 -0500110
Matt DeVillier53e41952017-06-27 13:07:43 -0500111static enum cb_err vbt_validate(struct region_device *rdev)
Matt DeVillierebe08e02017-07-14 13:28:42 -0500112{
Matt DeVillier53e41952017-06-27 13:07:43 -0500113 uint32_t sig;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500114
Matt DeVillier53e41952017-06-27 13:07:43 -0500115 if (rdev_readat(rdev, &sig, 0, sizeof(sig)) != sizeof(sig))
Matt DeVillierebe08e02017-07-14 13:28:42 -0500116 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500117
Matt DeVillier53e41952017-06-27 13:07:43 -0500118 if (sig != VBT_SIGNATURE)
Matt DeVillierebe08e02017-07-14 13:28:42 -0500119 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500120
121 return CB_SUCCESS;
122}
123
Matt DeVillier53e41952017-06-27 13:07:43 -0500124static enum cb_err locate_vbt_vbios(const u8 *vbios, struct region_device *rdev)
Matt DeVillierebe08e02017-07-14 13:28:42 -0500125{
Matt DeVillier53e41952017-06-27 13:07:43 -0500126 const optionrom_header_t *oprom;
127 const optionrom_pcir_t *pcir;
128 struct region_device rd;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500129 enum cb_err ret;
Matt DeVillier53e41952017-06-27 13:07:43 -0500130 u8 opromsize;
131 size_t offset;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500132
Matt DeVillier53e41952017-06-27 13:07:43 -0500133 // FIXME: caller should supply a region_device instead of vbios pointer
134 if (rdev_chain(&rd, &addrspace_32bit.rdev, (uintptr_t)vbios,
135 sizeof(*oprom)))
136 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500137
Matt DeVillier53e41952017-06-27 13:07:43 -0500138 if (rdev_readat(&rd, &opromsize, offsetof(optionrom_header_t, size),
139 sizeof(opromsize)) != sizeof(opromsize) || !opromsize)
140 return CB_ERR;
141
142 if (rdev_chain(&rd, &addrspace_32bit.rdev, (uintptr_t)vbios,
143 opromsize * 512))
144 return CB_ERR;
145
146 oprom = rdev_mmap(&rd, 0, sizeof(*oprom));
147 if (!oprom)
148 return CB_ERR;
149
150 if (!oprom->pcir_offset || !oprom->vbt_offset) {
151 rdev_munmap(&rd, (void *)oprom);
152 return CB_ERR;
153 }
154
155 pcir = rdev_mmap(&rd, oprom->pcir_offset, sizeof(*pcir));
156 if (pcir == NULL) {
157 rdev_munmap(&rd, (void *)oprom);
158 return CB_ERR;
159 }
160
161 printk(BIOS_DEBUG, "GMA: locate_vbt_vbios: %x %x %x %x %x\n",
162 oprom->signature, pcir->vendor, pcir->classcode[0],
163 pcir->classcode[1], pcir->classcode[2]);
164
165 /* Make sure we got an Intel VGA option rom */
166 if ((oprom->signature != OPROM_SIGNATURE) ||
167 (pcir->vendor != PCI_VENDOR_ID_INTEL) ||
168 (pcir->signature != 0x52494350) ||
169 (pcir->classcode[0] != 0x00) ||
170 (pcir->classcode[1] != 0x00) ||
171 (pcir->classcode[2] != 0x03)) {
172 rdev_munmap(&rd, (void *)oprom);
173 rdev_munmap(&rd, (void *)pcir);
174 return CB_ERR;
175 }
176
177 rdev_munmap(&rd, (void *)pcir);
178
179 /* Search for $VBT as some VBIOS are broken... */
180 offset = oprom->vbt_offset;
181 do {
182 ret = rdev_chain(rdev, &rd, offset,
183 (opromsize * 512) - offset);
184 offset++;
185 } while (ret == CB_SUCCESS && vbt_validate(rdev) != CB_SUCCESS);
186
187 offset--;
188
189 if (ret == CB_SUCCESS && offset != oprom->vbt_offset)
190 printk(BIOS_WARNING, "GMA: Buggy VBIOS found\n");
191 else if (ret != CB_SUCCESS)
192 printk(BIOS_ERR, "GMA: Broken VBIOS found\n");
193
194 rdev_munmap(&rd, (void *)oprom);
195 return ret;
196}
197
198static enum cb_err locate_vbt_cbfs(struct region_device *rdev)
199{
Patrick Georgi4a3956d2018-05-03 19:15:13 +0200200 size_t vbt_data_size;
201 void *vbt = locate_vbt(&vbt_data_size);
Matt DeVillier53e41952017-06-27 13:07:43 -0500202
Patrick Georgi4a3956d2018-05-03 19:15:13 +0200203 if (vbt == NULL)
204 return CB_ERR;
Matt DeVillier53e41952017-06-27 13:07:43 -0500205
Patrick Georgi4a3956d2018-05-03 19:15:13 +0200206 if (rdev_chain(rdev, &addrspace_32bit.rdev, (uintptr_t)vbt,
207 vbt_data_size))
208 return CB_ERR;
209
210 printk(BIOS_INFO, "GMA: Found VBT in CBFS\n");
211
212 return CB_SUCCESS;
Matt DeVillier53e41952017-06-27 13:07:43 -0500213}
214
215static enum cb_err locate_vbt_vbios_cbfs(struct region_device *rdev)
216{
217 const u8 *oprom =
Kyösti Mälkkic70eed12018-05-22 02:18:00 +0300218 (const u8 *)pci_rom_probe(pcidev_on_root(0x2, 0));
Matt DeVillier53e41952017-06-27 13:07:43 -0500219 if (oprom == NULL)
220 return CB_ERR;
221
222 printk(BIOS_INFO, "GMA: Found VBIOS in CBFS\n");
223
224 return locate_vbt_vbios(oprom, rdev);
225}
226
227/* Initialize IGD OpRegion, called from ACPI code and OS drivers */
228enum cb_err
229intel_gma_init_igd_opregion(igd_opregion_t *opregion)
230{
231 struct region_device rdev;
232 optionrom_vbt_t *vbt = NULL;
233 optionrom_vbt_t *ext_vbt;
234 bool found = false;
235
236 /* Search for vbt.bin in CBFS. */
237 if (locate_vbt_cbfs(&rdev) == CB_SUCCESS &&
238 vbt_validate(&rdev) == CB_SUCCESS) {
239 found = true;
240 printk(BIOS_INFO, "GMA: Found valid VBT in CBFS\n");
241 }
242 /* Search for pci8086,XXXX.rom in CBFS. */
243 else if (locate_vbt_vbios_cbfs(&rdev) == CB_SUCCESS &&
244 vbt_validate(&rdev) == CB_SUCCESS) {
245 found = true;
246 printk(BIOS_INFO, "GMA: Found valid VBT in VBIOS\n");
247 }
248 /*
249 * Try to locate Intel VBIOS at 0xc0000. It might have been placed by
250 * Native Graphics Init as fake Option ROM or when coreboot did run the
251 * VBIOS on legacy platforms.
252 * TODO: Place generated fake VBT in CBMEM and get rid of this.
253 */
254 else if (locate_vbt_vbios((u8 *)0xc0000, &rdev) == CB_SUCCESS &&
255 vbt_validate(&rdev) == CB_SUCCESS) {
256 found = true;
257 printk(BIOS_INFO, "GMA: Found valid VBT in legacy area\n");
258 }
259
260 if (!found) {
261 printk(BIOS_ERR, "GMA: VBT couldn't be found\n");
262 return CB_ERR;
263 }
264
265 vbt = rdev_mmap_full(&rdev);
266 if (!vbt) {
267 printk(BIOS_ERR, "GMA: Error mapping VBT\n");
268 return CB_ERR;
269 }
270
271 if (vbt->hdr_vbt_size > region_device_sz(&rdev)) {
272 printk(BIOS_ERR, "GMA: Error mapped only a partial VBT\n");
273 rdev_munmap(&rdev, vbt);
274 return CB_ERR;
275 }
276
277 memset(opregion, 0, sizeof(igd_opregion_t));
Matt DeVillierebe08e02017-07-14 13:28:42 -0500278
279 memcpy(&opregion->header.signature, IGD_OPREGION_SIGNATURE,
280 sizeof(opregion->header.signature));
Matt DeVillier53e41952017-06-27 13:07:43 -0500281 memcpy(opregion->header.vbios_version, vbt->coreblock_biosbuild,
282 ARRAY_SIZE(vbt->coreblock_biosbuild));
283 /* Extended VBT support */
284 if (vbt->hdr_vbt_size > sizeof(opregion->vbt.gvd1)) {
285 ext_vbt = cbmem_add(CBMEM_ID_EXT_VBT, vbt->hdr_vbt_size);
286
287 if (ext_vbt == NULL) {
288 printk(BIOS_ERR,
289 "GMA: Unable to add Ext VBT to cbmem!\n");
290 rdev_munmap(&rdev, vbt);
291 return CB_ERR;
292 }
293
294 memcpy(ext_vbt, vbt, vbt->hdr_vbt_size);
295 opregion->mailbox3.rvda = (uintptr_t)ext_vbt;
296 opregion->mailbox3.rvds = vbt->hdr_vbt_size;
297 } else {
298 /* Raw VBT size which can fit in gvd1 */
299 memcpy(opregion->vbt.gvd1, vbt, vbt->hdr_vbt_size);
300 }
301
302 rdev_munmap(&rdev, vbt);
Matt DeVillierebe08e02017-07-14 13:28:42 -0500303
304 /* 8kb */
305 opregion->header.size = sizeof(igd_opregion_t) / 1024;
Matt DeVillierfd0a8912017-10-19 22:44:18 -0500306
307 /*
Elyes HAOUAS18958382018-08-07 12:23:16 +0200308 * Left-shift version field to accommodate Intel Windows driver quirk
Matt DeVillierfd0a8912017-10-19 22:44:18 -0500309 * when not using a VBIOS.
310 * Required for Legacy boot + NGI, UEFI + NGI, and UEFI + GOP driver.
311 *
312 * Tested on: (platform, GPU, windows driver version)
313 * samsung/stumpy (SNB, GT2, 9.17.10.4459)
314 * google/link (IVB, GT2, 15.33.4653)
315 * google/wolf (HSW, GT1, 15.40.36.4703)
316 * google/panther (HSW, GT2, 15.40.36.4703)
317 * google/rikku (BDW, GT1, 15.40.36.4703)
318 * google/lulu (BDW, GT2, 15.40.36.4703)
319 * google/chell (SKL-Y, GT2, 15.45.21.4821)
320 * google/sentry (SKL-U, GT1, 15.45.21.4821)
321 * purism/librem13v2 (SKL-U, GT2, 15.45.21.4821)
322 *
323 * No adverse effects when using VBIOS or booting Linux.
324 */
325 opregion->header.version = IGD_OPREGION_VERSION << 24;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500326
327 // FIXME We just assume we're mobile for now
328 opregion->header.mailboxes = MAILBOXES_MOBILE;
329
330 // TODO Initialize Mailbox 1
Patrick Georgi0f68b232018-01-25 18:23:15 +0100331 opregion->mailbox1.clid = 1;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500332
333 // TODO Initialize Mailbox 3
334 opregion->mailbox3.bclp = IGD_BACKLIGHT_BRIGHTNESS;
335 opregion->mailbox3.pfit = IGD_FIELD_VALID | IGD_PFIT_STRETCH;
336 opregion->mailbox3.pcft = 0; // should be (IMON << 1) & 0x3e
337 opregion->mailbox3.cblv = IGD_FIELD_VALID | IGD_INITIAL_BRIGHTNESS;
338 opregion->mailbox3.bclm[0] = IGD_WORD_FIELD_VALID + 0x0000;
339 opregion->mailbox3.bclm[1] = IGD_WORD_FIELD_VALID + 0x0a19;
340 opregion->mailbox3.bclm[2] = IGD_WORD_FIELD_VALID + 0x1433;
341 opregion->mailbox3.bclm[3] = IGD_WORD_FIELD_VALID + 0x1e4c;
342 opregion->mailbox3.bclm[4] = IGD_WORD_FIELD_VALID + 0x2866;
343 opregion->mailbox3.bclm[5] = IGD_WORD_FIELD_VALID + 0x327f;
344 opregion->mailbox3.bclm[6] = IGD_WORD_FIELD_VALID + 0x3c99;
345 opregion->mailbox3.bclm[7] = IGD_WORD_FIELD_VALID + 0x46b2;
346 opregion->mailbox3.bclm[8] = IGD_WORD_FIELD_VALID + 0x50cc;
347 opregion->mailbox3.bclm[9] = IGD_WORD_FIELD_VALID + 0x5ae5;
348 opregion->mailbox3.bclm[10] = IGD_WORD_FIELD_VALID + 0x64ff;
349
Matt DeVillierebe08e02017-07-14 13:28:42 -0500350 /* Write ASLS PCI register and prepare SWSCI register. */
351 intel_gma_opregion_register((uintptr_t)opregion);
352
353 return CB_SUCCESS;
354}