1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * AGPGART driver backend routines.
3*4882a593Smuzhiyun * Copyright (C) 2004 Silicon Graphics, Inc.
4*4882a593Smuzhiyun * Copyright (C) 2002-2003 Dave Jones.
5*4882a593Smuzhiyun * Copyright (C) 1999 Jeff Hartmann.
6*4882a593Smuzhiyun * Copyright (C) 1999 Precision Insight, Inc.
7*4882a593Smuzhiyun * Copyright (C) 1999 Xi Graphics, Inc.
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * Permission is hereby granted, free of charge, to any person obtaining a
10*4882a593Smuzhiyun * copy of this software and associated documentation files (the "Software"),
11*4882a593Smuzhiyun * to deal in the Software without restriction, including without limitation
12*4882a593Smuzhiyun * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13*4882a593Smuzhiyun * and/or sell copies of the Software, and to permit persons to whom the
14*4882a593Smuzhiyun * Software is furnished to do so, subject to the following conditions:
15*4882a593Smuzhiyun *
16*4882a593Smuzhiyun * The above copyright notice and this permission notice shall be included
17*4882a593Smuzhiyun * in all copies or substantial portions of the Software.
18*4882a593Smuzhiyun *
19*4882a593Smuzhiyun * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20*4882a593Smuzhiyun * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21*4882a593Smuzhiyun * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22*4882a593Smuzhiyun * JEFF HARTMANN, DAVE JONES, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM,
23*4882a593Smuzhiyun * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24*4882a593Smuzhiyun * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
25*4882a593Smuzhiyun * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26*4882a593Smuzhiyun *
27*4882a593Smuzhiyun * TODO:
28*4882a593Smuzhiyun * - Allocate more than order 0 pages to avoid too much linear map splitting.
29*4882a593Smuzhiyun */
30*4882a593Smuzhiyun #include <linux/module.h>
31*4882a593Smuzhiyun #include <linux/pci.h>
32*4882a593Smuzhiyun #include <linux/init.h>
33*4882a593Smuzhiyun #include <linux/slab.h>
34*4882a593Smuzhiyun #include <linux/pagemap.h>
35*4882a593Smuzhiyun #include <linux/miscdevice.h>
36*4882a593Smuzhiyun #include <linux/pm.h>
37*4882a593Smuzhiyun #include <linux/agp_backend.h>
38*4882a593Smuzhiyun #include <linux/agpgart.h>
39*4882a593Smuzhiyun #include <linux/vmalloc.h>
40*4882a593Smuzhiyun #include <asm/io.h>
41*4882a593Smuzhiyun #include "agp.h"
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun /* Due to XFree86 brain-damage, we can't go to 1.0 until they
44*4882a593Smuzhiyun * fix some real stupidity. It's only by chance we can bump
45*4882a593Smuzhiyun * past 0.99 at all due to some boolean logic error. */
46*4882a593Smuzhiyun #define AGPGART_VERSION_MAJOR 0
47*4882a593Smuzhiyun #define AGPGART_VERSION_MINOR 103
48*4882a593Smuzhiyun static const struct agp_version agp_current_version =
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun .major = AGPGART_VERSION_MAJOR,
51*4882a593Smuzhiyun .minor = AGPGART_VERSION_MINOR,
52*4882a593Smuzhiyun };
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun struct agp_bridge_data *(*agp_find_bridge)(struct pci_dev *) =
55*4882a593Smuzhiyun &agp_generic_find_bridge;
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun struct agp_bridge_data *agp_bridge;
58*4882a593Smuzhiyun LIST_HEAD(agp_bridges);
59*4882a593Smuzhiyun EXPORT_SYMBOL(agp_bridge);
60*4882a593Smuzhiyun EXPORT_SYMBOL(agp_bridges);
61*4882a593Smuzhiyun EXPORT_SYMBOL(agp_find_bridge);
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun /**
64*4882a593Smuzhiyun * agp_backend_acquire - attempt to acquire an agp backend.
65*4882a593Smuzhiyun *
66*4882a593Smuzhiyun */
agp_backend_acquire(struct pci_dev * pdev)67*4882a593Smuzhiyun struct agp_bridge_data *agp_backend_acquire(struct pci_dev *pdev)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun struct agp_bridge_data *bridge;
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun bridge = agp_find_bridge(pdev);
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun if (!bridge)
74*4882a593Smuzhiyun return NULL;
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun if (atomic_read(&bridge->agp_in_use))
77*4882a593Smuzhiyun return NULL;
78*4882a593Smuzhiyun atomic_inc(&bridge->agp_in_use);
79*4882a593Smuzhiyun return bridge;
80*4882a593Smuzhiyun }
81*4882a593Smuzhiyun EXPORT_SYMBOL(agp_backend_acquire);
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun /**
85*4882a593Smuzhiyun * agp_backend_release - release the lock on the agp backend.
86*4882a593Smuzhiyun *
87*4882a593Smuzhiyun * The caller must insure that the graphics aperture translation table
88*4882a593Smuzhiyun * is read for use by another entity.
89*4882a593Smuzhiyun *
90*4882a593Smuzhiyun * (Ensure that all memory it bound is unbound.)
91*4882a593Smuzhiyun */
agp_backend_release(struct agp_bridge_data * bridge)92*4882a593Smuzhiyun void agp_backend_release(struct agp_bridge_data *bridge)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun if (bridge)
96*4882a593Smuzhiyun atomic_dec(&bridge->agp_in_use);
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun EXPORT_SYMBOL(agp_backend_release);
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun static const struct { int mem, agp; } maxes_table[] = {
102*4882a593Smuzhiyun {0, 0},
103*4882a593Smuzhiyun {32, 4},
104*4882a593Smuzhiyun {64, 28},
105*4882a593Smuzhiyun {128, 96},
106*4882a593Smuzhiyun {256, 204},
107*4882a593Smuzhiyun {512, 440},
108*4882a593Smuzhiyun {1024, 942},
109*4882a593Smuzhiyun {2048, 1920},
110*4882a593Smuzhiyun {4096, 3932}
111*4882a593Smuzhiyun };
112*4882a593Smuzhiyun
agp_find_max(void)113*4882a593Smuzhiyun static int agp_find_max(void)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun long memory, index, result;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun #if PAGE_SHIFT < 20
118*4882a593Smuzhiyun memory = totalram_pages() >> (20 - PAGE_SHIFT);
119*4882a593Smuzhiyun #else
120*4882a593Smuzhiyun memory = totalram_pages() << (PAGE_SHIFT - 20);
121*4882a593Smuzhiyun #endif
122*4882a593Smuzhiyun index = 1;
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun while ((memory > maxes_table[index].mem) && (index < 8))
125*4882a593Smuzhiyun index++;
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun result = maxes_table[index - 1].agp +
128*4882a593Smuzhiyun ( (memory - maxes_table[index - 1].mem) *
129*4882a593Smuzhiyun (maxes_table[index].agp - maxes_table[index - 1].agp)) /
130*4882a593Smuzhiyun (maxes_table[index].mem - maxes_table[index - 1].mem);
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun result = result << (20 - PAGE_SHIFT);
133*4882a593Smuzhiyun return result;
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun
agp_backend_initialize(struct agp_bridge_data * bridge)137*4882a593Smuzhiyun static int agp_backend_initialize(struct agp_bridge_data *bridge)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun int size_value, rc, got_gatt=0, got_keylist=0;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun bridge->max_memory_agp = agp_find_max();
142*4882a593Smuzhiyun bridge->version = &agp_current_version;
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun if (bridge->driver->needs_scratch_page) {
145*4882a593Smuzhiyun struct page *page = bridge->driver->agp_alloc_page(bridge);
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun if (!page) {
148*4882a593Smuzhiyun dev_err(&bridge->dev->dev,
149*4882a593Smuzhiyun "can't get memory for scratch page\n");
150*4882a593Smuzhiyun return -ENOMEM;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun bridge->scratch_page_page = page;
154*4882a593Smuzhiyun bridge->scratch_page_dma = page_to_phys(page);
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun bridge->scratch_page = bridge->driver->mask_memory(bridge,
157*4882a593Smuzhiyun bridge->scratch_page_dma, 0);
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun size_value = bridge->driver->fetch_size();
161*4882a593Smuzhiyun if (size_value == 0) {
162*4882a593Smuzhiyun dev_err(&bridge->dev->dev, "can't determine aperture size\n");
163*4882a593Smuzhiyun rc = -EINVAL;
164*4882a593Smuzhiyun goto err_out;
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun if (bridge->driver->create_gatt_table(bridge)) {
167*4882a593Smuzhiyun dev_err(&bridge->dev->dev,
168*4882a593Smuzhiyun "can't get memory for graphics translation table\n");
169*4882a593Smuzhiyun rc = -ENOMEM;
170*4882a593Smuzhiyun goto err_out;
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun got_gatt = 1;
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun bridge->key_list = vzalloc(PAGE_SIZE * 4);
175*4882a593Smuzhiyun if (bridge->key_list == NULL) {
176*4882a593Smuzhiyun dev_err(&bridge->dev->dev,
177*4882a593Smuzhiyun "can't allocate memory for key lists\n");
178*4882a593Smuzhiyun rc = -ENOMEM;
179*4882a593Smuzhiyun goto err_out;
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun got_keylist = 1;
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun /* FIXME vmalloc'd memory not guaranteed contiguous */
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun if (bridge->driver->configure()) {
186*4882a593Smuzhiyun dev_err(&bridge->dev->dev, "error configuring host chipset\n");
187*4882a593Smuzhiyun rc = -EINVAL;
188*4882a593Smuzhiyun goto err_out;
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun INIT_LIST_HEAD(&bridge->mapped_list);
191*4882a593Smuzhiyun spin_lock_init(&bridge->mapped_lock);
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun return 0;
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun err_out:
196*4882a593Smuzhiyun if (bridge->driver->needs_scratch_page) {
197*4882a593Smuzhiyun struct page *page = bridge->scratch_page_page;
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun bridge->driver->agp_destroy_page(page, AGP_PAGE_DESTROY_UNMAP);
200*4882a593Smuzhiyun bridge->driver->agp_destroy_page(page, AGP_PAGE_DESTROY_FREE);
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun if (got_gatt)
203*4882a593Smuzhiyun bridge->driver->free_gatt_table(bridge);
204*4882a593Smuzhiyun if (got_keylist) {
205*4882a593Smuzhiyun vfree(bridge->key_list);
206*4882a593Smuzhiyun bridge->key_list = NULL;
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun return rc;
209*4882a593Smuzhiyun }
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun /* cannot be __exit b/c as it could be called from __init code */
agp_backend_cleanup(struct agp_bridge_data * bridge)212*4882a593Smuzhiyun static void agp_backend_cleanup(struct agp_bridge_data *bridge)
213*4882a593Smuzhiyun {
214*4882a593Smuzhiyun if (bridge->driver->cleanup)
215*4882a593Smuzhiyun bridge->driver->cleanup();
216*4882a593Smuzhiyun if (bridge->driver->free_gatt_table)
217*4882a593Smuzhiyun bridge->driver->free_gatt_table(bridge);
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun vfree(bridge->key_list);
220*4882a593Smuzhiyun bridge->key_list = NULL;
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun if (bridge->driver->agp_destroy_page &&
223*4882a593Smuzhiyun bridge->driver->needs_scratch_page) {
224*4882a593Smuzhiyun struct page *page = bridge->scratch_page_page;
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun bridge->driver->agp_destroy_page(page, AGP_PAGE_DESTROY_UNMAP);
227*4882a593Smuzhiyun bridge->driver->agp_destroy_page(page, AGP_PAGE_DESTROY_FREE);
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun }
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun /* When we remove the global variable agp_bridge from all drivers
232*4882a593Smuzhiyun * then agp_alloc_bridge and agp_generic_find_bridge need to be updated
233*4882a593Smuzhiyun */
234*4882a593Smuzhiyun
agp_alloc_bridge(void)235*4882a593Smuzhiyun struct agp_bridge_data *agp_alloc_bridge(void)
236*4882a593Smuzhiyun {
237*4882a593Smuzhiyun struct agp_bridge_data *bridge;
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
240*4882a593Smuzhiyun if (!bridge)
241*4882a593Smuzhiyun return NULL;
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun atomic_set(&bridge->agp_in_use, 0);
244*4882a593Smuzhiyun atomic_set(&bridge->current_memory_agp, 0);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun if (list_empty(&agp_bridges))
247*4882a593Smuzhiyun agp_bridge = bridge;
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun return bridge;
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun EXPORT_SYMBOL(agp_alloc_bridge);
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun
agp_put_bridge(struct agp_bridge_data * bridge)254*4882a593Smuzhiyun void agp_put_bridge(struct agp_bridge_data *bridge)
255*4882a593Smuzhiyun {
256*4882a593Smuzhiyun kfree(bridge);
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun if (list_empty(&agp_bridges))
259*4882a593Smuzhiyun agp_bridge = NULL;
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun EXPORT_SYMBOL(agp_put_bridge);
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun
agp_add_bridge(struct agp_bridge_data * bridge)264*4882a593Smuzhiyun int agp_add_bridge(struct agp_bridge_data *bridge)
265*4882a593Smuzhiyun {
266*4882a593Smuzhiyun int error;
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun if (agp_off) {
269*4882a593Smuzhiyun error = -ENODEV;
270*4882a593Smuzhiyun goto err_put_bridge;
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun if (!bridge->dev) {
274*4882a593Smuzhiyun printk (KERN_DEBUG PFX "Erk, registering with no pci_dev!\n");
275*4882a593Smuzhiyun error = -EINVAL;
276*4882a593Smuzhiyun goto err_put_bridge;
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun /* Grab reference on the chipset driver. */
280*4882a593Smuzhiyun if (!try_module_get(bridge->driver->owner)) {
281*4882a593Smuzhiyun dev_info(&bridge->dev->dev, "can't lock chipset driver\n");
282*4882a593Smuzhiyun error = -EINVAL;
283*4882a593Smuzhiyun goto err_put_bridge;
284*4882a593Smuzhiyun }
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun error = agp_backend_initialize(bridge);
287*4882a593Smuzhiyun if (error) {
288*4882a593Smuzhiyun dev_info(&bridge->dev->dev,
289*4882a593Smuzhiyun "agp_backend_initialize() failed\n");
290*4882a593Smuzhiyun goto err_out;
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun if (list_empty(&agp_bridges)) {
294*4882a593Smuzhiyun error = agp_frontend_initialize();
295*4882a593Smuzhiyun if (error) {
296*4882a593Smuzhiyun dev_info(&bridge->dev->dev,
297*4882a593Smuzhiyun "agp_frontend_initialize() failed\n");
298*4882a593Smuzhiyun goto frontend_err;
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun dev_info(&bridge->dev->dev, "AGP aperture is %dM @ 0x%lx\n",
302*4882a593Smuzhiyun bridge->driver->fetch_size(), bridge->gart_bus_addr);
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun list_add(&bridge->list, &agp_bridges);
307*4882a593Smuzhiyun return 0;
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun frontend_err:
310*4882a593Smuzhiyun agp_backend_cleanup(bridge);
311*4882a593Smuzhiyun err_out:
312*4882a593Smuzhiyun module_put(bridge->driver->owner);
313*4882a593Smuzhiyun err_put_bridge:
314*4882a593Smuzhiyun agp_put_bridge(bridge);
315*4882a593Smuzhiyun return error;
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(agp_add_bridge);
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun
agp_remove_bridge(struct agp_bridge_data * bridge)320*4882a593Smuzhiyun void agp_remove_bridge(struct agp_bridge_data *bridge)
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun agp_backend_cleanup(bridge);
323*4882a593Smuzhiyun list_del(&bridge->list);
324*4882a593Smuzhiyun if (list_empty(&agp_bridges))
325*4882a593Smuzhiyun agp_frontend_cleanup();
326*4882a593Smuzhiyun module_put(bridge->driver->owner);
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(agp_remove_bridge);
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun int agp_off;
331*4882a593Smuzhiyun int agp_try_unsupported_boot;
332*4882a593Smuzhiyun EXPORT_SYMBOL(agp_off);
333*4882a593Smuzhiyun EXPORT_SYMBOL(agp_try_unsupported_boot);
334*4882a593Smuzhiyun
agp_init(void)335*4882a593Smuzhiyun static int __init agp_init(void)
336*4882a593Smuzhiyun {
337*4882a593Smuzhiyun if (!agp_off)
338*4882a593Smuzhiyun printk(KERN_INFO "Linux agpgart interface v%d.%d\n",
339*4882a593Smuzhiyun AGPGART_VERSION_MAJOR, AGPGART_VERSION_MINOR);
340*4882a593Smuzhiyun return 0;
341*4882a593Smuzhiyun }
342*4882a593Smuzhiyun
agp_exit(void)343*4882a593Smuzhiyun static void __exit agp_exit(void)
344*4882a593Smuzhiyun {
345*4882a593Smuzhiyun }
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun #ifndef MODULE
agp_setup(char * s)348*4882a593Smuzhiyun static __init int agp_setup(char *s)
349*4882a593Smuzhiyun {
350*4882a593Smuzhiyun if (!strcmp(s,"off"))
351*4882a593Smuzhiyun agp_off = 1;
352*4882a593Smuzhiyun if (!strcmp(s,"try_unsupported"))
353*4882a593Smuzhiyun agp_try_unsupported_boot = 1;
354*4882a593Smuzhiyun return 1;
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun __setup("agp=", agp_setup);
357*4882a593Smuzhiyun #endif
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun MODULE_AUTHOR("Dave Jones, Jeff Hartmann");
360*4882a593Smuzhiyun MODULE_DESCRIPTION("AGP GART driver");
361*4882a593Smuzhiyun MODULE_LICENSE("GPL and additional rights");
362*4882a593Smuzhiyun MODULE_ALIAS_MISCDEV(AGPGART_MINOR);
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun module_init(agp_init);
365*4882a593Smuzhiyun module_exit(agp_exit);
366*4882a593Smuzhiyun
367