diff options
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_bios.c | 275 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_bios.h | 4 |
2 files changed, 158 insertions, 121 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 773d9cbe19df..8dbeeea91872 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c | |||
@@ -65,195 +65,232 @@ static bool nv_cksum(const uint8_t *data, unsigned int length) | |||
65 | } | 65 | } |
66 | 66 | ||
67 | static int | 67 | static int |
68 | score_vbios(struct drm_device *dev, const uint8_t *data, const bool writeable) | 68 | score_vbios(struct nvbios *bios, const bool writeable) |
69 | { | 69 | { |
70 | if (!(data[0] == 0x55 && data[1] == 0xAA)) { | 70 | if (!bios->data || bios->data[0] != 0x55 || bios->data[1] != 0xAA) { |
71 | NV_TRACEWARN(dev, "... BIOS signature not found\n"); | 71 | NV_TRACEWARN(bios->dev, "... BIOS signature not found\n"); |
72 | return 0; | 72 | return 0; |
73 | } | 73 | } |
74 | 74 | ||
75 | if (nv_cksum(data, data[2] * 512)) { | 75 | if (nv_cksum(bios->data, bios->data[2] * 512)) { |
76 | NV_TRACEWARN(dev, "... BIOS checksum invalid\n"); | 76 | NV_TRACEWARN(bios->dev, "... BIOS checksum invalid\n"); |
77 | /* if a ro image is somewhat bad, it's probably all rubbish */ | 77 | /* if a ro image is somewhat bad, it's probably all rubbish */ |
78 | return writeable ? 2 : 1; | 78 | return writeable ? 2 : 1; |
79 | } else | 79 | } |
80 | NV_TRACE(dev, "... appears to be valid\n"); | ||
81 | 80 | ||
81 | NV_TRACE(bios->dev, "... appears to be valid\n"); | ||
82 | return 3; | 82 | return 3; |
83 | } | 83 | } |
84 | 84 | ||
85 | static void load_vbios_prom(struct drm_device *dev, uint8_t *data) | 85 | static void |
86 | bios_shadow_prom(struct nvbios *bios) | ||
86 | { | 87 | { |
88 | struct drm_device *dev = bios->dev; | ||
87 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 89 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
88 | uint32_t pci_nv_20, save_pci_nv_20; | 90 | u32 pcireg, access; |
89 | int pcir_ptr; | 91 | u16 pcir; |
90 | int i; | 92 | int i; |
91 | 93 | ||
94 | /* enable access to rom */ | ||
92 | if (dev_priv->card_type >= NV_50) | 95 | if (dev_priv->card_type >= NV_50) |
93 | pci_nv_20 = 0x88050; | 96 | pcireg = 0x088050; |
94 | else | 97 | else |
95 | pci_nv_20 = NV_PBUS_PCI_NV_20; | 98 | pcireg = NV_PBUS_PCI_NV_20; |
99 | access = nv_mask(dev, pcireg, 0x00000001, 0x00000000); | ||
96 | 100 | ||
97 | /* enable ROM access */ | 101 | /* bail if no rom signature, with a workaround for a PROM reading |
98 | save_pci_nv_20 = nvReadMC(dev, pci_nv_20); | 102 | * issue on some chipsets. the first read after a period of |
99 | nvWriteMC(dev, pci_nv_20, | 103 | * inactivity returns the wrong result, so retry the first header |
100 | save_pci_nv_20 & ~NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED); | 104 | * byte a few times before giving up as a workaround |
105 | */ | ||
106 | i = 16; | ||
107 | do { | ||
108 | if (nv_rd08(dev, NV_PROM_OFFSET + 0) == 0x55) | ||
109 | break; | ||
110 | } while (i--); | ||
101 | 111 | ||
102 | /* bail if no rom signature */ | 112 | if (!i || nv_rd08(dev, NV_PROM_OFFSET + 1) != 0xaa) |
103 | if (nv_rd08(dev, NV_PROM_OFFSET) != 0x55 || | ||
104 | nv_rd08(dev, NV_PROM_OFFSET + 1) != 0xaa) | ||
105 | goto out; | 113 | goto out; |
106 | 114 | ||
107 | /* additional check (see note below) - read PCI record header */ | 115 | /* additional check (see note below) - read PCI record header */ |
108 | pcir_ptr = nv_rd08(dev, NV_PROM_OFFSET + 0x18) | | 116 | pcir = nv_rd08(dev, NV_PROM_OFFSET + 0x18) | |
109 | nv_rd08(dev, NV_PROM_OFFSET + 0x19) << 8; | 117 | nv_rd08(dev, NV_PROM_OFFSET + 0x19) << 8; |
110 | if (nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr) != 'P' || | 118 | if (nv_rd08(dev, NV_PROM_OFFSET + pcir + 0) != 'P' || |
111 | nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 1) != 'C' || | 119 | nv_rd08(dev, NV_PROM_OFFSET + pcir + 1) != 'C' || |
112 | nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 2) != 'I' || | 120 | nv_rd08(dev, NV_PROM_OFFSET + pcir + 2) != 'I' || |
113 | nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 3) != 'R') | 121 | nv_rd08(dev, NV_PROM_OFFSET + pcir + 3) != 'R') |
114 | goto out; | 122 | goto out; |
115 | 123 | ||
116 | /* on some 6600GT/6800LE prom reads are messed up. nvclock alleges a | 124 | /* read entire bios image to system memory */ |
117 | * a good read may be obtained by waiting or re-reading (cargocult: 5x) | 125 | bios->length = nv_rd08(dev, NV_PROM_OFFSET + 2) * 512; |
118 | * each byte. we'll hope pramin has something usable instead | 126 | bios->data = kmalloc(bios->length, GFP_KERNEL); |
119 | */ | 127 | if (bios->data) { |
120 | for (i = 0; i < NV_PROM_SIZE; i++) | 128 | for (i = 0; i < bios->length; i++) |
121 | data[i] = nv_rd08(dev, NV_PROM_OFFSET + i); | 129 | bios->data[i] = nv_rd08(dev, NV_PROM_OFFSET + i); |
130 | } | ||
122 | 131 | ||
123 | out: | 132 | out: |
124 | /* disable ROM access */ | 133 | /* disable access to rom */ |
125 | nvWriteMC(dev, pci_nv_20, | 134 | nv_wr32(dev, pcireg, access); |
126 | save_pci_nv_20 | NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED); | ||
127 | } | 135 | } |
128 | 136 | ||
129 | static void load_vbios_pramin(struct drm_device *dev, uint8_t *data) | 137 | static void |
138 | bios_shadow_pramin(struct nvbios *bios) | ||
130 | { | 139 | { |
140 | struct drm_device *dev = bios->dev; | ||
131 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 141 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
132 | uint32_t old_bar0_pramin = 0; | 142 | u32 bar0 = 0; |
133 | int i; | 143 | int i; |
134 | 144 | ||
135 | if (dev_priv->card_type >= NV_50) { | 145 | if (dev_priv->card_type >= NV_50) { |
136 | u64 addr = (u64)(nv_rd32(dev, 0x619f04) & 0xffffff00) << 8; | 146 | u64 addr = (u64)(nv_rd32(dev, 0x619f04) & 0xffffff00) << 8; |
137 | if (!addr) { | 147 | if (!addr) { |
138 | addr = (u64)nv_rd32(dev, 0x1700) << 16; | 148 | addr = (u64)nv_rd32(dev, 0x001700) << 16; |
139 | addr += 0xf0000; | 149 | addr += 0xf0000; |
140 | } | 150 | } |
141 | 151 | ||
142 | old_bar0_pramin = nv_rd32(dev, 0x1700); | 152 | bar0 = nv_mask(dev, 0x001700, 0xffffffff, addr >> 16); |
143 | nv_wr32(dev, 0x1700, addr >> 16); | ||
144 | } | 153 | } |
145 | 154 | ||
146 | /* bail if no rom signature */ | 155 | /* bail if no rom signature */ |
147 | if (nv_rd08(dev, NV_PRAMIN_OFFSET) != 0x55 || | 156 | if (nv_rd08(dev, NV_PRAMIN_OFFSET + 0) != 0x55 || |
148 | nv_rd08(dev, NV_PRAMIN_OFFSET + 1) != 0xaa) | 157 | nv_rd08(dev, NV_PRAMIN_OFFSET + 1) != 0xaa) |
149 | goto out; | 158 | goto out; |
150 | 159 | ||
151 | for (i = 0; i < NV_PROM_SIZE; i++) | 160 | bios->length = nv_rd08(dev, NV_PRAMIN_OFFSET + 2) * 512; |
152 | data[i] = nv_rd08(dev, NV_PRAMIN_OFFSET + i); | 161 | bios->data = kmalloc(bios->length, GFP_KERNEL); |
162 | if (bios->data) { | ||
163 | for (i = 0; i < bios->length; i++) | ||
164 | bios->data[i] = nv_rd08(dev, NV_PRAMIN_OFFSET + i); | ||
165 | } | ||
153 | 166 | ||
154 | out: | 167 | out: |
155 | if (dev_priv->card_type >= NV_50) | 168 | if (dev_priv->card_type >= NV_50) |
156 | nv_wr32(dev, 0x1700, old_bar0_pramin); | 169 | nv_wr32(dev, 0x001700, bar0); |
157 | } | 170 | } |
158 | 171 | ||
159 | static void load_vbios_pci(struct drm_device *dev, uint8_t *data) | 172 | static void |
173 | bios_shadow_pci(struct nvbios *bios) | ||
174 | { | ||
175 | struct pci_dev *pdev = bios->dev->pdev; | ||
176 | size_t length; | ||
177 | |||
178 | if (!pci_enable_rom(pdev)) { | ||
179 | void __iomem *rom = pci_map_rom(pdev, &length); | ||
180 | if (rom) { | ||
181 | bios->data = kmalloc(length, GFP_KERNEL); | ||
182 | if (bios->data) { | ||
183 | memcpy_fromio(bios->data, rom, length); | ||
184 | bios->length = length; | ||
185 | } | ||
186 | pci_unmap_rom(pdev, rom); | ||
187 | } | ||
188 | |||
189 | pci_disable_rom(pdev); | ||
190 | } | ||
191 | } | ||
192 | |||
193 | static void | ||
194 | bios_shadow_acpi(struct nvbios *bios) | ||
160 | { | 195 | { |
161 | void __iomem *rom = NULL; | 196 | struct pci_dev *pdev = bios->dev->pdev; |
162 | size_t rom_len; | 197 | int ptr, len, ret; |
163 | int ret; | 198 | u8 data[3]; |
164 | 199 | ||
165 | ret = pci_enable_rom(dev->pdev); | 200 | if (!nouveau_acpi_rom_supported(pdev)) |
166 | if (ret) | ||
167 | return; | 201 | return; |
168 | 202 | ||
169 | rom = pci_map_rom(dev->pdev, &rom_len); | 203 | ret = nouveau_acpi_get_bios_chunk(data, 0, sizeof(data)); |
170 | if (!rom) | 204 | if (ret != sizeof(data)) |
171 | goto out; | 205 | return; |
172 | memcpy_fromio(data, rom, rom_len); | ||
173 | pci_unmap_rom(dev->pdev, rom); | ||
174 | 206 | ||
175 | out: | 207 | bios->length = min(data[2] * 512, 65536); |
176 | pci_disable_rom(dev->pdev); | 208 | bios->data = kmalloc(bios->length, GFP_KERNEL); |
177 | } | 209 | if (!bios->data) |
210 | return; | ||
178 | 211 | ||
179 | static void load_vbios_acpi(struct drm_device *dev, uint8_t *data) | 212 | len = bios->length; |
180 | { | 213 | ptr = 0; |
181 | int i; | 214 | while (len) { |
182 | int ret; | 215 | int size = (len > ROM_BIOS_PAGE) ? ROM_BIOS_PAGE : len; |
183 | int size = 64 * 1024; | ||
184 | 216 | ||
185 | if (!nouveau_acpi_rom_supported(dev->pdev)) | 217 | ret = nouveau_acpi_get_bios_chunk(bios->data, ptr, size); |
186 | return; | 218 | if (ret != size) { |
219 | kfree(bios->data); | ||
220 | bios->data = NULL; | ||
221 | return; | ||
222 | } | ||
187 | 223 | ||
188 | for (i = 0; i < (size / ROM_BIOS_PAGE); i++) { | 224 | len -= size; |
189 | ret = nouveau_acpi_get_bios_chunk(data, | 225 | ptr += size; |
190 | (i * ROM_BIOS_PAGE), | ||
191 | ROM_BIOS_PAGE); | ||
192 | if (ret <= 0) | ||
193 | break; | ||
194 | } | 226 | } |
195 | return; | ||
196 | } | 227 | } |
197 | 228 | ||
198 | struct methods { | 229 | struct methods { |
199 | const char desc[8]; | 230 | const char desc[8]; |
200 | void (*loadbios)(struct drm_device *, uint8_t *); | 231 | void (*shadow)(struct nvbios *); |
201 | const bool rw; | 232 | const bool rw; |
233 | int score; | ||
234 | u32 size; | ||
235 | u8 *data; | ||
202 | }; | 236 | }; |
203 | 237 | ||
204 | static struct methods shadow_methods[] = { | 238 | static bool |
205 | { "PRAMIN", load_vbios_pramin, true }, | 239 | bios_shadow(struct drm_device *dev) |
206 | { "PROM", load_vbios_prom, false }, | 240 | { |
207 | { "ACPI", load_vbios_acpi, true }, | 241 | struct methods shadow_methods[] = { |
208 | { "PCIROM", load_vbios_pci, true }, | 242 | { "PRAMIN", bios_shadow_pramin, true, 0, 0, NULL }, |
209 | }; | 243 | { "PROM", bios_shadow_prom, false, 0, 0, NULL }, |
210 | #define NUM_SHADOW_METHODS ARRAY_SIZE(shadow_methods) | 244 | { "ACPI", bios_shadow_acpi, true, 0, 0, NULL }, |
211 | 245 | { "PCIROM", bios_shadow_pci, true, 0, 0, NULL }, | |
212 | static bool NVShadowVBIOS(struct drm_device *dev, uint8_t *data) | 246 | {} |
213 | { | 247 | }; |
214 | struct methods *methods = shadow_methods; | 248 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
215 | int testscore = 3; | 249 | struct nvbios *bios = &dev_priv->vbios; |
216 | int scores[NUM_SHADOW_METHODS], i; | 250 | struct methods *mthd, *best; |
217 | 251 | ||
218 | if (nouveau_vbios) { | 252 | if (nouveau_vbios) { |
219 | for (i = 0; i < NUM_SHADOW_METHODS; i++) | 253 | mthd = shadow_methods; |
220 | if (!strcasecmp(nouveau_vbios, methods[i].desc)) | 254 | do { |
221 | break; | 255 | if (strcasecmp(nouveau_vbios, mthd->desc)) |
222 | 256 | continue; | |
223 | if (i < NUM_SHADOW_METHODS) { | 257 | NV_INFO(dev, "VBIOS source: %s\n", mthd->desc); |
224 | NV_INFO(dev, "Attempting to use BIOS image from %s\n", | ||
225 | methods[i].desc); | ||
226 | 258 | ||
227 | methods[i].loadbios(dev, data); | 259 | mthd->shadow(bios); |
228 | if (score_vbios(dev, data, methods[i].rw)) | 260 | mthd->score = score_vbios(bios, mthd->rw); |
261 | if (mthd->score) | ||
229 | return true; | 262 | return true; |
230 | } | 263 | } while ((++mthd)->shadow); |
231 | 264 | ||
232 | NV_ERROR(dev, "VBIOS source \'%s\' invalid\n", nouveau_vbios); | 265 | NV_ERROR(dev, "VBIOS source \'%s\' invalid\n", nouveau_vbios); |
233 | } | 266 | } |
234 | 267 | ||
235 | for (i = 0; i < NUM_SHADOW_METHODS; i++) { | 268 | mthd = shadow_methods; |
236 | NV_TRACE(dev, "Attempting to load BIOS image from %s\n", | 269 | do { |
237 | methods[i].desc); | 270 | NV_TRACE(dev, "Checking %s for VBIOS\n", mthd->desc); |
238 | data[0] = data[1] = 0; /* avoid reuse of previous image */ | 271 | mthd->shadow(bios); |
239 | methods[i].loadbios(dev, data); | 272 | mthd->score = score_vbios(bios, mthd->rw); |
240 | scores[i] = score_vbios(dev, data, methods[i].rw); | 273 | mthd->size = bios->length; |
241 | if (scores[i] == testscore) | 274 | mthd->data = bios->data; |
242 | return true; | 275 | } while (mthd->score != 3 && (++mthd)->shadow); |
243 | } | 276 | |
244 | 277 | mthd = shadow_methods; | |
245 | while (--testscore > 0) { | 278 | best = mthd; |
246 | for (i = 0; i < NUM_SHADOW_METHODS; i++) { | 279 | do { |
247 | if (scores[i] == testscore) { | 280 | if (mthd->score > best->score) { |
248 | NV_TRACE(dev, "Using BIOS image from %s\n", | 281 | kfree(best->data); |
249 | methods[i].desc); | 282 | best = mthd; |
250 | methods[i].loadbios(dev, data); | ||
251 | return true; | ||
252 | } | ||
253 | } | 283 | } |
284 | } while ((++mthd)->shadow); | ||
285 | |||
286 | if (best->score) { | ||
287 | NV_TRACE(dev, "Using VBIOS from %s\n", best->desc); | ||
288 | bios->length = best->size; | ||
289 | bios->data = best->data; | ||
290 | return true; | ||
254 | } | 291 | } |
255 | 292 | ||
256 | NV_ERROR(dev, "No valid BIOS image found\n"); | 293 | NV_ERROR(dev, "No valid VBIOS image found\n"); |
257 | return false; | 294 | return false; |
258 | } | 295 | } |
259 | 296 | ||
@@ -6334,11 +6371,7 @@ static bool NVInitVBIOS(struct drm_device *dev) | |||
6334 | spin_lock_init(&bios->lock); | 6371 | spin_lock_init(&bios->lock); |
6335 | bios->dev = dev; | 6372 | bios->dev = dev; |
6336 | 6373 | ||
6337 | if (!NVShadowVBIOS(dev, bios->data)) | 6374 | return bios_shadow(dev); |
6338 | return false; | ||
6339 | |||
6340 | bios->length = NV_PROM_SIZE; | ||
6341 | return true; | ||
6342 | } | 6375 | } |
6343 | 6376 | ||
6344 | static int nouveau_parse_vbios_struct(struct drm_device *dev) | 6377 | static int nouveau_parse_vbios_struct(struct drm_device *dev) |
@@ -6498,6 +6531,10 @@ nouveau_bios_init(struct drm_device *dev) | |||
6498 | void | 6531 | void |
6499 | nouveau_bios_takedown(struct drm_device *dev) | 6532 | nouveau_bios_takedown(struct drm_device *dev) |
6500 | { | 6533 | { |
6534 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
6535 | |||
6501 | nouveau_mxm_fini(dev); | 6536 | nouveau_mxm_fini(dev); |
6502 | nouveau_i2c_fini(dev); | 6537 | nouveau_i2c_fini(dev); |
6538 | |||
6539 | kfree(dev_priv->vbios.data); | ||
6503 | } | 6540 | } |
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index 0fc4e21c748b..1f3233df00e6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h | |||
@@ -211,6 +211,8 @@ struct nvbios { | |||
211 | NVBIOS_BIT | 211 | NVBIOS_BIT |
212 | } type; | 212 | } type; |
213 | uint16_t offset; | 213 | uint16_t offset; |
214 | uint32_t length; | ||
215 | uint8_t *data; | ||
214 | 216 | ||
215 | uint8_t chip_version; | 217 | uint8_t chip_version; |
216 | 218 | ||
@@ -221,8 +223,6 @@ struct nvbios { | |||
221 | 223 | ||
222 | spinlock_t lock; | 224 | spinlock_t lock; |
223 | 225 | ||
224 | uint8_t data[NV_PROM_SIZE]; | ||
225 | unsigned int length; | ||
226 | bool execute; | 226 | bool execute; |
227 | 227 | ||
228 | uint8_t major_version; | 228 | uint8_t major_version; |