blob: 7682af199b8be74a874ab86d7d61e3be6988f24d [file] [log] [blame]
Patrick Georgiac959032020-05-05 22:49:26 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Patrick Rudolphfa470422017-06-20 17:49:53 +02002
Furquan Shaikh76cedd22020-05-02 10:24:23 -07003#include <acpi/acpi.h>
Matt DeVillierebe08e02017-07-14 13:28:42 -05004#include <types.h>
5#include <string.h>
Matt DeVillier53e41952017-06-27 13:07:43 -05006#include <cbfs.h>
Patrick Rudolphfa470422017-06-20 17:49:53 +02007#include <device/device.h>
8#include <device/pci.h>
9#include <device/pci_ids.h>
10#include <device/pci_ops.h>
Patrick Rudolphbac23032017-06-30 15:18:23 +020011#include <console/console.h>
12#include <cbmem.h>
Matt DeVillierebe08e02017-07-14 13:28:42 -050013#include "intel_bios.h"
Patrick Rudolphfa470422017-06-20 17:49:53 +020014#include "opregion.h"
15
Patrick Georgi4a3956d2018-05-03 19:15:13 +020016__weak
17const char *mainboard_vbt_filename(void)
18{
19 return "vbt.bin";
20}
21
22static char vbt_data[8 * KiB];
Aaron Durbin44f80652018-05-11 11:43:52 -060023static size_t vbt_data_sz;
Patrick Georgi4a3956d2018-05-03 19:15:13 +020024
25void *locate_vbt(size_t *vbt_size)
26{
27 uint32_t vbtsig = 0;
28
Aaron Durbin44f80652018-05-11 11:43:52 -060029 if (vbt_data_sz != 0) {
30 if (vbt_size)
31 *vbt_size = vbt_data_sz;
Patrick Georgi4a3956d2018-05-03 19:15:13 +020032 return (void *)vbt_data;
Aaron Durbin44f80652018-05-11 11:43:52 -060033 }
Patrick Georgi4a3956d2018-05-03 19:15:13 +020034
35 const char *filename = mainboard_vbt_filename();
36
37 size_t file_size = cbfs_boot_load_file(filename,
38 vbt_data, sizeof(vbt_data), CBFS_TYPE_RAW);
39
40 if (file_size == 0)
41 return NULL;
42
43 if (vbt_size)
44 *vbt_size = file_size;
45
46 memcpy(&vbtsig, vbt_data, sizeof(vbtsig));
47 if (vbtsig != VBT_SIGNATURE) {
48 printk(BIOS_ERR, "Missing/invalid signature in VBT data file!\n");
49 return NULL;
50 }
51
52 printk(BIOS_INFO, "Found a VBT of %zu bytes after decompression\n",
53 file_size);
Aaron Durbin44f80652018-05-11 11:43:52 -060054 vbt_data_sz = file_size;
Patrick Georgi4a3956d2018-05-03 19:15:13 +020055
56 return (void *)vbt_data;
57}
58
Patrick Rudolphfa470422017-06-20 17:49:53 +020059/* Write ASLS PCI register and prepare SWSCI register. */
Nico Huberf2a0be22020-04-26 17:01:25 +020060static void intel_gma_opregion_register(uintptr_t opregion)
Patrick Rudolphfa470422017-06-20 17:49:53 +020061{
Elyes HAOUAS263076c2018-05-02 21:54:59 +020062 struct device *igd;
Patrick Rudolphfa470422017-06-20 17:49:53 +020063 u16 reg16;
Matt DeVillier681ef512018-02-11 01:17:01 -060064 u16 sci_reg;
Patrick Rudolphfa470422017-06-20 17:49:53 +020065
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030066 igd = pcidev_on_root(0x2, 0);
Patrick Rudolphfa470422017-06-20 17:49:53 +020067 if (!igd || !igd->enabled)
68 return;
69
70 /*
71 * Intel BIOS Specification
72 * Chapter 5.3.7 "Initialize Hardware State"
73 */
74 pci_write_config32(igd, ASLS, opregion);
75
76 /*
Matt DeVillier681ef512018-02-11 01:17:01 -060077 * Atom-based platforms use a combined SMI/SCI register,
78 * whereas non-Atom platforms use a separate SCI register.
79 */
Julius Wernercd49cce2019-03-05 16:53:33 -080080 if (CONFIG(INTEL_GMA_SWSMISCI))
Matt DeVillier681ef512018-02-11 01:17:01 -060081 sci_reg = SWSMISCI;
82 else
83 sci_reg = SWSCI;
84
85 /*
Patrick Rudolphfa470422017-06-20 17:49:53 +020086 * Intel's Windows driver relies on this:
87 * Intel BIOS Specification
88 * Chapter 5.4 "ASL Software SCI Handler"
89 */
Matt DeVillier681ef512018-02-11 01:17:01 -060090 reg16 = pci_read_config16(igd, sci_reg);
Patrick Rudolphfa470422017-06-20 17:49:53 +020091 reg16 &= ~GSSCIE;
92 reg16 |= SMISCISEL;
Matt DeVillier681ef512018-02-11 01:17:01 -060093 pci_write_config16(igd, sci_reg, reg16);
Patrick Rudolphfa470422017-06-20 17:49:53 +020094}
Patrick Rudolphbac23032017-06-30 15:18:23 +020095
96/* Restore ASLS register on S3 resume and prepare SWSCI. */
Nico Huberf2a0be22020-04-26 17:01:25 +020097static enum cb_err intel_gma_restore_opregion(void)
Patrick Rudolphbac23032017-06-30 15:18:23 +020098{
Nico Huberf2a0be22020-04-26 17:01:25 +020099 const igd_opregion_t *const opregion = cbmem_find(CBMEM_ID_IGD_OPREGION);
100 if (!opregion) {
101 printk(BIOS_ERR, "GMA: Failed to find IGD OpRegion.\n");
102 return CB_ERR;
Patrick Rudolphbac23032017-06-30 15:18:23 +0200103 }
Nico Huberf2a0be22020-04-26 17:01:25 +0200104 /* Write ASLS PCI register and prepare SWSCI register. */
105 intel_gma_opregion_register((uintptr_t)opregion);
106 return CB_SUCCESS;
Patrick Rudolphbac23032017-06-30 15:18:23 +0200107}
Matt DeVillierebe08e02017-07-14 13:28:42 -0500108
Matt DeVillier53e41952017-06-27 13:07:43 -0500109static enum cb_err vbt_validate(struct region_device *rdev)
Matt DeVillierebe08e02017-07-14 13:28:42 -0500110{
Matt DeVillier53e41952017-06-27 13:07:43 -0500111 uint32_t sig;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500112
Matt DeVillier53e41952017-06-27 13:07:43 -0500113 if (rdev_readat(rdev, &sig, 0, sizeof(sig)) != sizeof(sig))
Matt DeVillierebe08e02017-07-14 13:28:42 -0500114 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500115
Matt DeVillier53e41952017-06-27 13:07:43 -0500116 if (sig != VBT_SIGNATURE)
Matt DeVillierebe08e02017-07-14 13:28:42 -0500117 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500118
119 return CB_SUCCESS;
120}
121
Matt DeVillier53e41952017-06-27 13:07:43 -0500122static enum cb_err locate_vbt_vbios(const u8 *vbios, struct region_device *rdev)
Matt DeVillierebe08e02017-07-14 13:28:42 -0500123{
Matt DeVillier53e41952017-06-27 13:07:43 -0500124 const optionrom_header_t *oprom;
125 const optionrom_pcir_t *pcir;
126 struct region_device rd;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500127 enum cb_err ret;
Matt DeVillier53e41952017-06-27 13:07:43 -0500128 u8 opromsize;
129 size_t offset;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500130
Matt DeVillier53e41952017-06-27 13:07:43 -0500131 // FIXME: caller should supply a region_device instead of vbios pointer
132 if (rdev_chain(&rd, &addrspace_32bit.rdev, (uintptr_t)vbios,
133 sizeof(*oprom)))
134 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500135
Matt DeVillier53e41952017-06-27 13:07:43 -0500136 if (rdev_readat(&rd, &opromsize, offsetof(optionrom_header_t, size),
137 sizeof(opromsize)) != sizeof(opromsize) || !opromsize)
138 return CB_ERR;
139
140 if (rdev_chain(&rd, &addrspace_32bit.rdev, (uintptr_t)vbios,
141 opromsize * 512))
142 return CB_ERR;
143
144 oprom = rdev_mmap(&rd, 0, sizeof(*oprom));
145 if (!oprom)
146 return CB_ERR;
147
148 if (!oprom->pcir_offset || !oprom->vbt_offset) {
149 rdev_munmap(&rd, (void *)oprom);
150 return CB_ERR;
151 }
152
153 pcir = rdev_mmap(&rd, oprom->pcir_offset, sizeof(*pcir));
154 if (pcir == NULL) {
155 rdev_munmap(&rd, (void *)oprom);
156 return CB_ERR;
157 }
158
159 printk(BIOS_DEBUG, "GMA: locate_vbt_vbios: %x %x %x %x %x\n",
160 oprom->signature, pcir->vendor, pcir->classcode[0],
161 pcir->classcode[1], pcir->classcode[2]);
162
163 /* Make sure we got an Intel VGA option rom */
164 if ((oprom->signature != OPROM_SIGNATURE) ||
165 (pcir->vendor != PCI_VENDOR_ID_INTEL) ||
166 (pcir->signature != 0x52494350) ||
167 (pcir->classcode[0] != 0x00) ||
168 (pcir->classcode[1] != 0x00) ||
169 (pcir->classcode[2] != 0x03)) {
170 rdev_munmap(&rd, (void *)oprom);
171 rdev_munmap(&rd, (void *)pcir);
172 return CB_ERR;
173 }
174
175 rdev_munmap(&rd, (void *)pcir);
176
177 /* Search for $VBT as some VBIOS are broken... */
178 offset = oprom->vbt_offset;
179 do {
180 ret = rdev_chain(rdev, &rd, offset,
181 (opromsize * 512) - offset);
182 offset++;
183 } while (ret == CB_SUCCESS && vbt_validate(rdev) != CB_SUCCESS);
184
185 offset--;
186
187 if (ret == CB_SUCCESS && offset != oprom->vbt_offset)
188 printk(BIOS_WARNING, "GMA: Buggy VBIOS found\n");
189 else if (ret != CB_SUCCESS)
190 printk(BIOS_ERR, "GMA: Broken VBIOS found\n");
191
192 rdev_munmap(&rd, (void *)oprom);
193 return ret;
194}
195
196static enum cb_err locate_vbt_cbfs(struct region_device *rdev)
197{
Patrick Georgi4a3956d2018-05-03 19:15:13 +0200198 size_t vbt_data_size;
199 void *vbt = locate_vbt(&vbt_data_size);
Matt DeVillier53e41952017-06-27 13:07:43 -0500200
Patrick Georgi4a3956d2018-05-03 19:15:13 +0200201 if (vbt == NULL)
202 return CB_ERR;
Matt DeVillier53e41952017-06-27 13:07:43 -0500203
Patrick Georgi4a3956d2018-05-03 19:15:13 +0200204 if (rdev_chain(rdev, &addrspace_32bit.rdev, (uintptr_t)vbt,
205 vbt_data_size))
206 return CB_ERR;
207
208 printk(BIOS_INFO, "GMA: Found VBT in CBFS\n");
209
210 return CB_SUCCESS;
Matt DeVillier53e41952017-06-27 13:07:43 -0500211}
212
213static enum cb_err locate_vbt_vbios_cbfs(struct region_device *rdev)
214{
215 const u8 *oprom =
Kyösti Mälkkic70eed12018-05-22 02:18:00 +0300216 (const u8 *)pci_rom_probe(pcidev_on_root(0x2, 0));
Matt DeVillier53e41952017-06-27 13:07:43 -0500217 if (oprom == NULL)
218 return CB_ERR;
219
220 printk(BIOS_INFO, "GMA: Found VBIOS in CBFS\n");
221
222 return locate_vbt_vbios(oprom, rdev);
223}
224
225/* Initialize IGD OpRegion, called from ACPI code and OS drivers */
Nico Huberf2a0be22020-04-26 17:01:25 +0200226enum cb_err intel_gma_init_igd_opregion(void)
Matt DeVillier53e41952017-06-27 13:07:43 -0500227{
Nico Huberf2a0be22020-04-26 17:01:25 +0200228 igd_opregion_t *opregion;
Matt DeVillier53e41952017-06-27 13:07:43 -0500229 struct region_device rdev;
230 optionrom_vbt_t *vbt = NULL;
231 optionrom_vbt_t *ext_vbt;
232 bool found = false;
233
Nico Huberf2a0be22020-04-26 17:01:25 +0200234 if (acpi_is_wakeup_s3())
235 return intel_gma_restore_opregion();
236
Matt DeVillier53e41952017-06-27 13:07:43 -0500237 /* Search for vbt.bin in CBFS. */
238 if (locate_vbt_cbfs(&rdev) == CB_SUCCESS &&
239 vbt_validate(&rdev) == CB_SUCCESS) {
240 found = true;
241 printk(BIOS_INFO, "GMA: Found valid VBT in CBFS\n");
242 }
243 /* Search for pci8086,XXXX.rom in CBFS. */
244 else if (locate_vbt_vbios_cbfs(&rdev) == CB_SUCCESS &&
245 vbt_validate(&rdev) == CB_SUCCESS) {
246 found = true;
247 printk(BIOS_INFO, "GMA: Found valid VBT in VBIOS\n");
248 }
249 /*
250 * Try to locate Intel VBIOS at 0xc0000. It might have been placed by
251 * Native Graphics Init as fake Option ROM or when coreboot did run the
252 * VBIOS on legacy platforms.
253 * TODO: Place generated fake VBT in CBMEM and get rid of this.
254 */
255 else if (locate_vbt_vbios((u8 *)0xc0000, &rdev) == CB_SUCCESS &&
256 vbt_validate(&rdev) == CB_SUCCESS) {
257 found = true;
258 printk(BIOS_INFO, "GMA: Found valid VBT in legacy area\n");
259 }
260
261 if (!found) {
262 printk(BIOS_ERR, "GMA: VBT couldn't be found\n");
263 return CB_ERR;
264 }
265
266 vbt = rdev_mmap_full(&rdev);
267 if (!vbt) {
268 printk(BIOS_ERR, "GMA: Error mapping VBT\n");
269 return CB_ERR;
270 }
271
272 if (vbt->hdr_vbt_size > region_device_sz(&rdev)) {
273 printk(BIOS_ERR, "GMA: Error mapped only a partial VBT\n");
274 rdev_munmap(&rdev, vbt);
275 return CB_ERR;
276 }
277
Nico Huberf2a0be22020-04-26 17:01:25 +0200278 opregion = cbmem_add(CBMEM_ID_IGD_OPREGION, sizeof(*opregion));
279 if (!opregion) {
280 printk(BIOS_ERR, "GMA: Failed to add IGD OpRegion to CBMEM.\n");
281 return CB_ERR;
282 }
283
Matt DeVillier53e41952017-06-27 13:07:43 -0500284 memset(opregion, 0, sizeof(igd_opregion_t));
Matt DeVillierebe08e02017-07-14 13:28:42 -0500285
286 memcpy(&opregion->header.signature, IGD_OPREGION_SIGNATURE,
287 sizeof(opregion->header.signature));
Matt DeVillier53e41952017-06-27 13:07:43 -0500288 memcpy(opregion->header.vbios_version, vbt->coreblock_biosbuild,
289 ARRAY_SIZE(vbt->coreblock_biosbuild));
290 /* Extended VBT support */
291 if (vbt->hdr_vbt_size > sizeof(opregion->vbt.gvd1)) {
292 ext_vbt = cbmem_add(CBMEM_ID_EXT_VBT, vbt->hdr_vbt_size);
293
294 if (ext_vbt == NULL) {
295 printk(BIOS_ERR,
296 "GMA: Unable to add Ext VBT to cbmem!\n");
297 rdev_munmap(&rdev, vbt);
298 return CB_ERR;
299 }
300
301 memcpy(ext_vbt, vbt, vbt->hdr_vbt_size);
302 opregion->mailbox3.rvda = (uintptr_t)ext_vbt;
303 opregion->mailbox3.rvds = vbt->hdr_vbt_size;
304 } else {
305 /* Raw VBT size which can fit in gvd1 */
306 memcpy(opregion->vbt.gvd1, vbt, vbt->hdr_vbt_size);
307 }
308
309 rdev_munmap(&rdev, vbt);
Matt DeVillierebe08e02017-07-14 13:28:42 -0500310
311 /* 8kb */
312 opregion->header.size = sizeof(igd_opregion_t) / 1024;
Matt DeVillierfd0a8912017-10-19 22:44:18 -0500313
314 /*
Elyes HAOUAS18958382018-08-07 12:23:16 +0200315 * Left-shift version field to accommodate Intel Windows driver quirk
Matt DeVillierfd0a8912017-10-19 22:44:18 -0500316 * when not using a VBIOS.
317 * Required for Legacy boot + NGI, UEFI + NGI, and UEFI + GOP driver.
318 *
319 * Tested on: (platform, GPU, windows driver version)
320 * samsung/stumpy (SNB, GT2, 9.17.10.4459)
321 * google/link (IVB, GT2, 15.33.4653)
322 * google/wolf (HSW, GT1, 15.40.36.4703)
323 * google/panther (HSW, GT2, 15.40.36.4703)
324 * google/rikku (BDW, GT1, 15.40.36.4703)
325 * google/lulu (BDW, GT2, 15.40.36.4703)
326 * google/chell (SKL-Y, GT2, 15.45.21.4821)
327 * google/sentry (SKL-U, GT1, 15.45.21.4821)
328 * purism/librem13v2 (SKL-U, GT2, 15.45.21.4821)
329 *
330 * No adverse effects when using VBIOS or booting Linux.
331 */
332 opregion->header.version = IGD_OPREGION_VERSION << 24;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500333
334 // FIXME We just assume we're mobile for now
335 opregion->header.mailboxes = MAILBOXES_MOBILE;
336
337 // TODO Initialize Mailbox 1
Patrick Georgi0f68b232018-01-25 18:23:15 +0100338 opregion->mailbox1.clid = 1;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500339
340 // TODO Initialize Mailbox 3
341 opregion->mailbox3.bclp = IGD_BACKLIGHT_BRIGHTNESS;
342 opregion->mailbox3.pfit = IGD_FIELD_VALID | IGD_PFIT_STRETCH;
343 opregion->mailbox3.pcft = 0; // should be (IMON << 1) & 0x3e
344 opregion->mailbox3.cblv = IGD_FIELD_VALID | IGD_INITIAL_BRIGHTNESS;
345 opregion->mailbox3.bclm[0] = IGD_WORD_FIELD_VALID + 0x0000;
346 opregion->mailbox3.bclm[1] = IGD_WORD_FIELD_VALID + 0x0a19;
347 opregion->mailbox3.bclm[2] = IGD_WORD_FIELD_VALID + 0x1433;
348 opregion->mailbox3.bclm[3] = IGD_WORD_FIELD_VALID + 0x1e4c;
349 opregion->mailbox3.bclm[4] = IGD_WORD_FIELD_VALID + 0x2866;
350 opregion->mailbox3.bclm[5] = IGD_WORD_FIELD_VALID + 0x327f;
351 opregion->mailbox3.bclm[6] = IGD_WORD_FIELD_VALID + 0x3c99;
352 opregion->mailbox3.bclm[7] = IGD_WORD_FIELD_VALID + 0x46b2;
353 opregion->mailbox3.bclm[8] = IGD_WORD_FIELD_VALID + 0x50cc;
354 opregion->mailbox3.bclm[9] = IGD_WORD_FIELD_VALID + 0x5ae5;
355 opregion->mailbox3.bclm[10] = IGD_WORD_FIELD_VALID + 0x64ff;
356
Matt DeVillierebe08e02017-07-14 13:28:42 -0500357 /* Write ASLS PCI register and prepare SWSCI register. */
358 intel_gma_opregion_register((uintptr_t)opregion);
359
360 return CB_SUCCESS;
361}