1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
30 #include "pcm-internal.h"
31 #include "pcm_sampr.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
;
45 /* Mask the DMA interrupt */
46 void pcm_play_lock(void)
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)
58 VIC0INTENABLE
= 1 << IRQ_DMAC0
;
62 void INT_DMAC0C0(void) ICODE_ATTR
;
63 void INT_DMAC0C0(void)
68 pcm_play_dma_complete_callback(PCM_DMAST_OK
, &dataptr
, &pcm_remaining
);
69 pcm_chunksize
= pcm_remaining
;
73 pcm_lli
->nextlli
= NULL
;
74 pcm_lli
->control
= 0x7524a000;
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
;
90 uint32_t thislli
= MIN(PCM_LLIMAX
* 4, chunksize
);
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);
101 memcpy(dblbuf
[active_dblbuf
], dataptr
, lastsize
);
102 lastlli
->srcaddr
= dblbuf
[active_dblbuf
];
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);
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
)
124 pcm_remaining
= size
;
129 void pcm_play_dma_stop(void)
131 DMAC0C0CONFIG
= 0x8a80;
135 /* pause playback by disabling LRCK */
136 void pcm_play_dma_pause(bool pause
)
138 if (pause
) 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;
160 VIC0INTENABLE
= 1 << IRQ_DMAC0
;
163 pcm_dma_apply_settings();
166 void pcm_play_dma_postinit(void)
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
);
177 bytes
+= (lli
->control
& 0xfff) * 2;
178 if (lli
== lastlli
) break;
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
)
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
)
220 void pcm_rec_dma_close(void)
225 void pcm_rec_dma_init(void)
230 const void * pcm_rec_dma_get_peak_buffer(void)
235 #endif /* HAVE_RECORDING */