1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2017-2018 Etnaviv Project |
4 | */ |
5 | |
6 | #include <linux/dma-mapping.h> |
7 | |
8 | #include <drm/drm_mm.h> |
9 | |
10 | #include "etnaviv_cmdbuf.h" |
11 | #include "etnaviv_gem.h" |
12 | #include "etnaviv_gpu.h" |
13 | #include "etnaviv_mmu.h" |
14 | #include "etnaviv_perfmon.h" |
15 | |
16 | #define SUBALLOC_SIZE SZ_512K |
17 | #define SUBALLOC_GRANULE SZ_4K |
18 | #define SUBALLOC_GRANULES (SUBALLOC_SIZE / SUBALLOC_GRANULE) |
19 | |
20 | struct etnaviv_cmdbuf_suballoc { |
21 | /* suballocated dma buffer properties */ |
22 | struct device *dev; |
23 | void *vaddr; |
24 | dma_addr_t paddr; |
25 | |
26 | /* allocation management */ |
27 | struct mutex lock; |
28 | DECLARE_BITMAP(granule_map, SUBALLOC_GRANULES); |
29 | int free_space; |
30 | wait_queue_head_t free_event; |
31 | }; |
32 | |
33 | struct etnaviv_cmdbuf_suballoc * |
34 | etnaviv_cmdbuf_suballoc_new(struct device *dev) |
35 | { |
36 | struct etnaviv_cmdbuf_suballoc *suballoc; |
37 | int ret; |
38 | |
39 | suballoc = kzalloc(size: sizeof(*suballoc), GFP_KERNEL); |
40 | if (!suballoc) |
41 | return ERR_PTR(error: -ENOMEM); |
42 | |
43 | suballoc->dev = dev; |
44 | mutex_init(&suballoc->lock); |
45 | init_waitqueue_head(&suballoc->free_event); |
46 | |
47 | BUILD_BUG_ON(ETNAVIV_SOFTPIN_START_ADDRESS < SUBALLOC_SIZE); |
48 | suballoc->vaddr = dma_alloc_wc(dev, SUBALLOC_SIZE, |
49 | dma_addr: &suballoc->paddr, GFP_KERNEL); |
50 | if (!suballoc->vaddr) { |
51 | ret = -ENOMEM; |
52 | goto free_suballoc; |
53 | } |
54 | |
55 | return suballoc; |
56 | |
57 | free_suballoc: |
58 | kfree(objp: suballoc); |
59 | |
60 | return ERR_PTR(error: ret); |
61 | } |
62 | |
63 | int etnaviv_cmdbuf_suballoc_map(struct etnaviv_cmdbuf_suballoc *suballoc, |
64 | struct etnaviv_iommu_context *context, |
65 | struct etnaviv_vram_mapping *mapping, |
66 | u32 memory_base) |
67 | { |
68 | return etnaviv_iommu_get_suballoc_va(ctx: context, mapping, memory_base, |
69 | paddr: suballoc->paddr, SUBALLOC_SIZE); |
70 | } |
71 | |
72 | void etnaviv_cmdbuf_suballoc_unmap(struct etnaviv_iommu_context *context, |
73 | struct etnaviv_vram_mapping *mapping) |
74 | { |
75 | etnaviv_iommu_put_suballoc_va(ctx: context, mapping); |
76 | } |
77 | |
78 | void etnaviv_cmdbuf_suballoc_destroy(struct etnaviv_cmdbuf_suballoc *suballoc) |
79 | { |
80 | dma_free_wc(dev: suballoc->dev, SUBALLOC_SIZE, cpu_addr: suballoc->vaddr, |
81 | dma_addr: suballoc->paddr); |
82 | kfree(objp: suballoc); |
83 | } |
84 | |
85 | int etnaviv_cmdbuf_init(struct etnaviv_cmdbuf_suballoc *suballoc, |
86 | struct etnaviv_cmdbuf *cmdbuf, u32 size) |
87 | { |
88 | int granule_offs, order, ret; |
89 | |
90 | cmdbuf->suballoc = suballoc; |
91 | cmdbuf->size = size; |
92 | |
93 | order = order_base_2(ALIGN(size, SUBALLOC_GRANULE) / SUBALLOC_GRANULE); |
94 | retry: |
95 | mutex_lock(&suballoc->lock); |
96 | granule_offs = bitmap_find_free_region(bitmap: suballoc->granule_map, |
97 | SUBALLOC_GRANULES, order); |
98 | if (granule_offs < 0) { |
99 | suballoc->free_space = 0; |
100 | mutex_unlock(lock: &suballoc->lock); |
101 | ret = wait_event_interruptible_timeout(suballoc->free_event, |
102 | suballoc->free_space, |
103 | msecs_to_jiffies(10 * 1000)); |
104 | if (!ret) { |
105 | dev_err(suballoc->dev, |
106 | "Timeout waiting for cmdbuf space\n" ); |
107 | return -ETIMEDOUT; |
108 | } |
109 | goto retry; |
110 | } |
111 | mutex_unlock(lock: &suballoc->lock); |
112 | cmdbuf->suballoc_offset = granule_offs * SUBALLOC_GRANULE; |
113 | cmdbuf->vaddr = suballoc->vaddr + cmdbuf->suballoc_offset; |
114 | |
115 | return 0; |
116 | } |
117 | |
118 | void etnaviv_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf) |
119 | { |
120 | struct etnaviv_cmdbuf_suballoc *suballoc = cmdbuf->suballoc; |
121 | int order = order_base_2(ALIGN(cmdbuf->size, SUBALLOC_GRANULE) / |
122 | SUBALLOC_GRANULE); |
123 | |
124 | if (!suballoc) |
125 | return; |
126 | |
127 | mutex_lock(&suballoc->lock); |
128 | bitmap_release_region(bitmap: suballoc->granule_map, |
129 | pos: cmdbuf->suballoc_offset / SUBALLOC_GRANULE, |
130 | order); |
131 | suballoc->free_space = 1; |
132 | mutex_unlock(lock: &suballoc->lock); |
133 | wake_up_all(&suballoc->free_event); |
134 | } |
135 | |
136 | u32 etnaviv_cmdbuf_get_va(struct etnaviv_cmdbuf *buf, |
137 | struct etnaviv_vram_mapping *mapping) |
138 | { |
139 | return mapping->iova + buf->suballoc_offset; |
140 | } |
141 | |
142 | dma_addr_t etnaviv_cmdbuf_get_pa(struct etnaviv_cmdbuf *buf) |
143 | { |
144 | return buf->suballoc->paddr + buf->suballoc_offset; |
145 | } |
146 | |