rt-thread/bsp/x1000/drivers/audio/drv_dmic.c

429 lines
12 KiB
C

/*
* File : drv_dmic.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2008 - 2012, RT-Thread Development Team
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Change Logs:
* Date Author Notes
* 2015-11-19 Urey the first version
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <drivers/audio.h>
#ifdef RT_USING_FINSH
# include <finsh.h>
#endif
#include "board.h"
#include "drv_gpio.h"
#include "drv_dma.h"
#include "drv_aic.h"
#include "drv_clock.h"
#include "drv_dmic.h"
#include "dma.h"
#include "audio_pipe.h"
#define DMIC_DEBUG 0
#if DMIC_DEBUG
# define DMIC_DBG(...) rt_kprintf("[DMIC]"),rt_kprintf(__VA_ARGS__)
#else
# define DMIC_DBG(...)
#endif
#define DMIC_DMA_RX_CHAN 4
#define DMIC_FIFO_DEPTH 64
#define JZ_DMIC_FORMATS 16
#define JZ_DMIC_RATE (8000)
ALIGN(32) rt_uint32_t _g_dmic_dma_buffer[DMIC_DMA_PAGE_NUM*DMIC_DMA_PAGE_SIZE/sizeof(rt_uint32_t)];
static struct jz_dmic _g_jz_dmic=
{
.io_base = DMIC_BASE,
.dma_buf = (rt_uint8_t *)_g_dmic_dma_buffer,
.dma_offset = 0,
};
static void dump_dmic_registers(void)
{
struct jz_dmic *jz_dmic = &_g_jz_dmic;
rt_kprintf("DMICCR0 %p : 0x%08x\n", (jz_dmic->io_base+DMICCR0),dmic_read_reg(jz_dmic, DMICCR0));
rt_kprintf("DMICGCR %p : 0x%08x\n", (jz_dmic->io_base+DMICGCR),dmic_read_reg(jz_dmic, DMICGCR));
rt_kprintf("DMICIMR %p : 0x%08x\n", (jz_dmic->io_base+DMICIMR),dmic_read_reg(jz_dmic, DMICIMR));
rt_kprintf("DMICINTCR %p : 0x%08x\n", (jz_dmic->io_base+DMICINTCR),dmic_read_reg(jz_dmic, DMICINTCR));
rt_kprintf("DMICTRICR %p : 0x%08x\n", (jz_dmic->io_base+DMICTRICR),dmic_read_reg(jz_dmic, DMICTRICR));
rt_kprintf("DMICTHRH %p : 0x%08x\n", (jz_dmic->io_base+DMICTHRH),dmic_read_reg(jz_dmic, DMICTHRH));
rt_kprintf("DMICTHRL %p : 0x%08x\n", (jz_dmic->io_base+DMICTHRL),dmic_read_reg(jz_dmic, DMICTHRL));
rt_kprintf("DMICTRIMMAX %p : 0x%08x\n", (jz_dmic->io_base+DMICTRIMMAX),dmic_read_reg(jz_dmic, DMICTRIMMAX));
rt_kprintf("DMICTRINMAX %p : 0x%08x\n", (jz_dmic->io_base+DMICTRINMAX),dmic_read_reg(jz_dmic, DMICTRINMAX));
rt_kprintf("DMICDR %p : 0x%08x\n", (jz_dmic->io_base+DMICDR),dmic_read_reg(jz_dmic, DMICDR));
rt_kprintf("DMICFTHR %p : 0x%08x\n", (jz_dmic->io_base+DMICFTHR),dmic_read_reg(jz_dmic, DMICFTHR));
rt_kprintf("DMICFSR %p : 0x%08x\n", (jz_dmic->io_base+DMICFSR),dmic_read_reg(jz_dmic, DMICFSR));
rt_kprintf("DMICCGDIS %p : 0x%08x\n", (jz_dmic->io_base+DMICCGDIS),dmic_read_reg(jz_dmic, DMICCGDIS));
return;
}
MSH_CMD_EXPORT(dump_dmic_registers,"dump dmic regs...\n");
int jz_dmic_set_rate(struct jz_dmic* dmic, int rate)
{
// rt_kprintf("rate = %d\n",rate);
switch (rate)
{
case 8000:
__dmic_set_sr_8k(dmic);
break;
case 16000:
__dmic_set_sr_16k(dmic);
break;
case 48000:
__dmic_set_sr_48k(dmic);
break;
default:
DMIC_DBG("dmic unsupport rate %d\n", rate);
}
return 0;
}
int jz_dmic_set_channels(struct jz_dmic* dmic, int channels)
{
if (channels > 4)
channels = 4;
if (channels <= 1)
channels = 1;
__dmic_set_chnum(dmic,channels - 1);
}
int jz_dmic_set_record_volume(struct jz_dmic* dmic, int vol)
{
if(vol >= 32)
vol = 31;
__dmic_set_gcr(dmic,vol);
dmic->record_gain = vol;
}
static void jz_dmic_dma_complete(struct rt_dma_channel *dmac, struct dma_message *msg)
{
rt_base_t level;
if(msg->complete_cb)
msg->complete_cb(msg->complete_arg,msg->dst_addr);
/* restart DMA Job */
rt_dma_trans_message(dmac,msg);
}
void jz_dmic_start_recv(struct jz_dmic* dmic,void (*rx_callback)(void *,void *), void *rx_arg)
{
rt_base_t level;
rt_uint32_t i;
struct dma_message message;
__dmic_enable_rdms(dmic);
__dmic_enable(dmic);
level = rt_hw_interrupt_disable();
dmic->dma_offset = 0;
dmic->dma_buf = (rt_uint8_t *)_g_dmic_dma_buffer;
for (i = 0; i < DMIC_DMA_PAGE_NUM; ++i)
{
message.src_addr = (uint8_t *) (DMIC_BASE + DMICDR);
message.src_option = RT_DMA_ADDR_FIX;
message.dst_addr = (uint8_t *) (dmic->dma_buf + DMIC_DMA_PAGE_SIZE * i);
message.dst_option = RT_DMA_ADDR_INC;
message.t_size = DMIC_DMA_PAGE_SIZE;
message.t_mode = JZDMA_REQ_DMIC_RX;
/* init callback */
message.complete_cb = rx_callback;
message.complete_arg = rx_arg;
rt_dma_trans_message(dmic->rx_dmac,&message);
}
rt_hw_interrupt_enable(level);
return ;
}
void jz_dmic_stop_recv(struct jz_dmic* dmic)
{
if (__dmic_is_enable_rdms(dmic))
{
__dmic_disable_rdms(dmic);
}
__dmic_disable(dmic);
}
void dmic_rx_complete(void *data,void *pbuf)
{
struct jz_dmic *dmic = (struct jz_dmic *)data;
rt_device_write(RT_DEVICE(&dmic->pipe),0,pbuf,DMIC_DMA_PAGE_SIZE);
}
#define CFG_DMIC_PIPE_SIZE (2 * 1024)
struct jz_dmic* rt_hw_dmic_init(void)
{
struct jz_dmic *dmic = &_g_jz_dmic;
//init pipe for record
{
rt_uint8_t *buf = rt_malloc(CFG_DMIC_PIPE_SIZE);
if(buf == RT_NULL)
{
rt_kprintf("request pipe memory error\n");
return RT_NULL;
}
rt_audio_pipe_init(&dmic->pipe,"recdmic",RT_PIPE_FLAG_FORCE_WR | RT_PIPE_FLAG_BLOCK_RD,buf,(CFG_DMIC_PIPE_SIZE));
rt_device_open(RT_DEVICE(&dmic->pipe),RT_DEVICE_OFLAG_RDONLY);
}
/* GPIO config
* PB05 -> FUNC1 DMIC1_IN
* PB21 -> FUNC0 DMIC_CLK
* PB22 -> FUNC0 DMIC0_IN
* */
gpio_set_func(GPIO_PORT_B,GPIO_Pin_5,GPIO_FUNC_1);
gpio_set_func(GPIO_PORT_B,GPIO_Pin_21,GPIO_FUNC_0);
gpio_set_func(GPIO_PORT_B,GPIO_Pin_22,GPIO_FUNC_0);
/* enable clock */
dmic->clk_gate = clk_get("dmic");
if (dmic->clk_gate == RT_NULL)
{
DMIC_DBG("Failed to get dmic gate clock \n");
return RT_NULL;
}
clk_enable(dmic->clk_gate);
/*gain: 0, ..., e*/
__dmic_reset(dmic);
while (__dmic_get_reset(dmic)) ;
jz_dmic_set_rate(dmic, 8000);
__dmic_set_chnum(dmic,0); //mono
__dmic_enable_hpf1(dmic);
__dmic_set_gcr(dmic, 27);
__dmic_mask_all_int(dmic);
__dmic_enable_pack(dmic);
__dmic_enable_sw_lr(dmic);
__dmic_enable_lp(dmic);
__dmic_disable_lp(dmic);
__dmic_set_request(dmic, 48);
__dmic_enable_hpf2(dmic);
__dmic_set_thr_high(dmic, 32);
__dmic_set_thr_low(dmic, 16);
__dmic_enable_tri(dmic);
//config DMA
{
int trigger;
/* DMA config */
struct dma_config config;
dmic->rx_dmac = rt_dma_get_channel(DMIC_DMA_RX_CHAN);
if (dmic->rx_dmac != RT_NULL)
{
DMIC_DBG("config dmic dma rx channel...\n");
config.direction = RT_DMA_DEV_TO_MEM;
config.src_addr_width = RT_DMA_BUSWIDTH_2_BYTES;
config.src_maxburst = (DMIC_FIFO_DEPTH * RT_DMA_BUSWIDTH_2_BYTES) / 2;
config.dst_addr_width = RT_DMA_BUSWIDTH_2_BYTES;
config.dst_maxburst = (64 * 1024);
rt_dma_configture(dmic->rx_dmac, &config);
dmic->rx_dmac->start = RT_NULL;
dmic->rx_dmac->complete = jz_dmic_dma_complete;
}
trigger = config.src_maxburst / config.src_addr_width;
__dmic_set_request(dmic, trigger);
}
jz_dmic_start_recv(dmic,dmic_rx_complete,dmic);
return dmic;
_error_exit:
__dmic_disable(dmic);
rt_audio_pipe_detach(&dmic->pipe);
clk_disable(dmic->clk_gate);
return RT_NULL;
}
//INIT_ENV_EXPORT(rt_hw_dmic_init);
struct speech_wav_header
{
char riff_id[4]; //"RIFF"
uint32_t size0; //file len - 8
char wave_fmt[8]; //"WAVEfmt "
uint32_t size1; //0x10
uint16_t fmttag; //0x01
uint16_t channel; //1
uint32_t samplespersec; //8000
uint32_t bytepersec; //8000 * 2
uint16_t blockalign; //1 * 16 / 8
uint16_t bitpersamples; //16
char data_id[4]; //"data"
uint32_t size2; //file len - 44
};
static void speech_wav_init_header(struct speech_wav_header *header,rt_uint16_t Channels,int SamplesPerSec,int BitsPerSample)
{
strcpy(header->riff_id, "RIFF");
header->size0 = 0; // Final file size not known yet, write 0
strcpy(header->wave_fmt, "WAVEfmt ");
header->size1 = 16; // Sub-chunk size, 16 for PCM
header->fmttag = 1; // AudioFormat, 1 for PCM
header->channel = Channels; // Number of channels, 1 for mono, 2 for stereo
header->samplespersec = SamplesPerSec; // Sample rate
header->bytepersec = SamplesPerSec * BitsPerSample * Channels / 8; //Byte rate
header->blockalign = Channels * BitsPerSample / 8; // Block align, NumberOfChannels*BitsPerSample/8
header->bitpersamples = BitsPerSample;
strcpy(header->data_id, "data");
header->size2 = 0;
}
static void speech_wav_upgrade_size(struct speech_wav_header *header,rt_uint32_t paylodSize)
{
header->size0 = paylodSize + 36;
header->size2 = paylodSize;
}
#include <finsh.h>
#include <dfs_posix.h>
rt_uint8_t rec_buff[2048];
int dmic_record(int samplingrates)
{
struct jz_dmic *dmic = &_g_jz_dmic;
rt_device_t dmic_pipe;
struct speech_wav_header wav_header;
rt_uint32_t wav_len = 0;
char *file_name;
int fd;
int i = 0;
int rdlen, wrlen;
rt_kprintf("samplingrates = %d\n",samplingrates);
if((samplingrates != 8000) && (samplingrates != 16000))
{
rt_kprintf("un-support this samplingrates\n");
return -RT_EIO;
}
dmic_pipe = rt_device_find("recdmic");
if(dmic_pipe == RT_NULL)
{
rt_kprintf("can't find the record device\n");
return -RT_ERROR ;
}
rt_kprintf("pls hold WAKE key to start record...\n");
while(gpio_get_value(GPIO_PORT_B, GPIO_Pin_31) == 1)
rt_thread_delay(100);
rt_kprintf("OK,start record....\n");
if(samplingrates == 8000)
file_name = "/appfs/dmic8k.wav";
else
file_name = "/appfs/dmic16k.wav";
fd = open(file_name, O_WRONLY | O_CREAT | O_TRUNC, 0);
if (fd < 0)
{
rt_kprintf("open file for write failed\n");
return -RT_EIO;
}
speech_wav_init_header(&wav_header,1,samplingrates,16);
write(fd, &wav_header, wav_len);
jz_dmic_set_rate(dmic,samplingrates);
wav_len = 0;
while(i++ < 1000)
{
rdlen = rt_device_read(dmic_pipe,0,rec_buff,sizeof(rec_buff));
wrlen = write(fd, rec_buff, rdlen);
if (wrlen != rdlen)
{
rt_kprintf("write data failed\n");
close(fd);
return -RT_EIO;
}
wav_len += wrlen;
if(gpio_get_value(GPIO_PORT_B, GPIO_Pin_31) == 1)
break;
}
rt_kprintf("record complete...\n");
//upgrage wav header
lseek(fd,0,SEEK_SET);
speech_wav_upgrade_size(&wav_header,wav_len);
write(fd, &wav_header, sizeof(struct speech_wav_header));
close(fd);
rt_kprintf("WAV file saved ok!\n");
}
FINSH_FUNCTION_EXPORT(dmic_record,dmic record test);
#if 0
int dmic_test(void)
{
rt_device_t device;
int i = 0;
device = rt_device_find("recdmic");
if(device == RT_NULL)
{
rt_kprintf("can't find the record device\n");
return -RT_ERROR ;
}
audio_device_set_rate(8000);
while(i++ < 1000)
{
int len;
uint8_t *sendBuf;
sendBuf = audio_device_get_buffer(&len);
len = rt_device_read(device,0,sendBuf,len);
audio_device_write(sendBuf,len);
}
rt_kprintf("dmic test complete...\n");
return 0;
}
MSH_CMD_EXPORT(dmic_test,dmic test ....);
#endif