1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Writing IntelGraphicsMem table for ACPI
4 *
5 * Copyright 2019 Google LLC
6 * Modified from coreboot src/soc/intel/gma/opregion.c
7 */
8
9 #include <common.h>
10 #include <binman.h>
11 #include <bloblist.h>
12 #include <dm.h>
13 #include <spi_flash.h>
14 #include <asm/intel_opregion.h>
15
16 static char vbt_data[8 << 10];
17
locate_vbt(char ** vbtp,int * sizep)18 static int locate_vbt(char **vbtp, int *sizep)
19 {
20 struct binman_entry vbt;
21 struct udevice *dev;
22 u32 vbtsig = 0;
23 int size;
24 int ret;
25
26 ret = binman_entry_find("intel-vbt", &vbt);
27 if (ret)
28 return log_msg_ret("find VBT", ret);
29 ret = uclass_first_device_err(UCLASS_SPI_FLASH, &dev);
30 if (ret)
31 return log_msg_ret("find flash", ret);
32 size = vbt.size;
33 if (size > sizeof(vbt_data))
34 return log_msg_ret("vbt", -E2BIG);
35 ret = spi_flash_read_dm(dev, vbt.image_pos, size, vbt_data);
36 if (ret)
37 return log_msg_ret("read", ret);
38
39 memcpy(&vbtsig, vbt_data, sizeof(vbtsig));
40 if (vbtsig != VBT_SIGNATURE) {
41 log_err("Missing/invalid signature in VBT data file!\n");
42 return -EINVAL;
43 }
44
45 log_debug("Found a VBT of %u bytes\n", size);
46 *sizep = size;
47 *vbtp = vbt_data;
48
49 return 0;
50 }
51
52 /* Write ASLS PCI register and prepare SWSCI register */
intel_gma_opregion_register(struct udevice * dev,ulong opregion)53 static int intel_gma_opregion_register(struct udevice *dev, ulong opregion)
54 {
55 int sci_reg;
56
57 if (!device_active(dev))
58 return -ENOENT;
59
60 /*
61 * Intel BIOS Specification
62 * Chapter 5.3.7 "Initialise Hardware State"
63 */
64 dm_pci_write_config32(dev, ASLS, opregion);
65
66 /*
67 * Atom-based platforms use a combined SMI/SCI register,
68 * whereas non-Atom platforms use a separate SCI register
69 */
70 if (IS_ENABLED(CONFIG_INTEL_GMA_SWSMISCI))
71 sci_reg = SWSMISCI;
72 else
73 sci_reg = SWSCI;
74
75 /*
76 * Intel's Windows driver relies on this:
77 * Intel BIOS Specification
78 * Chapter 5.4 "ASL Software SCI Handler"
79 */
80 dm_pci_clrset_config16(dev, sci_reg, GSSCIE, SMISCISEL);
81
82 return 0;
83 }
84
intel_gma_init_igd_opregion(struct udevice * dev,struct igd_opregion * opregion)85 int intel_gma_init_igd_opregion(struct udevice *dev,
86 struct igd_opregion *opregion)
87 {
88 struct optionrom_vbt *vbt = NULL;
89 char *vbt_buf;
90 int vbt_size;
91 int ret;
92
93 ret = locate_vbt(&vbt_buf, &vbt_size);
94 if (ret) {
95 log_err("GMA: VBT couldn't be found\n");
96 return log_msg_ret("find vbt", ret);
97 }
98 vbt = (struct optionrom_vbt *)vbt_buf;
99
100 memset(opregion, '\0', sizeof(struct igd_opregion));
101
102 memcpy(&opregion->header.signature, IGD_OPREGION_SIGNATURE,
103 sizeof(opregion->header.signature));
104 memcpy(opregion->header.vbios_version, vbt->coreblock_biosbuild,
105 ARRAY_SIZE(vbt->coreblock_biosbuild));
106 /* Extended VBT support */
107 if (vbt->hdr_vbt_size > sizeof(opregion->vbt.gvd1)) {
108 struct optionrom_vbt *ext_vbt;
109
110 ret = bloblist_ensure_size(BLOBLISTT_INTEL_VBT,
111 vbt->hdr_vbt_size, 0,
112 (void **)&ext_vbt);
113 if (ret) {
114 log_err("GMA: Unable to add Ext VBT to bloblist\n");
115 return log_msg_ret("blob", ret);
116 }
117
118 memcpy(ext_vbt, vbt, vbt->hdr_vbt_size);
119 opregion->mailbox3.rvda = (uintptr_t)ext_vbt;
120 opregion->mailbox3.rvds = vbt->hdr_vbt_size;
121 } else {
122 /* Raw VBT size which can fit in gvd1 */
123 printf("copy to %p\n", opregion->vbt.gvd1);
124 memcpy(opregion->vbt.gvd1, vbt, vbt->hdr_vbt_size);
125 }
126
127 /* 8kb */
128 opregion->header.size = sizeof(struct igd_opregion) / 1024;
129
130 /*
131 * Left-shift version field to accommodate Intel Windows driver quirk
132 * when not using a VBIOS.
133 * Required for Legacy boot + NGI, UEFI + NGI, and UEFI + GOP driver.
134 *
135 * No adverse effects when using VBIOS or booting Linux.
136 */
137 opregion->header.version = IGD_OPREGION_VERSION << 24;
138
139 /* We just assume we're mobile for now */
140 opregion->header.mailboxes = MAILBOXES_MOBILE;
141
142 /* Initialise Mailbox 1 */
143 opregion->mailbox1.clid = 1;
144
145 /* Initialise Mailbox 3 */
146 opregion->mailbox3.bclp = IGD_BACKLIGHT_BRIGHTNESS;
147 opregion->mailbox3.pfit = IGD_FIELD_VALID | IGD_PFIT_STRETCH;
148 opregion->mailbox3.pcft = 0; /* should be (IMON << 1) & 0x3e */
149 opregion->mailbox3.cblv = IGD_FIELD_VALID | IGD_INITIAL_BRIGHTNESS;
150 opregion->mailbox3.bclm[0] = IGD_WORD_FIELD_VALID + 0x0000;
151 opregion->mailbox3.bclm[1] = IGD_WORD_FIELD_VALID + 0x0a19;
152 opregion->mailbox3.bclm[2] = IGD_WORD_FIELD_VALID + 0x1433;
153 opregion->mailbox3.bclm[3] = IGD_WORD_FIELD_VALID + 0x1e4c;
154 opregion->mailbox3.bclm[4] = IGD_WORD_FIELD_VALID + 0x2866;
155 opregion->mailbox3.bclm[5] = IGD_WORD_FIELD_VALID + 0x327f;
156 opregion->mailbox3.bclm[6] = IGD_WORD_FIELD_VALID + 0x3c99;
157 opregion->mailbox3.bclm[7] = IGD_WORD_FIELD_VALID + 0x46b2;
158 opregion->mailbox3.bclm[8] = IGD_WORD_FIELD_VALID + 0x50cc;
159 opregion->mailbox3.bclm[9] = IGD_WORD_FIELD_VALID + 0x5ae5;
160 opregion->mailbox3.bclm[10] = IGD_WORD_FIELD_VALID + 0x64ff;
161
162 /* Write ASLS PCI register and prepare SWSCI register */
163 ret = intel_gma_opregion_register(dev, (ulong)opregion);
164 if (ret)
165 return log_msg_ret("write asls", ret);
166
167 return 0;
168 }
169