5514a050 |
1 | /* |
2 | * SPU processing offload to TI C64x DSP using bsp's c64_tools |
3 | * (C) GraÅžvydas "notaz" Ignotas, 2015 |
4 | * |
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of |
6 | * this software and associated documentation files (the "Software"), to deal in |
7 | * the Software without restriction, including without limitation the rights to |
8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
9 | * of the Software, and to permit persons to whom the Software is furnished to do |
10 | * so, subject to the following conditions: |
11 | * |
12 | * The above copyright notice and this permission notice shall be included in all |
13 | * copies or substantial portions of the Software. |
14 | * |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
21 | * SOFTWARE. |
22 | */ |
23 | |
24 | #include <dlfcn.h> |
de4a0279 |
25 | #include <stddef.h> |
26 | |
5514a050 |
27 | #include <inc_libc64_mini.h> |
28 | #include "spu_c64x.h" |
29 | |
30 | static dsp_mem_region_t region; |
de4a0279 |
31 | static dsp_component_id_t compid; |
5514a050 |
32 | |
33 | static struct { |
34 | void *handle; |
35 | int (*dsp_open)(void); |
36 | dsp_mem_region_t (*dsp_shm_alloc)(dsp_cache_t _type, sU32 _numBytes); |
37 | int (*dsp_shm_free)(dsp_mem_region_t _mem); |
38 | void (*dsp_close)(void); |
39 | int (*dsp_component_load)(const char *_path, const char *_name, dsp_component_id_t *_id); |
40 | int (*dsp_cache_inv_virt)(void *_virtAddr, sU32 _size); |
41 | int (*dsp_rpc_send)(const dsp_msg_t *_msgTo); |
42 | int (*dsp_rpc_recv)(dsp_msg_t *_msgFrom); |
de4a0279 |
43 | int (*dsp_rpc)(const dsp_msg_t *_msgTo, dsp_msg_t *_msgFrom); |
5514a050 |
44 | void (*dsp_logbuf_print)(void); |
45 | } f; |
46 | |
47 | static void thread_work_start(void) |
48 | { |
de4a0279 |
49 | dsp_msg_t msg; |
50 | int ret; |
51 | |
52 | DSP_MSG_INIT(&msg, compid, CCMD_DOIT, 0, 0); |
53 | ret = f.dsp_rpc_send(&msg); |
54 | if (ret != 0) { |
55 | fprintf(stderr, "dsp_rpc_send failed: %d\n", ret); |
56 | f.dsp_logbuf_print(); |
57 | // maybe stop using the DSP? |
58 | } |
5514a050 |
59 | } |
60 | |
61 | static void thread_work_wait_sync(void) |
62 | { |
de4a0279 |
63 | dsp_msg_t msg; |
64 | int ns_to; |
65 | int ret; |
66 | |
67 | ns_to = worker->ns_to; |
68 | f.dsp_cache_inv_virt(spu.sRVBStart, sizeof(spu.sRVBStart[0]) * 2 * ns_to); |
69 | f.dsp_cache_inv_virt(SSumLR, sizeof(SSumLR[0]) * 2 * ns_to); |
70 | f.dsp_cache_inv_virt(&worker->r, sizeof(worker->r)); |
71 | worker->stale_cache = 1; // SB, ram |
72 | |
73 | ret = f.dsp_rpc_recv(&msg); |
74 | if (ret != 0) { |
75 | fprintf(stderr, "dsp_rpc_recv failed: %d\n", ret); |
76 | f.dsp_logbuf_print(); |
77 | } |
78 | //f.dsp_logbuf_print(); |
79 | } |
80 | |
81 | // called before ARM decides to do SPU mixing itself |
82 | static void thread_sync_caches(void) |
83 | { |
84 | if (worker->stale_cache) { |
85 | f.dsp_cache_inv_virt(spu.SB, sizeof(spu.SB[0]) * SB_SIZE * 24); |
86 | f.dsp_cache_inv_virt(spu.spuMemC + 0x800, 0x800); |
87 | worker->stale_cache = 0; |
88 | } |
5514a050 |
89 | } |
90 | |
91 | static void init_spu_thread(void) |
92 | { |
de4a0279 |
93 | dsp_msg_t init_msg, msg_in; |
5514a050 |
94 | struct region_mem *mem; |
95 | int ret; |
96 | |
97 | if (f.handle == NULL) { |
98 | const char lib[] = "libc64.so.1"; |
99 | int failed = 0; |
100 | |
101 | f.handle = dlopen(lib, RTLD_NOW); |
102 | if (f.handle == NULL) { |
103 | fprintf(stderr, "can't load %s: %s\n", lib, dlerror()); |
104 | return; |
105 | } |
106 | #define LDS(name) \ |
107 | failed |= (f.name = dlsym(f.handle, #name)) == NULL |
108 | LDS(dsp_open); |
109 | LDS(dsp_close); |
110 | LDS(dsp_shm_alloc); |
111 | LDS(dsp_shm_free); |
112 | LDS(dsp_cache_inv_virt); |
113 | LDS(dsp_component_load); |
114 | LDS(dsp_rpc_send); |
115 | LDS(dsp_rpc_recv); |
de4a0279 |
116 | LDS(dsp_rpc); |
5514a050 |
117 | LDS(dsp_logbuf_print); |
118 | #undef LDS |
119 | if (failed) { |
120 | fprintf(stderr, "missing symbol(s) in %s\n", lib); |
121 | dlclose(f.handle); |
122 | f.handle = NULL; |
123 | return; |
124 | } |
125 | } |
126 | |
127 | ret = f.dsp_open(); |
128 | if (ret != 0) { |
129 | fprintf(stderr, "dsp_open failed: %d\n", ret); |
130 | return; |
131 | } |
132 | |
de4a0279 |
133 | ret = f.dsp_component_load(NULL, COMPONENT_NAME, &compid); |
134 | if (ret != 0) { |
135 | fprintf(stderr, "dsp_component_load failed: %d\n", ret); |
136 | goto fail_cload; |
137 | } |
138 | |
5514a050 |
139 | region = f.dsp_shm_alloc(DSP_CACHE_R, sizeof(*mem)); // writethrough |
140 | if (region.size < sizeof(*mem) || region.virt_addr == 0) { |
141 | fprintf(stderr, "dsp_shm_alloc failed\n"); |
142 | goto fail_mem; |
143 | } |
144 | mem = (void *)region.virt_addr; |
145 | |
de4a0279 |
146 | memcpy(&mem->spu_config, &spu_config, sizeof(mem->spu_config)); |
147 | |
148 | DSP_MSG_INIT(&init_msg, compid, CCMD_INIT, region.phys_addr, 0); |
149 | ret = f.dsp_rpc(&init_msg, &msg_in); |
150 | if (ret != 0) { |
151 | fprintf(stderr, "dsp_rpc failed: %d\n", ret); |
152 | goto fail_init; |
153 | } |
154 | |
155 | if (mem->sizeof_region_mem != sizeof(*mem)) { |
156 | fprintf(stderr, "error: size mismatch 1: %d vs %zd\n", |
157 | mem->sizeof_region_mem, sizeof(*mem)); |
158 | goto fail_init; |
159 | } |
160 | if (mem->offsetof_s_chan1 != offsetof(typeof(*mem), s_chan[1])) { |
161 | fprintf(stderr, "error: size mismatch 2: %d vs %zd\n", |
162 | mem->offsetof_s_chan1, offsetof(typeof(*mem), s_chan[1])); |
163 | goto fail_init; |
164 | } |
165 | if (mem->offsetof_worker_ram != offsetof(typeof(*mem), worker.ch[1])) { |
166 | fprintf(stderr, "error: size mismatch 3: %d vs %zd\n", |
167 | mem->offsetof_worker_ram, offsetof(typeof(*mem), worker.ch[1])); |
168 | goto fail_init; |
169 | } |
170 | |
5514a050 |
171 | // override default allocations |
172 | free(spu.spuMemC); |
173 | spu.spuMemC = mem->spu_ram; |
174 | free(spu.sRVBStart); |
175 | spu.sRVBStart = mem->RVB; |
176 | free(SSumLR); |
177 | SSumLR = mem->SSumLR; |
de4a0279 |
178 | free(spu.SB); |
179 | spu.SB = mem->SB; |
5514a050 |
180 | free(spu.s_chan); |
181 | spu.s_chan = mem->s_chan; |
182 | worker = &mem->worker; |
183 | |
de4a0279 |
184 | printf("spu: C64x DSP ready (id=%d).\n", (int)compid); |
185 | f.dsp_logbuf_print(); |
186 | |
187 | pcnt_init(); |
188 | (void)do_channel_work; // used by DSP instead |
5514a050 |
189 | return; |
190 | |
de4a0279 |
191 | fail_init: |
192 | f.dsp_shm_free(region); |
5514a050 |
193 | fail_mem: |
de4a0279 |
194 | // no component unload func? |
195 | fail_cload: |
196 | printf("spu: C64x DSP init failed.\n"); |
197 | f.dsp_logbuf_print(); |
5514a050 |
198 | f.dsp_close(); |
199 | worker = NULL; |
200 | } |
201 | |
202 | static void exit_spu_thread(void) |
203 | { |
204 | if (worker == NULL) |
205 | return; |
206 | |
207 | if (worker->pending) |
208 | thread_work_wait_sync(); |
209 | f.dsp_shm_free(region); |
210 | f.dsp_close(); |
211 | |
212 | spu.spuMemC = NULL; |
213 | spu.sRVBStart = NULL; |
214 | SSumLR = NULL; |
de4a0279 |
215 | spu.SB = NULL; |
5514a050 |
216 | spu.s_chan = NULL; |
217 | worker = NULL; |
218 | } |
219 | |
220 | // vim:shiftwidth=1:expandtab |