1/*- 2 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 3 * All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice (including the next 13 * paragraph) shall be included in all copies or substantial portions of the 14 * Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22 * DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: 25 * Gareth Hughes <gareth@valinux.com> 26 * 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD$"); 31 32/** @file ati_pcigart.c 33 * Implementation of ATI's PCIGART, which provides an aperture in card virtual 34 * address space with addresses remapped to system memory. 35 */ 36 37#include "dev/drm/drmP.h" 38 39#define ATI_PCIGART_PAGE_SIZE 4096 /* PCI GART page size */ 40#define ATI_PCIGART_PAGE_MASK (~(ATI_PCIGART_PAGE_SIZE-1)) 41 42#define ATI_GART_NOSNOOP 0x1 43#define ATI_GART_WRITE 0x4 44#define ATI_GART_READ 0x8 45 46static void 47drm_ati_alloc_pcigart_table_cb(void *arg, bus_dma_segment_t *segs, 48 int nsegs, int error) 49{ 50 struct drm_dma_handle *dmah = arg; 51 52 if (error != 0) 53 return; 54 55 KASSERT(nsegs == 1, 56 ("drm_ati_alloc_pcigart_table_cb: bad dma segment count")); 57 58 dmah->busaddr = segs[0].ds_addr; 59} 60 61static int 62drm_ati_alloc_pcigart_table(struct drm_device *dev, 63 struct drm_ati_pcigart_info *gart_info) 64{ 65 struct drm_dma_handle *dmah; 66 int flags, ret; 67 68 dmah = malloc(sizeof(struct drm_dma_handle), DRM_MEM_DMA, 69 M_ZERO | M_NOWAIT); 70 if (dmah == NULL) 71 return ENOMEM; 72 73 DRM_UNLOCK(); 74 ret = bus_dma_tag_create(NULL, PAGE_SIZE, 0, /* tag, align, boundary */ 75 gart_info->table_mask, BUS_SPACE_MAXADDR, /* lowaddr, highaddr */ 76 NULL, NULL, /* filtfunc, filtfuncargs */ 77 gart_info->table_size, 1, /* maxsize, nsegs */ 78 gart_info->table_size, /* maxsegsize */ 79 0, NULL, NULL, /* flags, lockfunc, lockfuncargs */ 80 &dmah->tag); 81 if (ret != 0) { 82 free(dmah, DRM_MEM_DMA); 83 return ENOMEM; 84 } 85 86 flags = BUS_DMA_WAITOK | BUS_DMA_ZERO; 87 if (gart_info->gart_reg_if == DRM_ATI_GART_IGP) 88 flags |= BUS_DMA_NOCACHE; 89 90 ret = bus_dmamem_alloc(dmah->tag, &dmah->vaddr, flags, &dmah->map); 91 if (ret != 0) { 92 bus_dma_tag_destroy(dmah->tag); 93 free(dmah, DRM_MEM_DMA); 94 return ENOMEM; 95 } 96 DRM_LOCK(); 97 98 ret = bus_dmamap_load(dmah->tag, dmah->map, dmah->vaddr, 99 gart_info->table_size, drm_ati_alloc_pcigart_table_cb, dmah, 100 BUS_DMA_NOWAIT); 101 if (ret != 0) { 102 bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map); 103 bus_dma_tag_destroy(dmah->tag); 104 free(dmah, DRM_MEM_DMA); 105 return ENOMEM; 106 } 107 108 gart_info->dmah = dmah; 109 110 return 0; 111} 112 113static void 114drm_ati_free_pcigart_table(struct drm_device *dev, 115 struct drm_ati_pcigart_info *gart_info) 116{ 117 struct drm_dma_handle *dmah = gart_info->dmah; 118 119 bus_dmamap_unload(dmah->tag, dmah->map); 120 bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map); 121 bus_dma_tag_destroy(dmah->tag); 122 free(dmah, DRM_MEM_DMA); 123 gart_info->dmah = NULL; 124} 125 126int 127drm_ati_pcigart_cleanup(struct drm_device *dev, 128 struct drm_ati_pcigart_info *gart_info) 129{ 130 /* we need to support large memory configurations */ 131 if (dev->sg == NULL) { 132 DRM_ERROR("no scatter/gather memory!\n"); 133 return 0; 134 } 135 136 if (gart_info->bus_addr) { 137 if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { 138 gart_info->bus_addr = 0; 139 if (gart_info->dmah) 140 drm_ati_free_pcigart_table(dev, gart_info); 141 } 142 } 143 144 return 1; 145} 146 147int 148drm_ati_pcigart_init(struct drm_device *dev, 149 struct drm_ati_pcigart_info *gart_info) 150{ 151 void *address = NULL; 152 unsigned long pages; 153 u32 *pci_gart, page_base; 154 dma_addr_t bus_address = 0; 155 dma_addr_t entry_addr; 156 int i, j, ret = 0; 157 int max_pages; 158 159 /* we need to support large memory configurations */ 160 if (dev->sg == NULL) { 161 DRM_ERROR("no scatter/gather memory!\n"); 162 goto done; 163 } 164 165 if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { 166 DRM_DEBUG("PCI: no table in VRAM: using normal RAM\n"); 167 168 ret = drm_ati_alloc_pcigart_table(dev, gart_info); 169 if (ret) { 170 DRM_ERROR("cannot allocate PCI GART page!\n"); 171 goto done; 172 } 173 174 address = (void *)gart_info->dmah->vaddr; 175 bus_address = gart_info->dmah->busaddr; 176 } else { 177 address = gart_info->addr; 178 bus_address = gart_info->bus_addr; 179 DRM_DEBUG("PCI: Gart Table: VRAM %08X mapped at %08lX\n", 180 (unsigned int)bus_address, (unsigned long)address); 181 } 182 183 pci_gart = (u32 *) address; 184 185 max_pages = (gart_info->table_size / sizeof(u32)); 186 pages = (dev->sg->pages <= max_pages) 187 ? dev->sg->pages : max_pages; 188 189 memset(pci_gart, 0, max_pages * sizeof(u32)); 190 191 KASSERT(PAGE_SIZE >= ATI_PCIGART_PAGE_SIZE, ("page size too small")); 192 193 for (i = 0; i < pages; i++) { 194 entry_addr = dev->sg->busaddr[i]; 195 for (j = 0; j < (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); j++) { 196 page_base = (u32) entry_addr & ATI_PCIGART_PAGE_MASK; 197 switch(gart_info->gart_reg_if) { 198 case DRM_ATI_GART_IGP: 199 page_base |= 200 (upper_32_bits(entry_addr) & 0xff) << 4; 201 page_base |= ATI_GART_READ | ATI_GART_WRITE; 202 page_base |= ATI_GART_NOSNOOP; 203 break; 204 case DRM_ATI_GART_PCIE: 205 page_base >>= 8; 206 page_base |= 207 (upper_32_bits(entry_addr) & 0xff) << 24; 208 page_base |= ATI_GART_READ | ATI_GART_WRITE; 209 page_base |= ATI_GART_NOSNOOP; 210 break; 211 default: 212 case DRM_ATI_GART_PCI: 213 break; 214 } 215 *pci_gart = cpu_to_le32(page_base); 216 pci_gart++; 217 entry_addr += ATI_PCIGART_PAGE_SIZE; 218 } 219 } 220 221 ret = 1; 222 223 done: 224 gart_info->addr = address; 225 gart_info->bus_addr = bus_address; 226 return ret; 227} 228