1145132Sanholt/*- 2145132Sanholt * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 3145132Sanholt * All Rights Reserved. 4145132Sanholt * 5145132Sanholt * Permission is hereby granted, free of charge, to any person obtaining a 6145132Sanholt * copy of this software and associated documentation files (the "Software"), 7145132Sanholt * to deal in the Software without restriction, including without limitation 8145132Sanholt * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9145132Sanholt * and/or sell copies of the Software, and to permit persons to whom the 10145132Sanholt * Software is furnished to do so, subject to the following conditions: 11145132Sanholt * 12145132Sanholt * The above copyright notice and this permission notice (including the next 13145132Sanholt * paragraph) shall be included in all copies or substantial portions of the 14145132Sanholt * Software. 15145132Sanholt * 16145132Sanholt * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17145132Sanholt * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18145132Sanholt * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19145132Sanholt * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20145132Sanholt * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21145132Sanholt * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22145132Sanholt * DEALINGS IN THE SOFTWARE. 23145132Sanholt * 24145132Sanholt * Authors: 25145132Sanholt * Gareth Hughes <gareth@valinux.com> 26145132Sanholt * 27145132Sanholt */ 28145132Sanholt 29152909Sanholt#include <sys/cdefs.h> 30152909Sanholt__FBSDID("$FreeBSD$"); 31152909Sanholt 32182080Srnoland/** @file ati_pcigart.c 33182080Srnoland * Implementation of ATI's PCIGART, which provides an aperture in card virtual 34182080Srnoland * address space with addresses remapped to system memory. 35182080Srnoland */ 36182080Srnoland 37145132Sanholt#include "dev/drm/drmP.h" 38145132Sanholt 39152909Sanholt#define ATI_PCIGART_PAGE_SIZE 4096 /* PCI GART page size */ 40182080Srnoland#define ATI_PCIGART_PAGE_MASK (~(ATI_PCIGART_PAGE_SIZE-1)) 41145132Sanholt 42207069Srnoland#define ATI_GART_NOSNOOP 0x1 43207069Srnoland#define ATI_GART_WRITE 0x4 44207069Srnoland#define ATI_GART_READ 0x8 45182080Srnoland 46182884Srnolandstatic void 47182884Srnolanddrm_ati_alloc_pcigart_table_cb(void *arg, bus_dma_segment_t *segs, 48182884Srnoland int nsegs, int error) 49145132Sanholt{ 50182884Srnoland struct drm_dma_handle *dmah = arg; 51182883Srnoland 52182884Srnoland if (error != 0) 53182884Srnoland return; 54182884Srnoland 55182884Srnoland KASSERT(nsegs == 1, 56182884Srnoland ("drm_ati_alloc_pcigart_table_cb: bad dma segment count")); 57182884Srnoland 58182884Srnoland dmah->busaddr = segs[0].ds_addr; 59182884Srnoland} 60182884Srnoland 61182884Srnolandstatic int 62182884Srnolanddrm_ati_alloc_pcigart_table(struct drm_device *dev, 63182884Srnoland struct drm_ati_pcigart_info *gart_info) 64182884Srnoland{ 65182884Srnoland struct drm_dma_handle *dmah; 66182884Srnoland int flags, ret; 67182884Srnoland 68183833Srnoland dmah = malloc(sizeof(struct drm_dma_handle), DRM_MEM_DMA, 69183833Srnoland M_ZERO | M_NOWAIT); 70182884Srnoland if (dmah == NULL) 71182884Srnoland return ENOMEM; 72182884Srnoland 73182883Srnoland DRM_UNLOCK(); 74182884Srnoland ret = bus_dma_tag_create(NULL, PAGE_SIZE, 0, /* tag, align, boundary */ 75182884Srnoland gart_info->table_mask, BUS_SPACE_MAXADDR, /* lowaddr, highaddr */ 76182884Srnoland NULL, NULL, /* filtfunc, filtfuncargs */ 77182884Srnoland gart_info->table_size, 1, /* maxsize, nsegs */ 78182884Srnoland gart_info->table_size, /* maxsegsize */ 79190123Srnoland 0, NULL, NULL, /* flags, lockfunc, lockfuncargs */ 80182884Srnoland &dmah->tag); 81182884Srnoland if (ret != 0) { 82183833Srnoland free(dmah, DRM_MEM_DMA); 83182884Srnoland return ENOMEM; 84182884Srnoland } 85182884Srnoland 86190282Srnoland flags = BUS_DMA_WAITOK | BUS_DMA_ZERO; 87190282Srnoland if (gart_info->gart_reg_if == DRM_ATI_GART_IGP) 88190282Srnoland flags |= BUS_DMA_NOCACHE; 89190282Srnoland 90190282Srnoland ret = bus_dmamem_alloc(dmah->tag, &dmah->vaddr, flags, &dmah->map); 91182884Srnoland if (ret != 0) { 92182884Srnoland bus_dma_tag_destroy(dmah->tag); 93183833Srnoland free(dmah, DRM_MEM_DMA); 94182884Srnoland return ENOMEM; 95182884Srnoland } 96182883Srnoland DRM_LOCK(); 97182884Srnoland 98182884Srnoland ret = bus_dmamap_load(dmah->tag, dmah->map, dmah->vaddr, 99190282Srnoland gart_info->table_size, drm_ati_alloc_pcigart_table_cb, dmah, 100190282Srnoland BUS_DMA_NOWAIT); 101182884Srnoland if (ret != 0) { 102182884Srnoland bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map); 103182884Srnoland bus_dma_tag_destroy(dmah->tag); 104183833Srnoland free(dmah, DRM_MEM_DMA); 105182080Srnoland return ENOMEM; 106182884Srnoland } 107145132Sanholt 108190399Srnoland gart_info->dmah = dmah; 109182883Srnoland 110182080Srnoland return 0; 111182080Srnoland} 112182080Srnoland 113182884Srnolandstatic void 114182884Srnolanddrm_ati_free_pcigart_table(struct drm_device *dev, 115182884Srnoland struct drm_ati_pcigart_info *gart_info) 116182080Srnoland{ 117190399Srnoland struct drm_dma_handle *dmah = gart_info->dmah; 118182884Srnoland 119267446Sjhb bus_dmamap_unload(dmah->tag, dmah->map); 120182884Srnoland bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map); 121182884Srnoland bus_dma_tag_destroy(dmah->tag); 122183833Srnoland free(dmah, DRM_MEM_DMA); 123190399Srnoland gart_info->dmah = NULL; 124182080Srnoland} 125182080Srnoland 126182884Srnolandint 127182884Srnolanddrm_ati_pcigart_cleanup(struct drm_device *dev, 128182884Srnoland struct drm_ati_pcigart_info *gart_info) 129182080Srnoland{ 130182080Srnoland /* we need to support large memory configurations */ 131152909Sanholt if (dev->sg == NULL) { 132182080Srnoland DRM_ERROR("no scatter/gather memory!\n"); 133152909Sanholt return 0; 134145132Sanholt } 135145132Sanholt 136182080Srnoland if (gart_info->bus_addr) { 137182080Srnoland if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { 138182080Srnoland gart_info->bus_addr = 0; 139190399Srnoland if (gart_info->dmah) 140182080Srnoland drm_ati_free_pcigart_table(dev, gart_info); 141182080Srnoland } 142182080Srnoland } 143182080Srnoland 144182080Srnoland return 1; 145182080Srnoland} 146182080Srnoland 147182884Srnolandint 148182884Srnolanddrm_ati_pcigart_init(struct drm_device *dev, 149182884Srnoland struct drm_ati_pcigart_info *gart_info) 150182080Srnoland{ 151182080Srnoland void *address = NULL; 152182080Srnoland unsigned long pages; 153182080Srnoland u32 *pci_gart, page_base; 154182080Srnoland dma_addr_t bus_address = 0; 155182884Srnoland dma_addr_t entry_addr; 156182080Srnoland int i, j, ret = 0; 157182080Srnoland int max_pages; 158182080Srnoland 159182080Srnoland /* we need to support large memory configurations */ 160182080Srnoland if (dev->sg == NULL) { 161182080Srnoland DRM_ERROR("no scatter/gather memory!\n"); 162182080Srnoland goto done; 163182080Srnoland } 164182080Srnoland 165152909Sanholt if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { 166182080Srnoland DRM_DEBUG("PCI: no table in VRAM: using normal RAM\n"); 167182080Srnoland 168182080Srnoland ret = drm_ati_alloc_pcigart_table(dev, gart_info); 169182080Srnoland if (ret) { 170182080Srnoland DRM_ERROR("cannot allocate PCI GART page!\n"); 171182080Srnoland goto done; 172152909Sanholt } 173182080Srnoland 174190399Srnoland address = (void *)gart_info->dmah->vaddr; 175190399Srnoland bus_address = gart_info->dmah->busaddr; 176152909Sanholt } else { 177182080Srnoland address = gart_info->addr; 178182080Srnoland bus_address = gart_info->bus_addr; 179182080Srnoland DRM_DEBUG("PCI: Gart Table: VRAM %08X mapped at %08lX\n", 180182080Srnoland (unsigned int)bus_address, (unsigned long)address); 181145132Sanholt } 182145132Sanholt 183182080Srnoland pci_gart = (u32 *) address; 184145132Sanholt 185182080Srnoland max_pages = (gart_info->table_size / sizeof(u32)); 186182080Srnoland pages = (dev->sg->pages <= max_pages) 187182080Srnoland ? dev->sg->pages : max_pages; 188182080Srnoland 189182080Srnoland memset(pci_gart, 0, max_pages * sizeof(u32)); 190182080Srnoland 191152909Sanholt KASSERT(PAGE_SIZE >= ATI_PCIGART_PAGE_SIZE, ("page size too small")); 192145132Sanholt 193182080Srnoland for (i = 0; i < pages; i++) { 194182080Srnoland entry_addr = dev->sg->busaddr[i]; 195145132Sanholt for (j = 0; j < (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); j++) { 196182080Srnoland page_base = (u32) entry_addr & ATI_PCIGART_PAGE_MASK; 197182080Srnoland switch(gart_info->gart_reg_if) { 198182080Srnoland case DRM_ATI_GART_IGP: 199182884Srnoland page_base |= 200182884Srnoland (upper_32_bits(entry_addr) & 0xff) << 4; 201207069Srnoland page_base |= ATI_GART_READ | ATI_GART_WRITE; 202207069Srnoland page_base |= ATI_GART_NOSNOOP; 203182080Srnoland break; 204182080Srnoland case DRM_ATI_GART_PCIE: 205182080Srnoland page_base >>= 8; 206182884Srnoland page_base |= 207182884Srnoland (upper_32_bits(entry_addr) & 0xff) << 24; 208207069Srnoland page_base |= ATI_GART_READ | ATI_GART_WRITE; 209207069Srnoland page_base |= ATI_GART_NOSNOOP; 210182080Srnoland break; 211182080Srnoland default: 212182080Srnoland case DRM_ATI_GART_PCI: 213182080Srnoland break; 214182080Srnoland } 215182080Srnoland *pci_gart = cpu_to_le32(page_base); 216148211Sanholt pci_gart++; 217182080Srnoland entry_addr += ATI_PCIGART_PAGE_SIZE; 218145132Sanholt } 219145132Sanholt } 220145132Sanholt 221182080Srnoland ret = 1; 222145132Sanholt 223182080Srnoland done: 224182080Srnoland gart_info->addr = address; 225182080Srnoland gart_info->bus_addr = bus_address; 226182080Srnoland return ret; 227145132Sanholt} 228