1 /*
2  * © Copyright 2018 Alyssa Rosenzweig
3  * Copyright (C) 2019 Collabora, Ltd.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  *
24  */
25 
26 #include "pan_bo.h"
27 #include "pan_pool.h"
28 
29 /* Transient command stream pooling: command stream uploads try to simply copy
30  * into whereever we left off. If there isn't space, we allocate a new entry
31  * into the pool and copy there */
32 
33 static struct panfrost_bo *
panfrost_pool_alloc_backing(struct pan_pool * pool,size_t bo_sz)34 panfrost_pool_alloc_backing(struct pan_pool *pool, size_t bo_sz)
35 {
36         /* We don't know what the BO will be used for, so let's flag it
37          * RW and attach it to both the fragment and vertex/tiler jobs.
38          * TODO: if we want fine grained BO assignment we should pass
39          * flags to this function and keep the read/write,
40          * fragment/vertex+tiler pools separate.
41          */
42         struct panfrost_bo *bo = panfrost_bo_create(pool->dev, bo_sz,
43                         pool->create_flags);
44 
45         util_dynarray_append(&pool->bos, struct panfrost_bo *, bo);
46         pool->transient_bo = bo;
47         pool->transient_offset = 0;
48 
49         return bo;
50 }
51 
52 void
panfrost_pool_init(struct pan_pool * pool,void * memctx,struct panfrost_device * dev,unsigned create_flags,bool prealloc)53 panfrost_pool_init(struct pan_pool *pool, void *memctx,
54                    struct panfrost_device *dev,
55                    unsigned create_flags, bool prealloc)
56 {
57         memset(pool, 0, sizeof(*pool));
58         pool->dev = dev;
59         pool->create_flags = create_flags;
60         util_dynarray_init(&pool->bos, memctx);
61 
62         if (prealloc)
63                 panfrost_pool_alloc_backing(pool, TRANSIENT_SLAB_SIZE);
64 }
65 
66 void
panfrost_pool_cleanup(struct pan_pool * pool)67 panfrost_pool_cleanup(struct pan_pool *pool)
68 {
69         util_dynarray_foreach(&pool->bos, struct panfrost_bo *, bo)
70                 panfrost_bo_unreference(*bo);
71 
72         util_dynarray_fini(&pool->bos);
73 }
74 
75 void
panfrost_pool_get_bo_handles(struct pan_pool * pool,uint32_t * handles)76 panfrost_pool_get_bo_handles(struct pan_pool *pool, uint32_t *handles)
77 {
78         unsigned idx = 0;
79         util_dynarray_foreach(&pool->bos, struct panfrost_bo *, bo) {
80                 assert((*bo)->gem_handle > 0);
81                 handles[idx++] = (*bo)->gem_handle;
82 
83                /* Update the BO access flags so that panfrost_bo_wait() knows
84                 * about all pending accesses.
85                 * We only keep the READ/WRITE info since this is all the BO
86                 * wait logic cares about.
87                 * We also preserve existing flags as this batch might not
88                 * be the first one to access the BO.
89                 */
90                 (*bo)->gpu_access |= PAN_BO_ACCESS_RW;
91         }
92 }
93 
94 struct panfrost_ptr
panfrost_pool_alloc_aligned(struct pan_pool * pool,size_t sz,unsigned alignment)95 panfrost_pool_alloc_aligned(struct pan_pool *pool, size_t sz, unsigned alignment)
96 {
97         assert(alignment == util_next_power_of_two(alignment));
98 
99         /* Find or create a suitable BO */
100         struct panfrost_bo *bo = pool->transient_bo;
101         unsigned offset = ALIGN_POT(pool->transient_offset, alignment);
102 
103         /* If we don't fit, allocate a new backing */
104         if (unlikely(bo == NULL || (offset + sz) >= TRANSIENT_SLAB_SIZE)) {
105                 bo = panfrost_pool_alloc_backing(pool,
106                                 ALIGN_POT(MAX2(TRANSIENT_SLAB_SIZE, sz), 4096));
107                 offset = 0;
108         }
109 
110         pool->transient_offset = offset + sz;
111 
112         struct panfrost_ptr ret = {
113                 .cpu = bo->ptr.cpu + offset,
114                 .gpu = bo->ptr.gpu + offset,
115         };
116 
117         return ret;
118 }
119 
120 mali_ptr
panfrost_pool_upload(struct pan_pool * pool,const void * data,size_t sz)121 panfrost_pool_upload(struct pan_pool *pool, const void *data, size_t sz)
122 {
123         return panfrost_pool_upload_aligned(pool, data, sz, sz);
124 }
125 
126 mali_ptr
panfrost_pool_upload_aligned(struct pan_pool * pool,const void * data,size_t sz,unsigned alignment)127 panfrost_pool_upload_aligned(struct pan_pool *pool, const void *data, size_t sz, unsigned alignment)
128 {
129         struct panfrost_ptr transfer = panfrost_pool_alloc_aligned(pool, sz, alignment);
130         memcpy(transfer.cpu, data, sz);
131         return transfer.gpu;
132 }
133