spu: finish offload code to TI C64x DSP
[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
27 #include <inc_libc64_mini.h>
28 #include "spu_c64x.h"
29
30 static dsp_mem_region_t region;
31 static dsp_component_id_t compid;
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);
43  int  (*dsp_rpc)(const dsp_msg_t *_msgTo, dsp_msg_t *_msgFrom);
44  void (*dsp_logbuf_print)(void);
45 } f;
46
47 static void thread_work_start(void)
48 {
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  }
59 }
60
61 static void thread_work_wait_sync(void)
62 {
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  }
89 }
90
91 static void init_spu_thread(void)
92 {
93  dsp_msg_t init_msg, msg_in;
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);
116   LDS(dsp_rpc);
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
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
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
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
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;
178  free(spu.SB);
179  spu.SB = mem->SB;
180  free(spu.s_chan);
181  spu.s_chan = mem->s_chan;
182  worker = &mem->worker;
183
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
189  return;
190
191 fail_init:
192  f.dsp_shm_free(region);
193 fail_mem:
194  // no component unload func?
195 fail_cload:
196  printf("spu: C64x DSP init failed.\n");
197  f.dsp_logbuf_print();
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;
215  spu.SB = NULL;
216  spu.s_chan = NULL;
217  worker = NULL;
218 }
219
220 // vim:shiftwidth=1:expandtab