diff options
author | Dave Airlie <airlied@redhat.com> | 2013-04-22 06:49:07 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2013-04-22 06:49:07 -0400 |
commit | f9df7ea33c87291365d943828bec852874f15c2f (patch) | |
tree | 53afb4d74f0af01555a13d440d978dff3318c6f3 /drivers/gpu/host1x/drm/gem.c | |
parent | ce83adf78bbbe6bdcd99f0b97212337ce6b84940 (diff) | |
parent | e1041ca41670dc5502deee1fa3517dbaf9c0a09e (diff) |
Merge tag 'drm/tegra/for-3.10' of git://anongit.freedesktop.org/tegra/linux into drm-next
drm/tegra: Changes for v3.10-rc1
The bulk of this pull-request is the host1x series that has been in the
works for a few months. The current implementation looks good and has
been tested by several independent parties. So far no issues have been
found. To be on the safe side, the new Tegra-specific DRM IOCTLs depend
on staging in order to give some amount of flexibility to change them
just in case. The plan is to remove that dependency once more userspace
exists to verify the adequacy of the IOCTLs.
Currently only the 2D engine is supported, but patches are in the works
to enable 3D support on top of this framework as well. Various bits of
open-source userspace exist to test the 2D and 3D support[0]. This is
still a bit immature but it allows to verify that the kernel interfaces
work properly.
To round things off there are two smaller cleanup patches, one of them
adding a new pixel format and the other removing a redundent Kconfig
dependency.
[0]: https://github.com/grate-driver
* tag 'drm/tegra/for-3.10' of git://anongit.freedesktop.org/tegra/linux:
drm/tegra: don't depend on OF
drm/tegra: Support the XBGR8888 pixelformat
drm/tegra: Add gr2d device
gpu: host1x: drm: Add memory manager and fb
gpu: host1x: Remove second host1x driver
gpu: host1x: drm: Rename host1x to host1x_drm
drm/tegra: Move drm to live under host1x
gpu: host1x: Add debug support
gpu: host1x: Add channel support
gpu: host1x: Add syncpoint wait and interrupts
gpu: host1x: Add host1x driver
Diffstat (limited to 'drivers/gpu/host1x/drm/gem.c')
-rw-r--r-- | drivers/gpu/host1x/drm/gem.c | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/drivers/gpu/host1x/drm/gem.c b/drivers/gpu/host1x/drm/gem.c new file mode 100644 index 000000000000..c5e9a9b494c2 --- /dev/null +++ b/drivers/gpu/host1x/drm/gem.c | |||
@@ -0,0 +1,270 @@ | |||
1 | /* | ||
2 | * NVIDIA Tegra DRM GEM helper functions | ||
3 | * | ||
4 | * Copyright (C) 2012 Sascha Hauer, Pengutronix | ||
5 | * Copyright (C) 2013 NVIDIA CORPORATION, All rights reserved. | ||
6 | * | ||
7 | * Based on the GEM/CMA helpers | ||
8 | * | ||
9 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * as published by the Free Software Foundation; either version 2 | ||
14 | * of the License, or (at your option) any later version. | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | */ | ||
20 | |||
21 | #include <linux/mm.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/mutex.h> | ||
24 | #include <linux/export.h> | ||
25 | #include <linux/dma-mapping.h> | ||
26 | |||
27 | #include <drm/drmP.h> | ||
28 | #include <drm/drm.h> | ||
29 | |||
30 | #include "gem.h" | ||
31 | |||
32 | static inline struct tegra_bo *host1x_to_drm_bo(struct host1x_bo *bo) | ||
33 | { | ||
34 | return container_of(bo, struct tegra_bo, base); | ||
35 | } | ||
36 | |||
37 | static void tegra_bo_put(struct host1x_bo *bo) | ||
38 | { | ||
39 | struct tegra_bo *obj = host1x_to_drm_bo(bo); | ||
40 | struct drm_device *drm = obj->gem.dev; | ||
41 | |||
42 | mutex_lock(&drm->struct_mutex); | ||
43 | drm_gem_object_unreference(&obj->gem); | ||
44 | mutex_unlock(&drm->struct_mutex); | ||
45 | } | ||
46 | |||
47 | static dma_addr_t tegra_bo_pin(struct host1x_bo *bo, struct sg_table **sgt) | ||
48 | { | ||
49 | struct tegra_bo *obj = host1x_to_drm_bo(bo); | ||
50 | |||
51 | return obj->paddr; | ||
52 | } | ||
53 | |||
54 | static void tegra_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt) | ||
55 | { | ||
56 | } | ||
57 | |||
58 | static void *tegra_bo_mmap(struct host1x_bo *bo) | ||
59 | { | ||
60 | struct tegra_bo *obj = host1x_to_drm_bo(bo); | ||
61 | |||
62 | return obj->vaddr; | ||
63 | } | ||
64 | |||
65 | static void tegra_bo_munmap(struct host1x_bo *bo, void *addr) | ||
66 | { | ||
67 | } | ||
68 | |||
69 | static void *tegra_bo_kmap(struct host1x_bo *bo, unsigned int page) | ||
70 | { | ||
71 | struct tegra_bo *obj = host1x_to_drm_bo(bo); | ||
72 | |||
73 | return obj->vaddr + page * PAGE_SIZE; | ||
74 | } | ||
75 | |||
76 | static void tegra_bo_kunmap(struct host1x_bo *bo, unsigned int page, | ||
77 | void *addr) | ||
78 | { | ||
79 | } | ||
80 | |||
81 | static struct host1x_bo *tegra_bo_get(struct host1x_bo *bo) | ||
82 | { | ||
83 | struct tegra_bo *obj = host1x_to_drm_bo(bo); | ||
84 | struct drm_device *drm = obj->gem.dev; | ||
85 | |||
86 | mutex_lock(&drm->struct_mutex); | ||
87 | drm_gem_object_reference(&obj->gem); | ||
88 | mutex_unlock(&drm->struct_mutex); | ||
89 | |||
90 | return bo; | ||
91 | } | ||
92 | |||
93 | const struct host1x_bo_ops tegra_bo_ops = { | ||
94 | .get = tegra_bo_get, | ||
95 | .put = tegra_bo_put, | ||
96 | .pin = tegra_bo_pin, | ||
97 | .unpin = tegra_bo_unpin, | ||
98 | .mmap = tegra_bo_mmap, | ||
99 | .munmap = tegra_bo_munmap, | ||
100 | .kmap = tegra_bo_kmap, | ||
101 | .kunmap = tegra_bo_kunmap, | ||
102 | }; | ||
103 | |||
104 | static void tegra_bo_destroy(struct drm_device *drm, struct tegra_bo *bo) | ||
105 | { | ||
106 | dma_free_writecombine(drm->dev, bo->gem.size, bo->vaddr, bo->paddr); | ||
107 | } | ||
108 | |||
109 | unsigned int tegra_bo_get_mmap_offset(struct tegra_bo *bo) | ||
110 | { | ||
111 | return (unsigned int)bo->gem.map_list.hash.key << PAGE_SHIFT; | ||
112 | } | ||
113 | |||
114 | struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size) | ||
115 | { | ||
116 | struct tegra_bo *bo; | ||
117 | int err; | ||
118 | |||
119 | bo = kzalloc(sizeof(*bo), GFP_KERNEL); | ||
120 | if (!bo) | ||
121 | return ERR_PTR(-ENOMEM); | ||
122 | |||
123 | host1x_bo_init(&bo->base, &tegra_bo_ops); | ||
124 | size = round_up(size, PAGE_SIZE); | ||
125 | |||
126 | bo->vaddr = dma_alloc_writecombine(drm->dev, size, &bo->paddr, | ||
127 | GFP_KERNEL | __GFP_NOWARN); | ||
128 | if (!bo->vaddr) { | ||
129 | dev_err(drm->dev, "failed to allocate buffer with size %u\n", | ||
130 | size); | ||
131 | err = -ENOMEM; | ||
132 | goto err_dma; | ||
133 | } | ||
134 | |||
135 | err = drm_gem_object_init(drm, &bo->gem, size); | ||
136 | if (err) | ||
137 | goto err_init; | ||
138 | |||
139 | err = drm_gem_create_mmap_offset(&bo->gem); | ||
140 | if (err) | ||
141 | goto err_mmap; | ||
142 | |||
143 | return bo; | ||
144 | |||
145 | err_mmap: | ||
146 | drm_gem_object_release(&bo->gem); | ||
147 | err_init: | ||
148 | tegra_bo_destroy(drm, bo); | ||
149 | err_dma: | ||
150 | kfree(bo); | ||
151 | |||
152 | return ERR_PTR(err); | ||
153 | |||
154 | } | ||
155 | |||
156 | struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file, | ||
157 | struct drm_device *drm, | ||
158 | unsigned int size, | ||
159 | unsigned int *handle) | ||
160 | { | ||
161 | struct tegra_bo *bo; | ||
162 | int ret; | ||
163 | |||
164 | bo = tegra_bo_create(drm, size); | ||
165 | if (IS_ERR(bo)) | ||
166 | return bo; | ||
167 | |||
168 | ret = drm_gem_handle_create(file, &bo->gem, handle); | ||
169 | if (ret) | ||
170 | goto err; | ||
171 | |||
172 | drm_gem_object_unreference_unlocked(&bo->gem); | ||
173 | |||
174 | return bo; | ||
175 | |||
176 | err: | ||
177 | tegra_bo_free_object(&bo->gem); | ||
178 | return ERR_PTR(ret); | ||
179 | } | ||
180 | |||
181 | void tegra_bo_free_object(struct drm_gem_object *gem) | ||
182 | { | ||
183 | struct tegra_bo *bo = to_tegra_bo(gem); | ||
184 | |||
185 | if (gem->map_list.map) | ||
186 | drm_gem_free_mmap_offset(gem); | ||
187 | |||
188 | drm_gem_object_release(gem); | ||
189 | tegra_bo_destroy(gem->dev, bo); | ||
190 | |||
191 | kfree(bo); | ||
192 | } | ||
193 | |||
194 | int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm, | ||
195 | struct drm_mode_create_dumb *args) | ||
196 | { | ||
197 | int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); | ||
198 | struct tegra_bo *bo; | ||
199 | |||
200 | if (args->pitch < min_pitch) | ||
201 | args->pitch = min_pitch; | ||
202 | |||
203 | if (args->size < args->pitch * args->height) | ||
204 | args->size = args->pitch * args->height; | ||
205 | |||
206 | bo = tegra_bo_create_with_handle(file, drm, args->size, | ||
207 | &args->handle); | ||
208 | if (IS_ERR(bo)) | ||
209 | return PTR_ERR(bo); | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | int tegra_bo_dumb_map_offset(struct drm_file *file, struct drm_device *drm, | ||
215 | uint32_t handle, uint64_t *offset) | ||
216 | { | ||
217 | struct drm_gem_object *gem; | ||
218 | struct tegra_bo *bo; | ||
219 | |||
220 | mutex_lock(&drm->struct_mutex); | ||
221 | |||
222 | gem = drm_gem_object_lookup(drm, file, handle); | ||
223 | if (!gem) { | ||
224 | dev_err(drm->dev, "failed to lookup GEM object\n"); | ||
225 | mutex_unlock(&drm->struct_mutex); | ||
226 | return -EINVAL; | ||
227 | } | ||
228 | |||
229 | bo = to_tegra_bo(gem); | ||
230 | |||
231 | *offset = tegra_bo_get_mmap_offset(bo); | ||
232 | |||
233 | drm_gem_object_unreference(gem); | ||
234 | |||
235 | mutex_unlock(&drm->struct_mutex); | ||
236 | |||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | const struct vm_operations_struct tegra_bo_vm_ops = { | ||
241 | .open = drm_gem_vm_open, | ||
242 | .close = drm_gem_vm_close, | ||
243 | }; | ||
244 | |||
245 | int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma) | ||
246 | { | ||
247 | struct drm_gem_object *gem; | ||
248 | struct tegra_bo *bo; | ||
249 | int ret; | ||
250 | |||
251 | ret = drm_gem_mmap(file, vma); | ||
252 | if (ret) | ||
253 | return ret; | ||
254 | |||
255 | gem = vma->vm_private_data; | ||
256 | bo = to_tegra_bo(gem); | ||
257 | |||
258 | ret = remap_pfn_range(vma, vma->vm_start, bo->paddr >> PAGE_SHIFT, | ||
259 | vma->vm_end - vma->vm_start, vma->vm_page_prot); | ||
260 | if (ret) | ||
261 | drm_gem_vm_close(vma); | ||
262 | |||
263 | return ret; | ||
264 | } | ||
265 | |||
266 | int tegra_bo_dumb_destroy(struct drm_file *file, struct drm_device *drm, | ||
267 | unsigned int handle) | ||
268 | { | ||
269 | return drm_gem_handle_delete(file, handle); | ||
270 | } | ||