blob: b939b88324e277ea978b32cabd9ab28dcafb7d05 [file] [log] [blame]
Patrick Rudolphfa470422017-06-20 17:49:53 +02001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2017 Patrick Rudolph <siro@das-labor.org>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; version 2, or (at your option)
9 * any later verion of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
Matt DeVillierebe08e02017-07-14 13:28:42 -050017#include <arch/acpi.h>
18#include <types.h>
19#include <string.h>
Matt DeVillier53e41952017-06-27 13:07:43 -050020#include <cbfs.h>
Patrick Rudolphfa470422017-06-20 17:49:53 +020021#include <device/device.h>
22#include <device/pci.h>
23#include <device/pci_ids.h>
24#include <device/pci_ops.h>
Patrick Rudolphbac23032017-06-30 15:18:23 +020025#include <console/console.h>
26#include <cbmem.h>
Matt DeVillierebe08e02017-07-14 13:28:42 -050027#include "intel_bios.h"
Patrick Rudolphfa470422017-06-20 17:49:53 +020028#include "opregion.h"
29
30/* Write ASLS PCI register and prepare SWSCI register. */
31void intel_gma_opregion_register(uintptr_t opregion)
32{
33 device_t igd;
34 u16 reg16;
Matt DeVillier681ef512018-02-11 01:17:01 -060035 u16 sci_reg;
Patrick Rudolphfa470422017-06-20 17:49:53 +020036
37 igd = dev_find_slot(0, PCI_DEVFN(0x2, 0));
38 if (!igd || !igd->enabled)
39 return;
40
41 /*
42 * Intel BIOS Specification
43 * Chapter 5.3.7 "Initialize Hardware State"
44 */
45 pci_write_config32(igd, ASLS, opregion);
46
47 /*
Matt DeVillier681ef512018-02-11 01:17:01 -060048 * Atom-based platforms use a combined SMI/SCI register,
49 * whereas non-Atom platforms use a separate SCI register.
50 */
51 if (IS_ENABLED(CONFIG_INTEL_GMA_SWSMISCI))
52 sci_reg = SWSMISCI;
53 else
54 sci_reg = SWSCI;
55
56 /*
Patrick Rudolphfa470422017-06-20 17:49:53 +020057 * Intel's Windows driver relies on this:
58 * Intel BIOS Specification
59 * Chapter 5.4 "ASL Software SCI Handler"
60 */
Matt DeVillier681ef512018-02-11 01:17:01 -060061 reg16 = pci_read_config16(igd, sci_reg);
Patrick Rudolphfa470422017-06-20 17:49:53 +020062 reg16 &= ~GSSCIE;
63 reg16 |= SMISCISEL;
Matt DeVillier681ef512018-02-11 01:17:01 -060064 pci_write_config16(igd, sci_reg, reg16);
Patrick Rudolphfa470422017-06-20 17:49:53 +020065}
Patrick Rudolphbac23032017-06-30 15:18:23 +020066
67/* Restore ASLS register on S3 resume and prepare SWSCI. */
68void intel_gma_restore_opregion(void)
69{
70 if (acpi_is_wakeup_s3()) {
71 const void *const gnvs = cbmem_find(CBMEM_ID_ACPI_GNVS);
72 uintptr_t aslb;
73
74 if (gnvs && (aslb = gma_get_gnvs_aslb(gnvs)))
75 intel_gma_opregion_register(aslb);
76 else
77 printk(BIOS_ERR, "Error: GNVS or ASLB not set.\n");
78 }
79}
Matt DeVillierebe08e02017-07-14 13:28:42 -050080
Matt DeVillier53e41952017-06-27 13:07:43 -050081static enum cb_err vbt_validate(struct region_device *rdev)
Matt DeVillierebe08e02017-07-14 13:28:42 -050082{
Matt DeVillier53e41952017-06-27 13:07:43 -050083 uint32_t sig;
Matt DeVillierebe08e02017-07-14 13:28:42 -050084
Matt DeVillier53e41952017-06-27 13:07:43 -050085 if (rdev_readat(rdev, &sig, 0, sizeof(sig)) != sizeof(sig))
Matt DeVillierebe08e02017-07-14 13:28:42 -050086 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -050087
Matt DeVillier53e41952017-06-27 13:07:43 -050088 if (sig != VBT_SIGNATURE)
Matt DeVillierebe08e02017-07-14 13:28:42 -050089 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -050090
91 return CB_SUCCESS;
92}
93
Matt DeVillier53e41952017-06-27 13:07:43 -050094static enum cb_err locate_vbt_vbios(const u8 *vbios, struct region_device *rdev)
Matt DeVillierebe08e02017-07-14 13:28:42 -050095{
Matt DeVillier53e41952017-06-27 13:07:43 -050096 const optionrom_header_t *oprom;
97 const optionrom_pcir_t *pcir;
98 struct region_device rd;
Matt DeVillierebe08e02017-07-14 13:28:42 -050099 enum cb_err ret;
Matt DeVillier53e41952017-06-27 13:07:43 -0500100 u8 opromsize;
101 size_t offset;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500102
Matt DeVillier53e41952017-06-27 13:07:43 -0500103 // FIXME: caller should supply a region_device instead of vbios pointer
104 if (rdev_chain(&rd, &addrspace_32bit.rdev, (uintptr_t)vbios,
105 sizeof(*oprom)))
106 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500107
Matt DeVillier53e41952017-06-27 13:07:43 -0500108 if (rdev_readat(&rd, &opromsize, offsetof(optionrom_header_t, size),
109 sizeof(opromsize)) != sizeof(opromsize) || !opromsize)
110 return CB_ERR;
111
112 if (rdev_chain(&rd, &addrspace_32bit.rdev, (uintptr_t)vbios,
113 opromsize * 512))
114 return CB_ERR;
115
116 oprom = rdev_mmap(&rd, 0, sizeof(*oprom));
117 if (!oprom)
118 return CB_ERR;
119
120 if (!oprom->pcir_offset || !oprom->vbt_offset) {
121 rdev_munmap(&rd, (void *)oprom);
122 return CB_ERR;
123 }
124
125 pcir = rdev_mmap(&rd, oprom->pcir_offset, sizeof(*pcir));
126 if (pcir == NULL) {
127 rdev_munmap(&rd, (void *)oprom);
128 return CB_ERR;
129 }
130
131 printk(BIOS_DEBUG, "GMA: locate_vbt_vbios: %x %x %x %x %x\n",
132 oprom->signature, pcir->vendor, pcir->classcode[0],
133 pcir->classcode[1], pcir->classcode[2]);
134
135 /* Make sure we got an Intel VGA option rom */
136 if ((oprom->signature != OPROM_SIGNATURE) ||
137 (pcir->vendor != PCI_VENDOR_ID_INTEL) ||
138 (pcir->signature != 0x52494350) ||
139 (pcir->classcode[0] != 0x00) ||
140 (pcir->classcode[1] != 0x00) ||
141 (pcir->classcode[2] != 0x03)) {
142 rdev_munmap(&rd, (void *)oprom);
143 rdev_munmap(&rd, (void *)pcir);
144 return CB_ERR;
145 }
146
147 rdev_munmap(&rd, (void *)pcir);
148
149 /* Search for $VBT as some VBIOS are broken... */
150 offset = oprom->vbt_offset;
151 do {
152 ret = rdev_chain(rdev, &rd, offset,
153 (opromsize * 512) - offset);
154 offset++;
155 } while (ret == CB_SUCCESS && vbt_validate(rdev) != CB_SUCCESS);
156
157 offset--;
158
159 if (ret == CB_SUCCESS && offset != oprom->vbt_offset)
160 printk(BIOS_WARNING, "GMA: Buggy VBIOS found\n");
161 else if (ret != CB_SUCCESS)
162 printk(BIOS_ERR, "GMA: Broken VBIOS found\n");
163
164 rdev_munmap(&rd, (void *)oprom);
165 return ret;
166}
167
168static enum cb_err locate_vbt_cbfs(struct region_device *rdev)
169{
170 struct cbfsf file_desc;
171
172 /* try to locate vbt.bin in CBFS */
173 if (cbfs_boot_locate(&file_desc, "vbt.bin", NULL) == CB_SUCCESS) {
174 cbfs_file_data(rdev, &file_desc);
175 printk(BIOS_INFO, "GMA: Found VBT in CBFS\n");
176 return CB_SUCCESS;
177 }
178
179 return CB_ERR;
180}
181
182static enum cb_err locate_vbt_vbios_cbfs(struct region_device *rdev)
183{
184 const u8 *oprom =
185 (const u8 *)pci_rom_probe(dev_find_slot(0, PCI_DEVFN(0x2, 0)));
186 if (oprom == NULL)
187 return CB_ERR;
188
189 printk(BIOS_INFO, "GMA: Found VBIOS in CBFS\n");
190
191 return locate_vbt_vbios(oprom, rdev);
192}
193
194/* Initialize IGD OpRegion, called from ACPI code and OS drivers */
195enum cb_err
196intel_gma_init_igd_opregion(igd_opregion_t *opregion)
197{
198 struct region_device rdev;
199 optionrom_vbt_t *vbt = NULL;
200 optionrom_vbt_t *ext_vbt;
201 bool found = false;
202
203 /* Search for vbt.bin in CBFS. */
204 if (locate_vbt_cbfs(&rdev) == CB_SUCCESS &&
205 vbt_validate(&rdev) == CB_SUCCESS) {
206 found = true;
207 printk(BIOS_INFO, "GMA: Found valid VBT in CBFS\n");
208 }
209 /* Search for pci8086,XXXX.rom in CBFS. */
210 else if (locate_vbt_vbios_cbfs(&rdev) == CB_SUCCESS &&
211 vbt_validate(&rdev) == CB_SUCCESS) {
212 found = true;
213 printk(BIOS_INFO, "GMA: Found valid VBT in VBIOS\n");
214 }
215 /*
216 * Try to locate Intel VBIOS at 0xc0000. It might have been placed by
217 * Native Graphics Init as fake Option ROM or when coreboot did run the
218 * VBIOS on legacy platforms.
219 * TODO: Place generated fake VBT in CBMEM and get rid of this.
220 */
221 else if (locate_vbt_vbios((u8 *)0xc0000, &rdev) == CB_SUCCESS &&
222 vbt_validate(&rdev) == CB_SUCCESS) {
223 found = true;
224 printk(BIOS_INFO, "GMA: Found valid VBT in legacy area\n");
225 }
226
227 if (!found) {
228 printk(BIOS_ERR, "GMA: VBT couldn't be found\n");
229 return CB_ERR;
230 }
231
232 vbt = rdev_mmap_full(&rdev);
233 if (!vbt) {
234 printk(BIOS_ERR, "GMA: Error mapping VBT\n");
235 return CB_ERR;
236 }
237
238 if (vbt->hdr_vbt_size > region_device_sz(&rdev)) {
239 printk(BIOS_ERR, "GMA: Error mapped only a partial VBT\n");
240 rdev_munmap(&rdev, vbt);
241 return CB_ERR;
242 }
243
244 memset(opregion, 0, sizeof(igd_opregion_t));
Matt DeVillierebe08e02017-07-14 13:28:42 -0500245
246 memcpy(&opregion->header.signature, IGD_OPREGION_SIGNATURE,
247 sizeof(opregion->header.signature));
Matt DeVillier53e41952017-06-27 13:07:43 -0500248 memcpy(opregion->header.vbios_version, vbt->coreblock_biosbuild,
249 ARRAY_SIZE(vbt->coreblock_biosbuild));
250 /* Extended VBT support */
251 if (vbt->hdr_vbt_size > sizeof(opregion->vbt.gvd1)) {
252 ext_vbt = cbmem_add(CBMEM_ID_EXT_VBT, vbt->hdr_vbt_size);
253
254 if (ext_vbt == NULL) {
255 printk(BIOS_ERR,
256 "GMA: Unable to add Ext VBT to cbmem!\n");
257 rdev_munmap(&rdev, vbt);
258 return CB_ERR;
259 }
260
261 memcpy(ext_vbt, vbt, vbt->hdr_vbt_size);
262 opregion->mailbox3.rvda = (uintptr_t)ext_vbt;
263 opregion->mailbox3.rvds = vbt->hdr_vbt_size;
264 } else {
265 /* Raw VBT size which can fit in gvd1 */
266 memcpy(opregion->vbt.gvd1, vbt, vbt->hdr_vbt_size);
267 }
268
269 rdev_munmap(&rdev, vbt);
Matt DeVillierebe08e02017-07-14 13:28:42 -0500270
271 /* 8kb */
272 opregion->header.size = sizeof(igd_opregion_t) / 1024;
Matt DeVillierfd0a8912017-10-19 22:44:18 -0500273
274 /*
275 * Left-shift version field to accomodate Intel Windows driver quirk
276 * when not using a VBIOS.
277 * Required for Legacy boot + NGI, UEFI + NGI, and UEFI + GOP driver.
278 *
279 * Tested on: (platform, GPU, windows driver version)
280 * samsung/stumpy (SNB, GT2, 9.17.10.4459)
281 * google/link (IVB, GT2, 15.33.4653)
282 * google/wolf (HSW, GT1, 15.40.36.4703)
283 * google/panther (HSW, GT2, 15.40.36.4703)
284 * google/rikku (BDW, GT1, 15.40.36.4703)
285 * google/lulu (BDW, GT2, 15.40.36.4703)
286 * google/chell (SKL-Y, GT2, 15.45.21.4821)
287 * google/sentry (SKL-U, GT1, 15.45.21.4821)
288 * purism/librem13v2 (SKL-U, GT2, 15.45.21.4821)
289 *
290 * No adverse effects when using VBIOS or booting Linux.
291 */
292 opregion->header.version = IGD_OPREGION_VERSION << 24;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500293
294 // FIXME We just assume we're mobile for now
295 opregion->header.mailboxes = MAILBOXES_MOBILE;
296
297 // TODO Initialize Mailbox 1
Patrick Georgi0f68b232018-01-25 18:23:15 +0100298 opregion->mailbox1.clid = 1;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500299
300 // TODO Initialize Mailbox 3
301 opregion->mailbox3.bclp = IGD_BACKLIGHT_BRIGHTNESS;
302 opregion->mailbox3.pfit = IGD_FIELD_VALID | IGD_PFIT_STRETCH;
303 opregion->mailbox3.pcft = 0; // should be (IMON << 1) & 0x3e
304 opregion->mailbox3.cblv = IGD_FIELD_VALID | IGD_INITIAL_BRIGHTNESS;
305 opregion->mailbox3.bclm[0] = IGD_WORD_FIELD_VALID + 0x0000;
306 opregion->mailbox3.bclm[1] = IGD_WORD_FIELD_VALID + 0x0a19;
307 opregion->mailbox3.bclm[2] = IGD_WORD_FIELD_VALID + 0x1433;
308 opregion->mailbox3.bclm[3] = IGD_WORD_FIELD_VALID + 0x1e4c;
309 opregion->mailbox3.bclm[4] = IGD_WORD_FIELD_VALID + 0x2866;
310 opregion->mailbox3.bclm[5] = IGD_WORD_FIELD_VALID + 0x327f;
311 opregion->mailbox3.bclm[6] = IGD_WORD_FIELD_VALID + 0x3c99;
312 opregion->mailbox3.bclm[7] = IGD_WORD_FIELD_VALID + 0x46b2;
313 opregion->mailbox3.bclm[8] = IGD_WORD_FIELD_VALID + 0x50cc;
314 opregion->mailbox3.bclm[9] = IGD_WORD_FIELD_VALID + 0x5ae5;
315 opregion->mailbox3.bclm[10] = IGD_WORD_FIELD_VALID + 0x64ff;
316
Matt DeVillierebe08e02017-07-14 13:28:42 -0500317 /* Write ASLS PCI register and prepare SWSCI register. */
318 intel_gma_opregion_register((uintptr_t)opregion);
319
320 return CB_SUCCESS;
321}