An idea (I'm not sure if it's a viable one): for bit-perfect spdif transfers, maybe we can insert/substitute audio samples in CALM/AIDA output (== SPDIF Tx input) ring buffer (???).
I.e.: AIDA set for PCM playback and running as usual (we will feed it with random/all-zeros samples from exeDSP in right quantities), with it's output buffer constantly/concurrently being overwritten with our own, arbitrary audio data.
Output buffer utilized by PCM spdif playback is located at 0x766d0000 (64kB total, two 32kB sub-buffers for left/right channel), 32bit/4bytes per sample, bytes 0-2 (from left to right) are most probably 24-bit low-endian samples, byte 3 is always 0x00. Overwriting output buffers shouldn't be a problem, I tested it with the simple program (below); biggest obstacle is: how to achieve a proper timings.
I believe Spdif Tx hardware is consuming samples from left/right sub-buffers in 1024-byte long chunks via DMA transfers;
DMA6 BASE register (0x30630300, mapped to 0xf8630300 in kernel address space) looks interesting for synchronization purposes (holding 0x766D0000, 0x766D0400, ..., 0x766D7C00 pointers, incremented in 0x400 steps while PCM audio is being played via spdif). I hope somewhere in samdrv.ko there is a function (for initiating/setting up Spdif Tx DMA transfers periodically, or perhaps an interrupt handler for Spdif Tx) we can use for write pointer synchronization..
Code: Select all
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/mman.h>
#define FATAL do { fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \
__LINE__, __FILE__, errno, strerror(errno)); exit(1); } while(0)
#define BLK_SIZE 4096UL
int main(int argc, char **argv) {
int fd;
unsigned nonpp_out_addr=0x766d0000;
unsigned mm_size=0x10000;
/*
[PES / ES In BASE ] 0x76680000 (SQB0 BASE)
[PP Out BASE ] 0x766c0000 (SQB1 BASE)
[Non-PP Out BASE ] 0x766d0000
[PCM In 1 BASE ] 0x76680000
[PCM In 2 BASE ] 0x76690000
[AD MIX BASE ] 0x766e0000
[ES Out BASE ] 0x766b8000
*/
/* [SPDIF TX 0 DMA Register Value (DMA 6)]
[BASE (0xf8630300)] 0x766d0000
[TRCNT (0xf8630304)] 0x00000800
[CTCNT (0xf8630308)] 0x00000660
[CADDR (0xf863030c)] 0x766d7ce0
[CFG (0xf8630310)] 0x310c8000
[COM (0xf8630314)] 0x00000000
[INTCFG (0xf8630318)] 0x00001000
[STATUS (0xf863031c)] 0x00000001
[WRSIZE (0xf8630320)] 0x00000000
[WRADDR (0xf8630324)] 0x00000000
[OBUF (0xf8630328)] 0x00000000
*/
/*
* 0x3063_0300 ~ 0x3063_0328 : 41. AIO DMA6
* 0x3063_0680 ~ 0x3063_06B0 : 41. AIO SPDIF Tx
*/
unsigned target=nonpp_out_addr;
if (mm_size % BLK_SIZE) { fprintf(stderr, "\nSize value (%d) outside block boundary !!!\n", mm_size); exit(1); }
if (target % BLK_SIZE) { fprintf(stderr, "\nInvalid address (%p) !!!\n", target); exit(1); }
if ((fd=open("/dev/mem", O_RDWR | O_SYNC)) == -1) FATAL;
void *mm_base=mmap(0, mm_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target);
if (mm_base == (void *) -1) FATAL;
printf("Memory %p mapped at address %p.\n", target, mm_base);
printf("Size: %d bytes / %d kB / %d block[s].\n", mm_size, (mm_size/1024), (mm_size/BLK_SIZE));
fflush(stdout);
int blen=mm_size/2;
unsigned char *b1addr=mm_base;
unsigned char *b2addr=mm_base+blen;
while (1) {
memset(b1addr, 0, blen); // LEFT CHANNEL 32kB buf
memset(b2addr, 0, blen); // RIGHT CHANNEL 32kB buf
printf("="); fflush(stdout);
usleep(50000); // 50ms sleep (?) ~enough to overwrite output buffers in real-time for 44.1kHz playback
}
if (munmap(mm_base, mm_size) == -1) FATAL;
close(fd);
return 0;
}