blob: ff77f653b0080ec20f42dbf47d6755d1954d6279 [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
Srinidhi N Kaushik6719c822020-11-02 16:47:29 -080022static char vbt_data[CONFIG_VBT_DATA_SIZE_KB * 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
Julius Werner834b3ec2020-03-04 16:52:08 -080037 size_t file_size = cbfs_load(filename, vbt_data, sizeof(vbt_data));
Patrick Georgi4a3956d2018-05-03 19:15:13 +020038
39 if (file_size == 0)
40 return NULL;
41
42 if (vbt_size)
43 *vbt_size = file_size;
44
45 memcpy(&vbtsig, vbt_data, sizeof(vbtsig));
46 if (vbtsig != VBT_SIGNATURE) {
47 printk(BIOS_ERR, "Missing/invalid signature in VBT data file!\n");
48 return NULL;
49 }
50
51 printk(BIOS_INFO, "Found a VBT of %zu bytes after decompression\n",
52 file_size);
Aaron Durbin44f80652018-05-11 11:43:52 -060053 vbt_data_sz = file_size;
Patrick Georgi4a3956d2018-05-03 19:15:13 +020054
55 return (void *)vbt_data;
56}
57
Patrick Rudolphfa470422017-06-20 17:49:53 +020058/* Write ASLS PCI register and prepare SWSCI register. */
Nico Huberf2a0be22020-04-26 17:01:25 +020059static void intel_gma_opregion_register(uintptr_t opregion)
Patrick Rudolphfa470422017-06-20 17:49:53 +020060{
Elyes HAOUAS263076c2018-05-02 21:54:59 +020061 struct device *igd;
Patrick Rudolphfa470422017-06-20 17:49:53 +020062 u16 reg16;
Matt DeVillier681ef512018-02-11 01:17:01 -060063 u16 sci_reg;
Patrick Rudolphfa470422017-06-20 17:49:53 +020064
Kyösti Mälkkic70eed12018-05-22 02:18:00 +030065 igd = pcidev_on_root(0x2, 0);
Patrick Rudolphfa470422017-06-20 17:49:53 +020066 if (!igd || !igd->enabled)
67 return;
68
69 /*
70 * Intel BIOS Specification
71 * Chapter 5.3.7 "Initialize Hardware State"
72 */
73 pci_write_config32(igd, ASLS, opregion);
74
75 /*
Matt DeVillier681ef512018-02-11 01:17:01 -060076 * Atom-based platforms use a combined SMI/SCI register,
77 * whereas non-Atom platforms use a separate SCI register.
78 */
Julius Wernercd49cce2019-03-05 16:53:33 -080079 if (CONFIG(INTEL_GMA_SWSMISCI))
Matt DeVillier681ef512018-02-11 01:17:01 -060080 sci_reg = SWSMISCI;
81 else
82 sci_reg = SWSCI;
83
84 /*
Patrick Rudolphfa470422017-06-20 17:49:53 +020085 * Intel's Windows driver relies on this:
86 * Intel BIOS Specification
87 * Chapter 5.4 "ASL Software SCI Handler"
88 */
Matt DeVillier681ef512018-02-11 01:17:01 -060089 reg16 = pci_read_config16(igd, sci_reg);
Patrick Rudolphfa470422017-06-20 17:49:53 +020090 reg16 &= ~GSSCIE;
91 reg16 |= SMISCISEL;
Matt DeVillier681ef512018-02-11 01:17:01 -060092 pci_write_config16(igd, sci_reg, reg16);
Patrick Rudolphfa470422017-06-20 17:49:53 +020093}
Patrick Rudolphbac23032017-06-30 15:18:23 +020094
95/* Restore ASLS register on S3 resume and prepare SWSCI. */
Nico Huberf2a0be22020-04-26 17:01:25 +020096static enum cb_err intel_gma_restore_opregion(void)
Patrick Rudolphbac23032017-06-30 15:18:23 +020097{
Nico Huberf2a0be22020-04-26 17:01:25 +020098 const igd_opregion_t *const opregion = cbmem_find(CBMEM_ID_IGD_OPREGION);
99 if (!opregion) {
100 printk(BIOS_ERR, "GMA: Failed to find IGD OpRegion.\n");
101 return CB_ERR;
Patrick Rudolphbac23032017-06-30 15:18:23 +0200102 }
Nico Huberf2a0be22020-04-26 17:01:25 +0200103 /* Write ASLS PCI register and prepare SWSCI register. */
104 intel_gma_opregion_register((uintptr_t)opregion);
105 return CB_SUCCESS;
Patrick Rudolphbac23032017-06-30 15:18:23 +0200106}
Matt DeVillierebe08e02017-07-14 13:28:42 -0500107
Matt DeVillier53e41952017-06-27 13:07:43 -0500108static enum cb_err vbt_validate(struct region_device *rdev)
Matt DeVillierebe08e02017-07-14 13:28:42 -0500109{
Matt DeVillier53e41952017-06-27 13:07:43 -0500110 uint32_t sig;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500111
Matt DeVillier53e41952017-06-27 13:07:43 -0500112 if (rdev_readat(rdev, &sig, 0, sizeof(sig)) != sizeof(sig))
Matt DeVillierebe08e02017-07-14 13:28:42 -0500113 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500114
Matt DeVillier53e41952017-06-27 13:07:43 -0500115 if (sig != VBT_SIGNATURE)
Matt DeVillierebe08e02017-07-14 13:28:42 -0500116 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500117
118 return CB_SUCCESS;
119}
120
Matt DeVillier53e41952017-06-27 13:07:43 -0500121static enum cb_err locate_vbt_vbios(const u8 *vbios, struct region_device *rdev)
Matt DeVillierebe08e02017-07-14 13:28:42 -0500122{
Matt DeVillier53e41952017-06-27 13:07:43 -0500123 const optionrom_header_t *oprom;
124 const optionrom_pcir_t *pcir;
125 struct region_device rd;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500126 enum cb_err ret;
Matt DeVillier53e41952017-06-27 13:07:43 -0500127 u8 opromsize;
128 size_t offset;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500129
Matt DeVillier53e41952017-06-27 13:07:43 -0500130 // FIXME: caller should supply a region_device instead of vbios pointer
Julius Wernerc8931972021-04-16 16:48:32 -0700131 if (rdev_chain_mem(&rd, vbios, sizeof(*oprom)))
Matt DeVillier53e41952017-06-27 13:07:43 -0500132 return CB_ERR;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500133
Matt DeVillier53e41952017-06-27 13:07:43 -0500134 if (rdev_readat(&rd, &opromsize, offsetof(optionrom_header_t, size),
135 sizeof(opromsize)) != sizeof(opromsize) || !opromsize)
136 return CB_ERR;
137
Julius Wernerc8931972021-04-16 16:48:32 -0700138 if (rdev_chain_mem(&rd, vbios, opromsize * 512))
Matt DeVillier53e41952017-06-27 13:07:43 -0500139 return CB_ERR;
140
141 oprom = rdev_mmap(&rd, 0, sizeof(*oprom));
142 if (!oprom)
143 return CB_ERR;
144
145 if (!oprom->pcir_offset || !oprom->vbt_offset) {
146 rdev_munmap(&rd, (void *)oprom);
147 return CB_ERR;
148 }
149
150 pcir = rdev_mmap(&rd, oprom->pcir_offset, sizeof(*pcir));
151 if (pcir == NULL) {
152 rdev_munmap(&rd, (void *)oprom);
153 return CB_ERR;
154 }
155
Elyes HAOUAS75d19d72021-01-16 17:30:26 +0100156 printk(BIOS_DEBUG, "GMA: %s: %x %x %x %x %x\n", __func__,
Matt DeVillier53e41952017-06-27 13:07:43 -0500157 oprom->signature, pcir->vendor, pcir->classcode[0],
158 pcir->classcode[1], pcir->classcode[2]);
159
160 /* Make sure we got an Intel VGA option rom */
161 if ((oprom->signature != OPROM_SIGNATURE) ||
Felix Singer43b7f412022-03-07 04:34:52 +0100162 (pcir->vendor != PCI_VID_INTEL) ||
Matt DeVillier53e41952017-06-27 13:07:43 -0500163 (pcir->signature != 0x52494350) ||
164 (pcir->classcode[0] != 0x00) ||
165 (pcir->classcode[1] != 0x00) ||
166 (pcir->classcode[2] != 0x03)) {
167 rdev_munmap(&rd, (void *)oprom);
168 rdev_munmap(&rd, (void *)pcir);
169 return CB_ERR;
170 }
171
172 rdev_munmap(&rd, (void *)pcir);
173
174 /* Search for $VBT as some VBIOS are broken... */
175 offset = oprom->vbt_offset;
176 do {
177 ret = rdev_chain(rdev, &rd, offset,
178 (opromsize * 512) - offset);
179 offset++;
180 } while (ret == CB_SUCCESS && vbt_validate(rdev) != CB_SUCCESS);
181
182 offset--;
183
184 if (ret == CB_SUCCESS && offset != oprom->vbt_offset)
185 printk(BIOS_WARNING, "GMA: Buggy VBIOS found\n");
186 else if (ret != CB_SUCCESS)
187 printk(BIOS_ERR, "GMA: Broken VBIOS found\n");
188
189 rdev_munmap(&rd, (void *)oprom);
190 return ret;
191}
192
193static enum cb_err locate_vbt_cbfs(struct region_device *rdev)
194{
Patrick Georgi4a3956d2018-05-03 19:15:13 +0200195 size_t vbt_data_size;
196 void *vbt = locate_vbt(&vbt_data_size);
Matt DeVillier53e41952017-06-27 13:07:43 -0500197
Patrick Georgi4a3956d2018-05-03 19:15:13 +0200198 if (vbt == NULL)
199 return CB_ERR;
Matt DeVillier53e41952017-06-27 13:07:43 -0500200
Julius Wernerc8931972021-04-16 16:48:32 -0700201 if (rdev_chain_mem(rdev, vbt, vbt_data_size))
Patrick Georgi4a3956d2018-05-03 19:15:13 +0200202 return CB_ERR;
203
204 printk(BIOS_INFO, "GMA: Found VBT in CBFS\n");
205
206 return CB_SUCCESS;
Matt DeVillier53e41952017-06-27 13:07:43 -0500207}
208
209static enum cb_err locate_vbt_vbios_cbfs(struct region_device *rdev)
210{
211 const u8 *oprom =
Kyösti Mälkkic70eed12018-05-22 02:18:00 +0300212 (const u8 *)pci_rom_probe(pcidev_on_root(0x2, 0));
Matt DeVillier53e41952017-06-27 13:07:43 -0500213 if (oprom == NULL)
214 return CB_ERR;
215
216 printk(BIOS_INFO, "GMA: Found VBIOS in CBFS\n");
217
218 return locate_vbt_vbios(oprom, rdev);
219}
220
MAULIK V VAGHELA926949d2021-06-08 20:20:57 +0530221/*
222 * Try to locate VBT in possible locations and return if found.
223 * VBT can be possibly in one of 3 regions:
224 * 1. Stitched directly into CBFS region as VBT
225 * 2. Part of pci8086 option ROM within CBFS
226 * 3. part of VBIOS at location 0xC0000.
227 */
228static enum cb_err find_vbt_location(struct region_device *rdev)
Matt DeVillier53e41952017-06-27 13:07:43 -0500229{
Matt DeVillier53e41952017-06-27 13:07:43 -0500230 /* Search for vbt.bin in CBFS. */
MAULIK V VAGHELA926949d2021-06-08 20:20:57 +0530231 if (locate_vbt_cbfs(rdev) == CB_SUCCESS &&
232 vbt_validate(rdev) == CB_SUCCESS) {
Matt DeVillier53e41952017-06-27 13:07:43 -0500233 printk(BIOS_INFO, "GMA: Found valid VBT in CBFS\n");
MAULIK V VAGHELA926949d2021-06-08 20:20:57 +0530234 return CB_SUCCESS;
Matt DeVillier53e41952017-06-27 13:07:43 -0500235 }
236 /* Search for pci8086,XXXX.rom in CBFS. */
MAULIK V VAGHELA926949d2021-06-08 20:20:57 +0530237 else if (locate_vbt_vbios_cbfs(rdev) == CB_SUCCESS &&
238 vbt_validate(rdev) == CB_SUCCESS) {
Matt DeVillier53e41952017-06-27 13:07:43 -0500239 printk(BIOS_INFO, "GMA: Found valid VBT in VBIOS\n");
MAULIK V VAGHELA926949d2021-06-08 20:20:57 +0530240 return CB_SUCCESS;
Matt DeVillier53e41952017-06-27 13:07:43 -0500241 }
242 /*
243 * Try to locate Intel VBIOS at 0xc0000. It might have been placed by
244 * Native Graphics Init as fake Option ROM or when coreboot did run the
245 * VBIOS on legacy platforms.
246 * TODO: Place generated fake VBT in CBMEM and get rid of this.
247 */
MAULIK V VAGHELA926949d2021-06-08 20:20:57 +0530248 else if (locate_vbt_vbios((u8 *)0xc0000, rdev) == CB_SUCCESS &&
249 vbt_validate(rdev) == CB_SUCCESS) {
Matt DeVillier53e41952017-06-27 13:07:43 -0500250 printk(BIOS_INFO, "GMA: Found valid VBT in legacy area\n");
MAULIK V VAGHELA926949d2021-06-08 20:20:57 +0530251 return CB_SUCCESS;
Matt DeVillier53e41952017-06-27 13:07:43 -0500252 }
253
Angel Pons33dddc42021-06-16 11:56:25 +0200254 printk(BIOS_ERR, "GMA: VBT couldn't be found\n");
MAULIK V VAGHELA926949d2021-06-08 20:20:57 +0530255 return CB_ERR;
256}
257
Meera Ravindranath92637df2021-07-09 12:40:28 +0530258/* Function to get the IGD Opregion version */
259static struct opregion_version opregion_get_version(void)
260{
Meera Ravindranath1b8159b2021-04-29 20:30:27 +0530261 if (CONFIG(INTEL_GMA_OPREGION_2_1))
262 return (struct opregion_version) { .major = 2, .minor = 1 };
263
Meera Ravindranath92637df2021-07-09 12:40:28 +0530264 return (struct opregion_version) { .major = 2, .minor = 0 };
265}
266
MAULIK V VAGHELA926949d2021-06-08 20:20:57 +0530267/*
268 * Function to determine if we need to use extended VBT region to pass
269 * VBT pointer. If VBT size > 6 KiB then we need to use extended VBT
270 * region.
271 */
272static inline bool is_ext_vbt_required(igd_opregion_t *opregion, optionrom_vbt_t *vbt)
273{
274 return (vbt->hdr_vbt_size > sizeof(opregion->vbt.gvd1));
275}
276
Meera Ravindranath1b8159b2021-04-29 20:30:27 +0530277/* Function to determine if the VBT uses a relative address */
278static inline bool uses_relative_vbt_addr(opregion_header_t *header)
279{
280 if (header->opver.major > 2)
281 return true;
282
283 return header->opver.major >= 2 && header->opver.minor >= 1;
284}
285
Maulik V Vaghelaa6b60eb2021-06-09 11:36:20 +0530286/*
287 * Copy extended VBT at the end of opregion and fill rvda and rvds
288 * values correctly for the opregion.
289 */
290static void opregion_add_ext_vbt(igd_opregion_t *opregion, uint8_t *ext_vbt,
291 optionrom_vbt_t *vbt)
292{
Meera Ravindranath1b8159b2021-04-29 20:30:27 +0530293
294 opregion_header_t *header = &opregion->header;
Maulik V Vaghelaa6b60eb2021-06-09 11:36:20 +0530295 /* Copy VBT into extended VBT region (at offset 8 KiB) */
296 memcpy(ext_vbt, vbt, vbt->hdr_vbt_size);
297
Meera Ravindranath1b8159b2021-04-29 20:30:27 +0530298 /* Fill RVDA value with relative address of the opregion buffer in case of
299 IGD Opregion version 2.1+ and physical address otherwise */
300
301 if (uses_relative_vbt_addr(header))
302 opregion->mailbox3.rvda = sizeof(*opregion);
303 else
304 opregion->mailbox3.rvda = (uintptr_t)ext_vbt;
305
Maulik V Vaghelaa6b60eb2021-06-09 11:36:20 +0530306 opregion->mailbox3.rvds = vbt->hdr_vbt_size;
307}
308
MAULIK V VAGHELA926949d2021-06-08 20:20:57 +0530309/* Initialize IGD OpRegion, called from ACPI code and OS drivers */
310enum cb_err intel_gma_init_igd_opregion(void)
311{
312 igd_opregion_t *opregion;
313 struct region_device rdev;
314 optionrom_vbt_t *vbt = NULL;
Maulik V Vaghelaa6b60eb2021-06-09 11:36:20 +0530315 size_t opregion_size = sizeof(igd_opregion_t);
MAULIK V VAGHELA926949d2021-06-08 20:20:57 +0530316
317 if (acpi_is_wakeup_s3())
318 return intel_gma_restore_opregion();
319
320 if (find_vbt_location(&rdev) != CB_SUCCESS)
Matt DeVillier53e41952017-06-27 13:07:43 -0500321 return CB_ERR;
Matt DeVillier53e41952017-06-27 13:07:43 -0500322
323 vbt = rdev_mmap_full(&rdev);
324 if (!vbt) {
325 printk(BIOS_ERR, "GMA: Error mapping VBT\n");
326 return CB_ERR;
327 }
328
329 if (vbt->hdr_vbt_size > region_device_sz(&rdev)) {
330 printk(BIOS_ERR, "GMA: Error mapped only a partial VBT\n");
331 rdev_munmap(&rdev, vbt);
332 return CB_ERR;
333 }
334
Maulik V Vaghelaa6b60eb2021-06-09 11:36:20 +0530335 if (is_ext_vbt_required(opregion, vbt))
336 opregion_size += vbt->hdr_vbt_size;
337
338 opregion = cbmem_add(CBMEM_ID_IGD_OPREGION, opregion_size);
Nico Huberf2a0be22020-04-26 17:01:25 +0200339 if (!opregion) {
340 printk(BIOS_ERR, "GMA: Failed to add IGD OpRegion to CBMEM.\n");
341 return CB_ERR;
342 }
343
Maulik V Vaghelaa6b60eb2021-06-09 11:36:20 +0530344 memset(opregion, 0, opregion_size);
Matt DeVillierebe08e02017-07-14 13:28:42 -0500345
346 memcpy(&opregion->header.signature, IGD_OPREGION_SIGNATURE,
347 sizeof(opregion->header.signature));
Matt DeVillier53e41952017-06-27 13:07:43 -0500348 memcpy(opregion->header.vbios_version, vbt->coreblock_biosbuild,
349 ARRAY_SIZE(vbt->coreblock_biosbuild));
Meera Ravindranath1b8159b2021-04-29 20:30:27 +0530350
351 /* Get the opregion version information */
352 opregion->header.opver = opregion_get_version();
353
Matt DeVillier53e41952017-06-27 13:07:43 -0500354 /* Extended VBT support */
MAULIK V VAGHELA926949d2021-06-08 20:20:57 +0530355 if (is_ext_vbt_required(opregion, vbt)) {
Maulik V Vaghelaa6b60eb2021-06-09 11:36:20 +0530356 /* Place extended VBT just after opregion */
357 uint8_t *ext_vbt = (uint8_t *)opregion + sizeof(*opregion);
358 opregion_add_ext_vbt(opregion, ext_vbt, vbt);
Matt DeVillier53e41952017-06-27 13:07:43 -0500359 } else {
360 /* Raw VBT size which can fit in gvd1 */
361 memcpy(opregion->vbt.gvd1, vbt, vbt->hdr_vbt_size);
362 }
363
364 rdev_munmap(&rdev, vbt);
Matt DeVillierebe08e02017-07-14 13:28:42 -0500365
366 /* 8kb */
367 opregion->header.size = sizeof(igd_opregion_t) / 1024;
Matt DeVillierfd0a8912017-10-19 22:44:18 -0500368
Matt DeVillierebe08e02017-07-14 13:28:42 -0500369 // FIXME We just assume we're mobile for now
370 opregion->header.mailboxes = MAILBOXES_MOBILE;
371
372 // TODO Initialize Mailbox 1
Patrick Georgi0f68b232018-01-25 18:23:15 +0100373 opregion->mailbox1.clid = 1;
Matt DeVillierebe08e02017-07-14 13:28:42 -0500374
375 // TODO Initialize Mailbox 3
376 opregion->mailbox3.bclp = IGD_BACKLIGHT_BRIGHTNESS;
377 opregion->mailbox3.pfit = IGD_FIELD_VALID | IGD_PFIT_STRETCH;
378 opregion->mailbox3.pcft = 0; // should be (IMON << 1) & 0x3e
379 opregion->mailbox3.cblv = IGD_FIELD_VALID | IGD_INITIAL_BRIGHTNESS;
380 opregion->mailbox3.bclm[0] = IGD_WORD_FIELD_VALID + 0x0000;
381 opregion->mailbox3.bclm[1] = IGD_WORD_FIELD_VALID + 0x0a19;
382 opregion->mailbox3.bclm[2] = IGD_WORD_FIELD_VALID + 0x1433;
383 opregion->mailbox3.bclm[3] = IGD_WORD_FIELD_VALID + 0x1e4c;
384 opregion->mailbox3.bclm[4] = IGD_WORD_FIELD_VALID + 0x2866;
385 opregion->mailbox3.bclm[5] = IGD_WORD_FIELD_VALID + 0x327f;
386 opregion->mailbox3.bclm[6] = IGD_WORD_FIELD_VALID + 0x3c99;
387 opregion->mailbox3.bclm[7] = IGD_WORD_FIELD_VALID + 0x46b2;
388 opregion->mailbox3.bclm[8] = IGD_WORD_FIELD_VALID + 0x50cc;
389 opregion->mailbox3.bclm[9] = IGD_WORD_FIELD_VALID + 0x5ae5;
390 opregion->mailbox3.bclm[10] = IGD_WORD_FIELD_VALID + 0x64ff;
391
Matt DeVillierebe08e02017-07-14 13:28:42 -0500392 /* Write ASLS PCI register and prepare SWSCI register. */
393 intel_gma_opregion_register((uintptr_t)opregion);
394
395 return CB_SUCCESS;
396}