📄 mx27-wm8782-pcm.c
字号:
ret = mxc_dma_enable(s->dma_channel);
s->tx_spin = 1; /* FGA little trick to retrieve DMA pos */
if (ret) {
printk("audio_process_dma: cannot queue DMA buffer (%i)\n", ret);
return;
}
s->period++;
s->period %= runtime->periods;
}
}
static void audio_playback_dma_callback(void *data, int error,
unsigned int count)
{
audio_stream_t *s;
snd_pcm_substream_t *substream;
snd_pcm_runtime_t *runtime;
unsigned int dma_size;
unsigned int previous_period;
unsigned int offset;
ENTRY(0);
s = data;
substream = s->substream;
runtime = substream->runtime;
previous_period = s->periods;
dma_size = frames_to_bytes(runtime, runtime->period_size);
offset = dma_size * previous_period;
s->tx_spin = 0;
s->periods++;
s->periods %= runtime->periods;
/*
* Give back to the CPU the access to the non cached memory
*/
dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
DMA_TO_DEVICE);
/*
* If we are getting a callback for an active stream then we inform
* the PCM middle layer we've finished a period
*/
if (s->active)
snd_pcm_period_elapsed(s->substream);
spin_lock(&s->dma_lock);
/*
* Trig next DMA transfer
*/
audio_playback_dma(s);
spin_unlock(&s->dma_lock);
}
/*!
* This is a callback which will be called when a RX transfer finishes.
* The call occurs in interrupt context.
* @param substream pointer to the structure of the current stream.
*/
static void audio_capture_dma_callback(void *data, int error,
unsigned int count)
{
audio_stream_t *s;
snd_pcm_substream_t *substream;
snd_pcm_runtime_t *runtime;
unsigned int dma_size;
unsigned int previous_period;
unsigned int offset;
s = data;
substream = s->substream;
runtime = substream->runtime;
previous_period = s->periods;
dma_size = frames_to_bytes(runtime, runtime->period_size);
offset = dma_size * previous_period;
s->tx_spin = 0;
s->periods++;
s->periods %= runtime->periods;
/*
* Give back to the CPU the access to the non cached memory
*/
dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
DMA_FROM_DEVICE);
/*
* If we are getting a callback for an active stream then we inform
* the PCM middle layer we've finished a period
*/
if (s->active)
snd_pcm_period_elapsed(s->substream);
spin_lock(&s->dma_lock);
/*
* Trig next DMA transfer
*/
audio_capture_dma(s);
spin_unlock(&s->dma_lock);
}
static int wm8782_pcm_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime * runtime = substream->runtime;
int rate = runtime->rate;
printk(KERN_INFO "%s\n",__FUNCTION__);
printk("rate=%d\n", rate);
return 0;
}
static int mx27_codec_open(snd_pcm_substream_t * substream)
{
mx27_codec_t* codec = snd_pcm_substream_chip (substream);
snd_pcm_runtime_t* runtime = substream->runtime;
int stream_id = substream->pstr->stream;
int result;
int err;
ENTRY (0);
codec->s[stream_id].substream = substream;
if (stream_id == SNDRV_PCM_STREAM_PLAYBACK){
runtime->hw = mx27_codec_playback_hw;
if ((err =
configure_write_channel(&codec->s[SNDRV_PCM_STREAM_PLAYBACK],
audio_playback_dma_callback)) < 0)
return err;
}
else if(stream_id == SNDRV_PCM_STREAM_CAPTURE){
runtime->hw = mx27_codec_capture_hw;
if ((err =
configure_read_channel(&codec->s[SNDRV_PCM_STREAM_CAPTURE],
audio_capture_dma_callback)) < 0){
return err;
}
}
else{
return -ENODEV;
}
result = snd_pcm_hw_constraint_integer(runtime,SNDRV_PCM_HW_PARAM_PERIODS);
if (result < 0)
return result;
result = snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&hw_constraints_rates);
return result < 0 ? result : 0;
}
static int mx27_codec_close (snd_pcm_substream_t * substream)
{
mx27_codec_t* codec = snd_pcm_substream_chip (substream);
int stream_id = substream->pstr->stream;
int channel=-1;
int result ;
ENTRY(1);
codec->s[substream->pstr->stream].substream = NULL;
channel=codec->s[substream->pstr->stream].dma_channel;
result=mxc_dma_free(channel);
if (stream_id == SNDRV_PCM_STREAM_PLAYBACK){
ssi_interrupt_disable(SSI_CODEC, ssi_tx_dma_interrupt_enable);
ssi_interrupt_disable(SSI_CODEC, ssi_tx_fifo_0_empty);
ssi_transmit_enable(SSI_CODEC, 0);
ssi_tx_flush_fifo(SSI_CODEC);
}
if (stream_id == SNDRV_PCM_STREAM_CAPTURE){
ssi_interrupt_disable(SSI_CODEC, ssi_rx_dma_interrupt_enable);
ssi_interrupt_disable(SSI_CODEC, ssi_rx_fifo_0_full);
ssi_receive_enable(SSI_CODEC,0);
ssi_rx_flush_fifo(SSI_CODEC);
}
return result < 0 ?result:0;
}
static int mx27_codec_hw_params (snd_pcm_substream_t* substream,
snd_pcm_hw_params_t* hw_params)
{
snd_pcm_runtime_t *runtime;
int result;
ENTRY (0);
runtime=substream->runtime;
printk("dma_addr=%x\n",substream->runtime->dma_addr);
result = snd_pcm_lib_malloc_pages (substream,
params_buffer_bytes (hw_params));
if(result<0)
return result;
runtime->dma_addr = virt_to_phys(runtime->dma_area);
DBG(1,"%s: dma_area=%x ",__FUNCTION__, (int)substream->runtime->dma_area);
DBG(1,"dma_addr=%x,dma_bytes=%d\n",
substream->runtime->dma_addr,substream->runtime->dma_bytes);
return result;
}
static int mx27_codec_hw_free (snd_pcm_substream_t* substream)
{
ENTRY (0);
return snd_pcm_lib_free_pages (substream);
}
//start the codec.
static int mx27_codec_trigger(snd_pcm_substream_t* substream, int cmd)
{
mx27_codec_t* codec = snd_pcm_substream_chip (substream);
int stream_id = substream->pstr->stream;
audio_stream_t* s= &codec->s[stream_id];
int result = 0;
ENTRY (0);
#if 0
printk("%s\n",__FUNCTION__);
printk("rate=%d \n",substream->runtime->rate);
printk("channel=%d \n",substream->runtime->channels);
printk("frame_bits=%d \n",substream->runtime->frame_bits);
printk("sample_bits=%d \n",substream->runtime->sample_bits);
printk("periods=%d \n",substream->runtime->periods);
printk("period_size=%ld \n",substream->runtime->period_size);
printk("s->period=%d\n",s->period);
printk("s->periods=%d\n",s->periods);
printk("s->active=%d\n",s->active);
printk("s->dma_channel=%d\n",s->dma_channel);
printk("s->tx_spin=%d\n",s->tx_spin);
printk("s->active=%d\n",s->active);
#endif
DBG(0, "%s: trigger id %d cmd %d\n", __FUNCTION__, stream_id, cmd);
if(stream_id==0)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
DBG (1, "trigger start stream %d\n", stream_id);
s->tx_spin = 0;
s->active = 1;
audio_playback_dma(s);
break;
case SNDRV_PCM_TRIGGER_STOP:
DBG (1, "trigger stop entry\n");
s->tx_spin=0;
s->active=0;
audio_playback_stop_dma(s);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
break;
case SNDRV_PCM_TRIGGER_RESUME:
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
break;
default:
result = -EINVAL;
break;
}
if(stream_id==1)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
DBG (1, "trigger start stream %d\n", stream_id);
s->tx_spin = 0;
s->active = 1;
audio_capture_dma(s);
break;
case SNDRV_PCM_TRIGGER_STOP:
DBG (1, "trigger stop\n");
s->tx_spin=0;
s->active=0;
audio_capture_stop_dma(s);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
break;
case SNDRV_PCM_TRIGGER_RESUME:
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
break;
default:
result = -EINVAL;
break;
}
return result;
}
static int mx27_codec_prepare (snd_pcm_substream_t* substream)
{
mx27_codec_t* codec = snd_pcm_substream_chip (substream);
snd_pcm_runtime_t* runtime = substream->runtime;
audio_stream_t* s = &codec->s[substream->pstr->stream];
int id = substream->pstr->stream;
//int channel = substream->runtime->channels;
ENTRY (0);
s->period = 0;
s->periods = 0;
wm8782_pcm_prepare(substream);
if(id==0){
printk("it's playback!\n");
}
if(id==1){
ssi_receive_enable(SSI_CODEC, 0);
mx27_i2s_master_mode_config();
printk("rate=%d\n", runtime->rate);
ssi_interrupt_enable(SSI_CODEC, ssi_rx_dma_interrupt_enable);
ssi_interrupt_enable(SSI_CODEC, ssi_rx_fifo_0_full);
ssi_receive_enable(SSI_CODEC,1);
}
return 0;
}
static snd_pcm_uframes_t mx27_codec_pointer (snd_pcm_substream_t* substream)
{
mx27_codec_t* codec = snd_pcm_substream_chip (substream);
audio_stream_t* s = (audio_stream_t *)(&(codec->s[substream->pstr->stream]));
int stream_id=substream->pstr->stream;
unsigned int c=0;
ENTRY (3);
if(stream_id==0){
c = audio_get_playback_dma_pos(s);
DBG (3,"ID0 pos c=0x%x\n", c);
}
if(stream_id==1){
c = audio_get_capture_dma_pos(s);
DBG(3,"ID1 pos c=%x\n", c);
}
return c;
}
static snd_pcm_ops_t mx27_codec_playback_ops = {
.open = mx27_codec_open,
.close = mx27_codec_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = mx27_codec_hw_params,
.hw_free = mx27_codec_hw_free,
.prepare = mx27_codec_prepare,
.trigger = mx27_codec_trigger,
.pointer = mx27_codec_pointer,
};
static snd_pcm_ops_t mx27_codec_capture_ops = {
.open = mx27_codec_open,
.close = mx27_codec_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = mx27_codec_hw_params,
.hw_free = mx27_codec_hw_free,
.prepare = mx27_codec_prepare,
.trigger = mx27_codec_trigger,
.pointer = mx27_codec_pointer,
};
/* mx27_codec_pcm_init
performs the hardware initialization for the PCM device.
*/
int mx27_codec_pcm_init (mx27_codec_t* codec, int device)
{
snd_pcm_t* pcm;
int result = 0;
ENTRY (0);
result = snd_pcm_new (codec->card,
"mx27 PCM", device, 1, 1, &pcm);
if (result)
goto done;
snd_pcm_set_ops (pcm, SNDRV_PCM_STREAM_PLAYBACK,
&mx27_codec_playback_ops);
snd_pcm_set_ops (pcm, SNDRV_PCM_STREAM_CAPTURE,
&mx27_codec_capture_ops);
snd_pcm_lib_preallocate_pages_for_all (pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
//((struct device *)(unsigned long)(GFP_KERNEL)),
snd_dma_continuous_data(GFP_KERNEL),
MAX_BUFFER_SIZE * 2 , MAX_BUFFER_SIZE * 2 );
pcm->private_data = codec;
pcm->info_flags = 0;
strcpy (pcm->name, "mx27 PCM");
mx27_codec_init (codec);
// audio_stream init
codec->pcm = pcm;
done:
return result;
}
EXPORT_SYMBOL_GPL(mx27_codec_pcm_init);
static int __init mx27_codec_driver_init (void)
{
printk(KERN_INFO "MXC-WM8782 PCM module loaded successfully\n");
return 0;
}
static void __exit mx27_codec_driver_exit (void)
{
printk(KERN_INFO "MXC-WM8782 PCM module unloaded successfully\n");
}
module_init(mx27_codec_driver_init);
module_exit(mx27_codec_driver_exit);
MODULE_AUTHOR("Sitek Hengke, Ltd");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("xtp-d501 driver for ALSA");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -