spu: fix some thread safety issues
[pcsx_rearmed.git] / plugins / dfsound / spu_c64x_dspcode.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 #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.. */
32 static void thread_work_start(void) {}
33 static void thread_work_wait_sync(struct work_item *work, int force) {}
34 static void thread_sync_caches(void) {}
35 static int  thread_get_i_done(void) { return 0; }
36 struct out_driver *out_current;
37 void SetupSound(void) {}
38
39
40 static void invalidate_cache(struct work_item *work)
41 {
42  // see comment in writeout_cache()
43  //syscalls.cache_inv(work, offsetof(typeof(*work), SSumLR), 1);
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
49 static void writeout_cache(struct work_item *work)
50 {
51  int ns_to = work->ns_to;
52
53  syscalls.cache_wb(work->SSumLR, sizeof(work->SSumLR[0]) * 2 * ns_to, 1);
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);
57 }
58
59 static void do_processing(void)
60 {
61  int left, dirty = 0, had_rvb = 0;
62  struct work_item *work;
63
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);
77    had_rvb |= work->rvb_addr;
78    spu.spuCtrl = work->ctrl;
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);
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    }
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  }
105 }
106
107 static 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;
112
113  switch (cmd) {
114   case CCMD_INIT:
115    InitADSR();
116
117    spu.spuMemC = mem->spu_ram;
118    spu.SB = mem->SB;
119    spu.s_chan = mem->in.s_chan;
120    spu.rvb = &mem->in.rvb;
121    worker = &mem->worker;
122    memcpy(&spu_config, &mem->spu_config, sizeof(spu_config));
123
124    mem->sizeof_region_mem = sizeof(*mem);
125    mem->offsetof_s_chan1 = offsetof(typeof(*mem), in.s_chan[1]);
126    mem->offsetof_spos_3_20 = offsetof(typeof(*mem), worker.i[3].ch[20]);
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:
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
139    // c64_tools lib does BCACHE_wbInvAll() when it receives mailbox irq,
140    // but invalidate anyway in case c64_tools is ever fixed..
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);
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");
155 dsp_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
166 DSP_COMPONENT_MAIN
167
168 // vim:shiftwidth=1:expandtab