1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2023 Loongson Technology Corporation Limited
4 */
5
6 #include <linux/dma-buf.h>
7
8 #include <drm/drm_debugfs.h>
9 #include <drm/drm_dumb_buffers.h>
10 #include <drm/drm_file.h>
11 #include <drm/drm_gem.h>
12 #include <drm/drm_prime.h>
13 #include <drm/drm_print.h>
14
15 #include "lsdc_drv.h"
16 #include "lsdc_gem.h"
17 #include "lsdc_ttm.h"
18
lsdc_gem_prime_pin(struct drm_gem_object * obj)19 static int lsdc_gem_prime_pin(struct drm_gem_object *obj)
20 {
21 struct lsdc_bo *lbo = gem_to_lsdc_bo(obj);
22 int ret;
23
24 dma_resv_assert_held(obj->resv);
25
26 ret = lsdc_bo_pin(lbo, LSDC_GEM_DOMAIN_GTT, NULL);
27 if (likely(ret == 0))
28 lbo->sharing_count++;
29
30 return ret;
31 }
32
lsdc_gem_prime_unpin(struct drm_gem_object * obj)33 static void lsdc_gem_prime_unpin(struct drm_gem_object *obj)
34 {
35 struct lsdc_bo *lbo = gem_to_lsdc_bo(obj);
36
37 dma_resv_assert_held(obj->resv);
38
39 lsdc_bo_unpin(lbo);
40 if (lbo->sharing_count)
41 lbo->sharing_count--;
42 }
43
lsdc_gem_prime_get_sg_table(struct drm_gem_object * obj)44 static struct sg_table *lsdc_gem_prime_get_sg_table(struct drm_gem_object *obj)
45 {
46 struct ttm_buffer_object *tbo = to_ttm_bo(obj);
47 struct ttm_tt *tt = tbo->ttm;
48
49 if (!tt) {
50 drm_err(obj->dev, "sharing a buffer without backing memory\n");
51 return ERR_PTR(-ENOMEM);
52 }
53
54 return drm_prime_pages_to_sg(obj->dev, tt->pages, tt->num_pages);
55 }
56
lsdc_gem_object_free(struct drm_gem_object * obj)57 static void lsdc_gem_object_free(struct drm_gem_object *obj)
58 {
59 struct ttm_buffer_object *tbo = to_ttm_bo(obj);
60
61 if (tbo)
62 ttm_bo_fini(tbo);
63 }
64
lsdc_gem_object_vmap(struct drm_gem_object * obj,struct iosys_map * map)65 static int lsdc_gem_object_vmap(struct drm_gem_object *obj, struct iosys_map *map)
66 {
67 struct ttm_buffer_object *tbo = to_ttm_bo(obj);
68 struct lsdc_bo *lbo = to_lsdc_bo(tbo);
69 int ret;
70
71 if (lbo->vmap_count > 0) {
72 ++lbo->vmap_count;
73 goto out;
74 }
75
76 ret = lsdc_bo_pin(lbo, 0, NULL);
77 if (unlikely(ret)) {
78 drm_err(obj->dev, "pin %p for vmap failed\n", lbo);
79 return ret;
80 }
81
82 ret = ttm_bo_vmap(tbo, &lbo->map);
83 if (ret) {
84 drm_err(obj->dev, "ttm bo vmap failed\n");
85 lsdc_bo_unpin(lbo);
86 return ret;
87 }
88
89 lbo->vmap_count = 1;
90
91 out:
92 *map = lbo->map;
93
94 return 0;
95 }
96
lsdc_gem_object_vunmap(struct drm_gem_object * obj,struct iosys_map * map)97 static void lsdc_gem_object_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
98 {
99 struct ttm_buffer_object *tbo = to_ttm_bo(obj);
100 struct lsdc_bo *lbo = to_lsdc_bo(tbo);
101
102 if (unlikely(!lbo->vmap_count)) {
103 drm_warn(obj->dev, "%p is not mapped\n", lbo);
104 return;
105 }
106
107 --lbo->vmap_count;
108 if (lbo->vmap_count == 0) {
109 ttm_bo_vunmap(tbo, &lbo->map);
110
111 lsdc_bo_unpin(lbo);
112 }
113 }
114
lsdc_gem_object_mmap(struct drm_gem_object * obj,struct vm_area_struct * vma)115 static int lsdc_gem_object_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
116 {
117 struct ttm_buffer_object *tbo = to_ttm_bo(obj);
118 int ret;
119
120 ret = ttm_bo_mmap_obj(vma, tbo);
121 if (unlikely(ret)) {
122 drm_warn(obj->dev, "mmap %p failed\n", tbo);
123 return ret;
124 }
125
126 drm_gem_object_put(obj);
127
128 return 0;
129 }
130
131 static const struct drm_gem_object_funcs lsdc_gem_object_funcs = {
132 .free = lsdc_gem_object_free,
133 .export = drm_gem_prime_export,
134 .pin = lsdc_gem_prime_pin,
135 .unpin = lsdc_gem_prime_unpin,
136 .get_sg_table = lsdc_gem_prime_get_sg_table,
137 .vmap = lsdc_gem_object_vmap,
138 .vunmap = lsdc_gem_object_vunmap,
139 .mmap = lsdc_gem_object_mmap,
140 };
141
lsdc_gem_object_create(struct drm_device * ddev,u32 domain,size_t size,bool kerenl,struct sg_table * sg,struct dma_resv * resv)142 struct drm_gem_object *lsdc_gem_object_create(struct drm_device *ddev,
143 u32 domain,
144 size_t size,
145 bool kerenl,
146 struct sg_table *sg,
147 struct dma_resv *resv)
148 {
149 struct lsdc_device *ldev = to_lsdc(ddev);
150 struct drm_gem_object *gobj;
151 struct lsdc_bo *lbo;
152 int ret;
153
154 lbo = lsdc_bo_create(ddev, domain, size, kerenl, sg, resv);
155 if (IS_ERR(lbo)) {
156 ret = PTR_ERR(lbo);
157 return ERR_PTR(ret);
158 }
159
160 if (!sg) {
161 /* VRAM is filled with random data */
162 lsdc_bo_clear(lbo);
163 }
164
165 gobj = &lbo->tbo.base;
166 gobj->funcs = &lsdc_gem_object_funcs;
167
168 /* tracking the BOs we created */
169 mutex_lock(&ldev->gem.mutex);
170 list_add_tail(&lbo->list, &ldev->gem.objects);
171 mutex_unlock(&ldev->gem.mutex);
172
173 return gobj;
174 }
175
176 struct drm_gem_object *
lsdc_prime_import_sg_table(struct drm_device * ddev,struct dma_buf_attachment * attach,struct sg_table * sg)177 lsdc_prime_import_sg_table(struct drm_device *ddev,
178 struct dma_buf_attachment *attach,
179 struct sg_table *sg)
180 {
181 struct dma_resv *resv = attach->dmabuf->resv;
182 u64 size = attach->dmabuf->size;
183 struct drm_gem_object *gobj;
184 struct lsdc_bo *lbo;
185
186 dma_resv_lock(resv, NULL);
187 gobj = lsdc_gem_object_create(ddev, LSDC_GEM_DOMAIN_GTT, size, false,
188 sg, resv);
189 dma_resv_unlock(resv);
190
191 if (IS_ERR(gobj)) {
192 drm_err(ddev, "Failed to import sg table\n");
193 return gobj;
194 }
195
196 lbo = gem_to_lsdc_bo(gobj);
197 lbo->sharing_count = 1;
198
199 return gobj;
200 }
201
lsdc_dumb_create(struct drm_file * file,struct drm_device * ddev,struct drm_mode_create_dumb * args)202 int lsdc_dumb_create(struct drm_file *file, struct drm_device *ddev,
203 struct drm_mode_create_dumb *args)
204 {
205 struct lsdc_device *ldev = to_lsdc(ddev);
206 const struct lsdc_desc *descp = ldev->descp;
207 u32 domain = LSDC_GEM_DOMAIN_VRAM;
208 struct drm_gem_object *gobj;
209 int ret;
210
211 ret = drm_mode_size_dumb(ddev, args, descp->pitch_align, 0);
212 if (ret)
213 return ret;
214
215 /* Maximum single bo size allowed is the half vram size available */
216 if (args->size > ldev->vram_size / 2) {
217 drm_err(ddev, "Requesting(%zuMiB) failed\n", (size_t)(args->size >> PAGE_SHIFT));
218 return -ENOMEM;
219 }
220
221 gobj = lsdc_gem_object_create(ddev, domain, args->size, false, NULL, NULL);
222 if (IS_ERR(gobj)) {
223 drm_err(ddev, "Failed to create gem object\n");
224 return PTR_ERR(gobj);
225 }
226
227 ret = drm_gem_handle_create(file, gobj, &args->handle);
228
229 /* drop reference from allocate, handle holds it now */
230 drm_gem_object_put(gobj);
231 if (ret)
232 return ret;
233
234 return 0;
235 }
236
lsdc_dumb_map_offset(struct drm_file * filp,struct drm_device * ddev,u32 handle,uint64_t * offset)237 int lsdc_dumb_map_offset(struct drm_file *filp, struct drm_device *ddev,
238 u32 handle, uint64_t *offset)
239 {
240 struct drm_gem_object *gobj;
241
242 gobj = drm_gem_object_lookup(filp, handle);
243 if (!gobj)
244 return -ENOENT;
245
246 *offset = drm_vma_node_offset_addr(&gobj->vma_node);
247
248 drm_gem_object_put(gobj);
249
250 return 0;
251 }
252
lsdc_gem_init(struct drm_device * ddev)253 void lsdc_gem_init(struct drm_device *ddev)
254 {
255 struct lsdc_device *ldev = to_lsdc(ddev);
256
257 mutex_init(&ldev->gem.mutex);
258 INIT_LIST_HEAD(&ldev->gem.objects);
259 }
260
lsdc_show_buffer_object(struct seq_file * m,void * arg)261 int lsdc_show_buffer_object(struct seq_file *m, void *arg)
262 {
263 struct drm_info_node *node = (struct drm_info_node *)m->private;
264 struct drm_device *ddev = node->minor->dev;
265 struct lsdc_device *ldev = to_lsdc(ddev);
266 struct lsdc_bo *lbo;
267 unsigned int i;
268
269 mutex_lock(&ldev->gem.mutex);
270
271 i = 0;
272
273 list_for_each_entry(lbo, &ldev->gem.objects, list) {
274 struct ttm_buffer_object *tbo = &lbo->tbo;
275 struct ttm_resource *resource = tbo->resource;
276
277 seq_printf(m, "bo[%04u][%p]: size: %8zuKiB %s offset: %8llx\n",
278 i, lbo, lsdc_bo_size(lbo) >> 10,
279 lsdc_mem_type_to_str(resource->mem_type),
280 lsdc_bo_gpu_offset(lbo));
281 i++;
282 }
283
284 mutex_unlock(&ldev->gem.mutex);
285
286 seq_printf(m, "Pinned BO size: VRAM: %zuKiB, GTT: %zu KiB\n",
287 ldev->vram_pinned_size >> 10, ldev->gtt_pinned_size >> 10);
288
289 return 0;
290 }
291