diff options
Diffstat (limited to 'drivers/char/agp/generic.c')
-rw-r--r-- | drivers/char/agp/generic.c | 125 |
1 files changed, 117 insertions, 8 deletions
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 3491d6f84bc6..7923337c3d26 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c | |||
@@ -101,6 +101,63 @@ 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 | if (mem->memory == NULL) { | ||
118 | mem->memory = vmalloc(size); | ||
119 | mem->vmalloc_flag = 1; | ||
120 | } | ||
121 | } | ||
122 | EXPORT_SYMBOL(agp_alloc_page_array); | ||
123 | |||
124 | void agp_free_page_array(struct agp_memory *mem) | ||
125 | { | ||
126 | if (mem->vmalloc_flag) { | ||
127 | vfree(mem->memory); | ||
128 | } else { | ||
129 | kfree(mem->memory); | ||
130 | } | ||
131 | } | ||
132 | EXPORT_SYMBOL(agp_free_page_array); | ||
133 | |||
134 | |||
135 | static struct agp_memory *agp_create_user_memory(unsigned long num_agp_pages) | ||
136 | { | ||
137 | struct agp_memory *new; | ||
138 | unsigned long alloc_size = num_agp_pages*sizeof(struct page *); | ||
139 | |||
140 | new = kzalloc(sizeof(struct agp_memory), GFP_KERNEL); | ||
141 | if (new == NULL) | ||
142 | return NULL; | ||
143 | |||
144 | new->key = agp_get_key(); | ||
145 | |||
146 | if (new->key < 0) { | ||
147 | kfree(new); | ||
148 | return NULL; | ||
149 | } | ||
150 | |||
151 | agp_alloc_page_array(alloc_size, new); | ||
152 | |||
153 | if (new->memory == NULL) { | ||
154 | agp_free_key(new->key); | ||
155 | kfree(new); | ||
156 | return NULL; | ||
157 | } | ||
158 | new->num_scratch_pages = 0; | ||
159 | return new; | ||
160 | } | ||
104 | 161 | ||
105 | struct agp_memory *agp_create_memory(int scratch_pages) | 162 | struct agp_memory *agp_create_memory(int scratch_pages) |
106 | { | 163 | { |
@@ -116,7 +173,8 @@ struct agp_memory *agp_create_memory(int scratch_pages) | |||
116 | kfree(new); | 173 | kfree(new); |
117 | return NULL; | 174 | return NULL; |
118 | } | 175 | } |
119 | new->memory = vmalloc(PAGE_SIZE * scratch_pages); | 176 | |
177 | agp_alloc_page_array(PAGE_SIZE * scratch_pages, new); | ||
120 | 178 | ||
121 | if (new->memory == NULL) { | 179 | if (new->memory == NULL) { |
122 | agp_free_key(new->key); | 180 | agp_free_key(new->key); |
@@ -124,6 +182,7 @@ struct agp_memory *agp_create_memory(int scratch_pages) | |||
124 | return NULL; | 182 | return NULL; |
125 | } | 183 | } |
126 | new->num_scratch_pages = scratch_pages; | 184 | new->num_scratch_pages = scratch_pages; |
185 | new->type = AGP_NORMAL_MEMORY; | ||
127 | return new; | 186 | return new; |
128 | } | 187 | } |
129 | EXPORT_SYMBOL(agp_create_memory); | 188 | EXPORT_SYMBOL(agp_create_memory); |
@@ -146,6 +205,11 @@ void agp_free_memory(struct agp_memory *curr) | |||
146 | if (curr->is_bound == TRUE) | 205 | if (curr->is_bound == TRUE) |
147 | agp_unbind_memory(curr); | 206 | agp_unbind_memory(curr); |
148 | 207 | ||
208 | if (curr->type >= AGP_USER_TYPES) { | ||
209 | agp_generic_free_by_type(curr); | ||
210 | return; | ||
211 | } | ||
212 | |||
149 | if (curr->type != 0) { | 213 | if (curr->type != 0) { |
150 | curr->bridge->driver->free_by_type(curr); | 214 | curr->bridge->driver->free_by_type(curr); |
151 | return; | 215 | return; |
@@ -157,7 +221,7 @@ void agp_free_memory(struct agp_memory *curr) | |||
157 | flush_agp_mappings(); | 221 | flush_agp_mappings(); |
158 | } | 222 | } |
159 | agp_free_key(curr->key); | 223 | agp_free_key(curr->key); |
160 | vfree(curr->memory); | 224 | agp_free_page_array(curr); |
161 | kfree(curr); | 225 | kfree(curr); |
162 | } | 226 | } |
163 | EXPORT_SYMBOL(agp_free_memory); | 227 | EXPORT_SYMBOL(agp_free_memory); |
@@ -188,6 +252,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) | 252 | if ((atomic_read(&bridge->current_memory_agp) + page_count) > bridge->max_memory_agp) |
189 | return NULL; | 253 | return NULL; |
190 | 254 | ||
255 | if (type >= AGP_USER_TYPES) { | ||
256 | new = agp_generic_alloc_user(page_count, type); | ||
257 | if (new) | ||
258 | new->bridge = bridge; | ||
259 | return new; | ||
260 | } | ||
261 | |||
191 | if (type != 0) { | 262 | if (type != 0) { |
192 | new = bridge->driver->alloc_by_type(page_count, type); | 263 | new = bridge->driver->alloc_by_type(page_count, type); |
193 | if (new) | 264 | if (new) |
@@ -960,6 +1031,7 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) | |||
960 | off_t j; | 1031 | off_t j; |
961 | void *temp; | 1032 | void *temp; |
962 | struct agp_bridge_data *bridge; | 1033 | struct agp_bridge_data *bridge; |
1034 | int mask_type; | ||
963 | 1035 | ||
964 | bridge = mem->bridge; | 1036 | bridge = mem->bridge; |
965 | if (!bridge) | 1037 | if (!bridge) |
@@ -995,7 +1067,11 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) | |||
995 | num_entries -= agp_memory_reserved/PAGE_SIZE; | 1067 | num_entries -= agp_memory_reserved/PAGE_SIZE; |
996 | if (num_entries < 0) num_entries = 0; | 1068 | if (num_entries < 0) num_entries = 0; |
997 | 1069 | ||
998 | if (type != 0 || mem->type != 0) { | 1070 | if (type != mem->type) |
1071 | return -EINVAL; | ||
1072 | |||
1073 | mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); | ||
1074 | if (mask_type != 0) { | ||
999 | /* The generic routines know nothing of memory types */ | 1075 | /* The generic routines know nothing of memory types */ |
1000 | return -EINVAL; | 1076 | return -EINVAL; |
1001 | } | 1077 | } |
@@ -1018,7 +1094,8 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) | |||
1018 | } | 1094 | } |
1019 | 1095 | ||
1020 | for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { | 1096 | 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); | 1097 | writel(bridge->driver->mask_memory(bridge, mem->memory[i], mask_type), |
1098 | bridge->gatt_table+j); | ||
1022 | } | 1099 | } |
1023 | readl(bridge->gatt_table+j-1); /* PCI Posting. */ | 1100 | readl(bridge->gatt_table+j-1); /* PCI Posting. */ |
1024 | 1101 | ||
@@ -1032,6 +1109,7 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) | |||
1032 | { | 1109 | { |
1033 | size_t i; | 1110 | size_t i; |
1034 | struct agp_bridge_data *bridge; | 1111 | struct agp_bridge_data *bridge; |
1112 | int mask_type; | ||
1035 | 1113 | ||
1036 | bridge = mem->bridge; | 1114 | bridge = mem->bridge; |
1037 | if (!bridge) | 1115 | if (!bridge) |
@@ -1040,7 +1118,11 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) | |||
1040 | if (mem->page_count == 0) | 1118 | if (mem->page_count == 0) |
1041 | return 0; | 1119 | return 0; |
1042 | 1120 | ||
1043 | if (type != 0 || mem->type != 0) { | 1121 | if (type != mem->type) |
1122 | return -EINVAL; | ||
1123 | |||
1124 | mask_type = bridge->driver->agp_type_to_mask_type(bridge, type); | ||
1125 | if (mask_type != 0) { | ||
1044 | /* The generic routines know nothing of memory types */ | 1126 | /* The generic routines know nothing of memory types */ |
1045 | return -EINVAL; | 1127 | return -EINVAL; |
1046 | } | 1128 | } |
@@ -1056,22 +1138,40 @@ int agp_generic_remove_memory(struct agp_memory *mem, off_t pg_start, int type) | |||
1056 | } | 1138 | } |
1057 | EXPORT_SYMBOL(agp_generic_remove_memory); | 1139 | EXPORT_SYMBOL(agp_generic_remove_memory); |
1058 | 1140 | ||
1059 | |||
1060 | struct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type) | 1141 | struct agp_memory *agp_generic_alloc_by_type(size_t page_count, int type) |
1061 | { | 1142 | { |
1062 | return NULL; | 1143 | return NULL; |
1063 | } | 1144 | } |
1064 | EXPORT_SYMBOL(agp_generic_alloc_by_type); | 1145 | EXPORT_SYMBOL(agp_generic_alloc_by_type); |
1065 | 1146 | ||
1066 | |||
1067 | void agp_generic_free_by_type(struct agp_memory *curr) | 1147 | void agp_generic_free_by_type(struct agp_memory *curr) |
1068 | { | 1148 | { |
1069 | vfree(curr->memory); | 1149 | agp_free_page_array(curr); |
1070 | agp_free_key(curr->key); | 1150 | agp_free_key(curr->key); |
1071 | kfree(curr); | 1151 | kfree(curr); |
1072 | } | 1152 | } |
1073 | EXPORT_SYMBOL(agp_generic_free_by_type); | 1153 | EXPORT_SYMBOL(agp_generic_free_by_type); |
1074 | 1154 | ||
1155 | struct agp_memory *agp_generic_alloc_user(size_t page_count, int type) | ||
1156 | { | ||
1157 | struct agp_memory *new; | ||
1158 | int i; | ||
1159 | int pages; | ||
1160 | |||
1161 | pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; | ||
1162 | new = agp_create_user_memory(page_count); | ||
1163 | if (new == NULL) | ||
1164 | return NULL; | ||
1165 | |||
1166 | for (i = 0; i < page_count; i++) | ||
1167 | new->memory[i] = 0; | ||
1168 | new->page_count = 0; | ||
1169 | new->type = type; | ||
1170 | new->num_scratch_pages = pages; | ||
1171 | |||
1172 | return new; | ||
1173 | } | ||
1174 | EXPORT_SYMBOL(agp_generic_alloc_user); | ||
1075 | 1175 | ||
1076 | /* | 1176 | /* |
1077 | * Basic Page Allocation Routines - | 1177 | * Basic Page Allocation Routines - |
@@ -1165,6 +1265,15 @@ unsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge, | |||
1165 | } | 1265 | } |
1166 | EXPORT_SYMBOL(agp_generic_mask_memory); | 1266 | EXPORT_SYMBOL(agp_generic_mask_memory); |
1167 | 1267 | ||
1268 | int agp_generic_type_to_mask_type(struct agp_bridge_data *bridge, | ||
1269 | int type) | ||
1270 | { | ||
1271 | if (type >= AGP_USER_TYPES) | ||
1272 | return 0; | ||
1273 | return type; | ||
1274 | } | ||
1275 | EXPORT_SYMBOL(agp_generic_type_to_mask_type); | ||
1276 | |||
1168 | /* | 1277 | /* |
1169 | * These functions are implemented according to the AGPv3 spec, | 1278 | * These functions are implemented according to the AGPv3 spec, |
1170 | * which covers implementation details that had previously been | 1279 | * which covers implementation details that had previously been |