📄 mx27-wm8782-pcm.c.bak
字号:
/*
* Copyright (c) sitek hengke elec .Ltd <ihanker.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
//#include <stdio.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/dma-mapping.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/spinlock_types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/dma.h>
#include <asm/arch/dma.h>
#include <asm/arch/mx27_pins.h>
#include <asm/arch/mx2_dma.h>
#include <asm/arch/clock.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/initval.h>
#include <sound/control.h>
#include <ssi/ssi.h>
#include <ssi/registers.h>
#include <ssi/ssi_types.h>
#include "wm8782.h"
#include "mxc-pcm.h"
#include "wm8782-mixer.h"
//#define TALK 5
#define debug 0
#if defined (TALK)
#define DBG(l,f...) do{ if (l <= TALK) printk (f); }while(0)
#define ENTRY(l) DBG(l, "%s: entry\n", __FUNCTION__)
#else
#define ENTRY(l) do {} while (0)
#define DBG(l,f...) do {} while (0)
#endif
#define MAX_BUFFER_SIZE (32 * 1024)
#define DMA_BUF_SIZE (8 * 1024)
#define MIN_PERIOD_SIZE 64
#define MIN_PERIOD 2
#define MAX_PERIOD 255
#define SSI_CODEC SSI1 // 0
extern void mx27_i2s_slave_mode_config(void);
extern void mx27_i2s_normal_mode_config(void);
static snd_pcm_hardware_t mx27_codec_playback_hw =
{
.info = ( SNDRV_PCM_INFO_INTERLEAVED
| SNDRV_PCM_INFO_BLOCK_TRANSFER
| SNDRV_PCM_INFO_MMAP
| SNDRV_PCM_INFO_MMAP_VALID
// | sNDRV_PCM_INFO_PAUsE
// | sNDRV_PCM_INFO_REsUME
),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = (0
| SNDRV_PCM_RATE_8000
| SNDRV_PCM_RATE_11025
| SNDRV_PCM_RATE_16000
| SNDRV_PCM_RATE_22050
| SNDRV_PCM_RATE_32000
| SNDRV_PCM_RATE_44100
| SNDRV_PCM_RATE_48000
| SNDRV_PCM_RATE_88200
| SNDRV_PCM_RATE_96000
| SNDRV_PCM_RATE_176400
| SNDRV_PCM_RATE_192000
),
.rate_min = 8000,
.rate_max = 192000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = MAX_BUFFER_SIZE,
.period_bytes_min = MIN_PERIOD_SIZE,
.period_bytes_max = DMA_BUF_SIZE,
.periods_min = MIN_PERIOD,
.periods_max = MAX_PERIOD,
.fifo_size = 0,
};
static snd_pcm_hardware_t mx27_codec_capture_hw =
{
.info = ( SNDRV_PCM_INFO_INTERLEAVED
| SNDRV_PCM_INFO_BLOCK_TRANSFER
| SNDRV_PCM_INFO_MMAP
| SNDRV_PCM_INFO_MMAP_VALID
// | SNDRV_PCM_INFO_PAUsE
// | SNDRV_PCM_INFO_REsUME
),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = (0
| SNDRV_PCM_RATE_8000
| SNDRV_PCM_RATE_11025
| SNDRV_PCM_RATE_16000
| SNDRV_PCM_RATE_22050
| SNDRV_PCM_RATE_32000
| SNDRV_PCM_RATE_44100
| SNDRV_PCM_RATE_48000
| SNDRV_PCM_RATE_88200
| SNDRV_PCM_RATE_96000
| SNDRV_PCM_RATE_176400
| SNDRV_PCM_RATE_192000
),
.rate_min = 8000,
.rate_max = 192000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = MAX_BUFFER_SIZE,
.period_bytes_min = MIN_PERIOD_SIZE,
.period_bytes_max = DMA_BUF_SIZE,
.periods_min = MIN_PERIOD,
.periods_max = MAX_PERIOD,
.fifo_size = 0,
};
static unsigned int rates[] = {
8000, 11025, 16000,22050, 32000, 44100, 48000,88200,96000,176400,192000
};
static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
.count = ARRAY_SIZE (rates),
.list = rates,
.mask = 0,
};
/*
mx27_codec_init, called from the driver initialization function to initialize the ALsA structures.
*/
static void mx27_codec_init (mx27_codec_t* codec)
{
ENTRY (0);
/* setup DMA stuff */
codec->s[SNDRV_PCM_STREAM_PLAYBACK].id = "wm8782 out";
codec->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id
= SNDRV_PCM_STREAM_PLAYBACK;
codec->s[SNDRV_PCM_STREAM_CAPTURE].id = "wm8782 in";
codec->s[SNDRV_PCM_STREAM_CAPTURE].stream_id
= SNDRV_PCM_STREAM_CAPTURE;
}
/*!
* This function configures the DMA channel used to transfer audio from MCU to wm9712
*
* @param substream pointer to the structure of the current stream.
* @param callback pointer to function that will be called
when a sDMA TX transfer finishes.
* @return 0 on success, -1 otherwise.
*/
static int configure_write_channel(audio_stream_t * s, mxc_dma_callback_t callback)
{
int ret = -1;
int channel = -1;
ENTRY(0);
channel = mxc_dma_request(MXC_DMA_SSI1_16BIT_TX0, "ALSA TX DMA");
if (channel < 0) {
printk("error requesting a write dma channel\n");
return -1;
}
s->dma_channel = channel;
ret = mxc_dma_callback_set(s->dma_channel,
(mxc_dma_callback_t) callback,(void *)s);
return ret;
}
//config for sitek.cn xtp-d501 board,using ssi1.
static int configure_read_channel(audio_stream_t * s, mxc_dma_callback_t callback)
{
int ret = -1;
int channel = -1;
ENTRY (0);
channel = mxc_dma_request(MXC_DMA_SSI1_16BIT_RX0, "ALSA RX DMA");
//need to check by zd
if (channel < 0) {
printk("error requesting a read dma channel\n");
return -1;
}
s->dma_channel = channel;
//printk("zd: configure_read_channel=%d\n", channel);
ret = mxc_dma_callback_set(s->dma_channel,
(mxc_dma_callback_t) callback,(void *)s);
return ret;
}
/*!
* This function gets the dma pointer position during record.
* Our DMA implementation does not allow to retrieve this position
* when a transfert is active, so, it answers the middle of
* the current period beeing transfered
*
* @param s pointer to the structure of the current stream.
*
*/
static unsigned int audio_get_capture_dma_pos(audio_stream_t *s)
{
snd_pcm_substream_t *substream;
snd_pcm_runtime_t *runtime;
unsigned int offset;
substream = s->substream;
runtime = substream->runtime;
offset = 0;
ENTRY (1);
// printk("%s\n",__FUNCTION__);
#if 0
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("buffer_size=%ld \n",substream->runtime->buffer_size);
printk("s->tx_pin=%d \n",s->tx_spin);
printk("s->periods=%d \n",s->periods);
printk("s->period=%d \n",s->period);
printk("s->dma_channel=%d \n",s->dma_channel);
printk("runtime->period_size=%ld \n",runtime->period_size);
//for debug
#endif
/* tx_spin value is used here to check if a transfert is active */
if (s->tx_spin) {
offset = (runtime->period_size * (s->periods)) +
(runtime->period_size >> 1);
// printk("zd:MXC before: audio_get_dma_pos offset %d\n", offset);
if (offset >= runtime->buffer_size)
offset = runtime->period_size >> 1;
//printk("MXC: audio_get_dma_pos offset %d\n", offset);
} else {
offset = (runtime->period_size * (s->periods));
// printk("zd:MXC after: audio_get_dma_pos offset %d\n", offset);
if (offset >= runtime->buffer_size)
offset = 0;
//printk("MXC: audio_get_dma_pos BIs offset %d\n", offset);
}
return offset;
}
/*!
* This function gets the dma pointer position during playback.
* Our DMA implementation does not allow to retrieve this position
* when a transfert is active, so, it answers the middle of
* the current period beeing transfered
*
* @param s pointer to the structure of the current stream.
*
*/
static unsigned int audio_get_playback_dma_pos(audio_stream_t * s)
{
snd_pcm_substream_t *substream;
snd_pcm_runtime_t *runtime;
unsigned int offset;
substream = s->substream;
runtime = substream->runtime;
offset = 0;
/* tx_spin value is used here to check if a transfert is active */
if (s->tx_spin) {
offset = runtime->period_size * (s->periods);
if (offset >= runtime->buffer_size)
offset = 0;
//DBG(1,"MXC: audio_get_dma_pos offset %d\n", offset);
} else {
offset = (runtime->period_size * (s->periods));
if (offset >= runtime->buffer_size)
offset = 0;
// DBG(1,"MXC: audio_get_dma_pos BIs offset %d\n", offset);
}
return offset;
}
/*!
* This function stops the current dma transfert for playback
* and clears the dma pointers.
* @param substream pointer to the structure of the current stream.
*/
static void audio_playback_stop_dma(audio_stream_t * s)
{
unsigned long flags;
snd_pcm_substream_t *substream;
snd_pcm_runtime_t *runtime;
unsigned int dma_size;
unsigned int offset;
substream = s->substream;
runtime = substream->runtime;
dma_size = frames_to_bytes(runtime, runtime->period_size);
offset = dma_size * s->periods;
spin_lock_irqsave(&s->dma_lock, flags);
DBG(1,"MXC : audio_stop_dma active = 0\n");
s->active = 0;
s->period = 0;
s->periods = 0;
/* this stops the dma channel and clears the buffer ptrs */
mxc_dma_disable(s->dma_channel);
dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
DMA_TO_DEVICE);
spin_unlock_irqrestore(&s->dma_lock, flags);
}
/*!
* This function stops the current dma transfer for capture
* and clears the dma pointers.
* @param substream pointer to the structure of the current stream.
*/
static void audio_capture_stop_dma(audio_stream_t * s)
{
unsigned long flags;
snd_pcm_substream_t *substream;
snd_pcm_runtime_t *runtime;
unsigned int dma_size;
unsigned int offset;
ENTRY (0);
substream = s->substream;
runtime = substream->runtime;
dma_size = frames_to_bytes(runtime, runtime->period_size);
offset = dma_size * s->periods;
spin_lock_irqsave(&s->dma_lock, flags);
DBG(1,"MXC : audio_stop_dma active = 0\n");
s->active = 0;
s->period = 0;
s->periods = 0;
/* this stops the dma channel and clears the buffer ptrs */
mxc_dma_disable(s->dma_channel);
dma_unmap_single(NULL, runtime->dma_addr + offset, dma_size,
DMA_FROM_DEVICE);
spin_unlock_irqrestore(&s->dma_lock, flags);
}
/*!
* This function is called whenever a new audio block needs to be
* transferred to wm9712. The function receives the address and the size
* of the new block and start a new DMA transfer.
*
* @param substream pointer to the structure of the current stream.
*
*/
static void audio_playback_dma(audio_stream_t * s)
{
snd_pcm_substream_t *substream;
snd_pcm_runtime_t *runtime;
unsigned int dma_size;
unsigned int offset;
int ret = 0;
mxc_dma_requestbuf_t dma_request;
substream = s->substream;
runtime = substream->runtime;
DBG(1,"\nDMA is playback\n");
memset(&dma_request, 0, sizeof(mxc_dma_requestbuf_t));
if (s->active) {
dma_size = frames_to_bytes(runtime, runtime->period_size);
DBG(1,"s->period (%x) runtime->periods (%d)\n",
s->period, runtime->periods);
DBG(1,"runtime->period_size (%d) dma_bytes (%d)\n",
(unsigned int)runtime->period_size,
runtime->dma_bytes);
offset = dma_size * s->period;
snd_assert(dma_size <= DMA_BUF_SIZE,);
dma_request.src_addr = (dma_addr_t) (dma_map_single(NULL,
runtime->dma_area+ offset,
dma_size,
DMA_TO_DEVICE));
dma_request.dst_addr = (dma_addr_t) (SSI1_BASE_ADDR + MXC_SSI1STX0);
dma_request.num_of_bytes = dma_size;
DBG(1,"MXC: start DMA offset=%d size=%d\n", offset,runtime->dma_bytes);
DBG(1,"dst=%x,src=%x\n", dma_request.dst_addr,dma_request.src_addr);
mxc_dma_config(s->dma_channel, &dma_request, 1,
MXC_DMA_MODE_WRITE);
ret = mxc_dma_enable(s->dma_channel);
//ssi_transmit_enable(SSI_CODEC, 1);
//printk("dma_size is 0x%x\n",dma_size);
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;
}
}
/*!
* This function is called whenever a new audio block needs to be
* transferred from wm8782. The function receives the address and the size
* of the block that will store the audio samples and start a new DMA transfer.
*
* @param substream pointer to the structure of the current stream.
*
*/
static void audio_capture_dma(audio_stream_t * s)
{
snd_pcm_substream_t *substream;
snd_pcm_runtime_t *runtime;
unsigned int dma_size;
unsigned int offset;
int ret = 0;
mxc_dma_requestbuf_t dma_request;
//ENTRY (0);
// printk("zd:audio_capture_dma entry\n");
substream = s->substream;
runtime = substream->runtime;
memset(&dma_request, 0, sizeof(mxc_dma_requestbuf_t));
if (s->active) {
dma_size = frames_to_bytes(runtime, runtime->period_size);
// printk("s->period (%x) runtime->periods (%d)\n",
// s->period, runtime->periods);
// printk("runtime->frame_bit=%d\n", runtime->frame_bits);
// printk("runtime->period_size (%d) dma_size (%d)\n",
// (unsigned int)runtime->period_size,runtime->dma_bytes);
offset = dma_size * s->period;
snd_assert(dma_size <= DMA_BUF_SIZE,);
dma_request.dst_addr = (dma_addr_t) (dma_map_single(NULL,
runtime->dma_area + offset,
dma_size,
DMA_FROM_DEVICE));
dma_request.src_addr = (dma_addr_t) (SSI1_BASE_ADDR + MXC_SSI1SRX0);
dma_request.num_of_bytes = dma_size;
mxc_dma_config(s->dma_channel, &dma_request, 1,
MXC_DMA_MODE_READ);
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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -