iPod Classic/6G: PCM support for all CS42L55 rates
[maemo-rb.git] / firmware / target / arm / s5l8702 / pcm-s5l8702.c
blobcbfe6ea00713e7bb807020298587f316a5c77ba8
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: pcm-s5l8700.c 28600 2010-11-14 19:49:20Z Buschel $
10 * Copyright © 2011 Michael Sparmann
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include <string.h>
23 #include "config.h"
24 #include "system.h"
25 #include "audio.h"
26 #include "s5l8702.h"
27 #include "panic.h"
28 #include "audiohw.h"
29 #include "pcm.h"
30 #include "pcm-internal.h"
31 #include "pcm_sampr.h"
32 #include "mmu-arm.h"
33 #include "pcm-target.h"
35 static volatile int locked = 0;
36 static const int zerosample = 0;
37 static unsigned char dblbuf[2][PCM_WATERMARK * 4];
38 static int active_dblbuf;
39 struct dma_lli pcm_lli[PCM_LLICOUNT] __attribute__((aligned(16)));
40 static struct dma_lli* lastlli;
41 static const void* dataptr;
42 size_t pcm_remaining;
43 size_t pcm_chunksize;
45 /* Mask the DMA interrupt */
46 void pcm_play_lock(void)
48 if (locked++ == 0) {
49 //TODO: Urgh, I don't like that at all...
50 VIC0INTENCLEAR = 1 << IRQ_DMAC0;
54 /* Unmask the DMA interrupt if enabled */
55 void pcm_play_unlock(void)
57 if (--locked == 0) {
58 VIC0INTENABLE = 1 << IRQ_DMAC0;
62 void INT_DMAC0C0(void) ICODE_ATTR;
63 void INT_DMAC0C0(void)
65 DMAC0INTTCCLR = 1;
66 if (!pcm_remaining)
68 pcm_play_dma_complete_callback(PCM_DMAST_OK, &dataptr, &pcm_remaining);
69 pcm_chunksize = pcm_remaining;
71 if (!pcm_remaining)
73 pcm_lli->nextlli = NULL;
74 pcm_lli->control = 0x7524a000;
75 commit_dcache();
76 return;
78 uint32_t lastsize = MIN(PCM_WATERMARK * 4, pcm_remaining / 2 + 1) & ~1;
79 pcm_remaining -= lastsize;
80 if (pcm_remaining) lastlli = &pcm_lli[ARRAYLEN(pcm_lli) - 1];
81 else lastlli = pcm_lli;
82 uint32_t chunksize = MIN(PCM_CHUNKSIZE * 4 - lastsize, pcm_remaining);
83 if (pcm_remaining > chunksize && chunksize > pcm_remaining - PCM_WATERMARK * 8)
84 chunksize = pcm_remaining - PCM_WATERMARK * 8;
85 pcm_remaining -= chunksize;
86 bool last = !chunksize;
87 int i = 0;
88 while (chunksize)
90 uint32_t thislli = MIN(PCM_LLIMAX * 4, chunksize);
91 chunksize -= thislli;
92 pcm_lli[i].srcaddr = (void*)dataptr;
93 pcm_lli[i].dstaddr = (void*)((int)&I2STXDB0);
94 pcm_lli[i].nextlli = chunksize ? &pcm_lli[i + 1] : lastlli;
95 pcm_lli[i].control = (chunksize ? 0x7524a000 : 0xf524a000) | (thislli / 2);
96 dataptr += thislli;
97 i++;
99 if (!pcm_remaining)
101 memcpy(dblbuf[active_dblbuf], dataptr, lastsize);
102 lastlli->srcaddr = dblbuf[active_dblbuf];
103 active_dblbuf ^= 1;
105 else lastlli->srcaddr = dataptr;
106 lastlli->dstaddr = (void*)((int)&I2STXDB0);
107 lastlli->nextlli = last ? NULL : pcm_lli;
108 lastlli->control = (last ? 0xf524a000 : 0x7524a000) | (lastsize / 2);
109 dataptr += lastsize;
110 commit_dcache();
111 if (!(DMAC0C0CONFIG & 1) && (pcm_lli[0].control & 0xfff))
113 DMAC0C0LLI = pcm_lli[0];
114 DMAC0C0CONFIG = 0x8a81;
116 else DMAC0C0NEXTLLI = pcm_lli;
118 pcm_play_dma_status_callback(PCM_DMAST_STARTED);
121 void pcm_play_dma_start(const void* addr, size_t size)
123 dataptr = addr;
124 pcm_remaining = size;
125 I2STXCOM = 0xe;
126 INT_DMAC0C0();
129 void pcm_play_dma_stop(void)
131 DMAC0C0CONFIG = 0x8a80;
132 I2STXCOM = 0xa;
135 /* pause playback by disabling LRCK */
136 void pcm_play_dma_pause(bool pause)
138 if (pause) I2STXCOM |= 1;
139 else I2STXCOM &= ~1;
142 /* MCLK = 12MHz (MCLKDIV2=1), [CS42L55 DS, s4.8] */
143 #define MCLK_FREQ 12000000
145 /* set the configured PCM frequency */
146 void pcm_dma_apply_settings(void)
148 /* configure I2S clock ratio */
149 I2SCLKDIV = MCLK_FREQ / hw_freq_sampr[pcm_fsel];
150 /* select CS42L55 sample rate */
151 audiohw_set_frequency(pcm_fsel);
154 void pcm_play_dma_init(void)
156 PWRCON(0) &= ~(1 << 4);
157 PWRCON(1) &= ~(1 << 7);
158 I2STXCON = 0xb100019;
159 I2SCLKCON = 1;
160 VIC0INTENABLE = 1 << IRQ_DMAC0;
162 audiohw_preinit();
163 pcm_dma_apply_settings();
166 void pcm_play_dma_postinit(void)
168 audiohw_postinit();
171 size_t pcm_get_bytes_waiting(void)
173 int bytes = pcm_remaining;
174 const struct dma_lli* lli = (const struct dma_lli*)((int)&DMAC0C0LLI);
175 while (lli)
177 bytes += (lli->control & 0xfff) * 2;
178 if (lli == lastlli) break;
179 lli = lli->nextlli;
181 return bytes;
184 const void* pcm_play_dma_get_peak_buffer(int *count)
186 *count = (DMAC0C0LLI.control & 0xfff) * 2;
187 return (void*)(((uint32_t)DMAC0C0LLI.srcaddr) & ~3);
190 #ifdef HAVE_PCM_DMA_ADDRESS
191 void * pcm_dma_addr(void *addr)
193 return addr;
195 #endif
198 /****************************************************************************
199 ** Recording DMA transfer
201 #ifdef HAVE_RECORDING
202 void pcm_rec_lock(void)
206 void pcm_rec_unlock(void)
210 void pcm_rec_dma_stop(void)
214 void pcm_rec_dma_start(void *addr, size_t size)
216 (void)addr;
217 (void)size;
220 void pcm_rec_dma_close(void)
225 void pcm_rec_dma_init(void)
230 const void * pcm_rec_dma_get_peak_buffer(void)
232 return NULL;
235 #endif /* HAVE_RECORDING */