Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / drivers / char / drm / drm_memory.c
1 /**
2  * \file drm_memory.c
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/highmem.h>
37 #include "drmP.h"
38
39 #ifdef DEBUG_MEMORY
40 #include "drm_memory_debug.h"
41 #else
42
43 /** No-op. */
44 void drm_mem_init(void)
45 {
46 }
47
48 /**
49  * Called when "/proc/dri/%dev%/mem" is read.
50  *
51  * \param buf output buffer.
52  * \param start start of output data.
53  * \param offset requested start offset.
54  * \param len requested number of bytes.
55  * \param eof whether there is no more data to return.
56  * \param data private data.
57  * \return number of written bytes.
58  *
59  * No-op.
60  */
61 int drm_mem_info(char *buf, char **start, off_t offset,
62                  int len, int *eof, void *data)
63 {
64         return 0;
65 }
66
67 /** Wrapper around kmalloc() and kfree() */
68 void *drm_realloc(void *oldpt, size_t oldsize, size_t size, int area)
69 {
70         void *pt;
71
72         if (!(pt = kmalloc(size, GFP_KERNEL)))
73                 return NULL;
74         if (oldpt && oldsize) {
75                 memcpy(pt, oldpt, oldsize);
76                 kfree(oldpt);
77         }
78         return pt;
79 }
80
81 #if __OS_HAS_AGP
82 /*
83  * Find the drm_map that covers the range [offset, offset+size).
84  */
85 static drm_map_t *drm_lookup_map(unsigned long offset,
86                                  unsigned long size, drm_device_t * dev)
87 {
88         struct list_head *list;
89         drm_map_list_t *r_list;
90         drm_map_t *map;
91
92         list_for_each(list, &dev->maplist->head) {
93                 r_list = (drm_map_list_t *) list;
94                 map = r_list->map;
95                 if (!map)
96                         continue;
97                 if (map->offset <= offset
98                     && (offset + size) <= (map->offset + map->size))
99                         return map;
100         }
101         return NULL;
102 }
103
104 static void *agp_remap(unsigned long offset, unsigned long size,
105                        drm_device_t * dev)
106 {
107         unsigned long *phys_addr_map, i, num_pages =
108             PAGE_ALIGN(size) / PAGE_SIZE;
109         struct drm_agp_mem *agpmem;
110         struct page **page_map;
111         void *addr;
112
113         size = PAGE_ALIGN(size);
114
115 #ifdef __alpha__
116         offset -= dev->hose->mem_space->start;
117 #endif
118
119         for (agpmem = dev->agp->memory; agpmem; agpmem = agpmem->next)
120                 if (agpmem->bound <= offset
121                     && (agpmem->bound + (agpmem->pages << PAGE_SHIFT)) >=
122                     (offset + size))
123                         break;
124         if (!agpmem)
125                 return NULL;
126
127         /*
128          * OK, we're mapping AGP space on a chipset/platform on which memory accesses by
129          * the CPU do not get remapped by the GART.  We fix this by using the kernel's
130          * page-table instead (that's probably faster anyhow...).
131          */
132         /* note: use vmalloc() because num_pages could be large... */
133         page_map = vmalloc(num_pages * sizeof(struct page *));
134         if (!page_map)
135                 return NULL;
136
137         phys_addr_map =
138             agpmem->memory->memory + (offset - agpmem->bound) / PAGE_SIZE;
139         for (i = 0; i < num_pages; ++i)
140                 page_map[i] = pfn_to_page(phys_addr_map[i] >> PAGE_SHIFT);
141         addr = vmap(page_map, num_pages, VM_IOREMAP, PAGE_AGP);
142         vfree(page_map);
143
144         return addr;
145 }
146
147 /** Wrapper around agp_allocate_memory() */
148 DRM_AGP_MEM *drm_alloc_agp(drm_device_t * dev, int pages, u32 type)
149 {
150         return drm_agp_allocate_memory(dev->agp->bridge, pages, type);
151 }
152
153 /** Wrapper around agp_free_memory() */
154 int drm_free_agp(DRM_AGP_MEM * handle, int pages)
155 {
156         return drm_agp_free_memory(handle) ? 0 : -EINVAL;
157 }
158
159 /** Wrapper around agp_bind_memory() */
160 int drm_bind_agp(DRM_AGP_MEM * handle, unsigned int start)
161 {
162         return drm_agp_bind_memory(handle, start);
163 }
164
165 /** Wrapper around agp_unbind_memory() */
166 int drm_unbind_agp(DRM_AGP_MEM * handle)
167 {
168         return drm_agp_unbind_memory(handle);
169 }
170
171 #else  /*  __OS_HAS_AGP  */
172
173 static inline drm_map_t *drm_lookup_map(unsigned long offset,
174                                         unsigned long size, drm_device_t * dev)
175 {
176         return NULL;
177 }
178
179 static inline void *agp_remap(unsigned long offset, unsigned long size,
180                               drm_device_t * dev)
181 {
182         return NULL;
183 }
184
185 #endif                          /* agp */
186
187 void *drm_ioremap(unsigned long offset, unsigned long size,
188                                 drm_device_t * dev)
189 {
190         if (drm_core_has_AGP(dev) && dev->agp && dev->agp->cant_use_aperture) {
191                 drm_map_t *map = drm_lookup_map(offset, size, dev);
192
193                 if (map && map->type == _DRM_AGP)
194                         return agp_remap(offset, size, dev);
195         }
196         return ioremap(offset, size);
197 }
198 EXPORT_SYMBOL(drm_ioremap);
199
200 #if 0
201 void *drm_ioremap_nocache(unsigned long offset,
202                                         unsigned long size, drm_device_t * dev)
203 {
204         if (drm_core_has_AGP(dev) && dev->agp && dev->agp->cant_use_aperture) {
205                 drm_map_t *map = drm_lookup_map(offset, size, dev);
206
207                 if (map && map->type == _DRM_AGP)
208                         return agp_remap(offset, size, dev);
209         }
210         return ioremap_nocache(offset, size);
211 }
212 #endif  /*  0  */
213
214 void drm_ioremapfree(void *pt, unsigned long size,
215                                    drm_device_t * dev)
216 {
217         /*
218          * This is a bit ugly.  It would be much cleaner if the DRM API would use separate
219          * routines for handling mappings in the AGP space.  Hopefully this can be done in
220          * a future revision of the interface...
221          */
222         if (drm_core_has_AGP(dev) && dev->agp && dev->agp->cant_use_aperture
223             && ((unsigned long)pt >= VMALLOC_START
224                 && (unsigned long)pt < VMALLOC_END)) {
225                 unsigned long offset;
226                 drm_map_t *map;
227
228                 offset = drm_follow_page(pt) | ((unsigned long)pt & ~PAGE_MASK);
229                 map = drm_lookup_map(offset, size, dev);
230                 if (map && map->type == _DRM_AGP) {
231                         vunmap(pt);
232                         return;
233                 }
234         }
235
236         iounmap(pt);
237 }
238 EXPORT_SYMBOL(drm_ioremapfree);
239
240 #endif                          /* debug_memory */