diff options
Diffstat (limited to 'drivers/char/agp/generic.c')
-rw-r--r-- | drivers/char/agp/generic.c | 130 |
1 files changed, 124 insertions, 6 deletions
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 3491d6f84bc6..a627b771c2eb 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c | |||
@@ -101,6 +101,67 @@ static int agp_get_key(void) | |||
101 | return -1; | 101 | return -1; |
102 | } | 102 | } |
103 | 103 | ||
104 | /* | ||
105 | * Use kmalloc if possible for the page list. Otherwise fall back to | ||
106 | * vmalloc. This speeds things up and also saves memory for small AGP | ||
107 | * regions. | ||
108 | */ | ||
109 | |||
110 | void agp_alloc_page_array(size_t size, struct agp_memory *mem) | ||
111 | { | ||
112 | mem->memory = NULL; | ||
113 | mem->vmalloc_flag = 0; | ||
114 | |||
115 | if (size <= 2*PAGE_SIZE) { | ||
116 | mem->memory = kmalloc(size, GFP_KERNEL | __GFP_NORETRY); | ||
117 | } | ||
118 | if (mem->memory == NULL) { | ||
119 | mem->memory = vmalloc(size); | ||
120 | mem->vmalloc_flag = 1; | ||
121 | } | ||
122 | } | ||
123 | EXPORT_SYMBOL(agp_alloc_page_array); | ||
124 | |||
125 | void agp_free_page_array(struct agp_memory *mem) | ||
126 | { | ||
127 | if (mem->vmalloc_flag) { | ||
128 | vfree(mem->memory); | ||
129 | } else { | ||
130 | kfree(mem->memory); | ||
131 | } | ||
132 | } | ||
133 | EXPORT_SYMBOL(agp_free_page_array); | ||
134 | |||
135 | |||
136 | static struct agp_memory *agp_create_user_memory(unsigned long num_agp_pages) | ||
137 | { | ||
138 | struct agp_memory *new; | ||
139 | unsigned long alloc_size = num_agp_pages*sizeof(struct page *); | ||
140 | |||
141 | new = kmalloc(sizeof(struct agp_memory), GFP_KERNEL); | ||
142 | |||
143 | if (new == NULL) | ||
144 | return NULL; | ||
145 | |||
146 | memset(new, 0, sizeof(struct agp_memory)); | ||
147 | new->key = agp_get_key(); | ||
148 | |||
149 | if (new->key < 0) { | ||
150 | kfree(new); | ||
151 | return NULL; | ||
152 | } | ||
153 | |||
154 | agp_alloc_page_array(alloc_size, new); | ||
155 | |||
156 | if (new->memory == NULL) { | ||
157 | agp_free_key(new->key); | ||
158 | kfree(new); | ||
159 | return NULL; | ||
160 | } | ||
161 | new->num_scratch_pages = 0; | ||
162 | return new; | ||
163 | } | ||
164 | |||
104 | 165 | ||
105 | struct agp_memory *agp_create_memory(int scratch_pages) | 166 | struct agp_memory *agp_create_memory(int scratch_pages) |
106 | { | 167 | { |
@@ -116,7 +177,8 @@ struct agp_memory *agp_create_memory(int scratch_pages) | |||
116 | kfree(new); | 177 | kfree(new); |
117 | return NULL; | 178 | return NULL; |
118 | } | 179 | } |
119 | new->memory = vmalloc(PAGE_SIZE * scratch_pages); | 180 | |
181 | agp_alloc_page_array(PAGE_SIZE * scratch_pages, new); | ||
120 | 182 | ||
121 | if (new->memory == NULL) { | 183 | if (new->memory == NULL) { |
122 | agp_free_key(new->key); | 184 | agp_free_key(new->key); |
@@ -124,6 +186,7 @@ struct agp_memory *agp_create_memory(int scratch_pages) | |||
124 | return NULL; | 186 | return NULL; |
125 | } | 187 | } |
126 | new->num_scratch_pages = scratch_pages; | 188 | new->num_scratch_pages = scratch_pages; |
189 | new->type = AGP_NORMAL_MEMORY; | ||
127 | return new; | 190 | return new; |
128 | } | 191 | } |
129 | EXPORT_SYMBOL(agp_create_memory); | 192 | EXPORT_SYMBOL(agp_create_memory); |
@@ -146,6 +209,11 @@ void agp_free_memory(struct agp_memory *curr) | |||
146 | if (curr->is_bound == TRUE) | 209 | if (curr->is_bound == TRUE) |
147 | agp_unbind_memory(curr); | 210 | agp_unbind_memory(curr); |
148 | 211 | ||
212 | if (curr->type >= AGP_USER_TYPES) { | ||
213 | agp_generic_free_by_type(curr); | ||
214 | return; | ||
215 | } | ||
216 | |||
149 | if (curr->type != 0) { | 217 | if (curr->type != 0) { |
150 | curr->bridge->driver->free_by_type(curr); | 218 | curr->bridge->driver->free_by_type(curr); |
151 | return; | 219 | return; |
@@ -157,7 +225,7 @@ void agp_free_memory(struct agp_memory *curr) | |||
157 | flush_agp_mappings(); | 225 | flush_agp_mappings(); |
158 | } | 226 | } |
159 | agp_free_key(curr->key); | 227 | agp_free_key(curr->key); |
160 | vfree(curr->memory); | 228 | agp_free_page_array(curr); |
161 | kfree(curr); | 229 | kfree(curr); |
162 | } | 230 | } |
163 | EXPORT_SYMBOL(agp_free_memory); | 231 | EXPORT_SYMBOL(agp_free_memory); |
@@ -188,6 +256,13 @@ struct agp_memory *agp_allocate_memory(struct agp_bridge_data *bridge, | |||
188 | if ((atomic_read(&bridge->current_memory_agp) + page_count) > bridge->max_memory_agp) | 256 | if ((atomic_read(&bridge->current_memory_agp) + page_count) > bridge->max_memory_agp) |
189 | return NULL; | 257 | return NULL; |
190 | 258 | ||
259 | if (type >= AGP_USER_TYPES) { | ||
260 | new = agp_generic_alloc_user(page_count, type); | ||
261 | if (new) | ||
262 | new->bridge = bridge; | ||
263 | return new; | ||
264 | } | ||
265 | |||
191 | if (type != 0) { | 266 | if (type != 0) { |
192 | new = bridge->driver->alloc_by_type(page_count, type); | 267 | new = bridge->driver->alloc_by_type(page_count, type); |
193 | if (new) | 268 | if (new) |
@@ -960,6 +1035,7 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) | |||
960 | off_t j; | 1035 | off_t j; |
961 | void *temp; | 1036 | void *temp; |
962 | struct agp_bridge_data *bridge; | 1037 | struct agp_bridge_data *bridge; |
1038 | int mask_type; | ||
963 | 1039 | ||
964 | bridge = mem->bridge; | 1040 | bridge = mem->bridge; |
965 | if (!bridge) | 1041 | if (!bridge) |
@@ -995,7 +1071,12 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) | |||
995 | num_entries -= agp_memory_reserved/PAGE_SIZE; | 1071 | num_entries -= agp_memory_reserved/PAGE_SIZE; |
996 | if (num_entries < 0) num_entries = 0; | 1072 | if (num_entries < 0) num_entries = 0; |
997 | 1073 | ||
998 | if (type != 0 || mem->type != 0) { | 1074 | if (type != mem->type) { |
1075 | return -EINVAL; | ||
1076 | } | ||
1077 | |||
1078 | mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); | ||
1079 | if (mask_type != 0) { | ||
999 | /* The generic routines know nothing of memory types */ | 1080 | /* The generic routines know nothing of memory types */ |
1000 | return -EINVAL; | 1081 | return -EINVAL; |
1001 | } | 1082 | } |
@@ -1018,7 +1099,8 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) | |||
1018 | } | 1099 | } |
1019 | 1100 | ||
1020 | for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { | 1101 | for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { |
1021 | writel(bridge->driver->mask_memory(bridge, mem->memory[i], mem->type), bridge->gatt_table+j); | 1102 | writel(bridge->driver->mask_memory(bridge, mem->memory[i], mask_type), |
1103 | bridge->gatt_table+j); | ||
1022 | } | 1104 | } |
1023 | readl(bridge->gatt_table+j-1); /* PCI Posting. */ | 1105 | readl(bridge->gatt_table+j-1); /* PCI Posting. */ |
1024 | 1106 | ||
@@ -1032,6 +1114,7 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) | |||
1032 | { | 1114 | { |
1033 | size_t i; | 1115 | size_t i; |
1034 | struct agp_bridge_data *bridge; | 1116 | struct agp_bridge_data *bridge; |
1117 | int mask_type; | ||
1035 | 1118 | ||
1036 | bridge = mem->bridge; | 1119 | bridge = mem->bridge; |
1037 | if (!bridge) | 1120 | if (!bridge) |
@@ -1040,7 +1123,11 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) | |||
1040 | if (mem->page_count == 0) | 1123 | if (mem->page_count == 0) |
1041 | return 0; | 1124 | return 0; |
1042 | 1125 | ||
1043 | if (type != 0 || mem->type != 0) { | 1126 | if (type != mem->type) |
1127 | return -EINVAL; | ||
1128 | |||
1129 | mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); | ||
1130 | if (mask_type != 0) { | ||
1044 | /* The generic routines know nothing of memory types */ | 1131 | /* The generic routines know nothing of memory types */ |
1045 | return -EINVAL; | 1132 | return -EINVAL; |
1046 | } | 1133 | } |
@@ -1066,12 +1153,34 @@ EXPORT_SYMBOL(agp_generic_alloc_by_type); | |||
1066 | 1153 | ||
1067 | void agp_generic_free_by_type(struct agp_memory *curr) | 1154 | void agp_generic_free_by_type(struct agp_memory *curr) |
1068 | { | 1155 | { |
1069 | vfree(curr->memory); | 1156 | agp_free_page_array(curr); |
1070 | agp_free_key(curr->key); | 1157 | agp_free_key(curr->key); |
1071 | kfree(curr); | 1158 | kfree(curr); |
1072 | } | 1159 | } |
1073 | EXPORT_SYMBOL(agp_generic_free_by_type); | 1160 | EXPORT_SYMBOL(agp_generic_free_by_type); |
1074 | 1161 | ||
1162 | struct agp_memory *agp_generic_alloc_user(size_t page_count, int type) | ||
1163 | { | ||
1164 | struct agp_memory *new; | ||
1165 | int i; | ||
1166 | int pages; | ||
1167 | |||
1168 | pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; | ||
1169 | new = agp_create_user_memory(page_count); | ||
1170 | if (new == NULL) | ||
1171 | return NULL; | ||
1172 | |||
1173 | for (i = 0; i < page_count; i++) { | ||
1174 | new->memory[i] = 0; | ||
1175 | } | ||
1176 | new->page_count = 0; | ||
1177 | new->type = type; | ||
1178 | new->num_scratch_pages = pages; | ||
1179 | |||
1180 | return new; | ||
1181 | } | ||
1182 | EXPORT_SYMBOL(agp_generic_alloc_user); | ||
1183 | |||
1075 | 1184 | ||
1076 | /* | 1185 | /* |
1077 | * Basic Page Allocation Routines - | 1186 | * Basic Page Allocation Routines - |
@@ -1165,6 +1274,15 @@ unsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge, | |||
1165 | } | 1274 | } |
1166 | EXPORT_SYMBOL(agp_generic_mask_memory); | 1275 | EXPORT_SYMBOL(agp_generic_mask_memory); |
1167 | 1276 | ||
1277 | int agp_generic_type_to_mask_type(struct agp_bridge_data *bridge, | ||
1278 | int type) | ||
1279 | { | ||
1280 | if (type >= AGP_USER_TYPES) | ||
1281 | return 0; | ||
1282 | return type; | ||
1283 | } | ||
1284 | EXPORT_SYMBOL(agp_generic_type_to_mask_type); | ||
1285 | |||
1168 | /* | 1286 | /* |
1169 | * These functions are implemented according to the AGPv3 spec, | 1287 | * These functions are implemented according to the AGPv3 spec, |
1170 | * which covers implementation details that had previously been | 1288 | * which covers implementation details that had previously been |