Fix CD volume issue in Star Wars - Dark Forces. (#232)
[pcsx_rearmed.git] / plugins / dfsound / spu_c64x.c
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
31 static 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
50 static 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
69 static 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
128 static 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
134 static 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
188 static 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
201 static 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
299 fail_init:
300  f.dsp_shm_free(f.region);
301 fail_mem:
302  // no component unload func?
303 fail_cload:
304  f.dsp_logbuf_print();
305  f.dsp_close();
306 fail_open:
307  printf("spu: C64x DSP init failed.\n");
308  spu_config.iUseThread = spu_config.iThreadAvail = 0;
309  worker = NULL;
310 }
311
312 static 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
337 struct region_mem *dbg_dsp_mem;
338
339 void 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