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 119182884Srnoland bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map); 120182884Srnoland bus_dma_tag_destroy(dmah->tag); 121183833Srnoland free(dmah, DRM_MEM_DMA); 122190399Srnoland gart_info->dmah = NULL; 123182080Srnoland} 124182080Srnoland 125182884Srnolandint 126182884Srnolanddrm_ati_pcigart_cleanup(struct drm_device *dev, 127182884Srnoland struct drm_ati_pcigart_info *gart_info) 128182080Srnoland{ 129182080Srnoland /* we need to support large memory configurations */ 130152909Sanholt if (dev->sg == NULL) { 131182080Srnoland DRM_ERROR("no scatter/gather memory!\n"); 132152909Sanholt return 0; 133145132Sanholt } 134145132Sanholt 135182080Srnoland if (gart_info->bus_addr) { 136182080Srnoland if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { 137182080Srnoland gart_info->bus_addr = 0; 138190399Srnoland if (gart_info->dmah) 139182080Srnoland drm_ati_free_pcigart_table(dev, gart_info); 140182080Srnoland } 141182080Srnoland } 142182080Srnoland 143182080Srnoland return 1; 144182080Srnoland} 145182080Srnoland 146182884Srnolandint 147182884Srnolanddrm_ati_pcigart_init(struct drm_device *dev, 148182884Srnoland struct drm_ati_pcigart_info *gart_info) 149182080Srnoland{ 150182080Srnoland void *address = NULL; 151182080Srnoland unsigned long pages; 152182080Srnoland u32 *pci_gart, page_base; 153182080Srnoland dma_addr_t bus_address = 0; 154182884Srnoland dma_addr_t entry_addr; 155182080Srnoland int i, j, ret = 0; 156182080Srnoland int max_pages; 157182080Srnoland 158182080Srnoland /* we need to support large memory configurations */ 159182080Srnoland if (dev->sg == NULL) { 160182080Srnoland DRM_ERROR("no scatter/gather memory!\n"); 161182080Srnoland goto done; 162182080Srnoland } 163182080Srnoland 164152909Sanholt if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { 165182080Srnoland DRM_DEBUG("PCI: no table in VRAM: using normal RAM\n"); 166182080Srnoland 167182080Srnoland ret = drm_ati_alloc_pcigart_table(dev, gart_info); 168182080Srnoland if (ret) { 169182080Srnoland DRM_ERROR("cannot allocate PCI GART page!\n"); 170182080Srnoland goto done; 171152909Sanholt } 172182080Srnoland 173190399Srnoland address = (void *)gart_info->dmah->vaddr; 174190399Srnoland bus_address = gart_info->dmah->busaddr; 175152909Sanholt } else { 176182080Srnoland address = gart_info->addr; 177182080Srnoland bus_address = gart_info->bus_addr; 178182080Srnoland DRM_DEBUG("PCI: Gart Table: VRAM %08X mapped at %08lX\n", 179182080Srnoland (unsigned int)bus_address, (unsigned long)address); 180145132Sanholt } 181145132Sanholt 182182080Srnoland pci_gart = (u32 *) address; 183145132Sanholt 184182080Srnoland max_pages = (gart_info->table_size / sizeof(u32)); 185182080Srnoland pages = (dev->sg->pages <= max_pages) 186182080Srnoland ? dev->sg->pages : max_pages; 187182080Srnoland 188182080Srnoland memset(pci_gart, 0, max_pages * sizeof(u32)); 189182080Srnoland 190152909Sanholt KASSERT(PAGE_SIZE >= ATI_PCIGART_PAGE_SIZE, ("page size too small")); 191145132Sanholt 192182080Srnoland for (i = 0; i < pages; i++) { 193182080Srnoland entry_addr = dev->sg->busaddr[i]; 194145132Sanholt for (j = 0; j < (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); j++) { 195182080Srnoland page_base = (u32) entry_addr & ATI_PCIGART_PAGE_MASK; 196182080Srnoland switch(gart_info->gart_reg_if) { 197182080Srnoland case DRM_ATI_GART_IGP: 198182884Srnoland page_base |= 199182884Srnoland (upper_32_bits(entry_addr) & 0xff) << 4; 200207069Srnoland page_base |= ATI_GART_READ | ATI_GART_WRITE; 201207069Srnoland page_base |= ATI_GART_NOSNOOP; 202182080Srnoland break; 203182080Srnoland case DRM_ATI_GART_PCIE: 204182080Srnoland page_base >>= 8; 205182884Srnoland page_base |= 206182884Srnoland (upper_32_bits(entry_addr) & 0xff) << 24; 207207069Srnoland page_base |= ATI_GART_READ | ATI_GART_WRITE; 208207069Srnoland page_base |= ATI_GART_NOSNOOP; 209182080Srnoland break; 210182080Srnoland default: 211182080Srnoland case DRM_ATI_GART_PCI: 212182080Srnoland break; 213182080Srnoland } 214182080Srnoland *pci_gart = cpu_to_le32(page_base); 215148211Sanholt pci_gart++; 216182080Srnoland entry_addr += ATI_PCIGART_PAGE_SIZE; 217145132Sanholt } 218145132Sanholt } 219145132Sanholt 220182080Srnoland ret = 1; 221145132Sanholt 222182080Srnoland done: 223182080Srnoland gart_info->addr = address; 224182080Srnoland gart_info->bus_addr = bus_address; 225182080Srnoland return ret; 226145132Sanholt} 227