at v2.6.14 197 lines 5.7 kB view raw
1/** 2 * \file drm_memory.h 3 * Memory management wrappers for DRM 4 * 5 * \author Rickard E. (Rik) Faith <faith@valinux.com> 6 * \author Gareth Hughes <gareth@valinux.com> 7 */ 8 9/* 10 * Created: Thu Feb 4 14:00:34 1999 by faith@valinux.com 11 * 12 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. 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 * VA LINUX SYSTEMS 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 33 * OTHER DEALINGS IN THE SOFTWARE. 34 */ 35 36#include <linux/config.h> 37#include <linux/highmem.h> 38#include <linux/vmalloc.h> 39#include "drmP.h" 40 41/** 42 * Cut down version of drm_memory_debug.h, which used to be called 43 * drm_memory.h. 44 */ 45 46#if __OS_HAS_AGP 47 48#include <linux/vmalloc.h> 49 50#ifdef HAVE_PAGE_AGP 51#include <asm/agp.h> 52#else 53# ifdef __powerpc__ 54# define PAGE_AGP __pgprot(_PAGE_KERNEL | _PAGE_NO_CACHE) 55# else 56# define PAGE_AGP PAGE_KERNEL 57# endif 58#endif 59 60/* 61 * Find the drm_map that covers the range [offset, offset+size). 62 */ 63static inline drm_map_t * 64drm_lookup_map (unsigned long offset, unsigned long size, drm_device_t *dev) 65{ 66 struct list_head *list; 67 drm_map_list_t *r_list; 68 drm_map_t *map; 69 70 list_for_each(list, &dev->maplist->head) { 71 r_list = (drm_map_list_t *) list; 72 map = r_list->map; 73 if (!map) 74 continue; 75 if (map->offset <= offset && (offset + size) <= (map->offset + map->size)) 76 return map; 77 } 78 return NULL; 79} 80 81static inline void * 82agp_remap (unsigned long offset, unsigned long size, drm_device_t *dev) 83{ 84 unsigned long *phys_addr_map, i, num_pages = PAGE_ALIGN(size) / PAGE_SIZE; 85 struct drm_agp_mem *agpmem; 86 struct page **page_map; 87 void *addr; 88 89 size = PAGE_ALIGN(size); 90 91#ifdef __alpha__ 92 offset -= dev->hose->mem_space->start; 93#endif 94 95 for (agpmem = dev->agp->memory; agpmem; agpmem = agpmem->next) 96 if (agpmem->bound <= offset 97 && (agpmem->bound + (agpmem->pages << PAGE_SHIFT)) >= (offset + size)) 98 break; 99 if (!agpmem) 100 return NULL; 101 102 /* 103 * OK, we're mapping AGP space on a chipset/platform on which memory accesses by 104 * the CPU do not get remapped by the GART. We fix this by using the kernel's 105 * page-table instead (that's probably faster anyhow...). 106 */ 107 /* note: use vmalloc() because num_pages could be large... */ 108 page_map = vmalloc(num_pages * sizeof(struct page *)); 109 if (!page_map) 110 return NULL; 111 112 phys_addr_map = agpmem->memory->memory + (offset - agpmem->bound) / PAGE_SIZE; 113 for (i = 0; i < num_pages; ++i) 114 page_map[i] = pfn_to_page(phys_addr_map[i] >> PAGE_SHIFT); 115 addr = vmap(page_map, num_pages, VM_IOREMAP, PAGE_AGP); 116 vfree(page_map); 117 118 return addr; 119} 120 121static inline unsigned long 122drm_follow_page (void *vaddr) 123{ 124 pgd_t *pgd = pgd_offset_k((unsigned long) vaddr); 125 pud_t *pud = pud_offset(pgd, (unsigned long) vaddr); 126 pmd_t *pmd = pmd_offset(pud, (unsigned long) vaddr); 127 pte_t *ptep = pte_offset_kernel(pmd, (unsigned long) vaddr); 128 return pte_pfn(*ptep) << PAGE_SHIFT; 129} 130 131#else /* __OS_HAS_AGP */ 132 133static inline drm_map_t *drm_lookup_map(unsigned long offset, unsigned long size, drm_device_t *dev) 134{ 135 return NULL; 136} 137 138static inline void *agp_remap(unsigned long offset, unsigned long size, drm_device_t *dev) 139{ 140 return NULL; 141} 142 143static inline unsigned long drm_follow_page (void *vaddr) 144{ 145 return 0; 146} 147 148#endif 149 150static inline void *drm_ioremap(unsigned long offset, unsigned long size, drm_device_t *dev) 151{ 152 if (drm_core_has_AGP(dev) && dev->agp && dev->agp->cant_use_aperture) { 153 drm_map_t *map = drm_lookup_map(offset, size, dev); 154 155 if (map && map->type == _DRM_AGP) 156 return agp_remap(offset, size, dev); 157 } 158 return ioremap(offset, size); 159} 160 161static inline void *drm_ioremap_nocache(unsigned long offset, unsigned long size, 162 drm_device_t *dev) 163{ 164 if (drm_core_has_AGP(dev) && dev->agp && dev->agp->cant_use_aperture) { 165 drm_map_t *map = drm_lookup_map(offset, size, dev); 166 167 if (map && map->type == _DRM_AGP) 168 return agp_remap(offset, size, dev); 169 } 170 return ioremap_nocache(offset, size); 171} 172 173static inline void drm_ioremapfree(void *pt, unsigned long size, drm_device_t *dev) 174{ 175 /* 176 * This is a bit ugly. It would be much cleaner if the DRM API would use separate 177 * routines for handling mappings in the AGP space. Hopefully this can be done in 178 * a future revision of the interface... 179 */ 180 if (drm_core_has_AGP(dev) && dev->agp && dev->agp->cant_use_aperture 181 && ((unsigned long) pt >= VMALLOC_START && (unsigned long) pt < VMALLOC_END)) 182 { 183 unsigned long offset; 184 drm_map_t *map; 185 186 offset = drm_follow_page(pt) | ((unsigned long) pt & ~PAGE_MASK); 187 map = drm_lookup_map(offset, size, dev); 188 if (map && map->type == _DRM_AGP) { 189 vunmap(pt); 190 return; 191 } 192 } 193 194 iounmap(pt); 195} 196 197