rockbox/firmware/pcm_record.c
Linus Nielsen Feltzing ed4d7a33bd Patch #5347 by Rani Hod - Adds FM radio and recording features to the iAudio X5.
Also includes a rewrite of the Coldfire I2C driver to include both read and write.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10272 a1c6a512-1295-4272-9138-f99709370657
2006-07-21 08:42:28 +00:00

1150 lines
29 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 by Linus Nielsen Feltzing
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "config.h"
#include "debug.h"
#include "panic.h"
#include "thread.h"
#include <kernel.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <string.h>
#include "cpu.h"
#include "i2c.h"
#include "power.h"
#ifdef HAVE_UDA1380
#include "uda1380.h"
#endif
#ifdef HAVE_TLV320
#include "tlv320.h"
#endif
#include "system.h"
#include "usb.h"
#include "buffer.h"
#include "audio.h"
#include "button.h"
#include "file.h"
#include "sprintf.h"
#include "logf.h"
#include "button.h"
#include "lcd.h"
#include "lcd-remote.h"
#include "pcm_playback.h"
#include "pcm_record.h"
extern int boost_counter; /* used for boost check */
/***************************************************************************/
static bool is_recording; /* We are recording */
static bool is_stopping; /* Are we going to stop */
static bool is_paused; /* We have paused */
static bool is_error; /* An error has occured */
static unsigned long num_rec_bytes; /* Num bytes recorded */
static unsigned long num_file_bytes; /* Num bytes written to current file */
static int error_count; /* Number of DMA errors */
static long record_start_time; /* Value of current_tick when recording was started */
static long pause_start_time; /* Value of current_tick when pause was started */
static volatile int buffered_chunks; /* number of valid chunks in buffer */
static unsigned int sample_rate; /* Sample rate at time of recording start */
static int rec_source; /* Current recording source */
static int wav_file;
static char recording_filename[MAX_PATH];
static bool init_done, close_done, record_done, stop_done, pause_done, resume_done, new_file_done;
static short peak_left, peak_right;
/***************************************************************************/
/*
Some estimates:
Normal recording rate: 44100 HZ * 4 = 176 KB/s
Total buffer size: 32 MB / 176 KB/s = 181s before writing to disk
*/
#define CHUNK_SIZE 8192 /* Multiple of 4 */
#define WRITE_THRESHOLD 250 /* (2 MB) Write when this many chunks (or less) until buffer full */
#define GET_CHUNK(x) (short*)(&rec_buffer[CHUNK_SIZE*(x)])
static unsigned int rec_buffer_offset;
static unsigned char *rec_buffer; /* Circular recording buffer */
static int num_chunks; /* Number of chunks available in rec_buffer */
#ifdef IAUDIO_X5
#define SET_IIS_PLAY(x) IIS1CONFIG = (x);
#define SET_IIS_REC(x) IIS1CONFIG = (x);
#else
#define SET_IIS_PLAY(x) IIS2CONFIG = (x);
#define SET_IIS_REC(x) IIS1CONFIG = (x);
#endif
/*
Overrun occures when DMA needs to write a new chunk and write_index == read_index
Solution to this is to optimize pcmrec_callback, use cpu_boost or save to disk
more often.
*/
static int write_index; /* Current chunk the DMA is writing to */
static int read_index; /* Oldest chunk that is not written to disk */
static int read2_index; /* Latest chunk that has not been converted to little endian */
static long pre_record_ticks; /* pre-record time expressed in ticks */
static int pre_record_chunks; /* pre-record time expressed in chunks */
/***************************************************************************/
static struct event_queue pcmrec_queue;
static long pcmrec_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)];
static const char pcmrec_thread_name[] = "pcmrec";
static void pcmrec_thread(void);
static void pcmrec_dma_start(void);
static void pcmrec_dma_stop(void);
/* Event IDs */
#define PCMREC_INIT 1 /* Enable recording */
#define PCMREC_CLOSE 2
#define PCMREC_START 3 /* Start a new recording */
#define PCMREC_STOP 4 /* Stop the current recording */
#define PCMREC_PAUSE 10
#define PCMREC_RESUME 11
#define PCMREC_NEW_FILE 12
#define PCMREC_SET_GAIN 13
/*******************************************************************/
/* Functions that are not executing in the pcmrec_thread first */
/*******************************************************************/
/* Creates pcmrec_thread */
void pcm_rec_init(void)
{
queue_init(&pcmrec_queue);
create_thread(pcmrec_thread, pcmrec_stack, sizeof(pcmrec_stack), pcmrec_thread_name);
}
/* Initializes recording:
* - Set up the UDA1380/TLV320 for recording
* - Prepare for DMA transfers
*/
void audio_init_recording(unsigned int buffer_offset)
{
rec_buffer_offset = buffer_offset;
init_done = false;
queue_post(&pcmrec_queue, PCMREC_INIT, 0);
while(!init_done)
sleep_thread();
wake_up_thread();
}
void audio_close_recording(void)
{
close_done = false;
queue_post(&pcmrec_queue, PCMREC_CLOSE, 0);
while(!close_done)
sleep_thread();
wake_up_thread();
}
unsigned long pcm_rec_status(void)
{
unsigned long ret = 0;
if (is_recording)
ret |= AUDIO_STATUS_RECORD;
if (is_paused)
ret |= AUDIO_STATUS_PAUSE;
if (is_error)
ret |= AUDIO_STATUS_ERROR;
return ret;
}
unsigned long audio_recorded_time(void)
{
if (is_recording)
{
if (is_paused)
return pause_start_time - record_start_time;
else
return current_tick - record_start_time;
}
return 0;
}
unsigned long audio_num_recorded_bytes(void)
{
if (is_recording)
return num_rec_bytes;
return 0;
}
#ifdef HAVE_SPDIF_IN
/* Only the last six of these are standard rates, but all sample rates are
* possible, so we support some other common ones as well.
*/
static unsigned long spdif_sample_rates[] = {
8000, 11025, 12000, 16000, 22050, 24000,
32000, 44100, 48000, 64000, 88200, 96000
};
/* Return SPDIF sample rate. Since we base our reading on the actual SPDIF
* sample rate (which might be a bit inaccurate), we round off to the closest
* sample rate that is supported by SPDIF.
*/
unsigned long audio_get_spdif_sample_rate(void)
{
int i = 0;
unsigned long measured_rate;
const int upper_bound = sizeof(spdif_sample_rates)/sizeof(long) - 1;
/* The following formula is specified in MCF5249 user's manual section
* 17.6.1. The 3*(1 << 13) part will need changing if the setup of the
* PHASECONFIG register is ever changed. The 128 divide is because of the
* fact that the SPDIF clock is the sample rate times 128.
*/
measured_rate = (unsigned long)((unsigned long long)FREQMEAS*CPU_FREQ/
((1 << 15)*3*(1 << 13))/128);
/* Find which SPDIF sample rate we're closest to. */
while (spdif_sample_rates[i] < measured_rate && i < upper_bound) ++i;
if (i > 0 && i < upper_bound)
{
long diff1 = measured_rate - spdif_sample_rates[i - 1];
long diff2 = spdif_sample_rates[i] - measured_rate;
if (diff2 > diff1) --i;
}
return spdif_sample_rates[i];
}
#endif
#ifdef HAVE_SPDIF_POWER
static bool spdif_power_setting;
void audio_set_spdif_power_setting(bool on)
{
spdif_power_setting = on;
}
#endif
/**
* Sets the audio source
*
* This functions starts feeding the CPU with audio data over the I2S bus
*
* @param source 0=mic, 1=line-in, 2=spdif
*/
void audio_set_recording_options(int frequency, int quality,
int source, int channel_mode,
bool editable, int prerecord_time)
{
/* TODO: */
(void)quality;
(void)channel_mode;
(void)editable;
/* NOTE: Coldfire UDA based recording does not yet support anything other
* than 44.1kHz sampling rate, so we limit it to that case here now. SPDIF
* based recording will overwrite this value with the proper sample rate in
* audio_record(), and will not be affected by this.
*/
frequency = 44100;
pre_record_ticks = prerecord_time * HZ;
pre_record_chunks = ((frequency * prerecord_time * 4)/CHUNK_SIZE)+1;
if(pre_record_chunks >= (num_chunks-250))
{
/* we can't prerecord more than our buffersize minus treshold to write to disk! */
pre_record_chunks = num_chunks-250;
/* don't forget to recalculate that time! */
pre_record_ticks = ((pre_record_chunks * CHUNK_SIZE)/(4*frequency)) * HZ;
}
//logf("pcmrec: src=%d", source);
rec_source = source;
#ifdef HAVE_SPDIF_POWER
/* Check if S/PDIF output power should be switched off or on. NOTE: assumes
both optical in and out is controlled by the same power source, which is
the case on H1x0. */
spdif_power_enable((source == 2) || spdif_power_setting);
#endif
switch (source)
{
/* mic */
case 0:
/* Generate int. when 6 samples in FIFO, PDIR2 src = IIS1recv */
DATAINCONTROL = 0xc020;
#ifdef HAVE_UDA1380
uda1380_enable_recording(true);
#endif
#ifdef HAVE_TLV320
tlv320_enable_recording(true);
#endif
break;
/* line-in */
case 1:
/* Generate int. when 6 samples in FIFO, PDIR2 src = IIS1recv */
DATAINCONTROL = 0xc020;
#ifdef HAVE_UDA1380
uda1380_enable_recording(false);
#endif
#ifdef HAVE_TLV320
tlv320_enable_recording(false);
#endif
break;
#ifdef HAVE_SPDIF_IN
/* SPDIF */
case 2:
/* Int. when 6 samples in FIFO. PDIR2 source = ebu1RcvData */
DATAINCONTROL = 0xc038;
#ifdef HAVE_SPDIF_POWER
EBU1CONFIG = spdif_power_setting ? (1 << 2) : 0;
/* Input source is EBUin1, Feed-through monitoring if desired */
#else
EBU1CONFIG = (1 << 2);
/* Input source is EBUin1, Feed-through monitoring */
#endif
uda1380_disable_recording();
break;
#endif
}
sample_rate = frequency;
/* Monitoring: route the signals through the coldfire audio interface. */
SET_IIS_PLAY(0x800); /* Reset before reprogram */
#ifdef HAVE_SPDIF_IN
if (source == 2) {
/* SCLK2 = Audioclk/4 (can't use EBUin clock), TXSRC = EBU1rcv, 64 bclk/wclk */
IIS2CONFIG = (6 << 12) | (7 << 8) | (4 << 2);
/* S/PDIF feed-through already configured */
}
else
{
/* SCLK2 follow IIS1 (UDA clock), TXSRC = IIS1rcv, 64 bclk/wclk */
IIS2CONFIG = (8 << 12) | (4 << 8) | (4 << 2);
EBU1CONFIG = 0x800; /* Reset before reprogram */
/* SCLK2, TXSRC = IIS1recv, validity, normal operation */
EBU1CONFIG = (7 << 12) | (4 << 8) | (1 << 5) | (5 << 2);
}
#else
/* SCLK2 follow IIS1 (UDA clock), TXSRC = IIS1rcv, 64 bclk/wclk */
SET_IIS_PLAY( (8 << 12) | (4 << 8) | (4 << 2) );
#endif
}
/**
* Note that microphone is mono, only left value is used
* See {uda1380,tlv320}_set_recvol() for exact ranges.
*
* @param type 0=line-in (radio), 1=mic
*
*/
void audio_set_recording_gain(int left, int right, int type)
{
//logf("rcmrec: t=%d l=%d r=%d", type, left, right);
#ifdef HAVE_UDA1380
uda1380_set_recvol(left, right, type);
#endif
#ifdef HAVE_TLV320
tlv320_set_recvol(left, right, type);
#endif
}
/**
* Start recording
*
* Use audio_set_recording_options first to select recording options
*/
void audio_record(const char *filename)
{
if (is_recording)
{
logf("record while recording");
return;
}
strncpy(recording_filename, filename, MAX_PATH - 1);
recording_filename[MAX_PATH - 1] = 0;
#ifdef HAVE_SPDIF_IN
if (rec_source == 2)
sample_rate = audio_get_spdif_sample_rate();
#endif
record_done = false;
queue_post(&pcmrec_queue, PCMREC_START, 0);
while(!record_done)
sleep_thread();
wake_up_thread();
}
void audio_new_file(const char *filename)
{
logf("pcm_new_file");
new_file_done = false;
strncpy(recording_filename, filename, MAX_PATH - 1);
recording_filename[MAX_PATH - 1] = 0;
queue_post(&pcmrec_queue, PCMREC_NEW_FILE, 0);
while(!new_file_done)
sleep_thread();
wake_up_thread();
logf("pcm_new_file done");
}
/**
*
*/
void audio_stop_recording(void)
{
if (!is_recording)
return;
logf("pcm_stop");
stop_done = false;
queue_post(&pcmrec_queue, PCMREC_STOP, 0);
while(!stop_done)
sleep_thread();
wake_up_thread();
logf("pcm_stop done");
}
void audio_pause_recording(void)
{
if (!is_recording)
{
logf("pause when not recording");
return;
}
if (is_paused)
{
logf("pause when paused");
return;
}
pause_done = false;
queue_post(&pcmrec_queue, PCMREC_PAUSE, 0);
while(!pause_done)
sleep_thread();
wake_up_thread();
}
void audio_resume_recording(void)
{
if (!is_paused)
{
logf("resume when not paused");
return;
}
resume_done = false;
queue_post(&pcmrec_queue, PCMREC_RESUME, 0);
while(!resume_done)
sleep_thread();
wake_up_thread();
}
/* return peaks as int, so convert from short first
note that peak values are always positive */
void pcm_rec_get_peaks(int *left, int *right)
{
if (left)
*left = (int)peak_left;
if (right)
*right = (int)peak_right;
peak_left = 0;
peak_right = 0;
}
/***************************************************************************/
/* Functions that executes in the context of pcmrec_thread */
/***************************************************************************/
/**
* Process the chunks using read_index and write_index.
*
* This function is called when queue_get_w_tmo times out.
*
* Other functions can also call this function with flush = true when
* they want to save everything in the buffers to disk.
*
*/
static void pcmrec_callback(bool flush) ICODE_ATTR;
static void pcmrec_callback(bool flush)
{
int num_ready, num_free, num_new;
short *ptr;
short value;
int i, j, w;
w = write_index;
num_new = w - read2_index;
if (num_new < 0)
num_new += num_chunks;
for (i=0; i<num_new; i++)
{
/* Convert the samples to little-endian so we only have to write later
(Less hd-spinning time), also do peak detection while we're at it
*/
ptr = GET_CHUNK(read2_index);
for (j=0; j<CHUNK_SIZE/4; j++)
{
value = *ptr;
if(value > peak_left)
peak_left = value;
else if (-value > peak_left)
peak_left = -value;
*ptr = htole16(value);
ptr++;
value = *ptr;
if(value > peak_right)
peak_right = value;
else if (-value > peak_right)
peak_right = -value;
*ptr = htole16(value);
ptr++;
}
if(is_recording && !is_paused)
num_rec_bytes += CHUNK_SIZE;
read2_index++;
if (read2_index >= num_chunks)
read2_index = 0;
}
if ((!is_recording || is_paused) && !flush)
{
/* not recording = no saving to disk, fake buffer clearing */
read_index = write_index;
return;
}
num_ready = w - read_index;
if (num_ready < 0)
num_ready += num_chunks;
num_free = num_chunks - num_ready;
if (num_free <= WRITE_THRESHOLD || flush)
{
bool must_boost = (boost_counter ? false : true);
logf("writing: %d (%d)", num_ready, flush);
if(must_boost)
cpu_boost(true);
for (i=0; i<num_ready; i++)
{
if (write(wav_file, GET_CHUNK(read_index), CHUNK_SIZE) != CHUNK_SIZE)
{
if(must_boost)
cpu_boost(false);
logf("pcmrec: write err");
pcmrec_dma_stop();
return;
}
num_file_bytes += CHUNK_SIZE;
read_index++;
if (read_index >= num_chunks)
read_index = 0;
yield();
}
if(must_boost)
cpu_boost(false);
/* sync file */
fsync(wav_file);
logf("done");
}
}
/* Abort dma transfer */
static void pcmrec_dma_stop(void)
{
DCR1 = 0;
is_error = true;
is_recording = false;
error_count++;
logf("dma1 stopped");
}
static void pcmrec_dma_start(void)
{
DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */
SAR1 = (unsigned long)&PDIR2; /* Source address */
BCR1 = CHUNK_SIZE; /* Bytes to transfer */
/* Start the DMA transfer.. */
DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_START;
#ifdef HAVE_SPDIF_IN
INTERRUPTCLEAR = 0x03c00000;
#endif
/* pre-recording: buffer count */
buffered_chunks = 0;
logf("dma1 started");
}
/* DMA1 Interrupt is called when the DMA has finished transfering a chunk */
void DMA1(void) __attribute__ ((interrupt_handler, section(".icode")));
void DMA1(void)
{
int res = DSR1;
DSR1 = 1; /* Clear interrupt */
if (res & 0x70)
{
DCR1 = 0; /* Stop DMA transfer */
error_count++;
logf("dma1 err: 0x%x", res);
DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */
BCR1 = CHUNK_SIZE;
DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC | DMA_START;
}
#ifdef HAVE_SPDIF_IN
else if ((rec_source == 2) && (INTERRUPTSTAT & 0x01c00000)) /* valnogood, symbolerr, parityerr */
{
INTERRUPTCLEAR = 0x03c00000;
error_count++;
logf("spdif err");
if (is_stopping)
{
DCR1 = 0; /* Stop DMA transfer */
is_stopping = false;
logf("dma1 stopping");
}
else
{
DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */
BCR1 = CHUNK_SIZE;
}
}
#endif
else
{
write_index++;
if (write_index >= num_chunks)
write_index = 0;
/* update number of valid chunks for pre-recording */
if(buffered_chunks < num_chunks)
buffered_chunks++;
if (is_stopping)
{
DCR1 = 0; /* Stop DMA transfer */
is_stopping = false;
logf("dma1 stopping");
}
else if (write_index == read_index)
{
DCR1 = 0; /* Stop DMA transfer */
is_recording = false;
logf("dma1 overrun");
}
else
{
DAR1 = (unsigned long)GET_CHUNK(write_index); /* Destination address */
BCR1 = CHUNK_SIZE;
}
}
IPR |= (1<<15); /* Clear pending interrupt request */
}
/* Create WAVE file and write header */
/* Sets returns 0 if success, -1 on failure */
static int start_wave(void)
{
unsigned char header[44] =
{
'R','I','F','F',0,0,0,0,'W','A','V','E','f','m','t',' ',
0x10,0,0,0,1,0,2,0,0,0,0,0,0,0,0,0,
4,0,0x10,0,'d','a','t','a',0,0,0,0
};
unsigned long avg_bytes_per_sec;
wav_file = open(recording_filename, O_RDWR|O_CREAT|O_TRUNC);
if (wav_file < 0)
{
wav_file = -1;
logf("rec: create failed: %d", wav_file);
is_error = true;
return -1;
}
/* Now set the sample rate field of the WAV header to what it should be */
header[24] = (unsigned char)(sample_rate & 0xff);
header[25] = (unsigned char)(sample_rate >> 8);
header[26] = (unsigned char)(sample_rate >> 16);
header[27] = (unsigned char)(sample_rate >> 24);
/* And then the average bytes per second field */
avg_bytes_per_sec = sample_rate*4; /* Hard coded to 16 bit stereo */
header[28] = (unsigned char)(avg_bytes_per_sec & 0xff);
header[29] = (unsigned char)(avg_bytes_per_sec >> 8);
header[30] = (unsigned char)(avg_bytes_per_sec >> 16);
header[31] = (unsigned char)(avg_bytes_per_sec >> 24);
if (sizeof(header) != write(wav_file, header, sizeof(header)))
{
close(wav_file);
wav_file = -1;
logf("rec: write failed");
is_error = true;
return -1;
}
return 0;
}
/* Update header and set correct length values */
static void close_wave(void)
{
long l;
if (wav_file != -1)
{
l = htole32(num_file_bytes + 36);
lseek(wav_file, 4, SEEK_SET);
write(wav_file, &l, 4);
l = htole32(num_file_bytes);
lseek(wav_file, 40, SEEK_SET);
write(wav_file, &l, 4);
close(wav_file);
wav_file = -1;
}
}
static void pcmrec_start(void)
{
int pre_chunks = pre_record_chunks; /* recalculate every time! */
long pre_ticks = pre_record_ticks; /* recalculate every time! */
logf("pcmrec_start");
if (is_recording)
{
logf("already recording");
record_done = true;
return;
}
if (wav_file != -1)
close(wav_file);
if (start_wave() != 0)
{
/* failed to create the file */
record_done = true;
return;
}
/* pre-recording calculation */
if(buffered_chunks < pre_chunks)
{
/* not enough good chunks available - limit pre-record time */
pre_chunks = buffered_chunks;
pre_ticks = ((buffered_chunks * CHUNK_SIZE)/(4*sample_rate)) * HZ;
}
record_start_time = current_tick - pre_ticks;
read_index = write_index - pre_chunks;
if(read_index < 0)
{
read_index += num_chunks;
}
peak_left = 0;
peak_right = 0;
num_rec_bytes = pre_chunks * CHUNK_SIZE;
num_file_bytes = 0;
pause_start_time = 0;
is_stopping = false;
is_paused = false;
is_recording = true;
record_done = true;
}
static void pcmrec_stop(void)
{
logf("pcmrec_stop");
if (!is_recording)
{
stop_done = true;
return;
}
if (!is_paused)
{
/* wait for recording to finish */
is_stopping = true;
while (is_stopping && is_recording)
sleep_thread();
wake_up_thread();
is_stopping = false;
}
is_recording = false;
/* Flush buffers to file */
pcmrec_callback(true);
close_wave();
stop_done = true;
/* Finally start dma again for peakmeters and pre-recoding to work. */
pcmrec_dma_start();
logf("pcmrec_stop done");
}
static void pcmrec_new_file(void)
{
logf("pcmrec_new_file");
if (!is_recording)
{
logf("not recording");
new_file_done = true;
return;
}
/* Since pcmrec_callback() blocks until the data has been written,
here is a good approximation when recording to the new file starts
*/
record_start_time = current_tick;
num_rec_bytes = 0;
if (is_paused)
pause_start_time = record_start_time;
/* Flush what we got in buffers to file */
pcmrec_callback(true);
close_wave();
num_file_bytes = 0;
/* start the new file */
if (start_wave() != 0)
{
logf("new_file failed");
pcmrec_stop();
}
new_file_done = true;
logf("pcmrec_new_file done");
}
static void pcmrec_pause(void)
{
logf("pcmrec_pause");
if (!is_recording)
{
logf("pause: not recording");
pause_done = true;
return;
}
/* Abort DMA transfer and flush to file? */
is_stopping = true;
while (is_stopping && is_recording)
sleep_thread();
wake_up_thread();
pause_start_time = current_tick;
is_paused = true;
/* Flush what we got in buffers to file */
pcmrec_callback(true);
pause_done = true;
logf("pcmrec_pause done");
}
static void pcmrec_resume(void)
{
logf("pcmrec_resume");
if (!is_paused)
{
logf("resume: not paused");
resume_done = true;
return;
}
is_paused = false;
is_recording = true;
/* Compensate for the time we have been paused */
if (pause_start_time)
{
record_start_time += current_tick - pause_start_time;
pause_start_time = 0;
}
pcmrec_dma_start();
resume_done = true;
logf("pcmrec_resume done");
}
/**
* audio_init_recording calls this function using PCMREC_INIT
*
*/
static void pcmrec_init(void)
{
unsigned long buffer_size;
wav_file = -1;
read_index = 0;
read2_index = 0;
write_index = 0;
pre_record_chunks = 0;
pre_record_ticks = 0;
peak_left = 0;
peak_right = 0;
num_rec_bytes = 0;
num_file_bytes = 0;
record_start_time = 0;
pause_start_time = 0;
buffered_chunks = 0;
is_recording = false;
is_stopping = false;
is_paused = false;
is_error = false;
rec_buffer = (unsigned char*)(((unsigned long)audiobuf + rec_buffer_offset) & ~3);
buffer_size = (long)audiobufend - (long)audiobuf - rec_buffer_offset - 16;
logf("buf size: %d kb", buffer_size/1024);
num_chunks = buffer_size / CHUNK_SIZE;
logf("num_chunks: %d", num_chunks);
SET_IIS_PLAY(0x800); /* Stop any playback */
AUDIOGLOB |= 0x180; /* IIS1 fifo auto sync = on, PDIR2 auto sync = on */
DATAINCONTROL = 0xc000; /* Generate Interrupt when 6 samples in fifo */
DIVR1 = 55; /* DMA1 is mapped into vector 55 in system.c */
DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */
DMAROUTE = (DMAROUTE & 0xffff00ff) | DMA1_REQ_AUDIO_2;
ICR7 = 0x1c; /* Enable interrupt at level 7, priority 0 */
IMR &= ~(1<<15); /* bit 15 is DMA1 */
#ifdef HAVE_SPDIF_IN
PHASECONFIG = 0x34; /* Gain = 3*2^13, source = EBUIN */
#endif
pcmrec_dma_start();
init_done = 1;
}
static void pcmrec_close(void)
{
#ifdef HAVE_UDA1380
uda1380_disable_recording();
#endif
#ifdef HAVE_TLV320
tlv320_disable_recording();
#endif
#ifdef HAVE_SPDIF_POWER
spdif_power_enable(spdif_power_setting);
#endif
DMAROUTE = (DMAROUTE & 0xffff00ff);
ICR7 = 0x00; /* Disable interrupt */
IMR |= (1<<15); /* bit 15 is DMA1 */
/* Reset PDIR2 data flow */
DATAINCONTROL = 0x200;
close_done = true;
}
static void pcmrec_thread(void)
{
struct event ev;
logf("thread pcmrec start");
error_count = 0;
while (1)
{
queue_wait_w_tmo(&pcmrec_queue, &ev, HZ / 40);
switch (ev.id)
{
case PCMREC_INIT:
pcmrec_init();
break;
case PCMREC_CLOSE:
pcmrec_close();
break;
case PCMREC_START:
pcmrec_start();
break;
case PCMREC_STOP:
pcmrec_stop();
break;
case PCMREC_PAUSE:
pcmrec_pause();
break;
case PCMREC_RESUME:
pcmrec_resume();
break;
case PCMREC_NEW_FILE:
pcmrec_new_file();
break;
case SYS_TIMEOUT:
pcmrec_callback(false);
break;
case SYS_USB_CONNECTED:
if (!is_recording && !is_stopping)
{
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_wait_for_disconnect(&pcmrec_queue);
}
break;
}
}
logf("thread pcmrec done");
}
/* Select VINL & VINR source: 0=Line-in, 1=FM Radio */
void pcm_rec_mux(int source)
{
#ifdef IRIVER_H300_SERIES
if(source == 0)
and_l(~0x40000000, &GPIO_OUT); /* Line In */
else
or_l(0x40000000, &GPIO_OUT); /* FM radio */
or_l(0x40000000, &GPIO_ENABLE);
or_l(0x40000000, &GPIO_FUNCTION);
#elif defined(IRIVER_H100_SERIES)
if(source == 0)
and_l(~0x00800000, &GPIO_OUT); /* Line In */
else
or_l(0x00800000, &GPIO_OUT); /* FM radio */
or_l(0x00800000, &GPIO_ENABLE);
or_l(0x00800000, &GPIO_FUNCTION);
#elif defined(IAUDIO_X5)
if(source == 0)
or_l((1<<29), &GPIO_OUT); /* Line In */
else
and_l(~(1<<29), &GPIO_OUT); /* FM radio */
or_l((1<<29), &GPIO_ENABLE);
or_l((1<<29), &GPIO_FUNCTION);
/* iAudio x5 */
#endif
}