Commit | Line | Data |
---|---|---|
2048e328 MY |
1 | /* |
2 | * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd | |
3 | * Author:Mark Yao <mark.yao@rock-chips.com> | |
4 | * | |
5 | * This software is licensed under the terms of the GNU General Public | |
6 | * License version 2, as published by the Free Software Foundation, and | |
7 | * may be copied, distributed, and modified under those terms. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | */ | |
14 | ||
15 | #include <drm/drm.h> | |
16 | #include <drm/drmP.h> | |
17 | #include <drm/drm_gem.h> | |
18 | #include <drm/drm_vma_manager.h> | |
19 | ||
20 | #include <linux/dma-attrs.h> | |
21 | ||
22 | #include "rockchip_drm_drv.h" | |
23 | #include "rockchip_drm_gem.h" | |
24 | ||
f76c83b5 DK |
25 | static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj, |
26 | bool alloc_kmap) | |
2048e328 MY |
27 | { |
28 | struct drm_gem_object *obj = &rk_obj->base; | |
29 | struct drm_device *drm = obj->dev; | |
30 | ||
31 | init_dma_attrs(&rk_obj->dma_attrs); | |
32 | dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs); | |
33 | ||
f76c83b5 DK |
34 | if (!alloc_kmap) |
35 | dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &rk_obj->dma_attrs); | |
36 | ||
2048e328 MY |
37 | rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size, |
38 | &rk_obj->dma_addr, GFP_KERNEL, | |
39 | &rk_obj->dma_attrs); | |
4b9a90c0 DK |
40 | if (!rk_obj->kvaddr) { |
41 | DRM_ERROR("failed to allocate %#x byte dma buffer", obj->size); | |
42 | return -ENOMEM; | |
2048e328 MY |
43 | } |
44 | ||
45 | return 0; | |
46 | } | |
47 | ||
48 | static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj) | |
49 | { | |
50 | struct drm_gem_object *obj = &rk_obj->base; | |
51 | struct drm_device *drm = obj->dev; | |
52 | ||
53 | dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr, | |
54 | &rk_obj->dma_attrs); | |
55 | } | |
56 | ||
41315b79 DK |
57 | static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj, |
58 | struct vm_area_struct *vma) | |
59 | ||
2048e328 | 60 | { |
41315b79 | 61 | int ret; |
2048e328 MY |
62 | struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); |
63 | struct drm_device *drm = obj->dev; | |
2048e328 | 64 | |
41315b79 DK |
65 | /* |
66 | * dma_alloc_attrs() allocated a struct page table for rk_obj, so clear | |
67 | * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap(). | |
68 | */ | |
69 | vma->vm_flags &= ~VM_PFNMAP; | |
2048e328 | 70 | |
41315b79 | 71 | ret = dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr, |
2048e328 | 72 | obj->size, &rk_obj->dma_attrs); |
41315b79 DK |
73 | if (ret) |
74 | drm_gem_vm_close(vma); | |
75 | ||
76 | return ret; | |
77 | } | |
78 | ||
79 | int rockchip_gem_mmap_buf(struct drm_gem_object *obj, | |
80 | struct vm_area_struct *vma) | |
81 | { | |
82 | struct drm_device *drm = obj->dev; | |
83 | int ret; | |
84 | ||
85 | mutex_lock(&drm->struct_mutex); | |
86 | ret = drm_gem_mmap_obj(obj, obj->size, vma); | |
87 | mutex_unlock(&drm->struct_mutex); | |
88 | if (ret) | |
89 | return ret; | |
90 | ||
91 | return rockchip_drm_gem_object_mmap(obj, vma); | |
2048e328 MY |
92 | } |
93 | ||
94 | /* drm driver mmap file operations */ | |
95 | int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma) | |
96 | { | |
2048e328 | 97 | struct drm_gem_object *obj; |
2048e328 MY |
98 | int ret; |
99 | ||
41315b79 DK |
100 | ret = drm_gem_mmap(filp, vma); |
101 | if (ret) | |
102 | return ret; | |
2048e328 | 103 | |
41315b79 | 104 | obj = vma->vm_private_data; |
2048e328 | 105 | |
41315b79 | 106 | return rockchip_drm_gem_object_mmap(obj, vma); |
2048e328 MY |
107 | } |
108 | ||
109 | struct rockchip_gem_object * | |
f76c83b5 DK |
110 | rockchip_gem_create_object(struct drm_device *drm, unsigned int size, |
111 | bool alloc_kmap) | |
2048e328 MY |
112 | { |
113 | struct rockchip_gem_object *rk_obj; | |
114 | struct drm_gem_object *obj; | |
115 | int ret; | |
116 | ||
117 | size = round_up(size, PAGE_SIZE); | |
118 | ||
119 | rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL); | |
120 | if (!rk_obj) | |
121 | return ERR_PTR(-ENOMEM); | |
122 | ||
123 | obj = &rk_obj->base; | |
124 | ||
125 | drm_gem_private_object_init(drm, obj, size); | |
126 | ||
f76c83b5 | 127 | ret = rockchip_gem_alloc_buf(rk_obj, alloc_kmap); |
2048e328 MY |
128 | if (ret) |
129 | goto err_free_rk_obj; | |
130 | ||
131 | return rk_obj; | |
132 | ||
133 | err_free_rk_obj: | |
134 | kfree(rk_obj); | |
135 | return ERR_PTR(ret); | |
136 | } | |
137 | ||
138 | /* | |
139 | * rockchip_gem_free_object - (struct drm_driver)->gem_free_object callback | |
140 | * function | |
141 | */ | |
142 | void rockchip_gem_free_object(struct drm_gem_object *obj) | |
143 | { | |
144 | struct rockchip_gem_object *rk_obj; | |
145 | ||
146 | drm_gem_free_mmap_offset(obj); | |
147 | ||
148 | rk_obj = to_rockchip_obj(obj); | |
149 | ||
150 | rockchip_gem_free_buf(rk_obj); | |
151 | ||
152 | kfree(rk_obj); | |
153 | } | |
154 | ||
155 | /* | |
156 | * rockchip_gem_create_with_handle - allocate an object with the given | |
157 | * size and create a gem handle on it | |
158 | * | |
159 | * returns a struct rockchip_gem_object* on success or ERR_PTR values | |
160 | * on failure. | |
161 | */ | |
162 | static struct rockchip_gem_object * | |
163 | rockchip_gem_create_with_handle(struct drm_file *file_priv, | |
164 | struct drm_device *drm, unsigned int size, | |
165 | unsigned int *handle) | |
166 | { | |
167 | struct rockchip_gem_object *rk_obj; | |
168 | struct drm_gem_object *obj; | |
169 | int ret; | |
170 | ||
f76c83b5 | 171 | rk_obj = rockchip_gem_create_object(drm, size, false); |
2048e328 MY |
172 | if (IS_ERR(rk_obj)) |
173 | return ERR_CAST(rk_obj); | |
174 | ||
175 | obj = &rk_obj->base; | |
176 | ||
177 | /* | |
178 | * allocate a id of idr table where the obj is registered | |
179 | * and handle has the id what user can see. | |
180 | */ | |
181 | ret = drm_gem_handle_create(file_priv, obj, handle); | |
182 | if (ret) | |
183 | goto err_handle_create; | |
184 | ||
185 | /* drop reference from allocate - handle holds it now. */ | |
186 | drm_gem_object_unreference_unlocked(obj); | |
187 | ||
188 | return rk_obj; | |
189 | ||
190 | err_handle_create: | |
191 | rockchip_gem_free_object(obj); | |
192 | ||
193 | return ERR_PTR(ret); | |
194 | } | |
195 | ||
196 | int rockchip_gem_dumb_map_offset(struct drm_file *file_priv, | |
197 | struct drm_device *dev, uint32_t handle, | |
198 | uint64_t *offset) | |
199 | { | |
200 | struct drm_gem_object *obj; | |
201 | int ret; | |
202 | ||
203 | mutex_lock(&dev->struct_mutex); | |
204 | ||
205 | obj = drm_gem_object_lookup(dev, file_priv, handle); | |
206 | if (!obj) { | |
207 | DRM_ERROR("failed to lookup gem object.\n"); | |
208 | ret = -EINVAL; | |
209 | goto unlock; | |
210 | } | |
211 | ||
212 | ret = drm_gem_create_mmap_offset(obj); | |
213 | if (ret) | |
214 | goto out; | |
215 | ||
216 | *offset = drm_vma_node_offset_addr(&obj->vma_node); | |
217 | DRM_DEBUG_KMS("offset = 0x%llx\n", *offset); | |
218 | ||
219 | out: | |
220 | drm_gem_object_unreference(obj); | |
221 | unlock: | |
222 | mutex_unlock(&dev->struct_mutex); | |
223 | return ret; | |
224 | } | |
225 | ||
226 | /* | |
227 | * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback | |
228 | * function | |
229 | * | |
230 | * This aligns the pitch and size arguments to the minimum required. wrap | |
231 | * this into your own function if you need bigger alignment. | |
232 | */ | |
233 | int rockchip_gem_dumb_create(struct drm_file *file_priv, | |
234 | struct drm_device *dev, | |
235 | struct drm_mode_create_dumb *args) | |
236 | { | |
237 | struct rockchip_gem_object *rk_obj; | |
238 | int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); | |
239 | ||
240 | /* | |
241 | * align to 64 bytes since Mali requires it. | |
242 | */ | |
243 | min_pitch = ALIGN(min_pitch, 64); | |
244 | ||
245 | if (args->pitch < min_pitch) | |
246 | args->pitch = min_pitch; | |
247 | ||
248 | if (args->size < args->pitch * args->height) | |
249 | args->size = args->pitch * args->height; | |
250 | ||
251 | rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size, | |
252 | &args->handle); | |
253 | ||
254 | return PTR_ERR_OR_ZERO(rk_obj); | |
255 | } | |
256 | ||
257 | /* | |
258 | * Allocate a sg_table for this GEM object. | |
259 | * Note: Both the table's contents, and the sg_table itself must be freed by | |
260 | * the caller. | |
261 | * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error. | |
262 | */ | |
263 | struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj) | |
264 | { | |
265 | struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); | |
266 | struct drm_device *drm = obj->dev; | |
267 | struct sg_table *sgt; | |
268 | int ret; | |
269 | ||
270 | sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); | |
271 | if (!sgt) | |
272 | return ERR_PTR(-ENOMEM); | |
273 | ||
274 | ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr, | |
275 | rk_obj->dma_addr, obj->size, | |
276 | &rk_obj->dma_attrs); | |
277 | if (ret) { | |
278 | DRM_ERROR("failed to allocate sgt, %d\n", ret); | |
279 | kfree(sgt); | |
280 | return ERR_PTR(ret); | |
281 | } | |
282 | ||
283 | return sgt; | |
284 | } | |
285 | ||
286 | void *rockchip_gem_prime_vmap(struct drm_gem_object *obj) | |
287 | { | |
288 | struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); | |
289 | ||
f76c83b5 DK |
290 | if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, &rk_obj->dma_attrs)) |
291 | return NULL; | |
292 | ||
2048e328 MY |
293 | return rk_obj->kvaddr; |
294 | } | |
295 | ||
296 | void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) | |
297 | { | |
298 | /* Nothing to do */ | |
299 | } |