add some missing license headers
[pcsx_rearmed.git] / plugins / dfsound / spu_c64x.c
... / ...
CommitLineData
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>
25#include <stddef.h>
26#include <unistd.h>
27
28#include <inc_libc64_mini.h>
29#include "spu_c64x.h"
30
31static struct {
32 void *handle;
33 int (*dsp_open)(void);
34 dsp_mem_region_t (*dsp_shm_alloc)(dsp_cache_t _type, sU32 _numBytes);
35 int (*dsp_shm_free)(dsp_mem_region_t _mem);
36 void (*dsp_close)(void);
37 int (*dsp_component_load)(const char *_path, const char *_name, dsp_component_id_t *_id);
38 int (*dsp_cache_inv_virt)(void *_virtAddr, sU32 _size);
39 int (*dsp_rpc_send)(const dsp_msg_t *_msgTo);
40 int (*dsp_rpc_recv)(dsp_msg_t *_msgFrom);
41 int (*dsp_rpc)(const dsp_msg_t *_msgTo, dsp_msg_t *_msgFrom);
42 void (*dsp_logbuf_print)(void);
43
44 dsp_mem_region_t region;
45 dsp_component_id_t compid;
46 unsigned int stale_caches:1;
47 unsigned int req_sent:1;
48} f;
49
50static noinline void dsp_fault(void)
51{
52 dsp_msg_t msg;
53
54 f.dsp_cache_inv_virt(worker, sizeof(*worker));
55 printf("dsp crash/fault/corruption:\n");
56 printf("state rdy/reap/done: %u %u %u\n",
57 worker->i_ready, worker->i_reaped, worker->i_done);
58 printf("active/boot: %u %u\n",
59 worker->active, worker->boot_cnt);
60
61 if (f.req_sent) {
62 f.dsp_rpc_recv(&msg);
63 f.req_sent = 0;
64 }
65 f.dsp_logbuf_print();
66 spu_config.iUseThread = 0;
67}
68
69static void thread_work_start(void)
70{
71 struct region_mem *mem;
72 dsp_msg_t msg;
73 int ret;
74
75 // make sure new work is written out
76 __sync_synchronize();
77
78 // this should be safe, as dsp checks for new work even
79 // after it decrements ->active
80 // cacheline: i_done, active
81 f.dsp_cache_inv_virt(&worker->i_done, 64);
82 if (worker->active == ACTIVE_CNT)
83 return;
84
85 // to start the DSP, dsp_rpc_send() must be used,
86 // but before that, previous request must be finished
87 if (f.req_sent) {
88 if (worker->boot_cnt == worker->last_boot_cnt) {
89 // hopefully still booting
90 //printf("booting?\n");
91 return;
92 }
93
94 ret = f.dsp_rpc_recv(&msg);
95 if (ret != 0) {
96 fprintf(stderr, "dsp_rpc_recv failed: %d\n", ret);
97 f.dsp_logbuf_print();
98 f.req_sent = 0;
99 spu_config.iUseThread = 0;
100 return;
101 }
102 }
103
104 f.dsp_cache_inv_virt(&worker->i_done, 64);
105 worker->last_boot_cnt = worker->boot_cnt;
106 worker->ram_dirty = spu.bMemDirty;
107 spu.bMemDirty = 0;
108
109 mem = (void *)f.region.virt_addr;
110 memcpy(&mem->in.spu_config, &spu_config, sizeof(mem->in.spu_config));
111
112 DSP_MSG_INIT(&msg, f.compid, CCMD_DOIT, f.region.phys_addr, 0);
113 ret = f.dsp_rpc_send(&msg);
114 if (ret != 0) {
115 fprintf(stderr, "dsp_rpc_send failed: %d\n", ret);
116 f.dsp_logbuf_print();
117 spu_config.iUseThread = 0;
118 return;
119 }
120 f.req_sent = 1;
121
122#if 0
123 f.dsp_rpc_recv(&msg);
124 f.req_sent = 0;
125#endif
126}
127
128static int thread_get_i_done(void)
129{
130 f.dsp_cache_inv_virt(&worker->i_done, sizeof(worker->i_done));
131 return worker->i_done;
132}
133
134static void thread_work_wait_sync(struct work_item *work, int force)
135{
136 int limit = 1000;
137 int ns_to;
138
139 if ((unsigned int)(worker->i_done - worker->i_reaped) > WORK_MAXCNT) {
140 dsp_fault();
141 return;
142 }
143
144 while (worker->i_done == worker->i_reaped && limit-- > 0) {
145 if (!f.req_sent) {
146 printf("dsp: req not sent?\n");
147 break;
148 }
149
150 if (worker->boot_cnt != worker->last_boot_cnt && !worker->active) {
151 printf("dsp: broken sync\n");
152 worker->last_boot_cnt = ~0;
153 break;
154 }
155
156 usleep(500);
157 f.dsp_cache_inv_virt(&worker->i_done, 64);
158 }
159
160 ns_to = work->ns_to;
161 f.dsp_cache_inv_virt(work->SSumLR, sizeof(work->SSumLR[0]) * 2 * ns_to);
162 preload(work->SSumLR);
163 preload(work->SSumLR + 64/4);
164
165 f.stale_caches = 1; // SB, spuMem
166
167 if (limit == 0)
168 printf("dsp: wait timeout\n");
169
170 // still in results loop?
171 if (worker->i_reaped != worker->i_done - 1)
172 return;
173
174 if (f.req_sent && (force || worker->i_done == worker->i_ready)) {
175 dsp_msg_t msg;
176 int ret;
177
178 ret = f.dsp_rpc_recv(&msg);
179 if (ret != 0) {
180 fprintf(stderr, "dsp_rpc_recv failed: %d\n", ret);
181 f.dsp_logbuf_print();
182 spu_config.iUseThread = 0;
183 }
184 f.req_sent = 0;
185 }
186}
187
188static void thread_sync_caches(void)
189{
190 if (f.stale_caches) {
191 f.dsp_cache_inv_virt(spu.SB, sizeof(spu.SB[0]) * SB_SIZE * 24);
192 f.dsp_cache_inv_virt(spu.spuMemC + 0x800, 0x800);
193 if (spu.rvb->StartAddr) {
194 int left = 0x40000 - spu.rvb->StartAddr;
195 f.dsp_cache_inv_virt(spu.spuMem + spu.rvb->StartAddr, left * 2);
196 }
197 f.stale_caches = 0;
198 }
199}
200
201static void init_spu_thread(void)
202{
203 dsp_msg_t init_msg, msg_in;
204 struct region_mem *mem;
205 int ret;
206
207 if (f.handle == NULL) {
208 const char lib[] = "libc64.so.1";
209 int failed = 0;
210
211 f.handle = dlopen(lib, RTLD_NOW);
212 if (f.handle == NULL) {
213 fprintf(stderr, "can't load %s: %s\n", lib, dlerror());
214 goto fail_open;
215 }
216 #define LDS(name) \
217 failed |= (f.name = dlsym(f.handle, #name)) == NULL
218 LDS(dsp_open);
219 LDS(dsp_close);
220 LDS(dsp_shm_alloc);
221 LDS(dsp_shm_free);
222 LDS(dsp_cache_inv_virt);
223 LDS(dsp_component_load);
224 LDS(dsp_rpc_send);
225 LDS(dsp_rpc_recv);
226 LDS(dsp_rpc);
227 LDS(dsp_logbuf_print);
228 #undef LDS
229 if (failed) {
230 fprintf(stderr, "missing symbol(s) in %s\n", lib);
231 dlclose(f.handle);
232 f.handle = NULL;
233 goto fail_open;
234 }
235 }
236
237 ret = f.dsp_open();
238 if (ret != 0) {
239 fprintf(stderr, "dsp_open failed: %d\n", ret);
240 goto fail_open;
241 }
242
243 ret = f.dsp_component_load(NULL, COMPONENT_NAME, &f.compid);
244 if (ret != 0) {
245 fprintf(stderr, "dsp_component_load failed: %d\n", ret);
246 goto fail_cload;
247 }
248
249 f.region = f.dsp_shm_alloc(DSP_CACHE_R, sizeof(*mem)); // writethrough
250 if (f.region.size < sizeof(*mem) || f.region.virt_addr == 0) {
251 fprintf(stderr, "dsp_shm_alloc failed\n");
252 goto fail_mem;
253 }
254 mem = (void *)f.region.virt_addr;
255
256 memcpy(&mem->in.spu_config, &spu_config, sizeof(mem->in.spu_config));
257
258 DSP_MSG_INIT(&init_msg, f.compid, CCMD_INIT, f.region.phys_addr, 0);
259 ret = f.dsp_rpc(&init_msg, &msg_in);
260 if (ret != 0) {
261 fprintf(stderr, "dsp_rpc failed: %d\n", ret);
262 goto fail_init;
263 }
264
265 if (mem->sizeof_region_mem != sizeof(*mem)) {
266 fprintf(stderr, "error: size mismatch 1: %d vs %zd\n",
267 mem->sizeof_region_mem, sizeof(*mem));
268 goto fail_init;
269 }
270 if (mem->offsetof_s_chan1 != offsetof(typeof(*mem), in.s_chan[1])) {
271 fprintf(stderr, "error: size mismatch 2: %d vs %zd\n",
272 mem->offsetof_s_chan1, offsetof(typeof(*mem), in.s_chan[1]));
273 goto fail_init;
274 }
275 if (mem->offsetof_spos_3_20 != offsetof(typeof(*mem), worker.i[3].ch[20])) {
276 fprintf(stderr, "error: size mismatch 3: %d vs %zd\n",
277 mem->offsetof_spos_3_20, offsetof(typeof(*mem), worker.i[3].ch[20]));
278 goto fail_init;
279 }
280
281 // override default allocations
282 free(spu.spuMemC);
283 spu.spuMemC = mem->spu_ram;
284 free(spu.SB);
285 spu.SB = mem->SB;
286 free(spu.s_chan);
287 spu.s_chan = mem->in.s_chan;
288 free(spu.rvb);
289 spu.rvb = &mem->in.rvb;
290 worker = &mem->worker;
291
292 printf("spu: C64x DSP ready (id=%d).\n", (int)f.compid);
293 f.dsp_logbuf_print();
294
295 spu_config.iThreadAvail = 1;
296 (void)do_channel_work; // used by DSP instead
297 return;
298
299fail_init:
300 f.dsp_shm_free(f.region);
301fail_mem:
302 // no component unload func?
303fail_cload:
304 f.dsp_logbuf_print();
305 f.dsp_close();
306fail_open:
307 printf("spu: C64x DSP init failed.\n");
308 spu_config.iUseThread = spu_config.iThreadAvail = 0;
309 worker = NULL;
310}
311
312static void exit_spu_thread(void)
313{
314 dsp_msg_t msg;
315
316 if (worker == NULL)
317 return;
318
319 if (f.req_sent) {
320 f.dsp_rpc_recv(&msg);
321 f.req_sent = 0;
322 }
323
324 f.dsp_logbuf_print();
325 f.dsp_shm_free(f.region);
326 f.dsp_close();
327
328 spu.spuMemC = NULL;
329 spu.SB = NULL;
330 spu.s_chan = NULL;
331 spu.rvb = NULL;
332 worker = NULL;
333}
334
335/* debug: "access" shared mem from gdb */
336#if 0
337struct region_mem *dbg_dsp_mem;
338
339void dbg_dsp_mem_update(void)
340{
341 struct region_mem *mem;
342
343 if (dbg_dsp_mem == NULL)
344 dbg_dsp_mem = malloc(sizeof(*dbg_dsp_mem));
345 if (dbg_dsp_mem == NULL)
346 return;
347
348 mem = (void *)f.region.virt_addr;
349 f.dsp_cache_inv_virt(mem, sizeof(*mem));
350 memcpy(dbg_dsp_mem, mem, sizeof(*dbg_dsp_mem));
351}
352#endif
353
354// vim:shiftwidth=1:expandtab