rockbox/firmware/target/arm/s5l8702/pcm-s5l8702.c
Cástor Muñoz 794169a18f iPod Classic: fix HW_FREQ_32
Change-Id: I1e1b4e6ceb92eb793affaefc61ab082d5da735b4
2014-11-18 06:22:32 +01:00

264 lines
6.6 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id: pcm-s5l8700.c 28600 2010-11-14 19:49:20Z Buschel $
*
* Copyright © 2011 Michael Sparmann
*
* 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 <string.h>
#include "config.h"
#include "system.h"
#include "audio.h"
#include "s5l8702.h"
#include "panic.h"
#include "audiohw.h"
#include "pcm.h"
#include "pcm-internal.h"
#include "pcm_sampr.h"
#include "mmu-arm.h"
#include "pcm-target.h"
static volatile int locked = 0;
static const int zerosample = 0;
static unsigned char dblbuf[2][PCM_WATERMARK * 4];
static int active_dblbuf;
struct dma_lli pcm_lli[PCM_LLICOUNT] __attribute__((aligned(16)));
static struct dma_lli* lastlli;
static const void* dataptr;
size_t pcm_remaining;
size_t pcm_chunksize;
/* Mask the DMA interrupt */
void pcm_play_lock(void)
{
if (locked++ == 0) {
//TODO: Urgh, I don't like that at all...
VIC0INTENCLEAR = 1 << IRQ_DMAC0;
}
}
/* Unmask the DMA interrupt if enabled */
void pcm_play_unlock(void)
{
if (--locked == 0) {
VIC0INTENABLE = 1 << IRQ_DMAC0;
}
}
void INT_DMAC0C0(void) ICODE_ATTR;
void INT_DMAC0C0(void)
{
DMAC0INTTCCLR = 1;
if (!pcm_remaining)
{
pcm_play_dma_complete_callback(PCM_DMAST_OK, &dataptr, &pcm_remaining);
pcm_chunksize = pcm_remaining;
}
if (!pcm_remaining)
{
pcm_lli->nextlli = NULL;
pcm_lli->control = 0x7524a000;
commit_dcache();
return;
}
uint32_t lastsize = MIN(PCM_WATERMARK * 4, pcm_remaining / 2 + 1) & ~1;
pcm_remaining -= lastsize;
if (pcm_remaining) lastlli = &pcm_lli[ARRAYLEN(pcm_lli) - 1];
else lastlli = pcm_lli;
uint32_t chunksize = MIN(PCM_CHUNKSIZE * 4 - lastsize, pcm_remaining);
if (pcm_remaining > chunksize && chunksize > pcm_remaining - PCM_WATERMARK * 8)
chunksize = pcm_remaining - PCM_WATERMARK * 8;
pcm_remaining -= chunksize;
bool last = !chunksize;
int i = 0;
while (chunksize)
{
uint32_t thislli = MIN(PCM_LLIMAX * 4, chunksize);
chunksize -= thislli;
pcm_lli[i].srcaddr = (void*)dataptr;
pcm_lli[i].dstaddr = (void*)((int)&I2STXDB0);
pcm_lli[i].nextlli = chunksize ? &pcm_lli[i + 1] : lastlli;
pcm_lli[i].control = (chunksize ? 0x7524a000 : 0xf524a000) | (thislli / 2);
dataptr += thislli;
i++;
}
if (!pcm_remaining)
{
memcpy(dblbuf[active_dblbuf], dataptr, lastsize);
lastlli->srcaddr = dblbuf[active_dblbuf];
active_dblbuf ^= 1;
}
else lastlli->srcaddr = dataptr;
lastlli->dstaddr = (void*)((int)&I2STXDB0);
lastlli->nextlli = last ? NULL : pcm_lli;
lastlli->control = (last ? 0xf524a000 : 0x7524a000) | (lastsize / 2);
dataptr += lastsize;
commit_dcache();
if (!(DMAC0C0CONFIG & 1) && (pcm_lli[0].control & 0xfff))
{
DMAC0C0LLI = pcm_lli[0];
DMAC0C0CONFIG = 0x8a81;
}
else DMAC0C0NEXTLLI = pcm_lli;
pcm_play_dma_status_callback(PCM_DMAST_STARTED);
}
void pcm_play_dma_start(const void* addr, size_t size)
{
dataptr = addr;
pcm_remaining = size;
I2STXCOM = 0xe;
INT_DMAC0C0();
}
void pcm_play_dma_stop(void)
{
DMAC0C0CONFIG = 0x8a80;
I2STXCOM = 0xa;
}
/* pause playback by disabling LRCK */
void pcm_play_dma_pause(bool pause)
{
if (pause) I2STXCOM |= 1;
else I2STXCOM &= ~1;
}
/* MCLK = 12MHz (MCLKDIV2=1), [CS42L55 DS, s4.8] */
#define MCLK_FREQ 12000000
/* set the configured PCM frequency */
void pcm_dma_apply_settings(void)
{
static uint16_t last_clkcon3l = 0;
uint16_t clkcon3l;
int fsel;
/* For unknown reasons, s5l8702 I2S controller does not synchronize
* with CS42L55 at 32000 Hz. To fix it, the CODEC is configured with
* a sample rate of 48000 Hz and MCLK is decreased 1/3 to 8 Mhz,
* obtaining 32 KHz in LRCK controller input and 8 MHz in SCLK input.
* OF uses this trick.
*/
if (pcm_fsel == HW_FREQ_32) {
fsel = HW_FREQ_48;
clkcon3l = 0x3028; /* PLL2 / 3 / 9 -> 8 MHz */
}
else {
fsel = pcm_fsel;
clkcon3l = 0; /* OSC0 -> 12 MHz */
}
/* configure MCLK */
/* TODO: maybe all CLKCON management should be moved to
cscodec-ipod6g.c and system-s5l8702.c */
if (last_clkcon3l != clkcon3l) {
CLKCON3 = (CLKCON3 & ~0xffff) | 0x8000 | clkcon3l;
udelay(100);
CLKCON3 &= ~0x8000; /* CLKCON3L on */
last_clkcon3l = clkcon3l;
}
/* configure I2S clock ratio */
I2SCLKDIV = MCLK_FREQ / hw_freq_sampr[fsel];
/* select CS42L55 sample rate */
audiohw_set_frequency(fsel);
}
void pcm_play_dma_init(void)
{
PWRCON(0) &= ~(1 << 4);
PWRCON(1) &= ~(1 << 7);
I2STXCON = 0xb100019;
I2SCLKCON = 1;
VIC0INTENABLE = 1 << IRQ_DMAC0;
audiohw_preinit();
pcm_dma_apply_settings();
}
void pcm_play_dma_postinit(void)
{
audiohw_postinit();
}
size_t pcm_get_bytes_waiting(void)
{
int bytes = pcm_remaining;
const struct dma_lli* lli = (const struct dma_lli*)((int)&DMAC0C0LLI);
while (lli)
{
bytes += (lli->control & 0xfff) * 2;
if (lli == lastlli) break;
lli = lli->nextlli;
}
return bytes;
}
const void* pcm_play_dma_get_peak_buffer(int *count)
{
*count = (DMAC0C0LLI.control & 0xfff) * 2;
return (void*)(((uint32_t)DMAC0C0LLI.srcaddr) & ~3);
}
#ifdef HAVE_PCM_DMA_ADDRESS
void * pcm_dma_addr(void *addr)
{
return addr;
}
#endif
/****************************************************************************
** Recording DMA transfer
**/
#ifdef HAVE_RECORDING
void pcm_rec_lock(void)
{
}
void pcm_rec_unlock(void)
{
}
void pcm_rec_dma_stop(void)
{
}
void pcm_rec_dma_start(void *addr, size_t size)
{
(void)addr;
(void)size;
}
void pcm_rec_dma_close(void)
{
}
void pcm_rec_dma_init(void)
{
}
const void * pcm_rec_dma_get_peak_buffer(void)
{
return NULL;
}
#endif /* HAVE_RECORDING */