blob: c2199226077fc6b1a9415d0f01e8cf803bf58f82 [file] [log] [blame]
Marcoen Hirschberg29536762006-12-29 02:49:12 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2006 by Michael Sevakis
11 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * 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.
Marcoen Hirschberg29536762006-12-29 02:49:12 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
Michael Sevakis3c38fe42007-05-02 22:33:24 +000021#include <stdlib.h>
Marcoen Hirschberg29536762006-12-29 02:49:12 +000022#include "system.h"
23#include "kernel.h"
24#include "logf.h"
25#include "audio.h"
Michael Sevakisc2d21062007-03-11 06:21:43 +000026#include "sound.h"
Marcoen Hirschberg29536762006-12-29 02:49:12 +000027#include "file.h"
28
Michael Sevakis6077e5b2007-10-06 22:27:27 +000029/* All exact rates for 16.9344MHz clock */
Michael Sevakis3c38fe42007-05-02 22:33:24 +000030#define GIGABEAT_11025HZ (0x19 << 1)
31#define GIGABEAT_22050HZ (0x1b << 1)
32#define GIGABEAT_44100HZ (0x11 << 1)
33#define GIGABEAT_88200HZ (0x1f << 1)
34
Michael Sevakis6077e5b2007-10-06 22:27:27 +000035/* PCM interrupt routine lockout */
36static struct
37{
38 int locked;
39 unsigned long state;
40} dma_play_lock =
41{
42 .locked = 0,
Michael Sevakisa65406e2008-03-31 01:29:50 +000043 .state = 0,
Michael Sevakis6077e5b2007-10-06 22:27:27 +000044};
45
46/* Last samplerate set by pcm_set_frequency */
47static unsigned long pcm_freq = 0; /* 44.1 is default */
48/* Samplerate control for audio codec */
Michael Sevakisd989f192007-05-03 18:08:00 +000049static int sr_ctrl = 0;
Michael Sevakis6077e5b2007-10-06 22:27:27 +000050
51#define FIFO_COUNT ((IISFCON >> 6) & 0x3F)
Marcoen Hirschberg29536762006-12-29 02:49:12 +000052
Marcoen Hirschberg29536762006-12-29 02:49:12 +000053/* Setup for the DMA controller */
54#define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20))
55
Marcoen Hirschberg29536762006-12-29 02:49:12 +000056/* DMA count has hit zero - no more data */
57/* Get more data from the callback and top off the FIFO */
Michael Sevakis6077e5b2007-10-06 22:27:27 +000058void fiq_handler(void) __attribute__((interrupt ("FIQ")));
Michael Sevakis3c38fe42007-05-02 22:33:24 +000059
60static void _pcm_apply_settings(void)
Marcoen Hirschberg29536762006-12-29 02:49:12 +000061{
Michael Sevakis6077e5b2007-10-06 22:27:27 +000062 if (pcm_freq != pcm_curr_sampr)
Marcoen Hirschberg29536762006-12-29 02:49:12 +000063 {
Michael Sevakis6077e5b2007-10-06 22:27:27 +000064 pcm_curr_sampr = pcm_freq;
Michael Sevakis3c38fe42007-05-02 22:33:24 +000065 audiohw_set_frequency(sr_ctrl);
Marcoen Hirschberg29536762006-12-29 02:49:12 +000066 }
Marcoen Hirschberg29536762006-12-29 02:49:12 +000067}
68
Michael Sevakis3c38fe42007-05-02 22:33:24 +000069void pcm_apply_settings(void)
70{
Michael Sevakisaf395f42008-03-26 01:50:41 +000071 int status = disable_fiq_save();
Michael Sevakis3c38fe42007-05-02 22:33:24 +000072 _pcm_apply_settings();
Michael Sevakisaf395f42008-03-26 01:50:41 +000073 restore_fiq(status);
Michael Sevakis3c38fe42007-05-02 22:33:24 +000074}
Marcoen Hirschberg29536762006-12-29 02:49:12 +000075
Michael Sevakisa65406e2008-03-31 01:29:50 +000076/* Mask the DMA interrupt */
Michael Sevakis6077e5b2007-10-06 22:27:27 +000077void pcm_play_lock(void)
Marcoen Hirschberg29536762006-12-29 02:49:12 +000078{
Michael Sevakis6077e5b2007-10-06 22:27:27 +000079 if (++dma_play_lock.locked == 1)
Michael Sevakisa65406e2008-03-31 01:29:50 +000080 s3c_regset(&INTMSK, DMA2_MASK);
Michael Sevakis6077e5b2007-10-06 22:27:27 +000081}
Greg Whitedd7b75b2007-01-04 11:36:25 +000082
Michael Sevakisa65406e2008-03-31 01:29:50 +000083/* Unmask the DMA interrupt if enabled */
Michael Sevakis6077e5b2007-10-06 22:27:27 +000084void pcm_play_unlock(void)
85{
Michael Sevakis6077e5b2007-10-06 22:27:27 +000086 if (--dma_play_lock.locked == 0)
Michael Sevakisa65406e2008-03-31 01:29:50 +000087 s3c_regclr(&INTMSK, dma_play_lock.state);
Michael Sevakis6077e5b2007-10-06 22:27:27 +000088}
89
90void pcm_play_dma_init(void)
91{
Steve Gotthardtcd05dbc2007-01-16 03:36:32 +000092 pcm_set_frequency(SAMPR_44);
Marcoen Hirschberg29536762006-12-29 02:49:12 +000093
Karl Kurbjun66010b92008-05-10 19:43:23 +000094 /* There seem to be problems when changing the IIS interface configuration
95 * when a clock is not present.
96 */
97 s3c_regset(&CLKCON, 1<<17);
98 /* slave, transmit mode, 16 bit samples - MCLK 384fs - use 16.9344Mhz -
99 BCLK 32fs */
100 IISMOD = (1<<9) | (1<<8) | (2<<6) | (1<<3) | (1<<2) | (1<<0);
Michael Sevakisf7836172007-06-24 20:41:27 +0000101
Karl Kurbjun66010b92008-05-10 19:43:23 +0000102 /* RX,TX off,on */
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000103 IISCON |= (1<<3) | (1<<2);
104
Karl Kurbjun66010b92008-05-10 19:43:23 +0000105 s3c_regclr(&CLKCON, 1<<17);
106
Michael Sevakisd989f192007-05-03 18:08:00 +0000107 audiohw_init();
108
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000109 /* init GPIO */
110 GPCCON = (GPCCON & ~(3<<14)) | (1<<14);
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000111 GPCDAT |= (1<<7);
112 /* GPE4=I2SDO, GPE3=I2SDI, GPE2=CDCLK, GPE1=I2SSCLK, GPE0=I2SLRCK */
113 GPECON = (GPECON & ~0x3ff) | 0x2aa;
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000114
Steve Gotthardtcd05dbc2007-01-16 03:36:32 +0000115 /* Do not service DMA requests, yet */
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000116
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000117 /* clear any pending int and mask it */
Michael Sevakisa65406e2008-03-31 01:29:50 +0000118 s3c_regset(&INTMSK, DMA2_MASK);
119 SRCPND = DMA2_MASK;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000120
121 /* connect to FIQ */
Michael Sevakisa65406e2008-03-31 01:29:50 +0000122 s3c_regset(&INTMOD, DMA2_MASK);
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000123}
124
Michael Sevakisc2d21062007-03-11 06:21:43 +0000125void pcm_postinit(void)
126{
127 audiohw_postinit();
Michael Sevakisd989f192007-05-03 18:08:00 +0000128 pcm_apply_settings();
Michael Sevakisc2d21062007-03-11 06:21:43 +0000129}
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000130
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000131/* Connect the DMA and start filling the FIFO */
132static void play_start_pcm(void)
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000133{
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000134 /* clear pending DMA interrupt */
Michael Sevakisa65406e2008-03-31 01:29:50 +0000135 SRCPND = DMA2_MASK;
Michael Sevakis3c38fe42007-05-02 22:33:24 +0000136
137 _pcm_apply_settings();
138
Greg Whitedd7b75b2007-01-04 11:36:25 +0000139 /* Flush any pending writes */
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000140 clean_dcache_range((void*)DISRC2, (DCON2 & 0xFFFFF) * 2);
141
142 /* unmask DMA interrupt when unlocking */
Michael Sevakisa65406e2008-03-31 01:29:50 +0000143 dma_play_lock.state = DMA2_MASK;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000144
145 /* turn on the request */
146 IISCON |= (1<<5);
Greg Whitedd7b75b2007-01-04 11:36:25 +0000147
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000148 /* Activate the channel */
149 DMASKTRIG2 = 0x2;
Greg Whitedd7b75b2007-01-04 11:36:25 +0000150
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000151 /* turn off the idle */
152 IISCON &= ~(1<<3);
153
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000154 /* start the IIS */
155 IISCON |= (1<<0);
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000156}
157
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000158/* Disconnect the DMA and wait for the FIFO to clear */
159static void play_stop_pcm(void)
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000160{
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000161 /* Mask DMA interrupt */
Michael Sevakisa65406e2008-03-31 01:29:50 +0000162 s3c_regset(&INTMSK, DMA2_MASK);
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000163
164 /* De-Activate the DMA channel */
165 DMASKTRIG2 = 0x4;
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000166
Steve Gotthardtcd05dbc2007-01-16 03:36:32 +0000167 /* are we playing? wait for the chunk to finish */
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000168 if (dma_play_lock.state != 0)
Steve Gotthardtcd05dbc2007-01-16 03:36:32 +0000169 {
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000170 /* wait for the FIFO to empty and DMA to stop */
171 while ((IISCON & (1<<7)) || (DMASKTRIG2 & 0x2));
Steve Gotthardtcd05dbc2007-01-16 03:36:32 +0000172 }
173
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000174 /* Keep interrupt masked when unlocking */
175 dma_play_lock.state = 0;
176
177 /* turn off the request */
178 IISCON &= ~(1<<5);
179
180 /* turn on the idle */
181 IISCON |= (1<<3);
182
183 /* stop the IIS */
184 IISCON &= ~(1<<0);
185}
186
187void pcm_play_dma_start(const void *addr, size_t size)
188{
189 /* Enable the IIS clock */
Michael Sevakisa65406e2008-03-31 01:29:50 +0000190 s3c_regset(&CLKCON, 1<<17);
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000191
192 /* stop any DMA in progress - idle IIS */
193 play_stop_pcm();
194
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000195 /* connect DMA to the FIFO and enable the FIFO */
196 IISFCON = (1<<15) | (1<<13);
197
198 /* set DMA dest */
199 DIDST2 = (unsigned int)&IISFIFO;
200
201 /* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */
202 DIDSTC2 = 0x03;
203
204 /* set DMA source and options */
205 DISRC2 = (unsigned int)addr + 0x30000000;
206 /* How many transfers to make - we transfer half-word at a time = 2 bytes */
207 /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */
208 /* no auto-reload, half-word (16bit) */
209 DCON2 = DMA_CONTROL_SETUP | (size / 2);
210 DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */
211
212 play_start_pcm();
213}
214
215/* Promptly stop DMA transfers and stop IIS */
216void pcm_play_dma_stop(void)
217{
218 play_stop_pcm();
219
Steve Gotthardtcd05dbc2007-01-16 03:36:32 +0000220 /* Disconnect the IIS clock */
Michael Sevakisa65406e2008-03-31 01:29:50 +0000221 s3c_regclr(&CLKCON, 1<<17);
Michael Sevakis3c38fe42007-05-02 22:33:24 +0000222}
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000223
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000224void pcm_play_dma_pause(bool pause)
225{
226 if (pause)
227 {
228 /* pause playback on current buffer */
229 play_stop_pcm();
230 }
231 else
232 {
233 /* restart playback on current buffer */
234 /* make sure we're aligned on left channel - skip any right
235 channel sample left waiting */
236 DISRC2 = (DCSRC2 + 2) & ~0x3;
237 DCON2 = DMA_CONTROL_SETUP | (DSTAT2 & 0xFFFFE);
238 play_start_pcm();
239 }
240}
241
Michael Sevakisf48e0b52007-05-03 12:39:36 +0000242void fiq_handler(void)
Michael Sevakis3c38fe42007-05-02 22:33:24 +0000243{
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000244 static unsigned char *start;
245 static size_t size;
Michael Sevakisf48e0b52007-05-03 12:39:36 +0000246 register pcm_more_callback_type get_more; /* No stack for this */
Michael Sevakisf48e0b52007-05-03 12:39:36 +0000247
Michael Sevakis3c38fe42007-05-02 22:33:24 +0000248 /* clear any pending interrupt */
Michael Sevakisa65406e2008-03-31 01:29:50 +0000249 SRCPND = DMA2_MASK;
Michael Sevakis3c38fe42007-05-02 22:33:24 +0000250
251 /* Buffer empty. Try to get more. */
Michael Sevakisf48e0b52007-05-03 12:39:36 +0000252 get_more = pcm_callback_for_more;
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000253 size = 0;
254
255 if (get_more == NULL || (get_more(&start, &size), size == 0))
Michael Sevakis3c38fe42007-05-02 22:33:24 +0000256 {
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000257 /* Callback missing or no more DMA to do */
258 pcm_play_dma_stop();
259 pcm_play_dma_stopped_callback();
Michael Sevakis3c38fe42007-05-02 22:33:24 +0000260 }
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000261 else
Michael Sevakis3c38fe42007-05-02 22:33:24 +0000262 {
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000263 /* Flush any pending cache writes */
264 clean_dcache_range(start, size);
265
266 /* set the new DMA values */
267 DCON2 = DMA_CONTROL_SETUP | (size >> 1);
268 DISRC2 = (unsigned int)start + 0x30000000;
269
270 /* Re-Activate the channel */
271 DMASKTRIG2 = 0x2;
Michael Sevakis3c38fe42007-05-02 22:33:24 +0000272 }
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000273}
274
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000275void pcm_set_frequency(unsigned int frequency)
276{
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000277 switch(frequency)
278 {
279 case SAMPR_11:
Marcoen Hirschbergb01da582007-01-18 13:48:06 +0000280 sr_ctrl = GIGABEAT_11025HZ;
281 break;
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000282 case SAMPR_22:
Marcoen Hirschbergb01da582007-01-18 13:48:06 +0000283 sr_ctrl = GIGABEAT_22050HZ;
284 break;
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000285 default:
Marcoen Hirschbergb01da582007-01-18 13:48:06 +0000286 frequency = SAMPR_44;
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000287 case SAMPR_44:
Marcoen Hirschbergb01da582007-01-18 13:48:06 +0000288 sr_ctrl = GIGABEAT_44100HZ;
289 break;
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000290 case SAMPR_88:
Marcoen Hirschbergb01da582007-01-18 13:48:06 +0000291 sr_ctrl = GIGABEAT_88200HZ;
292 break;
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000293 }
Marcoen Hirschbergb01da582007-01-18 13:48:06 +0000294
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000295 pcm_freq = frequency;
296}
297
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000298size_t pcm_get_bytes_waiting(void)
299{
Michael Sevakisf7836172007-06-24 20:41:27 +0000300 /* lie a little and only return full pairs */
301 return (DSTAT2 & 0xFFFFE) * 2;
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000302}
303
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000304const void * pcm_play_dma_get_peak_buffer(int *count)
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000305{
Michael Sevakis6077e5b2007-10-06 22:27:27 +0000306 unsigned long addr = DCSRC2;
307 int cnt = DSTAT2;
308 *count = (cnt & 0xFFFFF) >> 1;
309 return (void *)((addr + 2) & ~3);
Marcoen Hirschberg29536762006-12-29 02:49:12 +0000310}