Ticket #6641: intel_gart.cpp

File intel_gart.cpp, 14.2 KB (added by lukove, 14 years ago)
Line 
1/*
2 * Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <AreaKeeper.h>
8#include <intel_extreme.h>
9
10#include <stdlib.h>
11
12#include <AGP.h>
13#include <KernelExport.h>
14#include <PCI.h>
15
16
17//#define TRACE_INTEL
18#ifdef TRACE_INTEL
19# define TRACE(x...) dprintf("\33[33magp-intel:\33[0m " x)
20#else
21# define TRACE(x...) ;
22#endif
23
24#ifndef __HAIKU__
25# define B_KERNEL_READ_AREA 0
26# define B_KERNEL_WRITE_AREA 0
27#endif
28
29/* read and write to PCI config space */
30#define get_pci_config(info, offset, size) \
31 (sPCI->read_pci_config((info).bus, (info).device, (info).function, \
32 (offset), (size)))
33#define set_pci_config(info, offset, size, value) \
34 (sPCI->write_pci_config((info).bus, (info).device, (info).function, \
35 (offset), (size), (value)))
36#define write32(address, data) \
37 (*((volatile uint32 *)(address)) = (data))
38#define read32(address) \
39 (*((volatile uint32 *)(address)))
40
41
42const struct supported_device {
43 uint32 bridge_id;
44 uint32 display_id;
45 uint32 type;
46 const char *name;
47} kSupportedDevices[] = {
48 {0x3575, 0x3577, INTEL_TYPE_83x, "i830GM"},
49 {0x2560, 0x2562, INTEL_TYPE_83x, "i845G"},
50 {0x3580, 0x3582, INTEL_TYPE_85x, "i855G"},
51 {0x2570, 0x2572, INTEL_TYPE_85x, "i865G"},
52
53// {0x2792, INTEL_TYPE_91x, "i910"},
54// {0x258a, INTEL_TYPE_91x, "i915"},
55 {0x2580, 0x2582, INTEL_TYPE_91x, "i915G"},
56 {0x2590, 0x2592, INTEL_TYPE_91x, "i915GM"},
57 {0x2770, 0x2772, INTEL_TYPE_945, "i945G"},
58 {0x27a0, 0x27a2, INTEL_TYPE_945, "i945GM"},
59 {0x27ac, 0x27ae, INTEL_TYPE_945, "i945GME"},
60
61 {0x2970, 0x2972, INTEL_TYPE_965, "i946GZ"},
62 {0x2980, 0x2982, INTEL_TYPE_965, "i965G"},
63 {0x2990, 0x2992, INTEL_TYPE_965, "i965Q"},
64 {0x29a0, 0x29a2, INTEL_TYPE_965, "i965G"},
65 {0x2a00, 0x2a02, INTEL_TYPE_965, "i965GM"},
66 {0x2a10, 0x2a12, INTEL_TYPE_965, "i965GME"},
67
68 {0x29b0, 0x29b2, INTEL_TYPE_G33, "G33"},
69 {0x29c0, 0x29c2, INTEL_TYPE_G33, "Q35"},
70 {0x29d0, 0x29d2, INTEL_TYPE_G33, "Q33"},
71
72 {0x2e30, 0x2e32, INTEL_TYPE_GM45, "GMA_X4500_VGA"}, // Intel Mystic Lake (dg41mj)
73 {0x2a40, 0x2a42, INTEL_TYPE_GM45, "GM45"},
74};
75
76struct intel_info {
77 pci_info bridge;
78 pci_info display;
79 uint32 type;
80
81 uint32 *gtt_base;
82 addr_t gtt_physical_base;
83 area_id gtt_area;
84 size_t gtt_entries;
85 size_t gtt_stolen_entries;
86
87 vuint32 *registers;
88 area_id registers_area;
89
90 addr_t aperture_base;
91 addr_t aperture_physical_base;
92 area_id aperture_area;
93 size_t aperture_size;
94 size_t aperture_stolen_size;
95
96 phys_addr_t scratch_page;
97 area_id scratch_area;
98};
99
100static intel_info sInfo;
101static pci_module_info *sPCI;
102
103
104static bool
105has_display_device(pci_info &info, uint32 deviceID)
106{
107 for (uint32 index = 0; sPCI->get_nth_pci_info(index, &info) == B_OK;
108 index++) {
109 if (info.vendor_id != VENDOR_ID_INTEL
110 || info.device_id != deviceID
111 || info.class_base != PCI_display)
112 continue;
113
114 return true;
115 }
116
117 return false;
118}
119
120
121static void
122determine_memory_sizes(intel_info &info, size_t &gttSize, size_t &stolenSize)
123{
124 // read stolen memory from the PCI configuration of the PCI bridge
125 uint16 memoryConfig = get_pci_config(info.bridge,
126 INTEL_GRAPHICS_MEMORY_CONTROL, 2);
127 size_t memorySize = 1 << 20; // 1 MB
128 gttSize = 0;
129 stolenSize = 0;
130
131 if (info.type == INTEL_TYPE_965) {
132 switch (memoryConfig & i965_GTT_MASK) {
133 case i965_GTT_128K:
134 gttSize = 128 << 10;
135 break;
136 case i965_GTT_256K:
137 gttSize = 256 << 10;
138 break;
139 case i965_GTT_512K:
140 gttSize = 512 << 10;
141 break;
142 }
143 } else if (info.type == INTEL_TYPE_G33) {
144 switch (memoryConfig & G33_GTT_MASK) {
145 case G33_GTT_1M:
146 gttSize = 1 << 20;
147 break;
148 case G33_GTT_2M:
149 gttSize = 2 << 20;
150 break;
151 }
152 } else if ((info.type & INTEL_TYPE_GROUP_MASK) == INTEL_TYPE_G4x) {
153 switch (memoryConfig & G4X_GTT_MASK) {
154 case G4X_GTT_NONE:
155 gttSize = 0;
156 break;
157 case G4X_GTT_1M_NO_IVT:
158 gttSize = 1 << 20;
159 break;
160 case G4X_GTT_2M_NO_IVT:
161 case G4X_GTT_2M_IVT:
162 gttSize = 2 << 20;
163 break;
164 case G4X_GTT_3M_IVT:
165 gttSize = 3 << 20;
166 break;
167 case G4X_GTT_4M_IVT:
168 gttSize = 4 << 20;
169 break;
170 }
171 } else {
172 // older models have the GTT as large as their frame buffer mapping
173 // TODO: check if the i9xx version works with the i8xx chips as well
174 size_t frameBufferSize = 0;
175 if ((info.type & INTEL_TYPE_8xx) != 0) {
176 if (info.type == INTEL_TYPE_83x
177 && (memoryConfig & MEMORY_MASK) == i830_FRAME_BUFFER_64M)
178 frameBufferSize = 64 << 20;
179 else
180 frameBufferSize = 128 << 20;
181 } else if ((info.type & INTEL_TYPE_9xx) != 0)
182 frameBufferSize = info.display.u.h0.base_register_sizes[2];
183
184 TRACE(("frame buffer size %lu MB\n", frameBufferSize >> 20));
185 gttSize = frameBufferSize / 1024;
186 }
187
188 // TODO: test with different models!
189
190 if (info.type == INTEL_TYPE_83x) {
191 // Older chips
192 switch (memoryConfig & STOLEN_MEMORY_MASK) {
193 case i830_LOCAL_MEMORY_ONLY:
194 // TODO: determine its size!
195 dprintf("intel_gart: getting local memory size not implemented.\n");
196 break;
197 case i830_STOLEN_512K:
198 memorySize >>= 1;
199 break;
200 case i830_STOLEN_1M:
201 // default case
202 break;
203 case i830_STOLEN_8M:
204 memorySize *= 8;
205 break;
206 }
207 } else if (info.type == INTEL_TYPE_85x
208 || (info.type & INTEL_TYPE_9xx) == INTEL_TYPE_9xx) {
209 switch (memoryConfig & STOLEN_MEMORY_MASK) {
210 case i855_STOLEN_MEMORY_4M:
211 memorySize *= 4;
212 break;
213 case i855_STOLEN_MEMORY_8M:
214 memorySize *= 8;
215 break;
216 case i855_STOLEN_MEMORY_16M:
217 memorySize *= 16;
218 break;
219 case i855_STOLEN_MEMORY_32M:
220 memorySize *= 32;
221 break;
222 case i855_STOLEN_MEMORY_48M:
223 memorySize *= 48;
224 break;
225 case i855_STOLEN_MEMORY_64M:
226 memorySize *= 64;
227 break;
228 case i855_STOLEN_MEMORY_128M:
229 memorySize *= 128;
230 break;
231 case i855_STOLEN_MEMORY_256M:
232 memorySize *= 256;
233 break;
234 case G4X_STOLEN_MEMORY_96MB:
235 memorySize *= 96;
236 break;
237 case G4X_STOLEN_MEMORY_160MB:
238 memorySize *= 160;
239 break;
240 case G4X_STOLEN_MEMORY_224MB:
241 memorySize *= 224;
242 break;
243 case G4X_STOLEN_MEMORY_352MB:
244 memorySize *= 352;
245 break;
246 }
247 } else {
248 // TODO: error out!
249 memorySize = 4096;
250 }
251
252 stolenSize = memorySize - 4096;
253}
254
255
256static void
257set_gtt_entry(intel_info &info, uint32 offset, phys_addr_t physicalAddress)
258{
259 // TODO: this is not 64-bit safe!
260 write32(info.gtt_base + (offset >> GTT_PAGE_SHIFT),
261 (uint32)physicalAddress | GTT_ENTRY_VALID);
262}
263
264
265static void
266intel_unmap(intel_info &info)
267{
268 delete_area(info.registers_area);
269 delete_area(info.gtt_area);
270 delete_area(info.scratch_area);
271 delete_area(info.aperture_area);
272 info.aperture_size = 0;
273}
274
275
276static status_t
277intel_map(intel_info &info)
278{
279 int fbIndex = 0;
280 int mmioIndex = 1;
281 if ((info.type & INTEL_TYPE_FAMILY_MASK) == INTEL_TYPE_9xx) {
282 // for some reason Intel saw the need to change the order of the mappings
283 // with the introduction of the i9xx family
284 mmioIndex = 0;
285 fbIndex = 2;
286 }
287
288 AreaKeeper mmioMapper;
289 info.registers_area = mmioMapper.Map("intel GMCH mmio",
290 (void *)info.display.u.h0.base_registers[mmioIndex],
291 info.display.u.h0.base_register_sizes[mmioIndex], B_ANY_KERNEL_ADDRESS,
292 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void **)&info.registers);
293 if (mmioMapper.InitCheck() < B_OK) {
294 dprintf("agp_intel: could not map memory I/O!\n");
295 return info.registers_area;
296 }
297
298 // make sure bus master, memory-mapped I/O, and frame buffer is enabled
299 set_pci_config(info.display, PCI_command, 2,
300 get_pci_config(info.display, PCI_command, 2)
301 | PCI_command_io | PCI_command_memory | PCI_command_master);
302
303 void *scratchAddress;
304 AreaKeeper scratchCreator;
305 info.scratch_area = scratchCreator.Create("intel GMCH scratch",
306 &scratchAddress, B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_FULL_LOCK,
307 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
308 if (scratchCreator.InitCheck() < B_OK) {
309 dprintf("agp_intel: could not create scratch page!\n");
310 return info.scratch_area;
311 }
312
313 physical_entry entry;
314 if (get_memory_map(scratchAddress, B_PAGE_SIZE, &entry, 1) != B_OK)
315 return B_ERROR;
316
317 if ((info.type & INTEL_TYPE_FAMILY_MASK) == INTEL_TYPE_9xx) {
318 if ((info.type & INTEL_TYPE_GROUP_MASK) == INTEL_TYPE_G4x) {
319 info.gtt_physical_base = info.display.u.h0.base_registers[mmioIndex]
320 + (2UL << 20);
321 } else
322 info.gtt_physical_base = get_pci_config(info.display, i915_GTT_BASE, 4);
323 } else {
324 info.gtt_physical_base = read32(info.registers
325 + INTEL_PAGE_TABLE_CONTROL) & ~PAGE_TABLE_ENABLED;
326 if (info.gtt_physical_base == 0) {
327 // TODO: not sure how this is supposed to work under Linux/FreeBSD,
328 // but on my i865, this code is needed for Haiku.
329 dprintf("intel_gart: Use GTT address fallback.\n");
330 info.gtt_physical_base = info.display.u.h0.base_registers[mmioIndex]
331 + i830_GTT_BASE;
332 }
333 }
334
335 size_t gttSize, stolenSize;
336 determine_memory_sizes(info, gttSize, stolenSize);
337
338 info.gtt_entries = gttSize / 4096;
339 info.gtt_stolen_entries = stolenSize / 4096;
340
341 TRACE("GTT base %lx, size %lu, entries %lu, stolen %lu\n", info.gtt_physical_base,
342 gttSize, info.gtt_entries, stolenSize);
343
344 AreaKeeper gttMapper;
345 info.gtt_area = gttMapper.Map("intel GMCH gtt",
346 (void *)info.gtt_physical_base, gttSize, B_ANY_KERNEL_ADDRESS,
347 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void **)&info.gtt_base);
348 if (gttMapper.InitCheck() < B_OK) {
349 dprintf("intel_gart: could not map GTT!\n");
350 return info.gtt_area;
351 }
352
353 info.aperture_physical_base = info.display.u.h0.base_registers[fbIndex];
354 info.aperture_stolen_size = stolenSize;
355 if (info.aperture_size == 0)
356 info.aperture_size = info.display.u.h0.base_register_sizes[fbIndex];
357
358 dprintf("intel_gart: detected %ld MB of stolen memory, aperture "
359 "size %ld MB, GTT size %ld KB\n", (stolenSize + (1023 << 10)) >> 20,
360 info.aperture_size >> 20, gttSize >> 10);
361
362 dprintf("intel_gart: GTT base = 0x%lx\n", info.gtt_physical_base);
363 dprintf("intel_gart: MMIO base = 0x%lx\n", info.display.u.h0.base_registers[mmioIndex]);
364 dprintf("intel_gart: GMR base = 0x%lx\n", info.aperture_physical_base);
365
366 AreaKeeper apertureMapper;
367 info.aperture_area = apertureMapper.Map("intel graphics aperture",
368 (void *)info.aperture_physical_base, info.aperture_size,
369 B_ANY_KERNEL_BLOCK_ADDRESS | B_MTR_WC,
370 B_READ_AREA | B_WRITE_AREA, (void **)&info.aperture_base);
371 if (apertureMapper.InitCheck() < B_OK) {
372 // try again without write combining
373 dprintf(DEVICE_NAME ": enabling write combined mode failed.\n");
374
375 info.aperture_area = apertureMapper.Map("intel graphics aperture",
376 (void *)info.aperture_physical_base, info.aperture_size,
377 B_ANY_KERNEL_BLOCK_ADDRESS, B_READ_AREA | B_WRITE_AREA,
378 (void **)&info.aperture_base);
379 }
380 if (apertureMapper.InitCheck() < B_OK) {
381 dprintf(DEVICE_NAME ": could not map graphics aperture!\n");
382 return info.aperture_area;
383 }
384
385 info.scratch_page = entry.address;
386
387 gttMapper.Detach();
388 mmioMapper.Detach();
389 scratchCreator.Detach();
390 apertureMapper.Detach();
391
392 return B_OK;
393}
394
395
396// #pragma mark - module interface
397
398
399status_t
400intel_create_aperture(uint8 bus, uint8 device, uint8 function, size_t size,
401 void **_aperture)
402{
403 // TODO: we currently only support a single AGP bridge!
404 if ((bus != sInfo.bridge.bus || device != sInfo.bridge.device
405 || function != sInfo.bridge.function)
406 && (bus != sInfo.display.bus || device != sInfo.display.device
407 || function != sInfo.display.function))
408 return B_BAD_VALUE;
409
410 sInfo.aperture_size = size;
411
412 if (intel_map(sInfo) < B_OK)
413 return B_ERROR;
414
415 uint16 gmchControl = get_pci_config(sInfo.bridge,
416 INTEL_GRAPHICS_MEMORY_CONTROL, 2) | MEMORY_CONTROL_ENABLED;
417 set_pci_config(sInfo.bridge, INTEL_GRAPHICS_MEMORY_CONTROL, 2, gmchControl);
418
419 write32(sInfo.registers + INTEL_PAGE_TABLE_CONTROL,
420 sInfo.gtt_physical_base | PAGE_TABLE_ENABLED);
421 read32(sInfo.registers + INTEL_PAGE_TABLE_CONTROL);
422
423 if (sInfo.scratch_page != 0) {
424 for (size_t i = sInfo.gtt_stolen_entries; i < sInfo.gtt_entries; i++) {
425 set_gtt_entry(sInfo, i << GTT_PAGE_SHIFT, sInfo.scratch_page);
426 }
427 read32(sInfo.gtt_base + sInfo.gtt_entries - 1);
428 }
429
430 asm("wbinvd;");
431
432 *_aperture = NULL;
433 return B_OK;
434}
435
436
437void
438intel_delete_aperture(void *aperture)
439{
440 intel_unmap(sInfo);
441}
442
443
444static status_t
445intel_get_aperture_info(void *aperture, aperture_info *info)
446{
447 if (info == NULL)
448 return B_BAD_VALUE;
449
450 info->base = sInfo.aperture_base;
451 info->physical_base = sInfo.aperture_physical_base;
452 info->size = sInfo.aperture_size;
453 info->reserved_size = sInfo.aperture_stolen_size;
454
455 return B_OK;
456}
457
458
459status_t
460intel_set_aperture_size(void *aperture, size_t size)
461{
462 return B_ERROR;
463}
464
465
466static status_t
467intel_bind_page(void *aperture, uint32 offset, phys_addr_t physicalAddress)
468{
469 //TRACE("bind_page(offset %lx, physical %lx)\n", offset, physicalAddress);
470
471 set_gtt_entry(sInfo, offset, physicalAddress);
472 return B_OK;
473}
474
475
476static status_t
477intel_unbind_page(void *aperture, uint32 offset)
478{
479 //TRACE("unbind_page(offset %lx)\n", offset);
480
481 if (sInfo.scratch_page != 0)
482 set_gtt_entry(sInfo, offset, sInfo.scratch_page);
483
484 return B_OK;
485}
486
487
488void
489intel_flush_tlbs(void *aperture)
490{
491 read32(sInfo.gtt_base + sInfo.gtt_entries - 1);
492 asm("wbinvd;");
493}
494
495
496// #pragma mark -
497
498
499static status_t
500intel_init()
501{
502 TRACE("bus manager init\n");
503
504 if (get_module(B_PCI_MODULE_NAME, (module_info **)&sPCI) != B_OK)
505 return B_ERROR;
506
507 bool found = false;
508
509 for (uint32 index = 0; sPCI->get_nth_pci_info(index, &sInfo.bridge) == B_OK;
510 index++) {
511 if (sInfo.bridge.vendor_id != VENDOR_ID_INTEL
512 || sInfo.bridge.class_base != PCI_bridge)
513 continue;
514
515 // check device
516 for (uint32 i = 0; i < sizeof(kSupportedDevices)
517 / sizeof(kSupportedDevices[0]); i++) {
518 if (sInfo.bridge.device_id == kSupportedDevices[i].bridge_id) {
519 sInfo.type = kSupportedDevices[i].type;
520 found = has_display_device(sInfo.display,
521 kSupportedDevices[i].display_id);
522 break;
523 }
524 }
525
526 if (found)
527 break;
528 }
529
530 if (!found)
531 return ENODEV;
532
533 TRACE("found intel bridge\n");
534 return B_OK;
535}
536
537
538static void
539intel_uninit()
540{
541}
542
543
544static int32
545intel_std_ops(int32 op, ...)
546{
547 switch (op) {
548 case B_MODULE_INIT:
549 return intel_init();
550 case B_MODULE_UNINIT:
551 intel_uninit();
552 return B_OK;
553 }
554
555 return B_BAD_VALUE;
556}
557
558
559static struct agp_gart_bus_module_info sIntelModuleInfo = {
560 {
561 "busses/agp_gart/intel/v0",
562 0,
563 intel_std_ops
564 },
565
566 intel_create_aperture,
567 intel_delete_aperture,
568
569 intel_get_aperture_info,
570 intel_set_aperture_size,
571 intel_bind_page,
572 intel_unbind_page,
573 intel_flush_tlbs
574};
575
576module_info *modules[] = {
577 (module_info *)&sIntelModuleInfo,
578 NULL
579};