tegra_bo.c revision 310600
1/*-
2 * Copyright (c) 2015 Michal Meloun
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/arm/nvidia/drm2/tegra_bo.c 310600 2016-12-26 14:36:05Z mmel $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/bus.h>
33#include <sys/kernel.h>
34#include <sys/malloc.h>
35
36#include <machine/bus.h>
37
38#include <dev/extres/clk/clk.h>
39#include <dev/drm2/drmP.h>
40#include <dev/drm2/drm_crtc_helper.h>
41#include <dev/drm2/drm_fb_helper.h>
42
43#include <arm/nvidia/drm2/tegra_drm.h>
44
45#include <sys/vmem.h>
46#include <sys/vmem.h>
47#include <vm/vm.h>
48#include <vm/vm_pageout.h>
49
50static void
51tegra_bo_destruct(struct tegra_bo *bo)
52{
53	vm_page_t m;
54	size_t size;
55	int i;
56
57	if (bo->cdev_pager == NULL)
58		return;
59
60	size = round_page(bo->gem_obj.size);
61	if (bo->vbase != 0)
62		pmap_qremove(bo->vbase, bo->npages);
63
64	VM_OBJECT_WLOCK(bo->cdev_pager);
65	for (i = 0; i < bo->npages; i++) {
66		m = bo->m[i];
67		cdev_pager_free_page(bo->cdev_pager, m);
68		vm_page_lock(m);
69		m->flags &= ~PG_FICTITIOUS;
70		vm_page_unwire(m, PQ_NONE);
71		vm_page_free(m);
72		vm_page_unlock(m);
73	}
74	VM_OBJECT_WUNLOCK(bo->cdev_pager);
75
76	vm_object_deallocate(bo->cdev_pager);
77	if (bo->vbase != 0)
78		vmem_free(kmem_arena, bo->vbase, size);
79}
80
81static void
82tegra_bo_free_object(struct drm_gem_object *gem_obj)
83{
84	struct tegra_bo *bo;
85
86	bo = container_of(gem_obj, struct tegra_bo, gem_obj);
87	drm_gem_free_mmap_offset(gem_obj);
88	drm_gem_object_release(gem_obj);
89
90	tegra_bo_destruct(bo);
91
92	free(bo->m, DRM_MEM_DRIVER);
93	free(bo, DRM_MEM_DRIVER);
94}
95
96static int
97tegra_bo_alloc_contig(size_t npages, u_long alignment, vm_memattr_t memattr,
98    vm_page_t **ret_page)
99{
100	vm_page_t m;
101	int pflags, tries, i;
102	vm_paddr_t low, high, boundary;
103
104	low = 0;
105	high = -1UL;
106	boundary = 0;
107	pflags = VM_ALLOC_NORMAL  | VM_ALLOC_NOOBJ | VM_ALLOC_NOBUSY |
108	    VM_ALLOC_WIRED | VM_ALLOC_ZERO;
109	tries = 0;
110retry:
111	m = vm_page_alloc_contig(NULL, 0, pflags, npages, low, high, alignment,
112	    boundary, memattr);
113	if (m == NULL) {
114		if (tries < 3) {
115			if (!vm_page_reclaim_contig(pflags, npages, low, high,
116			    alignment, boundary))
117				VM_WAIT;
118			tries++;
119			goto retry;
120		}
121		return (ENOMEM);
122	}
123
124	for (i = 0; i < npages; i++, m++) {
125		if ((m->flags & PG_ZERO) == 0)
126			pmap_zero_page(m);
127		m->valid = VM_PAGE_BITS_ALL;
128		(*ret_page)[i] = m;
129	}
130
131	return (0);
132}
133
134/* Initialize pager and insert all object pages to it*/
135static int
136tegra_bo_init_pager(struct tegra_bo *bo)
137{
138	vm_page_t m;
139	size_t size;
140	int i;
141
142	size = round_page(bo->gem_obj.size);
143
144	bo->pbase = VM_PAGE_TO_PHYS(bo->m[0]);
145	if (vmem_alloc(kmem_arena, size, M_WAITOK | M_BESTFIT, &bo->vbase))
146		return (ENOMEM);
147
148	VM_OBJECT_WLOCK(bo->cdev_pager);
149	for (i = 0; i < bo->npages; i++) {
150		m = bo->m[i];
151		/*
152		 * XXX This is a temporary hack.
153		 * We need pager suitable for paging (mmap) managed
154		 * real (non-fictitious) pages.
155		 * - managed pages are needed for clean module unload.
156		 * - aliasing fictitious page to real one is bad,
157		 *   pmap cannot handle this situation without issues
158		 *   It expects that
159		 *    paddr = PHYS_TO_VM_PAGE(VM_PAGE_TO_PHYS(paddr))
160		 *   for every single page passed to pmap.
161		 */
162		m->oflags &= ~VPO_UNMANAGED;
163		m->flags |= PG_FICTITIOUS;
164		if (vm_page_insert(m, bo->cdev_pager, i) != 0)
165			return (EINVAL);
166	}
167	VM_OBJECT_WUNLOCK(bo->cdev_pager);
168
169	pmap_qenter(bo->vbase, bo->m, bo->npages);
170	return (0);
171}
172
173/* Allocate memory for frame buffer */
174static int
175tegra_bo_alloc(struct drm_device *drm, struct tegra_bo *bo)
176{
177	size_t size;
178	int rv;
179
180	size = bo->gem_obj.size;
181
182	bo->npages = atop(size);
183	bo->m = malloc(sizeof(vm_page_t *) * bo->npages, DRM_MEM_DRIVER,
184	    M_WAITOK | M_ZERO);
185
186	rv = tegra_bo_alloc_contig(bo->npages, PAGE_SIZE,
187	    VM_MEMATTR_WRITE_COMBINING, &(bo->m));
188	if (rv != 0) {
189		DRM_WARNING("Cannot allocate memory for gem object.\n");
190		return (rv);
191	}
192	rv = tegra_bo_init_pager(bo);
193	if (rv != 0) {
194		DRM_WARNING("Cannot initialize gem object pager.\n");
195		return (rv);
196	}
197	return (0);
198}
199
200int
201tegra_bo_create(struct drm_device *drm, size_t size, struct tegra_bo **res_bo)
202{
203	struct tegra_bo *bo;
204	int rv;
205
206	if (size <= 0)
207		return (-EINVAL);
208
209	bo = malloc(sizeof(*bo), DRM_MEM_DRIVER, M_WAITOK | M_ZERO);
210
211	size = round_page(size);
212	rv = drm_gem_object_init(drm, &bo->gem_obj, size);
213	if (rv != 0) {
214		free(bo, DRM_MEM_DRIVER);
215		return (rv);
216	}
217	rv = drm_gem_create_mmap_offset(&bo->gem_obj);
218	if (rv != 0) {
219		drm_gem_object_release(&bo->gem_obj);
220		free(bo, DRM_MEM_DRIVER);
221		return (rv);
222	}
223
224	bo->cdev_pager = cdev_pager_allocate(&bo->gem_obj, OBJT_MGTDEVICE,
225	    drm->driver->gem_pager_ops, size, 0, 0, NULL);
226	rv = tegra_bo_alloc(drm, bo);
227	if (rv != 0) {
228		tegra_bo_free_object(&bo->gem_obj);
229		return (rv);
230	}
231
232	*res_bo = bo;
233	return (0);
234}
235
236
237
238static int
239tegra_bo_create_with_handle(struct drm_file *file, struct drm_device *drm,
240    size_t size, uint32_t *handle, struct tegra_bo **res_bo)
241{
242	int rv;
243	struct tegra_bo *bo;
244
245	rv = tegra_bo_create(drm, size, &bo);
246	if (rv != 0)
247		return (rv);
248
249	rv = drm_gem_handle_create(file, &bo->gem_obj, handle);
250	if (rv != 0) {
251		tegra_bo_free_object(&bo->gem_obj);
252		drm_gem_object_release(&bo->gem_obj);
253		return (rv);
254	}
255
256	drm_gem_object_unreference_unlocked(&bo->gem_obj);
257
258	*res_bo = bo;
259	return (0);
260}
261
262static int
263tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm_dev,
264    struct drm_mode_create_dumb *args)
265{
266	struct tegra_drm *drm;
267	struct tegra_bo *bo;
268	int rv;
269
270	drm = container_of(drm_dev, struct tegra_drm, drm_dev);
271
272	args->pitch= (args->width * args->bpp + 7) / 8;
273	args->pitch = roundup(args->pitch, drm->pitch_align);
274	args->size = args->pitch * args->height;
275	rv = tegra_bo_create_with_handle(file, drm_dev, args->size,
276	    &args->handle, &bo);
277
278	return (rv);
279}
280
281static int
282tegra_bo_dumb_map_offset(struct drm_file *file_priv,
283    struct drm_device *drm_dev, uint32_t handle, uint64_t *offset)
284{
285	struct drm_gem_object *gem_obj;
286	int rv;
287
288	DRM_LOCK(drm_dev);
289	gem_obj = drm_gem_object_lookup(drm_dev, file_priv, handle);
290	if (gem_obj == NULL) {
291		device_printf(drm_dev->dev, "Object not found\n");
292		DRM_UNLOCK(drm_dev);
293		return (-EINVAL);
294	}
295	rv = drm_gem_create_mmap_offset(gem_obj);
296	if (rv != 0)
297		goto fail;
298
299	*offset = DRM_GEM_MAPPING_OFF(gem_obj->map_list.key) |
300	    DRM_GEM_MAPPING_KEY;
301
302	drm_gem_object_unreference(gem_obj);
303	DRM_UNLOCK(drm_dev);
304	return (0);
305
306fail:
307	drm_gem_object_unreference(gem_obj);
308	DRM_UNLOCK(drm_dev);
309	return (rv);
310}
311
312static int
313tegra_bo_dumb_destroy(struct drm_file *file_priv, struct drm_device *drm_dev,
314    unsigned int handle)
315{
316	int rv;
317
318	rv = drm_gem_handle_delete(file_priv, handle);
319	return (rv);
320}
321
322/*
323 * mmap support
324 */
325static int
326tegra_gem_pager_fault(vm_object_t vm_obj, vm_ooffset_t offset, int prot,
327    vm_page_t *mres)
328{
329
330#ifdef DRM_PAGER_DEBUG
331	DRM_DEBUG("object %p offset %jd prot %d mres %p\n",
332	    vm_obj, (intmax_t)offset, prot, mres);
333#endif
334	return (VM_PAGER_FAIL);
335
336}
337
338static int
339tegra_gem_pager_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot,
340    vm_ooffset_t foff, struct ucred *cred, u_short *color)
341{
342
343	if (color != NULL)
344		*color = 0;
345	return (0);
346}
347
348static void
349tegra_gem_pager_dtor(void *handle)
350{
351
352}
353
354static struct cdev_pager_ops tegra_gem_pager_ops = {
355	.cdev_pg_fault = tegra_gem_pager_fault,
356	.cdev_pg_ctor  = tegra_gem_pager_ctor,
357	.cdev_pg_dtor  = tegra_gem_pager_dtor
358};
359
360/* Fill up relevant fields in drm_driver ops */
361void
362tegra_bo_driver_register(struct drm_driver *drm_drv)
363{
364	drm_drv->gem_free_object = tegra_bo_free_object;
365	drm_drv->gem_pager_ops = &tegra_gem_pager_ops;
366	drm_drv->dumb_create = tegra_bo_dumb_create;
367	drm_drv->dumb_map_offset = tegra_bo_dumb_map_offset;
368	drm_drv->dumb_destroy = tegra_bo_dumb_destroy;
369}
370