Hi,
A long time ago I was working on an AGPGART driver for my PPC machine,
but couldn't get it working due to missing datasheet, etc. Now I started
working on it again, as Radeon KMS works well on my machine so far and I
also rediscovered an old binary-only AGPGART driver for which objdump
revealed some interesting information.
Well, the current driver code is basically a copy of the UniNorth AGPGART
driver. The driver initializes fine, as the excerpt below shows:
calling agp_init+0x0/0x54 @ 1
Linux agpgart interface v0.103
initcall agp_init+0x0/0x54 returned 0 after 2749 usecs
calling agp_articias_init+0x0/0x58 @ 1
agpgart-articias 0000:00:00.0: MAI Logic Articia S chipset
agpgart-articias 0000:00:00.0: configuring for size idx: 1
agpgart-articias 0000:00:00.0: AGP aperture is 4M @ 0xc0000000
initcall agp_articias_init+0x0/0x58 returned 0 after 19494 usecs
calling drm_core_init+0x0/0x158 @ 1
[drm] Initialized drm 1.1.0 20060810
initcall drm_core_init+0x0/0x158 returned 0 after 4526 usecs
calling ttm_init+0x0/0x8c @ 1
initcall ttm_init+0x0/0x8c returned 0 after 1170 usecs
calling radeon_init+0x0/0x100 @ 1
[drm] radeon defaulting to kernel modesetting.
[drm] radeon kernel modesetting enabled.
[drm] radeon: Initializing kernel modesetting.
[drm] register mmio base: 0x88000000
[drm] register mmio size: 65536
[drm] GPU reset succeed (RBBM_STATUS=0x00000140)
[drm] Generation 2 PCI interface, using max accessible memory
[drm] AGP mode requested: 1
agpgart-articias 0000:00:00.0: AGP 1.0 bridge
agpgart-articias 0000:00:00.0: putting AGP V2 device into 1x mode
radeon 0000:01:00.0: putting AGP V2 device into 1x mode
[drm] radeon: VRAM 128M
[drm] radeon: VRAM from 0x00000000 to 0x07FFFFFF
[drm] radeon: GTT 4M
[drm] radeon: GTT from 0xC0000000 to 0xC03FFFFF
[drm] radeon: irq initialized.
[drm] Detected VRAM RAM=128M, BAR=128M
[drm] RAM width 128bits DDR
[TTM] Zone kernel: Available graphics memory: 377416 kiB.
[TTM] Zone highmem: Available graphics memory: 770632 kiB.
[drm] radeon: 128M of VRAM memory ready
[drm] radeon: 4M of GTT memory ready.
[drm] radeon: cp idle (0x02000603)
[drm] Loading R200 Microcode
platform radeon_cp.0: firmware: using built-in firmware radeon/R200_cp.bin
agpgart-articias 0000:00:00.0: TLB flush!
[drm] radeon: ring at 0x00000000C0000000
[drm] ring test succeeded in 0 usecs
agpgart-articias 0000:00:00.0: TLB flush!
agpgart-articias 0000:00:00.0: TLB flush!
[drm] radeon: ib pool ready.
[drm] ib test succeeded in 0 usecs
[drm] DFP table revision: 3
[drm] Radeon Display Connectors
[drm] Connector 0:
[drm] VGA
[drm] DDC: 0x60 0x60 0x60 0x60 0x60 0x60 0x60 0x60
[drm] Encoders:
[drm] CRT1: INTERNAL_DAC1
[drm] Connector 1:
[drm] DVI-I
[drm] DDC: 0x64 0x64 0x64 0x64 0x64 0x64 0x64 0x64
[drm] Encoders:
[drm] CRT2: INTERNAL_DAC2
[drm] DFP1: INTERNAL_TMDS1
[drm] fb mappable at 0x80040000
[drm] vram apper at 0x80000000
[drm] size 7680000
[drm] fb depth is 24
[drm] pitch is 6400
[drm] TMDS-12: set mode 1600x1200 24
Console: switching to colour frame buffer device 200x75
fb0: radeondrmfb frame buffer device
registered panic notifier
[drm] Initialized radeon 2.0.0 20080528 for 0000:01:00.0 on minor 0
initcall radeon_init+0x0/0x100 returned 0 after 635215 usecs
Judging from what the log says, I would expect that the radeon driver
can make use of the AGP aperture (as the the ring and ib test succeed -
is this assumption correct?).
Next I booted the kernel with radeon.test=1 and this test fails
immediately with a message like this:
[drm:radeon_test_moves] *ERROR* Incorrect GTT->VRAM copy 0: Got 0xf14a88f0,
expected 0xf14a6680 (GTT map 0xf14a6000-0xf15a6000)
A different aperture size doesn't make any difference. However the test
works in PCIGART mode.
Now I wonder what the problem could be, as I don't have a clue about
the radeon/DRM code. Can somebody explain me, how the test works and
what the error message means for the AGPGART driver?
Thanks!
best regards,
Gerhard
PS: Please put me on CC:.
articias-agp.c:
/*
* Articia S AGPGART routines.
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/pagemap.h>
#include <linux/vmalloc.h>
#include <linux/agp_backend.h>
#include "agp.h"
#define ARTICIAS_GARTCTRL 0x58 /* bit 6 -> GART enable */
#define ARTICIAS_APSIZE 0x59 /* bit 2:0 -> aperture size */
#define ARTICIAS_TLBCTRL 0xe0 /* bit 5 -> TLB flush */
#define ARTICIAS_GATTBASE 0x58 /* 31:12 GATT base address */
static int articias_fetch_size(void)
{
int i;
u8 temp;
struct aper_size_info_8 *values;
values = A_SIZE_8(agp_bridge->driver->aperture_sizes);
pci_read_config_byte(agp_bridge->dev, ARTICIAS_APSIZE, &temp);
temp &= 0x07;
for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) {
if (temp == values[i].size_value) {
agp_bridge->previous_size =
agp_bridge->current_size = (void *) (values +
i);
agp_bridge->aperture_size_idx = i;
return values[i].size;
}
}
dev_err(&agp_bridge->dev->dev, "unknown aperture size from AGP bridge
(0x%x)\n",
temp);
return 0;
}
static void articias_tlbflush(struct agp_memory *mem)
{
/* Reverse engineered from binary-only driver. */
/* Does this really work? Register seems to be read-only? */
pci_write_config_byte(agp_bridge->dev, ARTICIAS_TLBCTRL, 0x20);
pci_write_config_byte(agp_bridge->dev, ARTICIAS_TLBCTRL, 0);
dev_info(&agp_bridge->dev->dev, "TLB flush!\n");
}
static void articias_cleanup(void)
{
u8 temp;
struct aper_size_info_8 *previous_size;
/* Disable GART transfer control. */
pci_read_config_byte(agp_bridge->dev, ARTICIAS_GARTCTRL, &temp);
pci_write_config_byte(agp_bridge->dev, ARTICIAS_GARTCTRL, temp & ~0x40);
/* Write back previous aperture size. */
previous_size = A_SIZE_8(agp_bridge->previous_size);
pci_write_config_byte(agp_bridge->dev, ARTICIAS_APSIZE,
previous_size->size_value);
}
static int articias_configure(void)
{
u32 temp;
struct aper_size_info_8 *current_size;
current_size = A_SIZE_8(agp_bridge->current_size);
dev_info(&agp_bridge->dev->dev, "configuring for size idx: %d\n",
current_size->size_value);
/* Setup aperture size and GATT base address. */
pci_read_config_dword(agp_bridge->dev, ARTICIAS_GATTBASE, &temp);
temp = (temp & 0x000000ff) | ((u32)current_size->size_value)<<8 |
(agp_bridge->gatt_bus_addr & 0xfffff000);
pci_write_config_dword(agp_bridge->dev, ARTICIAS_GATTBASE, temp);
/* Aperture address to map too. */
pci_read_config_dword(agp_bridge->dev, AGP_APBASE, &temp);
agp_bridge->gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
/* Enable GART transfer control. */
pci_read_config_dword(agp_bridge->dev, ARTICIAS_GARTCTRL, &temp);
pci_write_config_dword(agp_bridge->dev, ARTICIAS_GARTCTRL, temp | 0x40);
return 0;
}
static void articias_agp_enable(struct agp_bridge_data *bridge, u32
requested_mode)
{
u32 bridge_agpstat;
dev_info(&agp_bridge->dev->dev, "AGP %d.%d bridge\n",
agp_bridge->major_version, agp_bridge->minor_version);
pci_read_config_dword(agp_bridge->dev, agp_bridge->capndx +
PCI_AGP_STATUS,
&bridge_agpstat);
/* AGP 4X status bit is lying. */
bridge_agpstat &= ~AGPSTAT2_4X;
bridge_agpstat = agp_collect_device_status(agp_bridge, requested_mode,
bridge_agpstat);
bridge_agpstat |= AGPSTAT_AGP_ENABLE;
agp_device_command(bridge_agpstat, false);
}
static int articias_insert_memory(struct agp_memory *mem, off_t pg_start, int
type)
{
int i, num_entries, mask_type;
void *temp;
u32 *gp;
if (type != mem->type)
return -EINVAL;
mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type);
if (mask_type != 0) {
/* We know nothing of memory types */
return -EINVAL;
}
if (mem->page_count == 0)
return 0;
temp = agp_bridge->current_size;
num_entries = A_SIZE_8(temp)->num_entries;
if ((pg_start + mem->page_count) > num_entries)
return -EINVAL;
gp = (u32 *)&agp_bridge->gatt_table[pg_start];
for (i = 0; i < mem->page_count; ++i) {
if (gp[i]) {
dev_info(&agp_bridge->dev->dev,
"articias_insert_memory: entry 0x%x occupied
(%x)\n",
i, gp[i]);
return -EBUSY;
}
}
for (i = 0; i < mem->page_count; i++) {
gp[i] = cpu_to_le32(page_to_phys(mem->pages[i]) & 0xfffff000UL);
flush_dcache_range((unsigned
long)__va(page_to_phys(mem->pages[i])),
(unsigned
long)__va(page_to_phys(mem->pages[i]))+0x1000);
}
mb();
articias_tlbflush(mem);
return 0;
}
int articias_remove_memory(struct agp_memory *mem, off_t pg_start, int type)
{
size_t i;
u32 *gp;
int mask_type;
if (type != mem->type)
return -EINVAL;
mask_type = agp_bridge->driver->agp_type_to_mask_type(agp_bridge, type);
if (mask_type != 0) {
/* We know nothing of memory types */
return -EINVAL;
}
if (mem->page_count == 0)
return 0;
gp = (u32 *)&agp_bridge->gatt_table[pg_start];
for (i = 0; i < mem->page_count; ++i)
gp[i] = 0;
mb();
articias_tlbflush(mem);
return 0;
}
static int articias_create_gatt_table(struct agp_bridge_data *bridge)
{
char *table;
char *table_end;
int size;
int page_order;
int num_entries;
int i;
void *temp;
struct page *page;
struct page **pages;
/* We can't handle 2 level gatt's */
if (bridge->driver->size_type == LVL2_APER_SIZE)
return -EINVAL;
table = NULL;
i = bridge->aperture_size_idx;
temp = bridge->current_size;
size = page_order = num_entries = 0;
do {
size = A_SIZE_8(temp)->size;
page_order = A_SIZE_8(temp)->page_order;
num_entries = A_SIZE_8(temp)->num_entries;
table = (char *) __get_free_pages(GFP_KERNEL, page_order);
if (table == NULL) {
i++;
bridge->current_size = A_IDX8(bridge);
} else {
bridge->aperture_size_idx = i;
}
} while (!table && (i < bridge->driver->num_aperture_sizes));
if (table == NULL)
return -ENOMEM;
pages = kmalloc((1 << page_order) * sizeof(struct page*), GFP_KERNEL);
if (pages == NULL)
goto enomem;
table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
for (page = virt_to_page(table), i = 0; page <= virt_to_page(table_end);
page++, i++) {
SetPageReserved(page);
pages[i] = page;
}
bridge->gatt_table_real = (u32 *)table;
/* Need to clear out any dirty data still sitting in caches. */
flush_dcache_range((unsigned long)table,
(unsigned long)(table_end + PAGE_SIZE));
bridge->gatt_table = vmap(pages, (1 << page_order), 0, PAGE_KERNEL_NCG);
if (bridge->gatt_table == NULL)
goto enomem;
bridge->gatt_bus_addr = virt_to_phys(table);
for (i = 0; i < num_entries; i++)
bridge->gatt_table[i] = 0;
return 0;
enomem:
kfree(pages);
if (table)
free_pages((unsigned long)table, page_order);
return -ENOMEM;
}
static int articias_free_gatt_table(struct agp_bridge_data *bridge)
{
int page_order;
char *table, *table_end;
void *temp;
struct page *page;
temp = bridge->current_size;
page_order = A_SIZE_8(temp)->page_order;
/*
* Do not worry about freeing memory, because if this is called,
* then all agp memory is deallocated and removed from the table.
*/
vunmap(bridge->gatt_table);
table = (char *)bridge->gatt_table_real;
table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1);
for (page = virt_to_page(table); page <= virt_to_page(table_end);
page++)
ClearPageReserved(page);
free_pages((unsigned long)bridge->gatt_table_real, page_order);
return 0;
}
void articias_null_cache_flush(void)
{
mb();
}
static const struct aper_size_info_8 articias_sizes[] =
{
/* size, num_entries, page_order, size_value */
{256, 65536, 6, 7},
{128, 32768, 5, 6},
{64, 16384, 4, 5},
{32, 8192, 3, 4},
{16, 4096, 2, 3},
{8, 2048, 1, 2},
{4, 1024, 1, 1}
};
const struct agp_bridge_driver articias_agp_driver = {
.owner = THIS_MODULE,
.aperture_sizes = (void *)articias_sizes,
.size_type = U8_APER_SIZE,
.num_aperture_sizes = ARRAY_SIZE(articias_sizes),
.configure = articias_configure,
.fetch_size = articias_fetch_size,
.cleanup = articias_cleanup,
.tlb_flush = articias_tlbflush,
.mask_memory = agp_generic_mask_memory,
.masks = NULL,
.cache_flush = articias_null_cache_flush,
.agp_enable = articias_agp_enable,
/* .agp_enable = agp_generic_enable, */
.create_gatt_table = articias_create_gatt_table,
.free_gatt_table = articias_free_gatt_table,
.insert_memory = articias_insert_memory,
.remove_memory = articias_remove_memory,
.alloc_by_type = agp_generic_alloc_by_type,
.free_by_type = agp_generic_free_by_type,
.agp_alloc_page = agp_generic_alloc_page,
.agp_alloc_pages = agp_generic_alloc_pages,
.agp_destroy_page = agp_generic_destroy_page,
.agp_destroy_pages = agp_generic_destroy_pages,
.agp_type_to_mask_type = agp_generic_type_to_mask_type,
.cant_use_aperture = true,
};
static struct agp_device_ids articias_agp_device_ids[] __devinitdata = {
{
.device_id = PCI_DEVICE_ID_MAI_ARTICIAS,
.chipset_name = "Articia S",
},
{ }
};
static int __devinit agp_articias_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct agp_device_ids *devs = articias_agp_device_ids;
struct agp_bridge_data *bridge;
u8 cap_ptr;
int j;
cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
if (cap_ptr == 0)
return -ENODEV;
/* Probe for known chipsets. */
for (j = 0; devs[j].chipset_name != NULL; ++j) {
if (pdev->device == devs[j].device_id) {
dev_info(&pdev->dev, "MAI Logic %s chipset\n",
devs[j].chipset_name);
goto found;
}
}
dev_err(&pdev->dev, "unsupported MAI Logic chipset [%04x/%04x]\n",
pdev->vendor, pdev->device);
return -ENODEV;
found:
bridge = agp_alloc_bridge();
if (!bridge)
return -ENOMEM;
bridge->dev = pdev;
bridge->capndx = cap_ptr;
bridge->driver = &articias_agp_driver;
/* AGP 2X isn't reliable and the AGP 4X status bit is wrong. */
// bridge->flags = AGP_ERRATA_1X;
/* Get bridge revision. */
get_agp_version(bridge);
/* Fill in the mode register. */
pci_read_config_dword(pdev, bridge->capndx + PCI_AGP_STATUS,
&bridge->mode);
pci_set_drvdata(pdev, bridge);
return agp_add_bridge(bridge);
}
static void __devexit agp_articias_remove(struct pci_dev *pdev)
{
struct agp_bridge_data *bridge = pci_get_drvdata(pdev);
agp_remove_bridge(bridge);
agp_put_bridge(bridge);
}
static struct pci_device_id agp_articias_pci_table[] = {
{
.class = (PCI_CLASS_BRIDGE_HOST << 8),
.class_mask = ~0,
.vendor = PCI_VENDOR_ID_MAI,
.device = PCI_DEVICE_ID_MAI_ARTICIAS,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ }
};
MODULE_DEVICE_TABLE(pci, agp_articias_pci_table);
static struct pci_driver agp_articias_pci_driver = {
.name = "agpgart-articias",
.id_table = agp_articias_pci_table,
.probe = agp_articias_probe,
.remove = agp_articias_remove,
};
static int __init agp_articias_init(void)
{
if (agp_off)
return -EINVAL;
return pci_register_driver(&agp_articias_pci_driver);
}
static void __exit agp_articias_cleanup(void)
{
pci_unregister_driver(&agp_articias_pci_driver);
}
module_init(agp_articias_init);
module_exit(agp_articias_cleanup);
MODULE_AUTHOR("Gerhard Pircher <[email protected]");
MODULE_LICENSE("GPL");
--
Jetzt kostenlos herunterladen: Internet Explorer 8 und Mozilla Firefox 3.5 -
sicherer, schneller und einfacher! http://portal.gmx.net/de/go/atbrowser
------------------------------------------------------------------------------
Return on Information:
Google Enterprise Search pays you back
Get the facts.
http://p.sf.net/sfu/google-dev2dev
--
_______________________________________________
Dri-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/dri-devel