spu: fix some thread safety issues
[pcsx_rearmed.git] / plugins / dfsound / spu_c64x_dspcode.c
CommitLineData
de4a0279 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#define SYSCALLS_C
25#include <libc64_dsp/include/inc_overlay.h>
26#include <stddef.h>
27
28#include "spu.c"
29#include "spu_c64x.h"
30
31/* dummy deps, some bloat but avoids ifdef hell in SPU code.. */
32static void thread_work_start(void) {}
3bd31caf 33static void thread_work_wait_sync(struct work_item *work, int force) {}
05c7cec7 34static void thread_sync_caches(void) {}
3bd31caf 35static int thread_get_i_done(void) { return 0; }
de4a0279 36struct out_driver *out_current;
37void SetupSound(void) {}
38
3bd31caf 39
40static void invalidate_cache(struct work_item *work)
41{
05c7cec7 42 // see comment in writeout_cache()
43 //syscalls.cache_inv(work, offsetof(typeof(*work), SSumLR), 1);
3bd31caf 44 syscalls.cache_inv(spu.s_chan, sizeof(spu.s_chan[0]) * 24, 0);
45 syscalls.cache_inv(work->SSumLR,
46 sizeof(work->SSumLR[0]) * 2 * work->ns_to, 0);
47}
48
49static void writeout_cache(struct work_item *work)
de4a0279 50{
3bd31caf 51 int ns_to = work->ns_to;
52
3bd31caf 53 syscalls.cache_wb(work->SSumLR, sizeof(work->SSumLR[0]) * 2 * ns_to, 1);
05c7cec7 54 // have to invalidate now, otherwise there is a race between
55 // DSP evicting dirty lines and ARM writing new data to this area
56 syscalls.cache_inv(work, offsetof(typeof(*work), SSumLR), 0);
3bd31caf 57}
de4a0279 58
3bd31caf 59static void do_processing(void)
60{
05c7cec7 61 int left, dirty = 0, had_rvb = 0;
3bd31caf 62 struct work_item *work;
de4a0279 63
3bd31caf 64 while (worker->active)
65 {
66 // i_ready is in first cacheline
67 syscalls.cache_inv(worker, 64, 1);
68
69 left = worker->i_ready - worker->i_done;
70 if (left > 0) {
71 dirty = 1;
72 worker->active = ACTIVE_CNT;
73 syscalls.cache_wb(&worker->active, 4, 1);
74
75 work = &worker->i[worker->i_done & WORK_I_MASK];
76 invalidate_cache(work);
05c7cec7 77 had_rvb |= work->rvb_addr;
78 spu.spuCtrl = work->ctrl;
3bd31caf 79 do_channel_work(work);
80 writeout_cache(work);
81
82 worker->i_done++;
83 syscalls.cache_wb(&worker->i_done, 4, 1);
84 continue;
85 }
86
87 // nothing to do? Write out non-critical caches
88 if (dirty) {
89 syscalls.cache_wb(spu.spuMemC + 0x800, 0x800, 1);
90 syscalls.cache_wb(spu.SB, sizeof(spu.SB[0]) * SB_SIZE * 24, 1);
05c7cec7 91 if (had_rvb) {
92 left = 0x40000 - spu.rvb->StartAddr;
93 syscalls.cache_wb(spu.spuMem + spu.rvb->StartAddr, left * 2, 1);
94 had_rvb = 0;
95 }
3bd31caf 96 dirty = 0;
97 continue;
98 }
99
100 // this ->active loop thing is to avoid a race where we miss
101 // new work and clear ->active just after ARM checks it
102 worker->active--;
103 syscalls.cache_wb(&worker->active, 4, 1);
104 }
de4a0279 105}
de4a0279 106
107static unsigned int exec(dsp_component_cmd_t cmd,
108 unsigned int arg1, unsigned int arg2,
109 unsigned int *ret1, unsigned int *ret2)
110{
111 struct region_mem *mem = (void *)arg1;
de4a0279 112
113 switch (cmd) {
114 case CCMD_INIT:
115 InitADSR();
116
117 spu.spuMemC = mem->spu_ram;
de4a0279 118 spu.SB = mem->SB;
05c7cec7 119 spu.s_chan = mem->in.s_chan;
120 spu.rvb = &mem->in.rvb;
de4a0279 121 worker = &mem->worker;
122 memcpy(&spu_config, &mem->spu_config, sizeof(spu_config));
123
124 mem->sizeof_region_mem = sizeof(*mem);
05c7cec7 125 mem->offsetof_s_chan1 = offsetof(typeof(*mem), in.s_chan[1]);
3bd31caf 126 mem->offsetof_spos_3_20 = offsetof(typeof(*mem), worker.i[3].ch[20]);
de4a0279 127 // seems to be unneeded, no write-alloc? but just in case..
128 syscalls.cache_wb(&mem->sizeof_region_mem, 3 * 4, 1);
129 break;
130
131 case CCMD_DOIT:
3bd31caf 132 worker->active = ACTIVE_CNT;
133 worker->boot_cnt++;
134 syscalls.cache_wb(&worker->i_done, 64, 1);
135 memcpy(&spu_config, &mem->spu_config, sizeof(spu_config));
136
137 do_processing();
138
de4a0279 139 // c64_tools lib does BCACHE_wbInvAll() when it receives mailbox irq,
3bd31caf 140 // but invalidate anyway in case c64_tools is ever fixed..
05c7cec7 141 // XXX edit: don't bother as reverb is not handled, will fix if needed
142 //syscalls.cache_inv(mem, sizeof(mem->spu_ram) + sizeof(mem->SB), 0);
143 //syscalls.cache_inv(&mem->in, sizeof(mem->in), 0);
de4a0279 144 break;
145
146 default:
147 syscalls.printf("bad cmd: %x\n", cmd);
148 break;
149 }
150
151 return 0;
152}
153
154#pragma DATA_SECTION(component_test_dsp, ".sec_com");
155dsp_component_t component_test_dsp = {
156 {
157 NULL, /* init */
158 exec,
159 NULL, /* exec fastcall RPC */
160 NULL, /* exit */
161 },
162
163 COMPONENT_NAME,
164};
165
166DSP_COMPONENT_MAIN
167
168// vim:shiftwidth=1:expandtab