blob: cd42337d071f9631f0e043f8443371fa31e39373 [file] [log] [blame]
Patrick Rudolphfa470422017-06-20 17:49:53 +02001/*
2 * This file is part of the coreboot project.
3 *
Patrick Rudolphfa470422017-06-20 17:49:53 +02004 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; version 2, or (at your option)
Frans Hendriks01cb55b2018-11-28 09:07:54 +01007 * any later version of the License.
Patrick Rudolphfa470422017-06-20 17:49:53 +02008 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
Furquan Shaikh76cedd22020-05-02 10:24:23 -070015#include <acpi/acpi.h>
Matt DeVillierebe08e02017-07-14 13:28:42 -050016#include <types.h>
17#include <string.h>
Matt DeVillier53e41952017-06-27 13:07:43 -050018#include <cbfs.h>
Patrick Rudolphfa470422017-06-20 17:49:53 +020019#include <device/device.h>
20#include <device/pci.h>
21#include <device/pci_ids.h>
22#include <device/pci_ops.h>
Patrick Rudolphbac23032017-06-30 15:18:23 +020023#include <console/console.h>
24#include <cbmem.h>
Matt DeVillierebe08e02017-07-14 13:28:42 -050025#include "intel_bios.h"
Patrick Rudolphfa470422017-06-20 17:49:53 +020026#include "opregion.h"
27
Patrick Georgi4a3956d2018-05-03 19:15:13 +020028__weak
29const char *mainboard_vbt_filename(void)
30{
31 return "vbt.bin";
32}
33
34static char vbt_data[8 * KiB];
Aaron Durbin44f80652018-05-11 11:43:52 -060035static size_t vbt_data_sz;
Patrick Georgi4a3956d2018-05-03 19:15:13 +020036
37void *locate_vbt(size_t *vbt_size)
38{
39 uint32_t vbtsig = 0;
40
Aaron Durbin44f80652018-05-11 11:43:52 -060041 if (vbt_data_sz != 0) {
42 if (vbt_size)
43 *vbt_size = vbt_data_sz;
Patrick Georgi4a3956d2018-05-03 19:15:13 +020044 return (void *)vbt_data;
Aaron Durbin44f80652018-05-11 11:43:52 -060045 }
Patrick Georgi4a3956d2018-05-03 19:15:13 +020046
47 const char *filename = mainboard_vbt_filename();
48
49 size_t file_size = cbfs_boot_load_file(filename,
50 vbt_data, sizeof(vbt_data), CBFS_TYPE_RAW);
51
52 if (file_size == 0)
53 return NULL;
54
55 if (vbt_size)
56 *vbt_size = file_size;
57
58 memcpy(&vbtsig, vbt_data, sizeof(vbtsig));
59 if (vbtsig != VBT_SIGNATURE) {
60 printk(BIOS_ERR, "Missing/invalid signature in VBT data file!\n");
61 return NULL;
62 }
63
64 printk(BIOS_INFO, "Found a VBT of %zu bytes after decompression\n",
65 file_size);
Aaron Durbin44f80652018-05-11 11:43:52 -060066 vbt_data_sz = file_size;
Patrick Georgi4a3956d2018-05-03 19:15:13 +020067
68 return (void *)vbt_data;
69}
70
Patrick Rudolphfa470422017-06-20 17:49:53 +020071/* Write ASLS PCI register and prepare SWSCI register. */
72void intel_gma_opregion_register(uintptr_t opregion)
73{
Elyes HAOUAS263076c2018-05-02 21:54:59 +020074 struct device *igd;
Patrick Rudolphfa470422017-06-20 17:49:53 +020075 u16 reg16;
Matt DeVillier681ef512018-02-11 01:17:01 -060076 u16 sci_reg;
Patrick Rudolphfa470422017-06-20 17:49:53 +020077
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030078 igd = pcidev_on_root(0x2, 0);
Patrick Rudolphfa470422017-06-20 17:49:53 +020079 if (!igd || !igd->enabled)
80 return;
81
82 /*
83 * Intel BIOS Specification
84 * Chapter 5.3.7 "Initialize Hardware State"
85 */
86 pci_write_config32(igd, ASLS, opregion);
87
88 /*
Matt DeVillier681ef512018-02-11 01:17:01 -060089 * Atom-based platforms use a combined SMI/SCI register,
90 * whereas non-Atom platforms use a separate SCI register.
91 */
Julius Wernercd49cce2019-03-05 16:53:33 -080092 if (CONFIG(INTEL_GMA_SWSMISCI))
Matt DeVillier681ef512018-02-11 01:17:01 -060093 sci_reg = SWSMISCI;
94 else
95 sci_reg = SWSCI;
96
97 /*
Patrick Rudolphfa470422017-06-20 17:49:53 +020098 * Intel's Windows driver relies on this:
99 * Intel BIOS Specification
100 * Chapter 5.4 "ASL Software SCI Handler"
101 */
Matt DeVillier681ef512018-02-11 01:17:01 -0600102 reg16 = pci_read_config16(igd, sci_reg);
Patrick Rudolphfa470422017-06-20 17:49:53 +0200103 reg16 &= ~GSSCIE;
104 reg16 |= SMISCISEL;
Matt DeVillier681ef512018-02-11 01:17:01 -0600105 pci_write_config16(igd, sci_reg, reg16);
Patrick Rudolphfa470422017-06-20 17:49:53 +0200106}
Patrick Rudolphbac23032017-06-30 15:18:23 +0200107
108/* Restore ASLS register on S3 resume and prepare SWSCI. */
109void intel_gma_restore_opregion(void)
110{
111 if (acpi_is_wakeup_s3()) {
112 const void *const gnvs = cbmem_find(CBMEM_ID_ACPI_GNVS);
113 uintptr_t aslb;
114
115 if (gnvs && (aslb = gma_get_gnvs_aslb(gnvs)))
116 intel_gma_opregion_register(aslb);
117 else
118 printk(BIOS_ERR, "Error: GNVS or ASLB not set.\n");
119 }
120}
Matt DeVillierebe08e02017-07-14 13:28:42 -0500121
Matt DeVillier53e41952017-06-27 13:07:43 -0500122static enum cb_err vbt_validate(struct region_device *rdev)
Matt DeVillierebe08e02017-07-14 13:28:42 -0500123{
Matt DeVillier53e41952017-06-27 13:07:43 -0500124 uint32_t sig;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500125
Matt DeVillier53e41952017-06-27 13:07:43 -0500126 if (rdev_readat(rdev, &sig, 0, sizeof(sig)) != sizeof(sig))
Matt DeVillierebe08e02017-07-14 13:28:42 -0500127 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500128
Matt DeVillier53e41952017-06-27 13:07:43 -0500129 if (sig != VBT_SIGNATURE)
Matt DeVillierebe08e02017-07-14 13:28:42 -0500130 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500131
132 return CB_SUCCESS;
133}
134
Matt DeVillier53e41952017-06-27 13:07:43 -0500135static enum cb_err locate_vbt_vbios(const u8 *vbios, struct region_device *rdev)
Matt DeVillierebe08e02017-07-14 13:28:42 -0500136{
Matt DeVillier53e41952017-06-27 13:07:43 -0500137 const optionrom_header_t *oprom;
138 const optionrom_pcir_t *pcir;
139 struct region_device rd;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500140 enum cb_err ret;
Matt DeVillier53e41952017-06-27 13:07:43 -0500141 u8 opromsize;
142 size_t offset;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500143
Matt DeVillier53e41952017-06-27 13:07:43 -0500144 // FIXME: caller should supply a region_device instead of vbios pointer
145 if (rdev_chain(&rd, &addrspace_32bit.rdev, (uintptr_t)vbios,
146 sizeof(*oprom)))
147 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500148
Matt DeVillier53e41952017-06-27 13:07:43 -0500149 if (rdev_readat(&rd, &opromsize, offsetof(optionrom_header_t, size),
150 sizeof(opromsize)) != sizeof(opromsize) || !opromsize)
151 return CB_ERR;
152
153 if (rdev_chain(&rd, &addrspace_32bit.rdev, (uintptr_t)vbios,
154 opromsize * 512))
155 return CB_ERR;
156
157 oprom = rdev_mmap(&rd, 0, sizeof(*oprom));
158 if (!oprom)
159 return CB_ERR;
160
161 if (!oprom->pcir_offset || !oprom->vbt_offset) {
162 rdev_munmap(&rd, (void *)oprom);
163 return CB_ERR;
164 }
165
166 pcir = rdev_mmap(&rd, oprom->pcir_offset, sizeof(*pcir));
167 if (pcir == NULL) {
168 rdev_munmap(&rd, (void *)oprom);
169 return CB_ERR;
170 }
171
172 printk(BIOS_DEBUG, "GMA: locate_vbt_vbios: %x %x %x %x %x\n",
173 oprom->signature, pcir->vendor, pcir->classcode[0],
174 pcir->classcode[1], pcir->classcode[2]);
175
176 /* Make sure we got an Intel VGA option rom */
177 if ((oprom->signature != OPROM_SIGNATURE) ||
178 (pcir->vendor != PCI_VENDOR_ID_INTEL) ||
179 (pcir->signature != 0x52494350) ||
180 (pcir->classcode[0] != 0x00) ||
181 (pcir->classcode[1] != 0x00) ||
182 (pcir->classcode[2] != 0x03)) {
183 rdev_munmap(&rd, (void *)oprom);
184 rdev_munmap(&rd, (void *)pcir);
185 return CB_ERR;
186 }
187
188 rdev_munmap(&rd, (void *)pcir);
189
190 /* Search for $VBT as some VBIOS are broken... */
191 offset = oprom->vbt_offset;
192 do {
193 ret = rdev_chain(rdev, &rd, offset,
194 (opromsize * 512) - offset);
195 offset++;
196 } while (ret == CB_SUCCESS && vbt_validate(rdev) != CB_SUCCESS);
197
198 offset--;
199
200 if (ret == CB_SUCCESS && offset != oprom->vbt_offset)
201 printk(BIOS_WARNING, "GMA: Buggy VBIOS found\n");
202 else if (ret != CB_SUCCESS)
203 printk(BIOS_ERR, "GMA: Broken VBIOS found\n");
204
205 rdev_munmap(&rd, (void *)oprom);
206 return ret;
207}
208
209static enum cb_err locate_vbt_cbfs(struct region_device *rdev)
210{
Patrick Georgi4a3956d2018-05-03 19:15:13 +0200211 size_t vbt_data_size;
212 void *vbt = locate_vbt(&vbt_data_size);
Matt DeVillier53e41952017-06-27 13:07:43 -0500213
Patrick Georgi4a3956d2018-05-03 19:15:13 +0200214 if (vbt == NULL)
215 return CB_ERR;
Matt DeVillier53e41952017-06-27 13:07:43 -0500216
Patrick Georgi4a3956d2018-05-03 19:15:13 +0200217 if (rdev_chain(rdev, &addrspace_32bit.rdev, (uintptr_t)vbt,
218 vbt_data_size))
219 return CB_ERR;
220
221 printk(BIOS_INFO, "GMA: Found VBT in CBFS\n");
222
223 return CB_SUCCESS;
Matt DeVillier53e41952017-06-27 13:07:43 -0500224}
225
226static enum cb_err locate_vbt_vbios_cbfs(struct region_device *rdev)
227{
228 const u8 *oprom =
Kyösti Mälkkic70eed12018-05-22 02:18:00 +0300229 (const u8 *)pci_rom_probe(pcidev_on_root(0x2, 0));
Matt DeVillier53e41952017-06-27 13:07:43 -0500230 if (oprom == NULL)
231 return CB_ERR;
232
233 printk(BIOS_INFO, "GMA: Found VBIOS in CBFS\n");
234
235 return locate_vbt_vbios(oprom, rdev);
236}
237
238/* Initialize IGD OpRegion, called from ACPI code and OS drivers */
239enum cb_err
240intel_gma_init_igd_opregion(igd_opregion_t *opregion)
241{
242 struct region_device rdev;
243 optionrom_vbt_t *vbt = NULL;
244 optionrom_vbt_t *ext_vbt;
245 bool found = false;
246
247 /* Search for vbt.bin in CBFS. */
248 if (locate_vbt_cbfs(&rdev) == CB_SUCCESS &&
249 vbt_validate(&rdev) == CB_SUCCESS) {
250 found = true;
251 printk(BIOS_INFO, "GMA: Found valid VBT in CBFS\n");
252 }
253 /* Search for pci8086,XXXX.rom in CBFS. */
254 else if (locate_vbt_vbios_cbfs(&rdev) == CB_SUCCESS &&
255 vbt_validate(&rdev) == CB_SUCCESS) {
256 found = true;
257 printk(BIOS_INFO, "GMA: Found valid VBT in VBIOS\n");
258 }
259 /*
260 * Try to locate Intel VBIOS at 0xc0000. It might have been placed by
261 * Native Graphics Init as fake Option ROM or when coreboot did run the
262 * VBIOS on legacy platforms.
263 * TODO: Place generated fake VBT in CBMEM and get rid of this.
264 */
265 else if (locate_vbt_vbios((u8 *)0xc0000, &rdev) == CB_SUCCESS &&
266 vbt_validate(&rdev) == CB_SUCCESS) {
267 found = true;
268 printk(BIOS_INFO, "GMA: Found valid VBT in legacy area\n");
269 }
270
271 if (!found) {
272 printk(BIOS_ERR, "GMA: VBT couldn't be found\n");
273 return CB_ERR;
274 }
275
276 vbt = rdev_mmap_full(&rdev);
277 if (!vbt) {
278 printk(BIOS_ERR, "GMA: Error mapping VBT\n");
279 return CB_ERR;
280 }
281
282 if (vbt->hdr_vbt_size > region_device_sz(&rdev)) {
283 printk(BIOS_ERR, "GMA: Error mapped only a partial VBT\n");
284 rdev_munmap(&rdev, vbt);
285 return CB_ERR;
286 }
287
288 memset(opregion, 0, sizeof(igd_opregion_t));
Matt DeVillierebe08e02017-07-14 13:28:42 -0500289
290 memcpy(&opregion->header.signature, IGD_OPREGION_SIGNATURE,
291 sizeof(opregion->header.signature));
Matt DeVillier53e41952017-06-27 13:07:43 -0500292 memcpy(opregion->header.vbios_version, vbt->coreblock_biosbuild,
293 ARRAY_SIZE(vbt->coreblock_biosbuild));
294 /* Extended VBT support */
295 if (vbt->hdr_vbt_size > sizeof(opregion->vbt.gvd1)) {
296 ext_vbt = cbmem_add(CBMEM_ID_EXT_VBT, vbt->hdr_vbt_size);
297
298 if (ext_vbt == NULL) {
299 printk(BIOS_ERR,
300 "GMA: Unable to add Ext VBT to cbmem!\n");
301 rdev_munmap(&rdev, vbt);
302 return CB_ERR;
303 }
304
305 memcpy(ext_vbt, vbt, vbt->hdr_vbt_size);
306 opregion->mailbox3.rvda = (uintptr_t)ext_vbt;
307 opregion->mailbox3.rvds = vbt->hdr_vbt_size;
308 } else {
309 /* Raw VBT size which can fit in gvd1 */
310 memcpy(opregion->vbt.gvd1, vbt, vbt->hdr_vbt_size);
311 }
312
313 rdev_munmap(&rdev, vbt);
Matt DeVillierebe08e02017-07-14 13:28:42 -0500314
315 /* 8kb */
316 opregion->header.size = sizeof(igd_opregion_t) / 1024;
Matt DeVillierfd0a8912017-10-19 22:44:18 -0500317
318 /*
Elyes HAOUAS18958382018-08-07 12:23:16 +0200319 * Left-shift version field to accommodate Intel Windows driver quirk
Matt DeVillierfd0a8912017-10-19 22:44:18 -0500320 * when not using a VBIOS.
321 * Required for Legacy boot + NGI, UEFI + NGI, and UEFI + GOP driver.
322 *
323 * Tested on: (platform, GPU, windows driver version)
324 * samsung/stumpy (SNB, GT2, 9.17.10.4459)
325 * google/link (IVB, GT2, 15.33.4653)
326 * google/wolf (HSW, GT1, 15.40.36.4703)
327 * google/panther (HSW, GT2, 15.40.36.4703)
328 * google/rikku (BDW, GT1, 15.40.36.4703)
329 * google/lulu (BDW, GT2, 15.40.36.4703)
330 * google/chell (SKL-Y, GT2, 15.45.21.4821)
331 * google/sentry (SKL-U, GT1, 15.45.21.4821)
332 * purism/librem13v2 (SKL-U, GT2, 15.45.21.4821)
333 *
334 * No adverse effects when using VBIOS or booting Linux.
335 */
336 opregion->header.version = IGD_OPREGION_VERSION << 24;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500337
338 // FIXME We just assume we're mobile for now
339 opregion->header.mailboxes = MAILBOXES_MOBILE;
340
341 // TODO Initialize Mailbox 1
Patrick Georgi0f68b232018-01-25 18:23:15 +0100342 opregion->mailbox1.clid = 1;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500343
344 // TODO Initialize Mailbox 3
345 opregion->mailbox3.bclp = IGD_BACKLIGHT_BRIGHTNESS;
346 opregion->mailbox3.pfit = IGD_FIELD_VALID | IGD_PFIT_STRETCH;
347 opregion->mailbox3.pcft = 0; // should be (IMON << 1) & 0x3e
348 opregion->mailbox3.cblv = IGD_FIELD_VALID | IGD_INITIAL_BRIGHTNESS;
349 opregion->mailbox3.bclm[0] = IGD_WORD_FIELD_VALID + 0x0000;
350 opregion->mailbox3.bclm[1] = IGD_WORD_FIELD_VALID + 0x0a19;
351 opregion->mailbox3.bclm[2] = IGD_WORD_FIELD_VALID + 0x1433;
352 opregion->mailbox3.bclm[3] = IGD_WORD_FIELD_VALID + 0x1e4c;
353 opregion->mailbox3.bclm[4] = IGD_WORD_FIELD_VALID + 0x2866;
354 opregion->mailbox3.bclm[5] = IGD_WORD_FIELD_VALID + 0x327f;
355 opregion->mailbox3.bclm[6] = IGD_WORD_FIELD_VALID + 0x3c99;
356 opregion->mailbox3.bclm[7] = IGD_WORD_FIELD_VALID + 0x46b2;
357 opregion->mailbox3.bclm[8] = IGD_WORD_FIELD_VALID + 0x50cc;
358 opregion->mailbox3.bclm[9] = IGD_WORD_FIELD_VALID + 0x5ae5;
359 opregion->mailbox3.bclm[10] = IGD_WORD_FIELD_VALID + 0x64ff;
360
Matt DeVillierebe08e02017-07-14 13:28:42 -0500361 /* Write ASLS PCI register and prepare SWSCI register. */
362 intel_gma_opregion_register((uintptr_t)opregion);
363
364 return CB_SUCCESS;
365}