58b2e45782
GCC 4.9 always emits assembly with divided syntax. Setting unified syntax in inline assembly causes the assembler to complain about GCC's generated code, because the directive extends past the scope of the inline asm. Fix this by setting divided mode at the end of the inline assembly block. The assembler directives are hidden behind macros because later versions of GCC won't need this workaround: they can be told to use the unified syntax with -masm-syntax-unified. Change-Id: Ic09e729e5bbb6fd44d08dac348daf6f55c75d7d8
320 lines
9.1 KiB
C
320 lines
9.1 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2006 by Michael Sevakis
|
|
* Copyright (C) 2008 by Rob Purchase
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
#include <stdlib.h>
|
|
#include "system.h"
|
|
#include "kernel.h"
|
|
#include "logf.h"
|
|
#include "audio.h"
|
|
#include "sound.h"
|
|
#include "i2s.h"
|
|
#include "pcm.h"
|
|
#include "pcm-internal.h"
|
|
|
|
struct dma_data
|
|
{
|
|
/* NOTE: The order of size and p is important if you use assembler
|
|
optimised fiq handler, so don't change it. */
|
|
union
|
|
{
|
|
uint16_t *p;
|
|
const void *p_r;
|
|
void *p_w;
|
|
};
|
|
size_t size;
|
|
#if NUM_CORES > 1
|
|
unsigned core;
|
|
#endif
|
|
int locked;
|
|
int state;
|
|
};
|
|
|
|
/****************************************************************************
|
|
** Playback DMA transfer
|
|
**/
|
|
struct dma_data dma_play_data SHAREDBSS_ATTR =
|
|
{
|
|
/* Initialize to a locked, stopped state */
|
|
{ .p = NULL },
|
|
.size = 0,
|
|
#if NUM_CORES > 1
|
|
.core = 0x00,
|
|
#endif
|
|
.locked = 0,
|
|
.state = 0
|
|
};
|
|
|
|
void pcm_play_dma_init(void)
|
|
{
|
|
DAVC = 0x0; /* Digital Volume = max */
|
|
#ifdef COWON_D2
|
|
/* Set DAI clock divided from PLL0 (192MHz).
|
|
The best approximation of 256*44.1kHz is 11.291MHz. */
|
|
BCLKCTR &= ~DEV_DAI;
|
|
PCLK_DAI = (1<<28) | 61682; /* DCO mode */
|
|
BCLKCTR |= DEV_DAI;
|
|
|
|
/* Enable DAI block in Master mode, 256fs->32fs, 16bit LSB */
|
|
DAMR = 0x3c8e80;
|
|
#else
|
|
#error "Target isn't supported"
|
|
#endif
|
|
/* Set DAI interrupts as FIQs */
|
|
IRQSEL = ~(DAI_RX_IRQ_MASK | DAI_TX_IRQ_MASK);
|
|
|
|
/* Initialize default register values. */
|
|
audiohw_init();
|
|
|
|
dma_play_data.size = 0;
|
|
#if NUM_CORES > 1
|
|
dma_play_data.core = 0; /* no core in control */
|
|
#endif
|
|
}
|
|
|
|
void pcm_play_dma_postinit(void)
|
|
{
|
|
audiohw_postinit();
|
|
}
|
|
|
|
void pcm_dma_apply_settings(void)
|
|
{
|
|
}
|
|
|
|
static void play_start_pcm(void)
|
|
{
|
|
DAMR &= ~(1<<14); /* disable tx */
|
|
dma_play_data.state = 1;
|
|
|
|
if (dma_play_data.size >= 16)
|
|
{
|
|
DADO_L(0) = *dma_play_data.p++;
|
|
DADO_R(0) = *dma_play_data.p++;
|
|
DADO_L(1) = *dma_play_data.p++;
|
|
DADO_R(1) = *dma_play_data.p++;
|
|
DADO_L(2) = *dma_play_data.p++;
|
|
DADO_R(2) = *dma_play_data.p++;
|
|
DADO_L(3) = *dma_play_data.p++;
|
|
DADO_R(3) = *dma_play_data.p++;
|
|
dma_play_data.size -= 16;
|
|
}
|
|
|
|
DAMR |= (1<<14); /* enable tx */
|
|
}
|
|
|
|
static void play_stop_pcm(void)
|
|
{
|
|
DAMR &= ~(1<<14); /* disable tx */
|
|
dma_play_data.state = 0;
|
|
}
|
|
|
|
void pcm_play_dma_start(const void *addr, size_t size)
|
|
{
|
|
dma_play_data.p_r = addr;
|
|
dma_play_data.size = size;
|
|
|
|
#if NUM_CORES > 1
|
|
/* This will become more important later - and different ! */
|
|
dma_play_data.core = processor_id(); /* save initiating core */
|
|
#endif
|
|
|
|
IEN |= DAI_TX_IRQ_MASK;
|
|
|
|
play_start_pcm();
|
|
}
|
|
|
|
void pcm_play_dma_stop(void)
|
|
{
|
|
play_stop_pcm();
|
|
dma_play_data.size = 0;
|
|
#if NUM_CORES > 1
|
|
dma_play_data.core = 0; /* no core in control */
|
|
#endif
|
|
}
|
|
|
|
void pcm_play_lock(void)
|
|
{
|
|
int status = disable_fiq_save();
|
|
|
|
if (++dma_play_data.locked == 1)
|
|
{
|
|
IEN &= ~DAI_TX_IRQ_MASK;
|
|
}
|
|
|
|
restore_fiq(status);
|
|
}
|
|
|
|
void pcm_play_unlock(void)
|
|
{
|
|
int status = disable_fiq_save();
|
|
|
|
if (--dma_play_data.locked == 0 && dma_play_data.state != 0)
|
|
{
|
|
IEN |= DAI_TX_IRQ_MASK;
|
|
}
|
|
|
|
restore_fiq(status);
|
|
}
|
|
|
|
#ifdef HAVE_RECORDING
|
|
/* TODO: implement */
|
|
void pcm_rec_dma_init(void)
|
|
{
|
|
}
|
|
|
|
void pcm_rec_dma_close(void)
|
|
{
|
|
}
|
|
|
|
void pcm_rec_dma_start(void *addr, size_t size)
|
|
{
|
|
(void) addr;
|
|
(void) size;
|
|
}
|
|
|
|
void pcm_rec_dma_stop(void)
|
|
{
|
|
}
|
|
|
|
void pcm_rec_lock(void)
|
|
{
|
|
}
|
|
|
|
void pcm_rec_unlock(void)
|
|
{
|
|
}
|
|
|
|
const void * pcm_rec_dma_get_peak_buffer(void)
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
#if defined(CPU_TCC780X)
|
|
void fiq_handler(void) ICODE_ATTR __attribute__((naked));
|
|
void fiq_handler(void)
|
|
{
|
|
/* r10 contains DADO_L0 base address (set in crt0.S to minimise code in the
|
|
* FIQ handler. r11 contains address of p (also set in crt0.S). Most other
|
|
* addresses we need are generated by using offsets with these two.
|
|
* r8 and r9 contains local copies of p and size respectively.
|
|
* r0-r3 and r12 is a working register.
|
|
*/
|
|
asm volatile (
|
|
BEGIN_ARM_ASM_SYNTAX_UNIFIED
|
|
"sub lr, lr, #4 \n"
|
|
"stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */
|
|
"mov r14, #0 \n" /* Was the callback called? */
|
|
#if defined(CPU_TCC780X)
|
|
"mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */
|
|
"ldr r9, =0xf3001004 \n" /* CREQ */
|
|
#endif
|
|
"str r8, [r9] \n" /* clear DAI IRQs */
|
|
"ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */
|
|
"cmp r9, #0x10 \n" /* is size <16? */
|
|
"blo .more_data \n" /* if so, ask pcmbuf for more data */
|
|
|
|
".fill_fifo: \n"
|
|
"ldr r12, [r8], #4 \n" /* load two samples */
|
|
"str r12, [r10, #0x0] \n" /* write top sample to DADO_L0 */
|
|
"mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
|
|
"str r12, [r10, #0x4] \n" /* write low sample to DADO_R0*/
|
|
"ldr r12, [r8], #4 \n" /* load two samples */
|
|
"str r12, [r10, #0x8] \n" /* write top sample to DADO_L1 */
|
|
"mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
|
|
"str r12, [r10, #0xc] \n" /* write low sample to DADO_R1*/
|
|
"ldr r12, [r8], #4 \n" /* load two samples */
|
|
"str r12, [r10, #0x10] \n" /* write top sample to DADO_L2 */
|
|
"mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
|
|
"str r12, [r10, #0x14] \n" /* write low sample to DADO_R2*/
|
|
"ldr r12, [r8], #4 \n" /* load two samples */
|
|
"str r12, [r10, #0x18] \n" /* write top sample to DADO_L3 */
|
|
"mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
|
|
"str r12, [r10, #0x1c] \n" /* write low sample to DADO_R3*/
|
|
"sub r9, r9, #0x10 \n" /* 4 words written */
|
|
"stmia r11, { r8-r9 } \n" /* save p and size */
|
|
|
|
"cmp r14, #0 \n" /* Callback called? */
|
|
"ldmfdeq sp!, { r0-r3, pc }^ \n" /* no? -> exit */
|
|
|
|
"ldr r1, =pcm_play_status_callback \n"
|
|
"ldr r1, [r1] \n"
|
|
"cmp r1, #0 \n"
|
|
"movne r0, %1 \n"
|
|
"blxne r1 \n"
|
|
"ldmfd sp!, { r0-r3, pc }^ \n" /* exit */
|
|
|
|
".more_data: \n"
|
|
"mov r14, #1 \n" /* Remember we got more data in this FIQ */
|
|
"mov r0, %0 \n" /* r0 = status */
|
|
"mov r1, r11 \n" /* r1 = &dma_play_data.p_r */
|
|
"add r2, r11, #4 \n" /* r2 = &dma_play_data.size */
|
|
"mov lr, pc \n"
|
|
"ldr pc, =pcm_play_dma_complete_callback \n"
|
|
"cmp r0, #0 \n" /* any more to play? */
|
|
"ldmiane r11, { r8-r9 } \n" /* load new p and size */
|
|
"cmpne r9, #0x0f \n" /* did we actually get enough data? */
|
|
"bhi .fill_fifo \n" /* not stop and enough? refill */
|
|
"ldmfd sp!, { r0-r3, pc }^ \n" /* exit */
|
|
".ltorg \n"
|
|
END_ARM_ASM_SYNTAX_UNIFIED
|
|
: : "i"(PCM_DMAST_OK), "i"(PCM_DMAST_STARTED)
|
|
);
|
|
}
|
|
#else /* C version for reference */
|
|
void fiq_handler(void) ICODE_ATTR;
|
|
void fiq_handler(void)
|
|
{
|
|
register bool new_buffer = false;
|
|
|
|
if (dma_play_data.size < 16)
|
|
{
|
|
/* p is empty, get some more data */
|
|
new_buffer = pcm_play_dma_complete_callback(&dma_play_data.p_r,
|
|
&dma_play_data.size);
|
|
}
|
|
|
|
if (dma_play_data.size >= 16)
|
|
{
|
|
DADO_L(0) = *dma_play_data.p++;
|
|
DADO_R(0) = *dma_play_data.p++;
|
|
DADO_L(1) = *dma_play_data.p++;
|
|
DADO_R(1) = *dma_play_data.p++;
|
|
DADO_L(2) = *dma_play_data.p++;
|
|
DADO_R(2) = *dma_play_data.p++;
|
|
DADO_L(3) = *dma_play_data.p++;
|
|
DADO_R(3) = *dma_play_data.p++;
|
|
|
|
dma_play_data.size -= 16;
|
|
}
|
|
|
|
/* Clear FIQ status */
|
|
CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK;
|
|
|
|
if (new_buffer)
|
|
pcm_play_dma_status_callback(PCM_DMAST_STARTED);
|
|
}
|
|
#endif
|
|
|
|
/* TODO: required by wm8731 codec */
|
|
void i2s_reset(void)
|
|
{
|
|
/* DAMR = 0; */
|
|
}
|