Commit | Line | Data |
---|---|---|
119f5173 CH |
1 | /* |
2 | * Copyright (c) 2015 MediaTek Inc. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | */ | |
13 | ||
14 | #include <drm/drmP.h> | |
15 | #include <drm/drm_gem.h> | |
16 | #include <linux/dma-buf.h> | |
17 | ||
18 | #include "mtk_drm_drv.h" | |
19 | #include "mtk_drm_gem.h" | |
20 | ||
21 | static struct mtk_drm_gem_obj *mtk_drm_gem_init(struct drm_device *dev, | |
22 | unsigned long size) | |
23 | { | |
24 | struct mtk_drm_gem_obj *mtk_gem_obj; | |
25 | int ret; | |
26 | ||
27 | size = round_up(size, PAGE_SIZE); | |
28 | ||
29 | mtk_gem_obj = kzalloc(sizeof(*mtk_gem_obj), GFP_KERNEL); | |
30 | if (!mtk_gem_obj) | |
31 | return ERR_PTR(-ENOMEM); | |
32 | ||
33 | ret = drm_gem_object_init(dev, &mtk_gem_obj->base, size); | |
34 | if (ret < 0) { | |
35 | DRM_ERROR("failed to initialize gem object\n"); | |
36 | kfree(mtk_gem_obj); | |
37 | return ERR_PTR(ret); | |
38 | } | |
39 | ||
40 | return mtk_gem_obj; | |
41 | } | |
42 | ||
43 | struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev, | |
44 | size_t size, bool alloc_kmap) | |
45 | { | |
46 | struct mtk_drm_private *priv = dev->dev_private; | |
47 | struct mtk_drm_gem_obj *mtk_gem; | |
48 | struct drm_gem_object *obj; | |
49 | int ret; | |
50 | ||
51 | mtk_gem = mtk_drm_gem_init(dev, size); | |
52 | if (IS_ERR(mtk_gem)) | |
53 | return ERR_CAST(mtk_gem); | |
54 | ||
55 | obj = &mtk_gem->base; | |
56 | ||
57 | init_dma_attrs(&mtk_gem->dma_attrs); | |
58 | dma_set_attr(DMA_ATTR_WRITE_COMBINE, &mtk_gem->dma_attrs); | |
59 | ||
60 | if (!alloc_kmap) | |
61 | dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &mtk_gem->dma_attrs); | |
62 | ||
63 | mtk_gem->cookie = dma_alloc_attrs(priv->dma_dev, obj->size, | |
64 | &mtk_gem->dma_addr, GFP_KERNEL, | |
65 | &mtk_gem->dma_attrs); | |
66 | if (!mtk_gem->cookie) { | |
67 | DRM_ERROR("failed to allocate %zx byte dma buffer", obj->size); | |
68 | ret = -ENOMEM; | |
69 | goto err_gem_free; | |
70 | } | |
71 | ||
72 | if (alloc_kmap) | |
73 | mtk_gem->kvaddr = mtk_gem->cookie; | |
74 | ||
75 | DRM_DEBUG_DRIVER("cookie = %p dma_addr = %pad size = %zu\n", | |
76 | mtk_gem->cookie, &mtk_gem->dma_addr, | |
77 | size); | |
78 | ||
79 | return mtk_gem; | |
80 | ||
81 | err_gem_free: | |
82 | drm_gem_object_release(obj); | |
83 | kfree(mtk_gem); | |
84 | return ERR_PTR(ret); | |
85 | } | |
86 | ||
87 | void mtk_drm_gem_free_object(struct drm_gem_object *obj) | |
88 | { | |
89 | struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj); | |
90 | struct mtk_drm_private *priv = obj->dev->dev_private; | |
91 | ||
92 | if (mtk_gem->sg) | |
93 | drm_prime_gem_destroy(obj, mtk_gem->sg); | |
94 | else | |
95 | dma_free_attrs(priv->dma_dev, obj->size, mtk_gem->cookie, | |
96 | mtk_gem->dma_addr, &mtk_gem->dma_attrs); | |
97 | ||
98 | /* release file pointer to gem object. */ | |
99 | drm_gem_object_release(obj); | |
100 | ||
101 | kfree(mtk_gem); | |
102 | } | |
103 | ||
104 | int mtk_drm_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev, | |
105 | struct drm_mode_create_dumb *args) | |
106 | { | |
107 | struct mtk_drm_gem_obj *mtk_gem; | |
108 | int ret; | |
109 | ||
110 | args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8); | |
111 | args->size = args->pitch * args->height; | |
112 | ||
113 | mtk_gem = mtk_drm_gem_create(dev, args->size, false); | |
114 | if (IS_ERR(mtk_gem)) | |
115 | return PTR_ERR(mtk_gem); | |
116 | ||
117 | /* | |
118 | * allocate a id of idr table where the obj is registered | |
119 | * and handle has the id what user can see. | |
120 | */ | |
121 | ret = drm_gem_handle_create(file_priv, &mtk_gem->base, &args->handle); | |
122 | if (ret) | |
123 | goto err_handle_create; | |
124 | ||
125 | /* drop reference from allocate - handle holds it now. */ | |
126 | drm_gem_object_unreference_unlocked(&mtk_gem->base); | |
127 | ||
128 | return 0; | |
129 | ||
130 | err_handle_create: | |
131 | mtk_drm_gem_free_object(&mtk_gem->base); | |
132 | return ret; | |
133 | } | |
134 | ||
135 | int mtk_drm_gem_dumb_map_offset(struct drm_file *file_priv, | |
136 | struct drm_device *dev, uint32_t handle, | |
137 | uint64_t *offset) | |
138 | { | |
139 | struct drm_gem_object *obj; | |
140 | int ret; | |
141 | ||
de4ae068 | 142 | obj = drm_gem_object_lookup(file_priv, handle); |
119f5173 CH |
143 | if (!obj) { |
144 | DRM_ERROR("failed to lookup gem object.\n"); | |
145 | return -EINVAL; | |
146 | } | |
147 | ||
148 | ret = drm_gem_create_mmap_offset(obj); | |
149 | if (ret) | |
150 | goto out; | |
151 | ||
152 | *offset = drm_vma_node_offset_addr(&obj->vma_node); | |
153 | DRM_DEBUG_KMS("offset = 0x%llx\n", *offset); | |
154 | ||
155 | out: | |
156 | drm_gem_object_unreference_unlocked(obj); | |
157 | return ret; | |
158 | } | |
159 | ||
160 | static int mtk_drm_gem_object_mmap(struct drm_gem_object *obj, | |
161 | struct vm_area_struct *vma) | |
162 | ||
163 | { | |
164 | int ret; | |
165 | struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj); | |
166 | struct mtk_drm_private *priv = obj->dev->dev_private; | |
167 | ||
168 | /* | |
169 | * dma_alloc_attrs() allocated a struct page table for mtk_gem, so clear | |
170 | * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap(). | |
171 | */ | |
172 | vma->vm_flags &= ~VM_PFNMAP; | |
173 | vma->vm_pgoff = 0; | |
174 | ||
175 | ret = dma_mmap_attrs(priv->dma_dev, vma, mtk_gem->cookie, | |
176 | mtk_gem->dma_addr, obj->size, &mtk_gem->dma_attrs); | |
177 | if (ret) | |
178 | drm_gem_vm_close(vma); | |
179 | ||
180 | return ret; | |
181 | } | |
182 | ||
183 | int mtk_drm_gem_mmap_buf(struct drm_gem_object *obj, struct vm_area_struct *vma) | |
184 | { | |
185 | int ret; | |
186 | ||
187 | ret = drm_gem_mmap_obj(obj, obj->size, vma); | |
188 | if (ret) | |
189 | return ret; | |
190 | ||
191 | return mtk_drm_gem_object_mmap(obj, vma); | |
192 | } | |
193 | ||
194 | int mtk_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) | |
195 | { | |
196 | struct drm_gem_object *obj; | |
197 | int ret; | |
198 | ||
199 | ret = drm_gem_mmap(filp, vma); | |
200 | if (ret) | |
201 | return ret; | |
202 | ||
203 | obj = vma->vm_private_data; | |
204 | ||
205 | return mtk_drm_gem_object_mmap(obj, vma); | |
206 | } | |
207 | ||
208 | /* | |
209 | * Allocate a sg_table for this GEM object. | |
210 | * Note: Both the table's contents, and the sg_table itself must be freed by | |
211 | * the caller. | |
212 | * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error. | |
213 | */ | |
214 | struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj) | |
215 | { | |
216 | struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj); | |
217 | struct mtk_drm_private *priv = obj->dev->dev_private; | |
218 | struct sg_table *sgt; | |
219 | int ret; | |
220 | ||
221 | sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); | |
222 | if (!sgt) | |
223 | return ERR_PTR(-ENOMEM); | |
224 | ||
225 | ret = dma_get_sgtable_attrs(priv->dma_dev, sgt, mtk_gem->cookie, | |
226 | mtk_gem->dma_addr, obj->size, | |
227 | &mtk_gem->dma_attrs); | |
228 | if (ret) { | |
229 | DRM_ERROR("failed to allocate sgt, %d\n", ret); | |
230 | kfree(sgt); | |
231 | return ERR_PTR(ret); | |
232 | } | |
233 | ||
234 | return sgt; | |
235 | } | |
236 | ||
237 | struct drm_gem_object *mtk_gem_prime_import_sg_table(struct drm_device *dev, | |
238 | struct dma_buf_attachment *attach, struct sg_table *sg) | |
239 | { | |
240 | struct mtk_drm_gem_obj *mtk_gem; | |
241 | int ret; | |
242 | struct scatterlist *s; | |
243 | unsigned int i; | |
244 | dma_addr_t expected; | |
245 | ||
246 | mtk_gem = mtk_drm_gem_init(dev, attach->dmabuf->size); | |
247 | ||
248 | if (IS_ERR(mtk_gem)) | |
249 | return ERR_PTR(PTR_ERR(mtk_gem)); | |
250 | ||
251 | expected = sg_dma_address(sg->sgl); | |
252 | for_each_sg(sg->sgl, s, sg->nents, i) { | |
253 | if (sg_dma_address(s) != expected) { | |
254 | DRM_ERROR("sg_table is not contiguous"); | |
255 | ret = -EINVAL; | |
256 | goto err_gem_free; | |
257 | } | |
258 | expected = sg_dma_address(s) + sg_dma_len(s); | |
259 | } | |
260 | ||
261 | mtk_gem->dma_addr = sg_dma_address(sg->sgl); | |
262 | mtk_gem->sg = sg; | |
263 | ||
264 | return &mtk_gem->base; | |
265 | ||
266 | err_gem_free: | |
267 | kfree(mtk_gem); | |
268 | return ERR_PTR(ret); | |
269 | } |