diff options
author | Alan Cox <alan@linux.intel.com> | 2011-11-03 14:20:58 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2011-11-16 06:22:36 -0500 |
commit | e32681d66dd33a7792a3f1a1e3ea0eb0c415f895 (patch) | |
tree | 2c8b091cc0e31c1b1c523eaafddcfff6e7e4c5e3 /drivers/gpu/drm | |
parent | 3abcf41fb1fc6f127ff98d3bae75db7f8f575e47 (diff) |
gma500: GEM and GEM glue
The driver uses GEM along with a couple of small bits of wrapping of its
own. The only real oddity here is the support for using the 'stolen' memory
rather than wasting several MB.
We use a simple resource manager as we don't need to manage our space
intensively at all as we only do 2D work. We also have a GTT which is
entirely GPU facing so in the Cedarview case are not even allocating from
host address space.
Signed-off-by: Alan Cox <alan@linux.intel.com>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r-- | drivers/gpu/drm/gma500/gem.c | 295 | ||||
-rw-r--r-- | drivers/gpu/drm/gma500/gem_glue.c | 89 | ||||
-rw-r--r-- | drivers/gpu/drm/gma500/gem_glue.h | 2 |
3 files changed, 386 insertions, 0 deletions
diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c new file mode 100644 index 000000000000..65fdd6b8ab16 --- /dev/null +++ b/drivers/gpu/drm/gma500/gem.c | |||
@@ -0,0 +1,295 @@ | |||
1 | /* | ||
2 | * psb GEM interface | ||
3 | * | ||
4 | * Copyright (c) 2011, Intel Corporation. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License along with | ||
16 | * this program; if not, write to the Free Software Foundation, Inc., | ||
17 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | * | ||
19 | * Authors: Alan Cox | ||
20 | * | ||
21 | * TODO: | ||
22 | * - we need to work out if the MMU is relevant (eg for | ||
23 | * accelerated operations on a GEM object) | ||
24 | */ | ||
25 | |||
26 | #include <drm/drmP.h> | ||
27 | #include <drm/drm.h> | ||
28 | #include "psb_drm.h" | ||
29 | #include "psb_drv.h" | ||
30 | |||
31 | int psb_gem_init_object(struct drm_gem_object *obj) | ||
32 | { | ||
33 | return -EINVAL; | ||
34 | } | ||
35 | |||
36 | void psb_gem_free_object(struct drm_gem_object *obj) | ||
37 | { | ||
38 | struct gtt_range *gtt = container_of(obj, struct gtt_range, gem); | ||
39 | drm_gem_object_release_wrap(obj); | ||
40 | /* This must occur last as it frees up the memory of the GEM object */ | ||
41 | psb_gtt_free_range(obj->dev, gtt); | ||
42 | } | ||
43 | |||
44 | int psb_gem_get_aperture(struct drm_device *dev, void *data, | ||
45 | struct drm_file *file) | ||
46 | { | ||
47 | return -EINVAL; | ||
48 | } | ||
49 | |||
50 | /** | ||
51 | * psb_gem_dumb_map_gtt - buffer mapping for dumb interface | ||
52 | * @file: our drm client file | ||
53 | * @dev: drm device | ||
54 | * @handle: GEM handle to the object (from dumb_create) | ||
55 | * | ||
56 | * Do the necessary setup to allow the mapping of the frame buffer | ||
57 | * into user memory. We don't have to do much here at the moment. | ||
58 | */ | ||
59 | int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev, | ||
60 | uint32_t handle, uint64_t *offset) | ||
61 | { | ||
62 | int ret = 0; | ||
63 | struct drm_gem_object *obj; | ||
64 | |||
65 | if (!(dev->driver->driver_features & DRIVER_GEM)) | ||
66 | return -ENODEV; | ||
67 | |||
68 | mutex_lock(&dev->struct_mutex); | ||
69 | |||
70 | /* GEM does all our handle to object mapping */ | ||
71 | obj = drm_gem_object_lookup(dev, file, handle); | ||
72 | if (obj == NULL) { | ||
73 | ret = -ENOENT; | ||
74 | goto unlock; | ||
75 | } | ||
76 | /* What validation is needed here ? */ | ||
77 | |||
78 | /* Make it mmapable */ | ||
79 | if (!obj->map_list.map) { | ||
80 | ret = gem_create_mmap_offset(obj); | ||
81 | if (ret) | ||
82 | goto out; | ||
83 | } | ||
84 | /* GEM should really work out the hash offsets for us */ | ||
85 | *offset = (u64)obj->map_list.hash.key << PAGE_SHIFT; | ||
86 | out: | ||
87 | drm_gem_object_unreference(obj); | ||
88 | unlock: | ||
89 | mutex_unlock(&dev->struct_mutex); | ||
90 | return ret; | ||
91 | } | ||
92 | |||
93 | /** | ||
94 | * psb_gem_create - create a mappable object | ||
95 | * @file: the DRM file of the client | ||
96 | * @dev: our device | ||
97 | * @size: the size requested | ||
98 | * @handlep: returned handle (opaque number) | ||
99 | * | ||
100 | * Create a GEM object, fill in the boilerplate and attach a handle to | ||
101 | * it so that userspace can speak about it. This does the core work | ||
102 | * for the various methods that do/will create GEM objects for things | ||
103 | */ | ||
104 | static int psb_gem_create(struct drm_file *file, | ||
105 | struct drm_device *dev, uint64_t size, uint32_t *handlep) | ||
106 | { | ||
107 | struct gtt_range *r; | ||
108 | int ret; | ||
109 | u32 handle; | ||
110 | |||
111 | size = roundup(size, PAGE_SIZE); | ||
112 | |||
113 | /* Allocate our object - for now a direct gtt range which is not | ||
114 | stolen memory backed */ | ||
115 | r = psb_gtt_alloc_range(dev, size, "gem", 0); | ||
116 | if (r == NULL) { | ||
117 | dev_err(dev->dev, "no memory for %lld byte GEM object\n", size); | ||
118 | return -ENOSPC; | ||
119 | } | ||
120 | /* Initialize the extra goodies GEM needs to do all the hard work */ | ||
121 | if (drm_gem_object_init(dev, &r->gem, size) != 0) { | ||
122 | psb_gtt_free_range(dev, r); | ||
123 | /* GEM doesn't give an error code and we don't have an | ||
124 | EGEMSUCKS so make something up for now - FIXME */ | ||
125 | dev_err(dev->dev, "GEM init failed for %lld\n", size); | ||
126 | return -ENOMEM; | ||
127 | } | ||
128 | /* Give the object a handle so we can carry it more easily */ | ||
129 | ret = drm_gem_handle_create(file, &r->gem, &handle); | ||
130 | if (ret) { | ||
131 | dev_err(dev->dev, "GEM handle failed for %p, %lld\n", | ||
132 | &r->gem, size); | ||
133 | drm_gem_object_release(&r->gem); | ||
134 | psb_gtt_free_range(dev, r); | ||
135 | return ret; | ||
136 | } | ||
137 | /* We have the initial and handle reference but need only one now */ | ||
138 | drm_gem_object_unreference(&r->gem); | ||
139 | *handlep = handle; | ||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * psb_gem_dumb_create - create a dumb buffer | ||
145 | * @drm_file: our client file | ||
146 | * @dev: our device | ||
147 | * @args: the requested arguments copied from userspace | ||
148 | * | ||
149 | * Allocate a buffer suitable for use for a frame buffer of the | ||
150 | * form described by user space. Give userspace a handle by which | ||
151 | * to reference it. | ||
152 | */ | ||
153 | int psb_gem_dumb_create(struct drm_file *file, struct drm_device *dev, | ||
154 | struct drm_mode_create_dumb *args) | ||
155 | { | ||
156 | args->pitch = ALIGN(args->width * ((args->bpp + 7) / 8), 64); | ||
157 | args->size = args->pitch * args->height; | ||
158 | return psb_gem_create(file, dev, args->size, &args->handle); | ||
159 | } | ||
160 | |||
161 | /** | ||
162 | * psb_gem_dumb_destroy - destroy a dumb buffer | ||
163 | * @file: client file | ||
164 | * @dev: our DRM device | ||
165 | * @handle: the object handle | ||
166 | * | ||
167 | * Destroy a handle that was created via psb_gem_dumb_create, at least | ||
168 | * we hope it was created that way. i915 seems to assume the caller | ||
169 | * does the checking but that might be worth review ! FIXME | ||
170 | */ | ||
171 | int psb_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, | ||
172 | uint32_t handle) | ||
173 | { | ||
174 | /* No special work needed, drop the reference and see what falls out */ | ||
175 | return drm_gem_handle_delete(file, handle); | ||
176 | } | ||
177 | |||
178 | /** | ||
179 | * psb_gem_fault - pagefault handler for GEM objects | ||
180 | * @vma: the VMA of the GEM object | ||
181 | * @vmf: fault detail | ||
182 | * | ||
183 | * Invoked when a fault occurs on an mmap of a GEM managed area. GEM | ||
184 | * does most of the work for us including the actual map/unmap calls | ||
185 | * but we need to do the actual page work. | ||
186 | * | ||
187 | * This code eventually needs to handle faulting objects in and out | ||
188 | * of the GTT and repacking it when we run out of space. We can put | ||
189 | * that off for now and for our simple uses | ||
190 | * | ||
191 | * The VMA was set up by GEM. In doing so it also ensured that the | ||
192 | * vma->vm_private_data points to the GEM object that is backing this | ||
193 | * mapping. | ||
194 | * | ||
195 | * FIXME | ||
196 | */ | ||
197 | int psb_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | ||
198 | { | ||
199 | struct drm_gem_object *obj; | ||
200 | struct gtt_range *r; | ||
201 | int ret; | ||
202 | unsigned long pfn; | ||
203 | pgoff_t page_offset; | ||
204 | struct drm_device *dev; | ||
205 | struct drm_psb_private *dev_priv; | ||
206 | |||
207 | obj = vma->vm_private_data; /* GEM object */ | ||
208 | dev = obj->dev; | ||
209 | dev_priv = dev->dev_private; | ||
210 | |||
211 | r = container_of(obj, struct gtt_range, gem); /* Get the gtt range */ | ||
212 | |||
213 | /* Make sure we don't parallel update on a fault, nor move or remove | ||
214 | something from beneath our feet */ | ||
215 | mutex_lock(&dev->struct_mutex); | ||
216 | |||
217 | /* For now the mmap pins the object and it stays pinned. As things | ||
218 | stand that will do us no harm */ | ||
219 | if (r->mmapping == 0) { | ||
220 | ret = psb_gtt_pin(r); | ||
221 | if (ret < 0) { | ||
222 | dev_err(dev->dev, "gma500: pin failed: %d\n", ret); | ||
223 | goto fail; | ||
224 | } | ||
225 | r->mmapping = 1; | ||
226 | } | ||
227 | |||
228 | /* Page relative to the VMA start - we must calculate this ourselves | ||
229 | because vmf->pgoff is the fake GEM offset */ | ||
230 | page_offset = ((unsigned long) vmf->virtual_address - vma->vm_start) | ||
231 | >> PAGE_SHIFT; | ||
232 | |||
233 | /* CPU view of the page, don't go via the GART for CPU writes */ | ||
234 | if (r->stolen) | ||
235 | pfn = (dev_priv->stolen_base + r->offset) >> PAGE_SHIFT; | ||
236 | else | ||
237 | pfn = page_to_pfn(r->pages[page_offset]); | ||
238 | ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn); | ||
239 | |||
240 | fail: | ||
241 | mutex_unlock(&dev->struct_mutex); | ||
242 | switch (ret) { | ||
243 | case 0: | ||
244 | case -ERESTARTSYS: | ||
245 | case -EINTR: | ||
246 | return VM_FAULT_NOPAGE; | ||
247 | case -ENOMEM: | ||
248 | return VM_FAULT_OOM; | ||
249 | default: | ||
250 | return VM_FAULT_SIGBUS; | ||
251 | } | ||
252 | } | ||
253 | |||
254 | static int psb_gem_create_stolen(struct drm_file *file, struct drm_device *dev, | ||
255 | int size, u32 *handle) | ||
256 | { | ||
257 | struct gtt_range *gtt = psb_gtt_alloc_range(dev, size, "gem", 1); | ||
258 | if (gtt == NULL) | ||
259 | return -ENOMEM; | ||
260 | if (drm_gem_private_object_init(dev, >t->gem, size) != 0) | ||
261 | goto free_gtt; | ||
262 | if (drm_gem_handle_create(file, >t->gem, handle) == 0) | ||
263 | return 0; | ||
264 | free_gtt: | ||
265 | psb_gtt_free_range(dev, gtt); | ||
266 | return -ENOMEM; | ||
267 | } | ||
268 | |||
269 | /* | ||
270 | * GEM interfaces for our specific client | ||
271 | */ | ||
272 | int psb_gem_create_ioctl(struct drm_device *dev, void *data, | ||
273 | struct drm_file *file) | ||
274 | { | ||
275 | struct drm_psb_gem_create *args = data; | ||
276 | int ret; | ||
277 | if (args->flags & PSB_GEM_CREATE_STOLEN) { | ||
278 | ret = psb_gem_create_stolen(file, dev, args->size, | ||
279 | &args->handle); | ||
280 | if (ret == 0) | ||
281 | return 0; | ||
282 | /* Fall throguh */ | ||
283 | args->flags &= ~PSB_GEM_CREATE_STOLEN; | ||
284 | } | ||
285 | return psb_gem_create(file, dev, args->size, &args->handle); | ||
286 | } | ||
287 | |||
288 | int psb_gem_mmap_ioctl(struct drm_device *dev, void *data, | ||
289 | struct drm_file *file) | ||
290 | { | ||
291 | struct drm_psb_gem_mmap *args = data; | ||
292 | return dev->driver->dumb_map_offset(file, dev, | ||
293 | args->handle, &args->offset); | ||
294 | } | ||
295 | |||
diff --git a/drivers/gpu/drm/gma500/gem_glue.c b/drivers/gpu/drm/gma500/gem_glue.c new file mode 100644 index 000000000000..daac12120653 --- /dev/null +++ b/drivers/gpu/drm/gma500/gem_glue.c | |||
@@ -0,0 +1,89 @@ | |||
1 | /************************************************************************** | ||
2 | * Copyright (c) 2011, Intel Corporation. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms and conditions of the GNU General Public License, | ||
7 | * version 2, as published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
12 | * more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License along with | ||
15 | * this program; if not, write to the Free Software Foundation, Inc., | ||
16 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
17 | * | ||
18 | **************************************************************************/ | ||
19 | |||
20 | #include <drm/drmP.h> | ||
21 | #include <drm/drm.h> | ||
22 | |||
23 | void drm_gem_object_release_wrap(struct drm_gem_object *obj) | ||
24 | { | ||
25 | /* Remove the list map if one is present */ | ||
26 | if (obj->map_list.map) { | ||
27 | struct drm_gem_mm *mm = obj->dev->mm_private; | ||
28 | struct drm_map_list *list = &obj->map_list; | ||
29 | drm_ht_remove_item(&mm->offset_hash, &list->hash); | ||
30 | drm_mm_put_block(list->file_offset_node); | ||
31 | kfree(list->map); | ||
32 | list->map = NULL; | ||
33 | } | ||
34 | drm_gem_object_release(obj); | ||
35 | } | ||
36 | |||
37 | /** | ||
38 | * gem_create_mmap_offset - invent an mmap offset | ||
39 | * @obj: our object | ||
40 | * | ||
41 | * Standard implementation of offset generation for mmap as is | ||
42 | * duplicated in several drivers. This belongs in GEM. | ||
43 | */ | ||
44 | int gem_create_mmap_offset(struct drm_gem_object *obj) | ||
45 | { | ||
46 | struct drm_device *dev = obj->dev; | ||
47 | struct drm_gem_mm *mm = dev->mm_private; | ||
48 | struct drm_map_list *list; | ||
49 | struct drm_local_map *map; | ||
50 | int ret; | ||
51 | |||
52 | list = &obj->map_list; | ||
53 | list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL); | ||
54 | if (list->map == NULL) | ||
55 | return -ENOMEM; | ||
56 | map = list->map; | ||
57 | map->type = _DRM_GEM; | ||
58 | map->size = obj->size; | ||
59 | map->handle = obj; | ||
60 | |||
61 | list->file_offset_node = drm_mm_search_free(&mm->offset_manager, | ||
62 | obj->size / PAGE_SIZE, 0, 0); | ||
63 | if (!list->file_offset_node) { | ||
64 | dev_err(dev->dev, "failed to allocate offset for bo %d\n", | ||
65 | obj->name); | ||
66 | ret = -ENOSPC; | ||
67 | goto free_it; | ||
68 | } | ||
69 | list->file_offset_node = drm_mm_get_block(list->file_offset_node, | ||
70 | obj->size / PAGE_SIZE, 0); | ||
71 | if (!list->file_offset_node) { | ||
72 | ret = -ENOMEM; | ||
73 | goto free_it; | ||
74 | } | ||
75 | list->hash.key = list->file_offset_node->start; | ||
76 | ret = drm_ht_insert_item(&mm->offset_hash, &list->hash); | ||
77 | if (ret) { | ||
78 | dev_err(dev->dev, "failed to add to map hash\n"); | ||
79 | goto free_mm; | ||
80 | } | ||
81 | return 0; | ||
82 | |||
83 | free_mm: | ||
84 | drm_mm_put_block(list->file_offset_node); | ||
85 | free_it: | ||
86 | kfree(list->map); | ||
87 | list->map = NULL; | ||
88 | return ret; | ||
89 | } | ||
diff --git a/drivers/gpu/drm/gma500/gem_glue.h b/drivers/gpu/drm/gma500/gem_glue.h new file mode 100644 index 000000000000..ce5ce30f74db --- /dev/null +++ b/drivers/gpu/drm/gma500/gem_glue.h | |||
@@ -0,0 +1,2 @@ | |||
1 | extern void drm_gem_object_release_wrap(struct drm_gem_object *obj); | ||
2 | extern int gem_create_mmap_offset(struct drm_gem_object *obj); | ||