be10a6bb11d422721bcbb4827941c383d1ee3f13
[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 void thread_work_start(void)
51 {
52  struct region_mem *mem;
53  dsp_msg_t msg;
54  int ret;
55
56  // make sure new work is written out
57  __sync_synchronize();
58
59  // this should be safe, as dsp checks for new work even
60  // after it decrements ->active
61  // cacheline: i_done, active
62  f.dsp_cache_inv_virt(&worker->i_done, 64);
63  if (worker->active == ACTIVE_CNT)
64   return;
65
66  // to start the DSP, dsp_rpc_send() must be used,
67  // but before that, previous request must be finished
68  if (f.req_sent) {
69   if (worker->boot_cnt == worker->last_boot_cnt) {
70    // hopefully still booting
71    //printf("booting?\n");
72    return;
73   }
74
75   ret = f.dsp_rpc_recv(&msg);
76   if (ret != 0) {
77    fprintf(stderr, "dsp_rpc_recv failed: %d\n", ret);
78    f.dsp_logbuf_print();
79    f.req_sent = 0;
80    spu_config.iUseThread = 0;
81    return;
82   }
83  }
84
85  f.dsp_cache_inv_virt(&worker->i_done, 64);
86  worker->last_boot_cnt = worker->boot_cnt;
87
88  mem = (void *)f.region.virt_addr;
89  memcpy(&mem->spu_config, &spu_config, sizeof(mem->spu_config));
90
91  DSP_MSG_INIT(&msg, f.compid, CCMD_DOIT, f.region.phys_addr, 0);
92  ret = f.dsp_rpc_send(&msg);
93  if (ret != 0) {
94   fprintf(stderr, "dsp_rpc_send failed: %d\n", ret);
95   f.dsp_logbuf_print();
96   spu_config.iUseThread = 0;
97   return;
98  }
99  f.req_sent = 1;
100 }
101
102 static int thread_get_i_done(void)
103 {
104  f.dsp_cache_inv_virt(&worker->i_done, sizeof(worker->i_done));
105  return worker->i_done;
106 }
107
108 static void thread_work_wait_sync(struct work_item *work, int force)
109 {
110  int limit = 1000;
111  int ns_to;
112
113  while (worker->i_done == worker->i_reaped && limit-- > 0) {
114   if (!f.req_sent) {
115    printf("dsp: req not sent?\n");
116    break;
117   }
118
119   if (worker->boot_cnt != worker->last_boot_cnt && !worker->active) {
120    printf("dsp: broken sync\n");
121    worker->last_boot_cnt = ~0;
122    break;
123   }
124
125   usleep(500);
126   f.dsp_cache_inv_virt(&worker->i_done, 64);
127  }
128
129  ns_to = work->ns_to;
130  f.dsp_cache_inv_virt(work->SSumLR, sizeof(work->SSumLR[0]) * 2 * ns_to);
131  preload(work->SSumLR);
132  preload(work->SSumLR + 64/4);
133
134  f.stale_caches = 1; // SB, spuMem
135
136  if (limit == 0)
137   printf("dsp: wait timeout\n");
138
139  // still in results loop?
140  if (worker->i_reaped != worker->i_done - 1)
141   return;
142
143  if (f.req_sent && (force || worker->i_done == worker->i_ready)) {
144   dsp_msg_t msg;
145   int ret;
146
147   ret = f.dsp_rpc_recv(&msg);
148   if (ret != 0) {
149    fprintf(stderr, "dsp_rpc_recv failed: %d\n", ret);
150    f.dsp_logbuf_print();
151    spu_config.iUseThread = 0;
152   }
153   f.req_sent = 0;
154  }
155 }
156
157 static void thread_sync_caches(void)
158 {
159  if (f.stale_caches) {
160   f.dsp_cache_inv_virt(spu.SB, sizeof(spu.SB[0]) * SB_SIZE * 24);
161   f.dsp_cache_inv_virt(spu.spuMemC + 0x800, 0x800);
162   if (spu.rvb->StartAddr) {
163    int left = 0x40000 - spu.rvb->StartAddr;
164    f.dsp_cache_inv_virt(spu.spuMem + spu.rvb->StartAddr, left * 2);
165   }
166   f.stale_caches = 0;
167  }
168 }
169
170 static void init_spu_thread(void)
171 {
172  dsp_msg_t init_msg, msg_in;
173  struct region_mem *mem;
174  int ret;
175
176  if (f.handle == NULL) {
177   const char lib[] = "libc64.so.1";
178   int failed = 0;
179
180   f.handle = dlopen(lib, RTLD_NOW);
181   if (f.handle == NULL) {
182    fprintf(stderr, "can't load %s: %s\n", lib, dlerror());
183    goto fail_open;
184   }
185   #define LDS(name) \
186     failed |= (f.name = dlsym(f.handle, #name)) == NULL
187   LDS(dsp_open);
188   LDS(dsp_close);
189   LDS(dsp_shm_alloc);
190   LDS(dsp_shm_free);
191   LDS(dsp_cache_inv_virt);
192   LDS(dsp_component_load);
193   LDS(dsp_rpc_send);
194   LDS(dsp_rpc_recv);
195   LDS(dsp_rpc);
196   LDS(dsp_logbuf_print);
197   #undef LDS
198   if (failed) {
199    fprintf(stderr, "missing symbol(s) in %s\n", lib);
200    dlclose(f.handle);
201    f.handle = NULL;
202    goto fail_open;
203   }
204  }
205
206  ret = f.dsp_open();
207  if (ret != 0) {
208   fprintf(stderr, "dsp_open failed: %d\n", ret);
209   goto fail_open;
210  }
211
212  ret = f.dsp_component_load(NULL, COMPONENT_NAME, &f.compid);
213  if (ret != 0) {
214   fprintf(stderr, "dsp_component_load failed: %d\n", ret);
215   goto fail_cload;
216  }
217
218  f.region = f.dsp_shm_alloc(DSP_CACHE_R, sizeof(*mem)); // writethrough
219  if (f.region.size < sizeof(*mem) || f.region.virt_addr == 0) {
220   fprintf(stderr, "dsp_shm_alloc failed\n");
221   goto fail_mem;
222  }
223  mem = (void *)f.region.virt_addr;
224
225  memcpy(&mem->spu_config, &spu_config, sizeof(mem->spu_config));
226
227  DSP_MSG_INIT(&init_msg, f.compid, CCMD_INIT, f.region.phys_addr, 0);
228  ret = f.dsp_rpc(&init_msg, &msg_in);
229  if (ret != 0) {
230   fprintf(stderr, "dsp_rpc failed: %d\n", ret);
231   goto fail_init;
232  }
233
234  if (mem->sizeof_region_mem != sizeof(*mem)) {
235   fprintf(stderr, "error: size mismatch 1: %d vs %zd\n",
236     mem->sizeof_region_mem, sizeof(*mem));
237   goto fail_init;
238  }
239  if (mem->offsetof_s_chan1 != offsetof(typeof(*mem), in.s_chan[1])) {
240   fprintf(stderr, "error: size mismatch 2: %d vs %zd\n",
241     mem->offsetof_s_chan1, offsetof(typeof(*mem), in.s_chan[1]));
242   goto fail_init;
243  }
244  if (mem->offsetof_spos_3_20 != offsetof(typeof(*mem), worker.i[3].ch[20])) {
245   fprintf(stderr, "error: size mismatch 3: %d vs %zd\n",
246     mem->offsetof_spos_3_20, offsetof(typeof(*mem), worker.i[3].ch[20]));
247   goto fail_init;
248  }
249
250  // override default allocations
251  free(spu.spuMemC);
252  spu.spuMemC = mem->spu_ram;
253  free(spu.SB);
254  spu.SB = mem->SB;
255  free(spu.s_chan);
256  spu.s_chan = mem->in.s_chan;
257  free(spu.rvb);
258  spu.rvb = &mem->in.rvb;
259  worker = &mem->worker;
260
261  printf("spu: C64x DSP ready (id=%d).\n", (int)f.compid);
262  f.dsp_logbuf_print();
263
264  spu_config.iThreadAvail = 1;
265  (void)do_channel_work; // used by DSP instead
266  return;
267
268 fail_init:
269  f.dsp_shm_free(f.region);
270 fail_mem:
271  // no component unload func?
272 fail_cload:
273  f.dsp_logbuf_print();
274  f.dsp_close();
275 fail_open:
276  printf("spu: C64x DSP init failed.\n");
277  spu_config.iUseThread = spu_config.iThreadAvail = 0;
278  worker = NULL;
279 }
280
281 static void exit_spu_thread(void)
282 {
283  dsp_msg_t msg;
284
285  if (worker == NULL)
286   return;
287
288  if (f.req_sent) {
289   f.dsp_rpc_recv(&msg);
290   f.req_sent = 0;
291  }
292
293  f.dsp_logbuf_print();
294  f.dsp_shm_free(f.region);
295  f.dsp_close();
296
297  spu.spuMemC = NULL;
298  spu.SB = NULL;
299  spu.s_chan = NULL;
300  spu.rvb = NULL;
301  worker = NULL;
302 }
303
304 // vim:shiftwidth=1:expandtab