summaryrefslogtreecommitdiff
path: root/lib/rbcodec/dsp/dsp_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/dsp/dsp_core.c')
-rw-r--r--lib/rbcodec/dsp/dsp_core.c554
1 files changed, 554 insertions, 0 deletions
diff --git a/lib/rbcodec/dsp/dsp_core.c b/lib/rbcodec/dsp/dsp_core.c
new file mode 100644
index 0000000000..84fe64adb0
--- /dev/null
+++ b/lib/rbcodec/dsp/dsp_core.c
@@ -0,0 +1,554 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 Miika Pekkarinen
11 * Copyright (C) 2012 Michael Sevakis
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
22#include "config.h"
23#include "system.h"
24#include "dsp.h"
25#include "dsp_sample_io.h"
26#include <sys/types.h>
27
28/* Define LOGF_ENABLE to enable logf output in this file */
29/*#define LOGF_ENABLE*/
30#include "logf.h"
31
32/* Actually generate the database of stages */
33#define DSP_PROC_DB_CREATE
34#include "dsp_proc_entry.h"
35
36/* Linked lists give fewer loads in processing loop compared to some index
37 * list, which is more important than keeping occasionally executed code
38 * simple */
39
40struct dsp_config
41{
42 /** General DSP-local data **/
43 struct sample_io_data io_data; /* Sample input-output data (first) */
44 uint32_t slot_free_mask; /* Mask of free slots for this DSP */
45 uint32_t proc_masks[2]; /* Mask of active/enabled stages */
46 struct dsp_proc_slot
47 {
48 struct dsp_proc_entry proc_entry; /* This enabled stage */
49 struct dsp_proc_slot *next[2]; /* [0]=active next, [1]=enabled next */
50 const struct dsp_proc_db_entry *db_entry;
51 } *proc_slots[2]; /* Pointer to first in list of
52 active/enabled stages */
53
54 /** Misc. extra stuff **/
55#ifdef CPU_COLDFIRE
56 unsigned long old_macsr; /* Old macsr value to restore */
57#endif
58#if 0 /* Not needed now but enable if something must know this */
59 bool processing; /* DSP is processing (to thwart inopportune
60 buffer moves) */
61#endif
62};
63
64/* Pool of slots for stages - supports 32 or fewer combined as-is atm. */
65static struct dsp_proc_slot
66dsp_proc_slot_arr[DSP_NUM_PROC_STAGES+DSP_VOICE_NUM_PROC_STAGES] IBSS_ATTR;
67
68/* General DSP config */
69static struct dsp_config dsp_conf[DSP_COUNT] IBSS_ATTR;
70
71/** Processing stages support functions **/
72
73/* Find the slot for a given enabled id */
74static struct dsp_proc_slot * find_proc_slot(struct dsp_config *dsp,
75 unsigned int id)
76{
77 const uint32_t mask = BIT_N(id);
78
79 if ((dsp->proc_masks[1] & mask) == 0)
80 return NULL; /* Not enabled */
81
82 struct dsp_proc_slot *s = dsp->proc_slots[1];
83
84 while (1) /* In proc_masks == it must be there */
85 {
86 if (BIT_N(s->db_entry->id) == mask)
87 return s;
88
89 s = s->next[1];
90 }
91}
92
93/* Broadcast to all enabled stages or to the one with the specifically
94 * crafted setting */
95static intptr_t proc_broadcast(struct dsp_config *dsp, unsigned int setting,
96 intptr_t value)
97{
98 bool multi = setting < DSP_PROC_SETTING;
99 struct dsp_proc_slot *s = multi ?
100 dsp->proc_slots[1] : find_proc_slot(dsp, setting - DSP_PROC_SETTING);
101
102 while (s != NULL)
103 {
104 intptr_t ret = s->db_entry->configure(&s->proc_entry, dsp, setting,
105 value);
106 if (!multi)
107 return ret;
108
109 s = s->next[1];
110 }
111
112 return multi ? 1 : 0;
113}
114
115/* Generic handler for this->process[0] */
116static void dsp_process_null(struct dsp_proc_entry *this,
117 struct dsp_buffer **buf_p)
118{
119 (void)this; (void)buf_p;
120}
121
122/* Generic handler for this->process[1] */
123static void dsp_format_change_process(struct dsp_proc_entry *this,
124 struct dsp_buffer **buf_p)
125{
126 enum dsp_proc_ids id =
127 TYPE_FROM_MEMBER(struct dsp_proc_slot, this, proc_entry)->db_entry->id;
128
129 DSP_PRINT_FORMAT(<Default Handler>, id, (*buf_p)->format);
130
131 /* We don't keep back references to the DSP, so just search for it */
132 struct dsp_config *dsp;
133 for (int i = 0; (dsp = dsp_get_config(i)); i++)
134 {
135 struct dsp_proc_slot *slot = find_proc_slot(dsp, id);
136 /* Found one with the id, check if it's this one */
137 if (&slot->proc_entry == this && dsp_proc_active(dsp, id))
138 {
139 dsp_proc_call(this, buf_p, 0);
140 break;
141 }
142 }
143}
144
145/* Add an item to the enabled list */
146static struct dsp_proc_slot *
147dsp_proc_enable_enlink(struct dsp_config *dsp, uint32_t mask)
148{
149 /* Use the lowest-indexed available slot */
150 int slot = find_first_set_bit(dsp->slot_free_mask);
151
152 if (slot == 32)
153 {
154 /* Should NOT happen, ever, unless called before init */
155 DEBUGF("DSP %d: no slots!\n", (int)dsp_get_id(dsp));
156 return NULL;
157 }
158
159 const struct dsp_proc_db_entry *db_entry_prev = NULL;
160 const struct dsp_proc_db_entry *db_entry;
161
162 /* Order of enabled list is same as DB array */
163 for (unsigned int i = 0;; i++)
164 {
165 if (i >= DSP_NUM_PROC_STAGES)
166 return NULL;
167
168 db_entry = dsp_proc_database[i];
169
170 uint32_t m = BIT_N(db_entry->id);
171
172 if (m == mask)
173 break; /* This is the one */
174
175 if (dsp->proc_masks[1] & m)
176 db_entry_prev = db_entry;
177 }
178
179 struct dsp_proc_slot *s = &dsp_proc_slot_arr[slot];
180
181 if (db_entry_prev != NULL)
182 {
183 struct dsp_proc_slot *prev = find_proc_slot(dsp, db_entry_prev->id);
184 s->next[0] = prev->next[0];
185 s->next[1] = prev->next[1];
186 prev->next[1] = s;
187 }
188 else
189 {
190 s->next[0] = dsp->proc_slots[0];
191 s->next[1] = dsp->proc_slots[1];
192 dsp->proc_slots[1] = s;
193 }
194
195 s->db_entry = db_entry; /* record DB entry */
196 dsp->proc_masks[1] |= mask;
197 dsp->slot_free_mask &= ~BIT_N(slot);
198
199 return s;
200}
201
202/* Remove an item from the enabled list */
203static struct dsp_proc_slot *
204dsp_proc_enable_delink(struct dsp_config *dsp, uint32_t mask)
205{
206 struct dsp_proc_slot *s = dsp->proc_slots[1];
207 struct dsp_proc_slot *prev = NULL;
208
209 while (1) /* In proc_masks == it must be there */
210 {
211 if (BIT_N(s->db_entry->id) == mask)
212 {
213 if (prev)
214 prev->next[1] = s->next[1];
215 else
216 dsp->proc_slots[1] = s->next[1];
217
218 dsp->proc_masks[1] &= ~mask;
219 dsp->slot_free_mask |= BIT_N(s - dsp_proc_slot_arr);
220 return s;
221 }
222
223 prev = s;
224 s = s->next[1];
225 }
226}
227
228void dsp_proc_enable(struct dsp_config *dsp, enum dsp_proc_ids id,
229 bool enable)
230{
231 uint32_t mask = BIT_N(id);
232 bool enabled = dsp->proc_masks[1] & mask;
233
234 if (enable)
235 {
236 /* If enabled, just find it in list, if not, link a new one */
237 struct dsp_proc_slot *s = enabled ? find_proc_slot(dsp, id) :
238 dsp_proc_enable_enlink(dsp, mask);
239
240 if (s == NULL)
241 {
242 DEBUGF("DSP- proc id not valid: %d\n", (int)id);
243 return;
244 }
245
246 if (!enabled)
247 {
248 /* New entry - set defaults */
249 s->proc_entry.data = 0;
250 s->proc_entry.ip_mask = mask;
251 s->proc_entry.process[0] = dsp_process_null;
252 s->proc_entry.process[1] = dsp_format_change_process;
253 }
254
255 enabled = s->db_entry->configure(&s->proc_entry, dsp, DSP_PROC_INIT,
256 enabled) >= 0;
257 if (enabled)
258 return;
259
260 DEBUGF("DSP- proc init failed: %d\n", (int)id);
261 /* Cleanup below */
262 }
263 else if (!enabled)
264 {
265 return; /* No change */
266 }
267
268 dsp_proc_activate(dsp, id, false); /* Deactivate it first */
269 struct dsp_proc_slot *s = dsp_proc_enable_delink(dsp, mask);
270 s->db_entry->configure(&s->proc_entry, dsp, DSP_PROC_CLOSE, 0);
271}
272
273/* Maintain the list structure for the active list where each enabled entry
274 * has a link to the next active item, even if not active which facilitates
275 * switching out of format change mode by a stage during a format change.
276 * When that happens, the iterator must jump over inactive but enabled
277 * stages after its current position. */
278static struct dsp_proc_slot *
279dsp_proc_activate_link(struct dsp_config *dsp, uint32_t mask,
280 struct dsp_proc_slot *s)
281{
282 uint32_t m = BIT_N(s->db_entry->id);
283 uint32_t mor = m | mask;
284
285 if (mor == m) /* Only if same single bit in common */
286 {
287 dsp->proc_masks[0] |= mask;
288 return s;
289 }
290 else if (~mor == 0) /* Only if bits complement */
291 {
292 dsp->proc_masks[0] &= mask;
293 return s->next[0];
294 }
295
296 struct dsp_proc_slot *next = s->next[1];
297 next = dsp_proc_activate_link(dsp, mask, next);
298
299 s->next[0] = next;
300
301 return (m & dsp->proc_masks[0]) ? s : next;
302}
303
304/* Activate or deactivate a stage */
305void dsp_proc_activate(struct dsp_config *dsp, enum dsp_proc_ids id,
306 bool activate)
307{
308 const uint32_t mask = BIT_N(id);
309
310 if (!(dsp->proc_masks[1] & mask))
311 return; /* Not enabled */
312
313 if (activate != !(dsp->proc_masks[0] & mask))
314 return; /* No change in state */
315
316 /* Send mask bit if activating and ones complement if deactivating */
317 dsp->proc_slots[0] = dsp_proc_activate_link(
318 dsp, activate ? mask : ~mask, dsp->proc_slots[1]);
319}
320
321/* Is the stage specified by the id currently active? */
322bool dsp_proc_active(struct dsp_config *dsp, enum dsp_proc_ids id)
323{
324 return (dsp->proc_masks[0] & BIT_N(id)) != 0;
325}
326
327/* Determine by the rules if the processing function should be called */
328static FORCE_INLINE bool dsp_proc_should_call(struct dsp_proc_entry *this,
329 struct dsp_buffer *buf,
330 unsigned int fmt)
331{
332 uint32_t ip_mask = this->ip_mask;
333
334 return UNLIKELY(fmt != 0) || /* Also pass override value */
335 ip_mask == 0 || /* Not in-place */
336 ((ip_mask & buf->proc_mask) == 0 &&
337 (buf->proc_mask |= ip_mask, buf->remcount > 0));
338}
339
340/* Call this->process[fmt] according to the rules (for external call) */
341bool dsp_proc_call(struct dsp_proc_entry *this, struct dsp_buffer **buf_p,
342 unsigned int fmt)
343{
344 if (dsp_proc_should_call(this, *buf_p, fmt))
345 {
346 this->process[fmt == (0u-1u) ? 0 : fmt](this, buf_p);
347 return true;
348 }
349
350 return false;
351}
352
353static inline void dsp_process_start(struct dsp_config *dsp)
354{
355#if defined(CPU_COLDFIRE)
356 /* set emac unit for dsp processing, and save old macsr, we're running in
357 codec thread context at this point, so can't clobber it */
358 dsp->old_macsr = coldfire_get_macsr();
359 coldfire_set_macsr(EMAC_FRACTIONAL | EMAC_SATURATE);
360#endif
361#if 0 /* Not needed now but enable if something must know this */
362 dsp->processing = true;
363#endif
364 (void)dsp;
365}
366
367static inline void dsp_process_end(struct dsp_config *dsp)
368{
369#if 0 /* Not needed now but enable if something must know this */
370 dsp->processing = false;
371#endif
372#if defined(CPU_COLDFIRE)
373 /* set old macsr again */
374 coldfire_set_macsr(dsp->old_macsr);
375#endif
376 (void)dsp;
377}
378
379/**
380 * dsp_process:
381 *
382 * Process and convert src audio to dst based on the DSP configuration.
383 * dsp: the DSP instance in use
384 *
385 * src:
386 * remcount = number of input samples remaining; set to desired
387 * number of samples to be processed
388 * pin[0] = left channel if non-interleaved, audio data if
389 * interleaved or mono
390 * pin[1] = right channel if non-interleaved, ignored if
391 * interleaved or mono
392 * proc_mask = set to zero on first call, updated by this function
393 * to keep track of which in-place stages have been
394 * run on the buffers to avoid multiple applications of
395 * them
396 * format = for internal buffers, gives the relevant format
397 * details
398 *
399 * dst:
400 * remcount = number of samples placed in buffer so far; set to
401 * zero on first call
402 * p16out = current fill pointer in destination buffer; set to
403 * buffer start on first call
404 * bufcount = remaining buffer space in samples; set to maximum
405 * desired output count on first call
406 * format = ignored
407 *
408 * Processing stops when src is exhausted or dst is filled, whichever
409 * happens first. Samples can still be output when src buffer is empty
410 * if samples are held internally. Generally speaking, continue calling
411 * until no data is consumed and no data is produced to purge the DSP
412 * to the maximum extent feasible. Some internal processing stages may
413 * require more input before more output can be generated, thus there
414 * is no guarantee the DSP is free of data awaiting processing at that
415 * point.
416 *
417 * Additionally, samples consumed and samples produced do not necessarily
418 * have a direct correlation. Samples may be consumed without producing
419 * any output and samples may be produced without consuming any input.
420 * It depends on which stages are actively processing data at the time
421 * of the call and how they function internally.
422 */
423void dsp_process(struct dsp_config *dsp, struct dsp_buffer *src,
424 struct dsp_buffer *dst)
425{
426 if (dst->bufcount <= 0)
427 {
428 /* No place to put anything thus nothing may be safely consumed */
429 return;
430 }
431
432 /* At least perform one yield before starting */
433 long last_yield = current_tick;
434 yield();
435
436 dsp_process_start(dsp);
437
438 /* Tag input with codec-specified sample format */
439 src->format = dsp->io_data.format;
440
441 while (1)
442 {
443 /* Out-of-place-processing stages take the current buf as input
444 * and switch the buffer to their own output buffer */
445 struct dsp_buffer *buf = src;
446 unsigned int fmt = buf->format.changed;
447
448 /* Convert input samples to internal format */
449 dsp->io_data.input_samples[fmt](&dsp->io_data, &buf);
450 fmt = buf->format.changed;
451
452 struct dsp_proc_slot *s = dsp->proc_slots[fmt];
453
454 /* Call all active/enabled stages depending if format is
455 same/changed on the last output buffer */
456 while (s != NULL)
457 {
458 if (dsp_proc_should_call(&s->proc_entry, buf, fmt))
459 {
460 s->proc_entry.process[fmt](&s->proc_entry, &buf);
461 fmt = buf->format.changed;
462 }
463
464 /* The buffer may have changed along with the format flag */
465 s = s->next[fmt];
466 }
467
468 /* Don't overread/write src/destination */
469 int outcount = MIN(dst->bufcount, buf->remcount);
470
471 if (fmt == 0 && outcount <= 0)
472 break; /* Output full or purged internal buffers */
473
474 dsp->io_data.outcount = outcount;
475 dsp->io_data.output_samples[fmt](&dsp->io_data, buf, dst);
476
477 /* Advance buffers by what output consumed and produced */
478 dsp_advance_buffer32(buf, outcount);
479 dsp_advance_buffer_output(dst, outcount);
480
481 /* Yield at least once each tick */
482 long tick = current_tick;
483 if (TIME_AFTER(tick, last_yield))
484 {
485 last_yield = tick;
486 yield();
487 }
488 } /* while */
489
490 dsp_process_end(dsp);
491}
492
493intptr_t dsp_configure(struct dsp_config *dsp, unsigned int setting,
494 intptr_t value)
495{
496 dsp_sample_io_configure(&dsp->io_data, setting, value);
497 return proc_broadcast(dsp, setting, value);
498}
499
500struct dsp_config * dsp_get_config(enum dsp_ids id)
501{
502 if (id >= DSP_COUNT)
503 return NULL;
504
505 return &dsp_conf[id];
506}
507
508/* Return the id given a dsp pointer (or even via something within
509 the struct itself) */
510enum dsp_ids dsp_get_id(const struct dsp_config *dsp)
511{
512 ptrdiff_t id = dsp - dsp_conf;
513
514 if (id < 0 || id >= DSP_COUNT)
515 return DSP_COUNT; /* obviously invalid */
516
517 return (enum dsp_ids)id;
518}
519
520#if 0 /* Not needed now but enable if something must know this */
521bool dsp_is_busy(const struct dsp_config *dsp)
522{
523 return dsp->processing;
524}
525#endif /* 0 */
526
527/* Do what needs initializing before enable/disable calls can be made.
528 * Must be done before changing settings for the first time. */
529void INIT_ATTR dsp_init(void)
530{
531 static const uint8_t slot_count[DSP_COUNT] /* INITDATA_ATTR */ =
532 {
533 [CODEC_IDX_AUDIO] = DSP_NUM_PROC_STAGES,
534 [CODEC_IDX_VOICE] = DSP_VOICE_NUM_PROC_STAGES
535 };
536
537 for (unsigned int i = 0, count, shift = 0;
538 i < DSP_COUNT;
539 i++, shift += count)
540 {
541 struct dsp_config *dsp = &dsp_conf[i];
542
543 count = slot_count[i];
544 dsp->slot_free_mask = MASK_N(uint32_t, count, shift);
545
546 dsp_sample_io_configure(&dsp->io_data, DSP_INIT, i);
547
548 /* Notify each db entry of global init for each DSP */
549 for (unsigned int j = 0; j < DSP_NUM_PROC_STAGES; j++)
550 dsp_proc_database[j]->configure(NULL, dsp, DSP_INIT, i);
551
552 dsp_configure(dsp, DSP_RESET, 0);
553 }
554}