blob: b6d4e0779086244e7518d35050c62fc18f6e0a37 [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;
35
36 igd = dev_find_slot(0, PCI_DEVFN(0x2, 0));
37 if (!igd || !igd->enabled)
38 return;
39
40 /*
41 * Intel BIOS Specification
42 * Chapter 5.3.7 "Initialize Hardware State"
43 */
44 pci_write_config32(igd, ASLS, opregion);
45
46 /*
47 * Intel's Windows driver relies on this:
48 * Intel BIOS Specification
49 * Chapter 5.4 "ASL Software SCI Handler"
50 */
51 reg16 = pci_read_config16(igd, SWSCI);
52 reg16 &= ~GSSCIE;
53 reg16 |= SMISCISEL;
54 pci_write_config16(igd, SWSCI, reg16);
55}
Patrick Rudolphbac23032017-06-30 15:18:23 +020056
57/* Restore ASLS register on S3 resume and prepare SWSCI. */
58void intel_gma_restore_opregion(void)
59{
60 if (acpi_is_wakeup_s3()) {
61 const void *const gnvs = cbmem_find(CBMEM_ID_ACPI_GNVS);
62 uintptr_t aslb;
63
64 if (gnvs && (aslb = gma_get_gnvs_aslb(gnvs)))
65 intel_gma_opregion_register(aslb);
66 else
67 printk(BIOS_ERR, "Error: GNVS or ASLB not set.\n");
68 }
69}
Matt DeVillierebe08e02017-07-14 13:28:42 -050070
Matt DeVillier53e41952017-06-27 13:07:43 -050071static enum cb_err vbt_validate(struct region_device *rdev)
Matt DeVillierebe08e02017-07-14 13:28:42 -050072{
Matt DeVillier53e41952017-06-27 13:07:43 -050073 uint32_t sig;
Matt DeVillierebe08e02017-07-14 13:28:42 -050074
Matt DeVillier53e41952017-06-27 13:07:43 -050075 if (rdev_readat(rdev, &sig, 0, sizeof(sig)) != sizeof(sig))
Matt DeVillierebe08e02017-07-14 13:28:42 -050076 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -050077
Matt DeVillier53e41952017-06-27 13:07:43 -050078 if (sig != VBT_SIGNATURE)
Matt DeVillierebe08e02017-07-14 13:28:42 -050079 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -050080
81 return CB_SUCCESS;
82}
83
Matt DeVillier53e41952017-06-27 13:07:43 -050084static enum cb_err locate_vbt_vbios(const u8 *vbios, struct region_device *rdev)
Matt DeVillierebe08e02017-07-14 13:28:42 -050085{
Matt DeVillier53e41952017-06-27 13:07:43 -050086 const optionrom_header_t *oprom;
87 const optionrom_pcir_t *pcir;
88 struct region_device rd;
Matt DeVillierebe08e02017-07-14 13:28:42 -050089 enum cb_err ret;
Matt DeVillier53e41952017-06-27 13:07:43 -050090 u8 opromsize;
91 size_t offset;
Matt DeVillierebe08e02017-07-14 13:28:42 -050092
Matt DeVillier53e41952017-06-27 13:07:43 -050093 // FIXME: caller should supply a region_device instead of vbios pointer
94 if (rdev_chain(&rd, &addrspace_32bit.rdev, (uintptr_t)vbios,
95 sizeof(*oprom)))
96 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -050097
Matt DeVillier53e41952017-06-27 13:07:43 -050098 if (rdev_readat(&rd, &opromsize, offsetof(optionrom_header_t, size),
99 sizeof(opromsize)) != sizeof(opromsize) || !opromsize)
100 return CB_ERR;
101
102 if (rdev_chain(&rd, &addrspace_32bit.rdev, (uintptr_t)vbios,
103 opromsize * 512))
104 return CB_ERR;
105
106 oprom = rdev_mmap(&rd, 0, sizeof(*oprom));
107 if (!oprom)
108 return CB_ERR;
109
110 if (!oprom->pcir_offset || !oprom->vbt_offset) {
111 rdev_munmap(&rd, (void *)oprom);
112 return CB_ERR;
113 }
114
115 pcir = rdev_mmap(&rd, oprom->pcir_offset, sizeof(*pcir));
116 if (pcir == NULL) {
117 rdev_munmap(&rd, (void *)oprom);
118 return CB_ERR;
119 }
120
121 printk(BIOS_DEBUG, "GMA: locate_vbt_vbios: %x %x %x %x %x\n",
122 oprom->signature, pcir->vendor, pcir->classcode[0],
123 pcir->classcode[1], pcir->classcode[2]);
124
125 /* Make sure we got an Intel VGA option rom */
126 if ((oprom->signature != OPROM_SIGNATURE) ||
127 (pcir->vendor != PCI_VENDOR_ID_INTEL) ||
128 (pcir->signature != 0x52494350) ||
129 (pcir->classcode[0] != 0x00) ||
130 (pcir->classcode[1] != 0x00) ||
131 (pcir->classcode[2] != 0x03)) {
132 rdev_munmap(&rd, (void *)oprom);
133 rdev_munmap(&rd, (void *)pcir);
134 return CB_ERR;
135 }
136
137 rdev_munmap(&rd, (void *)pcir);
138
139 /* Search for $VBT as some VBIOS are broken... */
140 offset = oprom->vbt_offset;
141 do {
142 ret = rdev_chain(rdev, &rd, offset,
143 (opromsize * 512) - offset);
144 offset++;
145 } while (ret == CB_SUCCESS && vbt_validate(rdev) != CB_SUCCESS);
146
147 offset--;
148
149 if (ret == CB_SUCCESS && offset != oprom->vbt_offset)
150 printk(BIOS_WARNING, "GMA: Buggy VBIOS found\n");
151 else if (ret != CB_SUCCESS)
152 printk(BIOS_ERR, "GMA: Broken VBIOS found\n");
153
154 rdev_munmap(&rd, (void *)oprom);
155 return ret;
156}
157
158static enum cb_err locate_vbt_cbfs(struct region_device *rdev)
159{
160 struct cbfsf file_desc;
161
162 /* try to locate vbt.bin in CBFS */
163 if (cbfs_boot_locate(&file_desc, "vbt.bin", NULL) == CB_SUCCESS) {
164 cbfs_file_data(rdev, &file_desc);
165 printk(BIOS_INFO, "GMA: Found VBT in CBFS\n");
166 return CB_SUCCESS;
167 }
168
169 return CB_ERR;
170}
171
172static enum cb_err locate_vbt_vbios_cbfs(struct region_device *rdev)
173{
174 const u8 *oprom =
175 (const u8 *)pci_rom_probe(dev_find_slot(0, PCI_DEVFN(0x2, 0)));
176 if (oprom == NULL)
177 return CB_ERR;
178
179 printk(BIOS_INFO, "GMA: Found VBIOS in CBFS\n");
180
181 return locate_vbt_vbios(oprom, rdev);
182}
183
184/* Initialize IGD OpRegion, called from ACPI code and OS drivers */
185enum cb_err
186intel_gma_init_igd_opregion(igd_opregion_t *opregion)
187{
188 struct region_device rdev;
189 optionrom_vbt_t *vbt = NULL;
190 optionrom_vbt_t *ext_vbt;
191 bool found = false;
192
193 /* Search for vbt.bin in CBFS. */
194 if (locate_vbt_cbfs(&rdev) == CB_SUCCESS &&
195 vbt_validate(&rdev) == CB_SUCCESS) {
196 found = true;
197 printk(BIOS_INFO, "GMA: Found valid VBT in CBFS\n");
198 }
199 /* Search for pci8086,XXXX.rom in CBFS. */
200 else if (locate_vbt_vbios_cbfs(&rdev) == CB_SUCCESS &&
201 vbt_validate(&rdev) == CB_SUCCESS) {
202 found = true;
203 printk(BIOS_INFO, "GMA: Found valid VBT in VBIOS\n");
204 }
205 /*
206 * Try to locate Intel VBIOS at 0xc0000. It might have been placed by
207 * Native Graphics Init as fake Option ROM or when coreboot did run the
208 * VBIOS on legacy platforms.
209 * TODO: Place generated fake VBT in CBMEM and get rid of this.
210 */
211 else if (locate_vbt_vbios((u8 *)0xc0000, &rdev) == CB_SUCCESS &&
212 vbt_validate(&rdev) == CB_SUCCESS) {
213 found = true;
214 printk(BIOS_INFO, "GMA: Found valid VBT in legacy area\n");
215 }
216
217 if (!found) {
218 printk(BIOS_ERR, "GMA: VBT couldn't be found\n");
219 return CB_ERR;
220 }
221
222 vbt = rdev_mmap_full(&rdev);
223 if (!vbt) {
224 printk(BIOS_ERR, "GMA: Error mapping VBT\n");
225 return CB_ERR;
226 }
227
228 if (vbt->hdr_vbt_size > region_device_sz(&rdev)) {
229 printk(BIOS_ERR, "GMA: Error mapped only a partial VBT\n");
230 rdev_munmap(&rdev, vbt);
231 return CB_ERR;
232 }
233
234 memset(opregion, 0, sizeof(igd_opregion_t));
Matt DeVillierebe08e02017-07-14 13:28:42 -0500235
236 memcpy(&opregion->header.signature, IGD_OPREGION_SIGNATURE,
237 sizeof(opregion->header.signature));
Matt DeVillier53e41952017-06-27 13:07:43 -0500238 memcpy(opregion->header.vbios_version, vbt->coreblock_biosbuild,
239 ARRAY_SIZE(vbt->coreblock_biosbuild));
240 /* Extended VBT support */
241 if (vbt->hdr_vbt_size > sizeof(opregion->vbt.gvd1)) {
242 ext_vbt = cbmem_add(CBMEM_ID_EXT_VBT, vbt->hdr_vbt_size);
243
244 if (ext_vbt == NULL) {
245 printk(BIOS_ERR,
246 "GMA: Unable to add Ext VBT to cbmem!\n");
247 rdev_munmap(&rdev, vbt);
248 return CB_ERR;
249 }
250
251 memcpy(ext_vbt, vbt, vbt->hdr_vbt_size);
252 opregion->mailbox3.rvda = (uintptr_t)ext_vbt;
253 opregion->mailbox3.rvds = vbt->hdr_vbt_size;
254 } else {
255 /* Raw VBT size which can fit in gvd1 */
256 memcpy(opregion->vbt.gvd1, vbt, vbt->hdr_vbt_size);
257 }
258
259 rdev_munmap(&rdev, vbt);
Matt DeVillierebe08e02017-07-14 13:28:42 -0500260
261 /* 8kb */
262 opregion->header.size = sizeof(igd_opregion_t) / 1024;
Matt DeVillierfd0a8912017-10-19 22:44:18 -0500263
264 /*
265 * Left-shift version field to accomodate Intel Windows driver quirk
266 * when not using a VBIOS.
267 * Required for Legacy boot + NGI, UEFI + NGI, and UEFI + GOP driver.
268 *
269 * Tested on: (platform, GPU, windows driver version)
270 * samsung/stumpy (SNB, GT2, 9.17.10.4459)
271 * google/link (IVB, GT2, 15.33.4653)
272 * google/wolf (HSW, GT1, 15.40.36.4703)
273 * google/panther (HSW, GT2, 15.40.36.4703)
274 * google/rikku (BDW, GT1, 15.40.36.4703)
275 * google/lulu (BDW, GT2, 15.40.36.4703)
276 * google/chell (SKL-Y, GT2, 15.45.21.4821)
277 * google/sentry (SKL-U, GT1, 15.45.21.4821)
278 * purism/librem13v2 (SKL-U, GT2, 15.45.21.4821)
279 *
280 * No adverse effects when using VBIOS or booting Linux.
281 */
282 opregion->header.version = IGD_OPREGION_VERSION << 24;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500283
284 // FIXME We just assume we're mobile for now
285 opregion->header.mailboxes = MAILBOXES_MOBILE;
286
287 // TODO Initialize Mailbox 1
288
289 // TODO Initialize Mailbox 3
290 opregion->mailbox3.bclp = IGD_BACKLIGHT_BRIGHTNESS;
291 opregion->mailbox3.pfit = IGD_FIELD_VALID | IGD_PFIT_STRETCH;
292 opregion->mailbox3.pcft = 0; // should be (IMON << 1) & 0x3e
293 opregion->mailbox3.cblv = IGD_FIELD_VALID | IGD_INITIAL_BRIGHTNESS;
294 opregion->mailbox3.bclm[0] = IGD_WORD_FIELD_VALID + 0x0000;
295 opregion->mailbox3.bclm[1] = IGD_WORD_FIELD_VALID + 0x0a19;
296 opregion->mailbox3.bclm[2] = IGD_WORD_FIELD_VALID + 0x1433;
297 opregion->mailbox3.bclm[3] = IGD_WORD_FIELD_VALID + 0x1e4c;
298 opregion->mailbox3.bclm[4] = IGD_WORD_FIELD_VALID + 0x2866;
299 opregion->mailbox3.bclm[5] = IGD_WORD_FIELD_VALID + 0x327f;
300 opregion->mailbox3.bclm[6] = IGD_WORD_FIELD_VALID + 0x3c99;
301 opregion->mailbox3.bclm[7] = IGD_WORD_FIELD_VALID + 0x46b2;
302 opregion->mailbox3.bclm[8] = IGD_WORD_FIELD_VALID + 0x50cc;
303 opregion->mailbox3.bclm[9] = IGD_WORD_FIELD_VALID + 0x5ae5;
304 opregion->mailbox3.bclm[10] = IGD_WORD_FIELD_VALID + 0x64ff;
305
Matt DeVillierebe08e02017-07-14 13:28:42 -0500306 /* Write ASLS PCI register and prepare SWSCI register. */
307 intel_gma_opregion_register((uintptr_t)opregion);
308
309 return CB_SUCCESS;
310}