1 /* $NetBSD: ati_pcigart.c,v 1.2 2021/12/18 23:45:42 riastradh Exp $ */ 2 3 /** 4 * \file ati_pcigart.c 5 * ATI PCI GART support 6 * 7 * \author Gareth Hughes <gareth (at) valinux.com> 8 */ 9 10 /* 11 * Created: Wed Dec 13 21:52:19 2000 by gareth (at) valinux.com 12 * 13 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. 14 * All Rights Reserved. 15 * 16 * Permission is hereby granted, free of charge, to any person obtaining a 17 * copy of this software and associated documentation files (the "Software"), 18 * to deal in the Software without restriction, including without limitation 19 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 20 * and/or sell copies of the Software, and to permit persons to whom the 21 * Software is furnished to do so, subject to the following conditions: 22 * 23 * The above copyright notice and this permission notice (including the next 24 * paragraph) shall be included in all copies or substantial portions of the 25 * Software. 26 * 27 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 30 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 31 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 32 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 33 * DEALINGS IN THE SOFTWARE. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: ati_pcigart.c,v 1.2 2021/12/18 23:45:42 riastradh Exp $"); 38 39 #include <linux/export.h> 40 41 #include <drm/drm_device.h> 42 #include <drm/drm_pci.h> 43 #include <drm/drm_print.h> 44 45 #include "ati_pcigart.h" 46 47 # define ATI_PCIGART_PAGE_SIZE 4096 /**< PCI GART page size */ 48 49 static int drm_ati_alloc_pcigart_table(struct drm_device *dev, 50 struct drm_ati_pcigart_info *gart_info) 51 { 52 gart_info->table_handle = drm_pci_alloc(dev, gart_info->table_size, 53 PAGE_SIZE); 54 if (gart_info->table_handle == NULL) 55 return -ENOMEM; 56 57 return 0; 58 } 59 60 static void drm_ati_free_pcigart_table(struct drm_device *dev, 61 struct drm_ati_pcigart_info *gart_info) 62 { 63 drm_pci_free(dev, gart_info->table_handle); 64 gart_info->table_handle = NULL; 65 } 66 67 int drm_ati_pcigart_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info) 68 { 69 struct drm_sg_mem *entry = dev->sg; 70 unsigned long pages; 71 int i; 72 int max_pages; 73 74 /* we need to support large memory configurations */ 75 if (!entry) { 76 DRM_ERROR("no scatter/gather memory!\n"); 77 return 0; 78 } 79 80 if (gart_info->bus_addr) { 81 82 max_pages = (gart_info->table_size / sizeof(u32)); 83 pages = (entry->pages <= max_pages) 84 ? entry->pages : max_pages; 85 86 for (i = 0; i < pages; i++) { 87 if (!entry->busaddr[i]) 88 break; 89 pci_unmap_page(dev->pdev, entry->busaddr[i], 90 PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); 91 } 92 93 if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) 94 gart_info->bus_addr = 0; 95 } 96 97 if (gart_info->gart_table_location == DRM_ATI_GART_MAIN && 98 gart_info->table_handle) { 99 drm_ati_free_pcigart_table(dev, gart_info); 100 } 101 102 return 1; 103 } 104 105 int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info) 106 { 107 struct drm_local_map *map = &gart_info->mapping; 108 struct drm_sg_mem *entry = dev->sg; 109 void *address = NULL; 110 unsigned long pages; 111 u32 *pci_gart = NULL, page_base, gart_idx; 112 dma_addr_t bus_address = 0; 113 int i, j, ret = -ENOMEM; 114 int max_ati_pages, max_real_pages; 115 116 if (!entry) { 117 DRM_ERROR("no scatter/gather memory!\n"); 118 goto done; 119 } 120 121 if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { 122 DRM_DEBUG("PCI: no table in VRAM: using normal RAM\n"); 123 124 if (pci_set_dma_mask(dev->pdev, gart_info->table_mask)) { 125 DRM_ERROR("fail to set dma mask to 0x%Lx\n", 126 (unsigned long long)gart_info->table_mask); 127 ret = -EFAULT; 128 goto done; 129 } 130 131 ret = drm_ati_alloc_pcigart_table(dev, gart_info); 132 if (ret) { 133 DRM_ERROR("cannot allocate PCI GART page!\n"); 134 goto done; 135 } 136 137 pci_gart = gart_info->table_handle->vaddr; 138 address = gart_info->table_handle->vaddr; 139 bus_address = gart_info->table_handle->busaddr; 140 } else { 141 address = gart_info->addr; 142 bus_address = gart_info->bus_addr; 143 DRM_DEBUG("PCI: Gart Table: VRAM %08LX mapped at %08lX\n", 144 (unsigned long long)bus_address, 145 (unsigned long)address); 146 } 147 148 149 max_ati_pages = (gart_info->table_size / sizeof(u32)); 150 max_real_pages = max_ati_pages / (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); 151 pages = (entry->pages <= max_real_pages) 152 ? entry->pages : max_real_pages; 153 154 if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) { 155 memset(pci_gart, 0, max_ati_pages * sizeof(u32)); 156 } else { 157 memset_io((void __iomem *)map->handle, 0, max_ati_pages * sizeof(u32)); 158 } 159 160 gart_idx = 0; 161 for (i = 0; i < pages; i++) { 162 /* we need to support large memory configurations */ 163 entry->busaddr[i] = pci_map_page(dev->pdev, entry->pagelist[i], 164 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); 165 if (pci_dma_mapping_error(dev->pdev, entry->busaddr[i])) { 166 DRM_ERROR("unable to map PCIGART pages!\n"); 167 drm_ati_pcigart_cleanup(dev, gart_info); 168 address = NULL; 169 bus_address = 0; 170 ret = -ENOMEM; 171 goto done; 172 } 173 page_base = (u32) entry->busaddr[i]; 174 175 for (j = 0; j < (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); j++) { 176 u32 offset; 177 u32 val; 178 179 switch(gart_info->gart_reg_if) { 180 case DRM_ATI_GART_IGP: 181 val = page_base | 0xc; 182 break; 183 case DRM_ATI_GART_PCIE: 184 val = (page_base >> 8) | 0xc; 185 break; 186 default: 187 case DRM_ATI_GART_PCI: 188 val = page_base; 189 break; 190 } 191 if (gart_info->gart_table_location == 192 DRM_ATI_GART_MAIN) { 193 pci_gart[gart_idx] = cpu_to_le32(val); 194 } else { 195 offset = gart_idx * sizeof(u32); 196 writel(val, (void __iomem *)map->handle + offset); 197 } 198 gart_idx++; 199 page_base += ATI_PCIGART_PAGE_SIZE; 200 } 201 } 202 ret = 0; 203 204 #if defined(__i386__) || defined(__x86_64__) 205 wbinvd(); 206 #else 207 mb(); 208 #endif 209 210 done: 211 gart_info->addr = address; 212 gart_info->bus_addr = bus_address; 213 return ret; 214 } 215