CHROMIUM: drm/rockchip: Add GEM create ioctl support
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / rockchip / rockchip_drm_gem.c
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 #include <drm/rockchip_drm.h>
20
21 #include <linux/dma-attrs.h>
22
23 #include "rockchip_drm_drv.h"
24 #include "rockchip_drm_gem.h"
25
26 static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj,
27                                   bool alloc_kmap)
28 {
29         struct drm_gem_object *obj = &rk_obj->base;
30         struct drm_device *drm = obj->dev;
31
32         init_dma_attrs(&rk_obj->dma_attrs);
33         dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs);
34
35         if (!alloc_kmap)
36                 dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &rk_obj->dma_attrs);
37
38         rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
39                                          &rk_obj->dma_addr, GFP_KERNEL,
40                                          &rk_obj->dma_attrs);
41         if (!rk_obj->kvaddr) {
42                 DRM_ERROR("failed to allocate %zu byte dma buffer", obj->size);
43                 return -ENOMEM;
44         }
45
46         return 0;
47 }
48
49 static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
50 {
51         struct drm_gem_object *obj = &rk_obj->base;
52         struct drm_device *drm = obj->dev;
53
54         dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
55                        &rk_obj->dma_attrs);
56 }
57
58 static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj,
59                                         struct vm_area_struct *vma)
60
61 {
62         int ret;
63         struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
64         struct drm_device *drm = obj->dev;
65
66         /*
67          * dma_alloc_attrs() allocated a struct page table for rk_obj, so clear
68          * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap().
69          */
70         vma->vm_flags &= ~VM_PFNMAP;
71         vma->vm_pgoff = 0;
72
73         ret = dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
74                              obj->size, &rk_obj->dma_attrs);
75         if (ret)
76                 drm_gem_vm_close(vma);
77
78         return ret;
79 }
80
81 int rockchip_gem_mmap_buf(struct drm_gem_object *obj,
82                           struct vm_area_struct *vma)
83 {
84         int ret;
85
86         ret = drm_gem_mmap_obj(obj, obj->size, vma);
87         if (ret)
88                 return ret;
89
90         return rockchip_drm_gem_object_mmap(obj, vma);
91 }
92
93 /* drm driver mmap file operations */
94 int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma)
95 {
96         struct drm_gem_object *obj;
97         int ret;
98
99         ret = drm_gem_mmap(filp, vma);
100         if (ret)
101                 return ret;
102
103         obj = vma->vm_private_data;
104
105         return rockchip_drm_gem_object_mmap(obj, vma);
106 }
107
108 struct rockchip_gem_object *
109         rockchip_gem_create_object(struct drm_device *drm, unsigned int size,
110                                    bool alloc_kmap)
111 {
112         struct rockchip_gem_object *rk_obj;
113         struct drm_gem_object *obj;
114         int ret;
115
116         size = round_up(size, PAGE_SIZE);
117
118         rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL);
119         if (!rk_obj)
120                 return ERR_PTR(-ENOMEM);
121
122         obj = &rk_obj->base;
123
124         drm_gem_private_object_init(drm, obj, size);
125
126         ret = rockchip_gem_alloc_buf(rk_obj, alloc_kmap);
127         if (ret)
128                 goto err_free_rk_obj;
129
130         return rk_obj;
131
132 err_free_rk_obj:
133         kfree(rk_obj);
134         return ERR_PTR(ret);
135 }
136
137 /*
138  * rockchip_gem_free_object - (struct drm_driver)->gem_free_object callback
139  * function
140  */
141 void rockchip_gem_free_object(struct drm_gem_object *obj)
142 {
143         struct rockchip_gem_object *rk_obj;
144
145         drm_gem_free_mmap_offset(obj);
146
147         rk_obj = to_rockchip_obj(obj);
148
149         rockchip_gem_free_buf(rk_obj);
150
151         kfree(rk_obj);
152 }
153
154 /*
155  * rockchip_gem_create_with_handle - allocate an object with the given
156  * size and create a gem handle on it
157  *
158  * returns a struct rockchip_gem_object* on success or ERR_PTR values
159  * on failure.
160  */
161 static struct rockchip_gem_object *
162 rockchip_gem_create_with_handle(struct drm_file *file_priv,
163                                 struct drm_device *drm, unsigned int size,
164                                 unsigned int *handle)
165 {
166         struct rockchip_gem_object *rk_obj;
167         struct drm_gem_object *obj;
168         int ret;
169
170         rk_obj = rockchip_gem_create_object(drm, size, false);
171         if (IS_ERR(rk_obj))
172                 return ERR_CAST(rk_obj);
173
174         obj = &rk_obj->base;
175
176         /*
177          * allocate a id of idr table where the obj is registered
178          * and handle has the id what user can see.
179          */
180         ret = drm_gem_handle_create(file_priv, obj, handle);
181         if (ret)
182                 goto err_handle_create;
183
184         /* drop reference from allocate - handle holds it now. */
185         drm_gem_object_unreference_unlocked(obj);
186
187         return rk_obj;
188
189 err_handle_create:
190         rockchip_gem_free_object(obj);
191
192         return ERR_PTR(ret);
193 }
194
195 int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
196                                  struct drm_device *dev, uint32_t handle,
197                                  uint64_t *offset)
198 {
199         struct drm_gem_object *obj;
200         int ret;
201
202         obj = drm_gem_object_lookup(dev, file_priv, handle);
203         if (!obj) {
204                 DRM_ERROR("failed to lookup gem object.\n");
205                 return -EINVAL;
206         }
207
208         ret = drm_gem_create_mmap_offset(obj);
209         if (ret)
210                 goto out;
211
212         *offset = drm_vma_node_offset_addr(&obj->vma_node);
213         DRM_DEBUG_KMS("offset = 0x%llx\n", *offset);
214
215 out:
216         drm_gem_object_unreference_unlocked(obj);
217
218         return 0;
219 }
220
221 /*
222  * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback
223  * function
224  *
225  * This aligns the pitch and size arguments to the minimum required. wrap
226  * this into your own function if you need bigger alignment.
227  */
228 int rockchip_gem_dumb_create(struct drm_file *file_priv,
229                              struct drm_device *dev,
230                              struct drm_mode_create_dumb *args)
231 {
232         struct rockchip_gem_object *rk_obj;
233         int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
234
235         /*
236          * align to 64 bytes since Mali requires it.
237          */
238         args->pitch = ALIGN(min_pitch, 64);
239         args->size = args->pitch * args->height;
240
241         rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
242                                                  &args->handle);
243
244         return PTR_ERR_OR_ZERO(rk_obj);
245 }
246
247 int rockchip_gem_map_offset_ioctl(struct drm_device *drm, void *data,
248                                   struct drm_file *file_priv)
249 {
250         struct drm_rockchip_gem_map_off *args = data;
251
252         return rockchip_gem_dumb_map_offset(file_priv, drm, args->handle,
253                                             &args->offset);
254 }
255
256 int rockchip_gem_create_ioctl(struct drm_device *dev, void *data,
257                               struct drm_file *file_priv)
258 {
259         struct drm_rockchip_gem_create *args = data;
260         struct rockchip_gem_object *rk_obj;
261
262         rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
263                                                  &args->handle);
264         return PTR_ERR_OR_ZERO(rk_obj);
265 }
266
267 /*
268  * Allocate a sg_table for this GEM object.
269  * Note: Both the table's contents, and the sg_table itself must be freed by
270  *       the caller.
271  * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
272  */
273 struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
274 {
275         struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
276         struct drm_device *drm = obj->dev;
277         struct sg_table *sgt;
278         int ret;
279
280         sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
281         if (!sgt)
282                 return ERR_PTR(-ENOMEM);
283
284         ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
285                                     rk_obj->dma_addr, obj->size,
286                                     &rk_obj->dma_attrs);
287         if (ret) {
288                 DRM_ERROR("failed to allocate sgt, %d\n", ret);
289                 kfree(sgt);
290                 return ERR_PTR(ret);
291         }
292
293         return sgt;
294 }
295
296 void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
297 {
298         struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
299
300         if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, &rk_obj->dma_attrs))
301                 return NULL;
302
303         return rk_obj->kvaddr;
304 }
305
306 void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
307 {
308         /* Nothing to do */
309 }