Fix arch headers.
[linux-flexiantxendom0-3.2.10.git] / drivers / char / drm / drm_memory.h
1 /* drm_memory.h -- Memory management wrappers for DRM -*- linux-c -*-
2  * Created: Thu Feb  4 14:00:34 1999 by faith@valinux.com
3  *
4  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
5  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
6  * All Rights Reserved.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a
9  * copy of this software and associated documentation files (the "Software"),
10  * to deal in the Software without restriction, including without limitation
11  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12  * and/or sell copies of the Software, and to permit persons to whom the
13  * Software is furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice (including the next
16  * paragraph) shall be included in all copies or substantial portions of the
17  * Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
22  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  * OTHER DEALINGS IN THE SOFTWARE.
26  *
27  * Authors:
28  *    Rickard E. (Rik) Faith <faith@valinux.com>
29  *    Gareth Hughes <gareth@valinux.com>
30  */
31
32 #include <linux/config.h>
33 #include "drmP.h"
34 #include <linux/vmalloc.h>
35
36 #include <asm/agp.h>
37 #include <asm/tlbflush.h>
38
39 /* Cut down version of drm_memory_debug.h, which used to be called
40  * drm_memory.h.  If you want the debug functionality, change 0 to 1
41  * below.
42  */
43 #define DEBUG_MEMORY 0
44
45 #if __REALLY_HAVE_AGP
46
47 /*
48  * Find the drm_map that covers the range [offset, offset+size).
49  */
50 static inline drm_map_t *
51 drm_lookup_map (unsigned long offset, unsigned long size, drm_device_t *dev)
52 {
53         struct list_head *list;
54         drm_map_list_t *r_list;
55         drm_map_t *map;
56
57         list_for_each(list, &dev->maplist->head) {
58                 r_list = (drm_map_list_t *) list;
59                 map = r_list->map;
60                 if (!map)
61                         continue;
62                 if (map->offset <= offset && (offset + size) <= (map->offset + map->size))
63                         return map;
64         }
65         return NULL;
66 }
67
68 static inline void *
69 agp_remap (unsigned long offset, unsigned long size, drm_device_t *dev)
70 {
71         unsigned long *phys_addr_map, i, num_pages = PAGE_ALIGN(size) / PAGE_SIZE;
72         struct drm_agp_mem *agpmem;
73         struct page **page_map;
74         void *addr;
75
76         size = PAGE_ALIGN(size);
77
78         for (agpmem = dev->agp->memory; agpmem; agpmem = agpmem->next)
79                 if (agpmem->bound <= offset
80                     && (agpmem->bound + (agpmem->pages << PAGE_SHIFT)) >= (offset + size))
81                         break;
82         if (!agpmem)
83                 return NULL;
84
85         /*
86          * OK, we're mapping AGP space on a chipset/platform on which memory accesses by
87          * the CPU do not get remapped by the GART.  We fix this by using the kernel's
88          * page-table instead (that's probably faster anyhow...).
89          */
90         /* note: use vmalloc() because num_pages could be large... */
91         page_map = vmalloc(num_pages * sizeof(struct page *));
92         if (!page_map)
93                 return NULL;
94
95         phys_addr_map = agpmem->memory->memory + (offset - agpmem->bound) / PAGE_SIZE;
96         for (i = 0; i < num_pages; ++i)
97                 page_map[i] = pfn_to_page(phys_addr_map[i] >> PAGE_SHIFT);
98         addr = vmap(page_map, num_pages, VM_IOREMAP, PAGE_AGP);
99         vfree(page_map);
100         if (!addr)
101                 return NULL;
102
103         flush_tlb_kernel_range((unsigned long) addr, (unsigned long) addr + size);
104         return addr;
105 }
106
107 static inline unsigned long
108 drm_follow_page (void *vaddr)
109 {
110         pgd_t *pgd = pgd_offset_k((unsigned long) vaddr);
111         pmd_t *pmd = pmd_offset(pgd, (unsigned long) vaddr);
112         pte_t *ptep = pte_offset_kernel(pmd, (unsigned long) vaddr);
113         return pte_pfn(*ptep) << PAGE_SHIFT;
114 }
115
116 #else /* !__REALLY_HAVE_AGP */
117
118 static inline void *
119 agp_remap (unsigned long offset, unsigned long size, drm_device_t *dev)
120 {
121         return NULL;
122 }
123
124 #endif /* !__REALLY_HAVE_AGP */
125
126 static inline void *drm_ioremap(unsigned long offset, unsigned long size, drm_device_t *dev)
127 {
128         int remap_aperture = 0;
129
130 #if __REALLY_HAVE_AGP
131         if (dev->agp->cant_use_aperture) {
132                 drm_map_t *map = drm_lookup_map(offset, size, dev);
133
134                 if (map && map->type == _DRM_AGP)
135                         remap_aperture = 1;
136         }
137 #endif
138         if (remap_aperture)
139                 return agp_remap(offset, size, dev);
140         else
141                 return ioremap(offset, size);
142 }
143
144 static inline void *drm_ioremap_nocache(unsigned long offset, unsigned long size,
145                                         drm_device_t *dev)
146 {
147         int remap_aperture = 0;
148
149 #if __REALLY_HAVE_AGP
150         if (dev->agp->cant_use_aperture) {
151                 drm_map_t *map = drm_lookup_map(offset, size, dev);
152
153                 if (map && map->type == _DRM_AGP)
154                         remap_aperture = 1;
155         }
156 #endif
157         if (remap_aperture)
158                 return agp_remap(offset, size, dev);
159         else
160                 return ioremap_nocache(offset, size);
161 }
162
163 static inline void drm_ioremapfree(void *pt, unsigned long size, drm_device_t *dev)
164 {
165         int unmap_aperture = 0;
166 #if __REALLY_HAVE_AGP
167         /*
168          * This is a bit ugly.  It would be much cleaner if the DRM API would use separate
169          * routines for handling mappings in the AGP space.  Hopefully this can be done in
170          * a future revision of the interface...
171          */
172         if (dev->agp->cant_use_aperture
173             && ((unsigned long) pt >= VMALLOC_START && (unsigned long) pt < VMALLOC_END))
174         {
175                 unsigned long offset;
176                 drm_map_t *map;
177
178                 offset = drm_follow_page(pt) | ((unsigned long) pt & ~PAGE_MASK);
179                 map = drm_lookup_map(offset, size, dev);
180                 if (map && map->type == _DRM_AGP)
181                         unmap_aperture = 1;
182         }
183 #endif
184         if (unmap_aperture)
185                 vunmap(pt);
186         else
187                 iounmap(pt);
188 }
189
190 #if DEBUG_MEMORY
191 #include "drm_memory_debug.h"
192 #else
193 void DRM(mem_init)(void)
194 {
195 }
196
197 /* drm_mem_info is called whenever a process reads /dev/drm/mem. */
198 int DRM(mem_info)(char *buf, char **start, off_t offset,
199                   int len, int *eof, void *data)
200 {
201         return 0;
202 }
203
204 void *DRM(alloc)(size_t size, int area)
205 {
206         return kmalloc(size, GFP_KERNEL);
207 }
208
209 void *DRM(realloc)(void *oldpt, size_t oldsize, size_t size, int area)
210 {
211         void *pt;
212
213         if (!(pt = kmalloc(size, GFP_KERNEL))) return NULL;
214         if (oldpt && oldsize) {
215                 memcpy(pt, oldpt, oldsize);
216                 kfree(oldpt);
217         }
218         return pt;
219 }
220
221 void DRM(free)(void *pt, size_t size, int area)
222 {
223         kfree(pt);
224 }
225
226 unsigned long DRM(alloc_pages)(int order, int area)
227 {
228         unsigned long address;
229         unsigned long bytes       = PAGE_SIZE << order;
230         unsigned long addr;
231         unsigned int  sz;
232
233         address = __get_free_pages(GFP_KERNEL, order);
234         if (!address) 
235                 return 0;
236
237                                 /* Zero */
238         memset((void *)address, 0, bytes);
239
240                                 /* Reserve */
241         for (addr = address, sz = bytes;
242              sz > 0;
243              addr += PAGE_SIZE, sz -= PAGE_SIZE) {
244                 SetPageReserved(virt_to_page(addr));
245         }
246
247         return address;
248 }
249
250 void DRM(free_pages)(unsigned long address, int order, int area)
251 {
252         unsigned long bytes = PAGE_SIZE << order;
253         unsigned long addr;
254         unsigned int  sz;
255
256         if (!address) 
257                 return;
258
259         /* Unreserve */
260         for (addr = address, sz = bytes;
261              sz > 0;
262              addr += PAGE_SIZE, sz -= PAGE_SIZE) {
263                 ClearPageReserved(virt_to_page(addr));
264         }
265
266         free_pages(address, order);
267 }
268
269 void *DRM(ioremap)(unsigned long offset, unsigned long size, drm_device_t *dev)
270 {
271         return drm_ioremap(offset, size, dev);
272 }
273
274 void *DRM(ioremap_nocache)(unsigned long offset, unsigned long size, drm_device_t *dev)
275 {
276         return drm_ioremap_nocache(offset, size, dev);
277 }
278
279 void DRM(ioremapfree)(void *pt, unsigned long size, drm_device_t *dev)
280 {
281         drm_ioremapfree(pt, size, dev);
282 }
283
284 #if __REALLY_HAVE_AGP
285 agp_memory *DRM(alloc_agp)(int pages, u32 type)
286 {
287         return DRM(agp_allocate_memory)(pages, type);
288 }
289
290 int DRM(free_agp)(agp_memory *handle, int pages)
291 {
292         return DRM(agp_free_memory)(handle) ? 0 : -EINVAL;
293 }
294
295 int DRM(bind_agp)(agp_memory *handle, unsigned int start)
296 {
297         return DRM(agp_bind_memory)(handle, start);
298 }
299
300 int DRM(unbind_agp)(agp_memory *handle)
301 {
302         return DRM(agp_unbind_memory)(handle);
303 }
304 #endif /* agp */
305 #endif /* debug_memory */