spu: put reverb on the thread too
[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 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
102static 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
108static 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
157static 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
170static 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
268fail_init:
269 f.dsp_shm_free(f.region);
270fail_mem:
271 // no component unload func?
272fail_cload:
273 f.dsp_logbuf_print();
274 f.dsp_close();
275fail_open:
276 printf("spu: C64x DSP init failed.\n");
277 spu_config.iUseThread = spu_config.iThreadAvail = 0;
278 worker = NULL;
279}
280
281static 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