📄 hi_sio.c
字号:
/* \extdrv\interface\sio\hi_sio.c * * Copyright (c) 2006 Hisilicon Co., Ltd. * * 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; * * History: * 26-April-2006 create this file */ #include <linux/config.h>#include <linux/kernel.h>#include <linux/version.h>#include <linux/module.h>#include <linux/config.h>#include <linux/types.h>#include <linux/errno.h>#include <linux/fcntl.h>#include <linux/mm.h>#include <linux/miscdevice.h>#include <linux/proc_fs.h>#include <linux/fs.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/devfs_fs_kernel.h>#include <linux/init.h>#include <linux/delay.h>#include <asm/uaccess.h>#include <asm/hardware.h>#include <asm/io.h>#include <asm/system.h>#include <linux/interrupt.h>#include <linux/ioport.h>#include <linux/string.h>#include <linux/ioctl.h>#include <linux/miscdevice.h>#include "hi_sio.h"#define DEVICE_NAME_0 "hi_sio_0"#define DEVICE_NAME_1 "hi_sio_1"#define SIO_0 0#define SIO_1 1#define GPIO7_PHY_BASE 0x101FA000#define SIO_0_PHY_BASE 0x80080000#define SIO_1_PHY_BASE 0x90020000#define SYS_BASE_CLK (27000000)#define DEBUG_SIO/* SIO registers offset address */#define SIO_WORK_MODE(x) (SIO_VBASE[x] + 0x40) #define SIO_INTR_STAT(x) (SIO_VBASE[x] + 0x42)#define SIO_INTR_CLR(x) (SIO_VBASE[x] + 0x44)#define SIO_LEFT_TD(x) (SIO_VBASE[x] + 0x46)#define SIO_PCM_TD(x) SIO_LEFT_TD(x)#define SIO_RIGHT_TD(x) (SIO_VBASE[x] + 0x48)#define SIO_LEFT_RD(x) (SIO_VBASE[x] + 0x4a)#define SIO_PCM_RD(x) SIO_LEFT_RD(x)#define SIO_RIGHT_RD(x) (SIO_VBASE[x] + 0x4c)#define SIO_I2S_CT_SET(x) (SIO_VBASE[x] + 0x4e)#define SIO_I2S_CT_CLR(x) (SIO_VBASE[x] + 0x50)#define SIO_ICD_SET(x) (SIO_VBASE[x] + 0x52)#define SIO_RX_STAT(x) (SIO_VBASE[x] + 0x54)#define SIO_TX_STAT(x) (SIO_VBASE[x] + 0x56)#define SIO_PCM_CT_SET(x) (SIO_VBASE[x] + 0x58)#define SIO_PCM_CT_CLR(x) (SIO_VBASE[x] + 0x5a)/* SIO work mode */#define SIO_MODE_PCM 0x01#define SIO_MODE_I2S 0x00#define SIO_MODE_PCM_INTEGRATE 0x00#define SIO_MODE_PCM_DETACH (0x01<<1)/* SIO ICD Config */#define SIO_ICD_CONFIG 0x61 /* 25MHz and 8KHz config. *//* SIO interrupt mask */#define SIO_RX_INTR_MASK (0x01<<1)#define SIO_TX_INTR_MASK 0x01/* SIO interrupt clear */#define SIO_RX_INTR_CLEAR (0x01<<1)#define SIO_TX_INTR_CLEAR 0x01/* I2S_CT_SET register config */#define SIO_DSP_MODE_SET (0x01<<14)#define SIO_I2S_RX_ENABLE_SET (0x01<<13)#define SIO_I2S_TX_ENABLE_SET (0x01<<12)#define SIO_I2S_RX_DISABLE_SET (0x01<<13)#define SIO_I2S_TX_DISBLE_SET (0x01<<12) #define SIO_I2S_RX_FIFO_DISABLE_SET (0x01<<11)#define SIO_I2S_TX_FIFO_DISABLE_SET (0x01<<10)#define SIO_I2S_RX_FIFO_THRESHOLD 0x03#define SIO_I2S_RX_FIFO_THRESHOLD_SHIFT_NUM 7#define SIO_I2S_TX_FIFO_THRESHOLD 0x03#define SIO_I2S_TX_FIFO_THRESHOLD_SHIFT_NUM 4#define SIO_I2S_TX_CLK_SEL_EX_SET (0x01<<3)#define SIO_I2S_TX_WS_SEL_EX_SET (0x01<<2)#define SIO_I2S_RX_16BIT_SET (0x01<<1)#define SIO_I2S_TX_16BIT_SET 0x01/* I2S_CT_CLR register config */#define SIO_I2S_MODE_CLR (0x01<<14)#define SIO_I2S_RX_DISABLE_CLR (0x01<<13)#define SIO_I2S_TX_DISABLE_CLR (0x01<<12)#define SIO_I2S_RX_FIFO_ENABLE_CLR (0x01<<11)#define SIO_I2S_TX_FIFO_ENABLE_CLR (0x01<<10)#define SIO_I2S_TX_CLK_SEL_IN_CLR (0x01<<3)#define SIO_I2S_TX_WS_SEL_IN_CLR (0x01<<2)#define SIO_I2S_RX_8BIT_CLR (0x01<<1)#define SIO_I2S_TX_8BIT_CLR 0x01/* RX_STA register config */#define SIO_RX_LEFT_FIFO_DEPTH_GET(value) ((value&(0x0F<<6))>>6)#define SIO_RX_RIGHT_FIFO_DEPTH_GET(value) ((value&(0x0F<<2))>>2)#define SIO_RX_LEFT_FIFO_OVER_GET(value) ((value&(0x01<<1))>>1)#define SIO_RX_RIGHT_FIFO_OVER_GET(value) ( value&0x01 )/* TX_STA register config */#define SIO_TX_LEFT_FIFO_DEPTH_GET(value) ((value&(0x0F<<6))>>6)#define SIO_TX_RIGHT_FIFO_DEPTH_GET(value) ((value&(0x0F<<2))>>2)#define SIO_TX_LEFT_FIFO_OVER_GET(value) ((value&(0x01<<1))>>1)#define SIO_TX_RIGHT_FIFO_OVER_GET(value) ( value&0x01 )/* PCM_CT_SET register config */#define SIO_PCM_MODE_SET (0x01<<15)#define SIO_PCM_EDGE_SEL_UP_SET (0x01<<14)#define SIO_PCM_RX_ENABLE_SET (0x01<<13)#define SIO_PCM_TX_ENABLE_SET (0x01<<12)#define SIO_PCM_RX_FIFO_DISABLE_SET (0x01<<11)#define SIO_PCM_TX_FIFO_DISABLE_SET (0x01<<10)#define SIO_PCM_RX_FIFO_THRESHOLD 0x03#define SIO_PCM_RX_FIFO_THRESHOLD_SHIFT_NUM 7#define SIO_PCM_TX_FIFO_THRESHOLD 0x03#define SIO_PCM_TX_FIFO_THRESHOLD_SHIFT_NUM 4#define SIO_PCM_TX_CLK_SEL_EX_SET (0x01<<3)#define SIO_PCM_TX_WS_SEL_EX_SET (0x01<<2)#define SIO_PCM_RX_16BIT_SET (0x01<<1)#define SIO_PCM_TX_16BIT_SET 0x01/* PCM_CT_CLR register config */#define SIO_PCM_MODE_CLR (0x01<<15)#define SIO_PCM_EDGE_SEL_DOWN_CLR (0x01<<14)#define SIO_PCM_RX_DISABLE_CLR (0x01<<13)#define SIO_PCM_TX_DISABLE_CLR (0x01<<12)#define SIO_PCM_RX_FIFO_ENABLE_CLR (0x01<<11)#define SIO_PCM_TX_FIFO_ENABLE_CLR (0x01<<10)#define SIO_PCM_TX_CLK_SEL_IN_CLR (0x01<<3)#define SIO_PCM_TX_WS_SEL_IN_CLR (0x01<<2)#define SIO_PCM_RX_8BIT_CLR (0x01<<1)#define SIO_PCM_TX_8BIT_CLR 0x01/*定义宏用于读写寄存器*/#define HI_REG_READ16(addr,result) ((result) = (*(volatile unsigned short *)(addr)))#define HI_REG_READ32(addr,result) ((result) = *(volatile unsigned int *)(addr))#define HI_REG_WRITE8(addr,result) (*(volatile unsigned char *)(addr) = (result))#define HI_REG_WRITE16(addr,result) ((*(volatile unsigned short *)(addr)) = (result))#define ENABLE_LOOP _IOW('S', 15,int)#define DISABLE_LOOP _IOW('S', 16,int)unsigned int SIO_VBASE[2];/*存放两个SIO iomap后的虚拟地址*/unsigned int GPIO7_VIR_BASE;unsigned int SioInitialized[2] = {0, 0};/*判断是否SIO 0,1初始化的全局变量*/static unsigned int samplerate_record_0 = 0;static unsigned int samplerate_record_1 = 0;static unsigned int open_cnt_0 = 0;static unsigned int open_cnt_1 = 0;static unsigned int samplerate_list[13]={8000,11025,16000,22050,24000,32000,44100,48000,64000,88200,96000,176400,192000};static unsigned int ch1 = 1;/*获取系统AHB总线频率*/static unsigned int ahbFreqDectect(void){ unsigned int RegAddr, Result, AhbFreq, X, Y, Z, Loop; RegAddr = (unsigned int)IO_ADDRESS(0x101e0018); HI_REG_READ32(RegAddr, Result); X = Result&0xf000; X >>= 12; Y = Result&0x0ff0; Y >>= 4; Z = Result&0x000f; AhbFreq = SYS_BASE_CLK; /*系统时钟晶振频率 25MHz or 24.576MHz. */ for(Loop = 0; Loop < X; Loop ++) { AhbFreq /= 2; } AhbFreq /= Z; AhbFreq *= Y; AhbFreq /= 2; //4-26 del return(AhbFreq);}/*释放参数which_sio 指定的SIO占用的IO空间*/void sio_drv_exit(unsigned int which_sio){ if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,9)) { if(SIO_0 == which_sio) { iounmap((void *)SIO_VBASE[SIO_0]); release_mem_region(SIO_0_PHY_BASE, PAGE_SIZE); } else if(SIO_1 == which_sio) { iounmap((void *)SIO_VBASE[SIO_1]); release_mem_region(SIO_1_PHY_BASE, PAGE_SIZE); } } else SIO_VBASE[which_sio]=0; SioInitialized[which_sio] = 0; if(LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,9)) { if((SioInitialized[0] == 0) && (SioInitialized[1] == 0)) iounmap((void *)GPIO7_VIR_BASE); } }/*为参数which_sio 指定的SIO申请IO空间并建立映射关系*/int sio_drv_init(unsigned int which_sio){ unsigned int tmp; if(SioInitialized[which_sio] == 0) { if(LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,9)) { if((SioInitialized[0] == 0) && (SioInitialized[1] == 0)) GPIO7_VIR_BASE = (unsigned int)ioremap(GPIO7_PHY_BASE,PAGE_SIZE); if(SIO_0 == which_sio) { if (!request_mem_region(SIO_0_PHY_BASE,PAGE_SIZE, "SIO_VBASE :0")) { printk("SIO_VBASE[0]: cannot request mem\n"); return -1; } SIO_VBASE[SIO_0] = (unsigned int)ioremap(SIO_0_PHY_BASE, PAGE_SIZE); HI_REG_READ16((GPIO7_VIR_BASE+0X400),tmp); tmp |= 0x08; HI_REG_WRITE16((GPIO7_VIR_BASE+0X400),tmp); HI_REG_WRITE16((GPIO7_VIR_BASE+0X20),0X08); } else if(SIO_1 == which_sio) { if (!request_mem_region(SIO_1_PHY_BASE,PAGE_SIZE, "SIO_VBASE :1")) { printk("SIO_VBASE[1]: cannot request mem\n"); return -1; } SIO_VBASE[SIO_1] = (unsigned int)ioremap(SIO_1_PHY_BASE, PAGE_SIZE); HI_REG_READ16((GPIO7_VIR_BASE+0X400),tmp); tmp |= 0x04; HI_REG_WRITE16((GPIO7_VIR_BASE+0X400),tmp); HI_REG_WRITE16((GPIO7_VIR_BASE+0X10),0X40); } } else { if((SioInitialized[0] == 0)&&(SioInitialized[1] == 0)) GPIO7_VIR_BASE =IO_ADDRESS(GPIO7_PHY_BASE); if(SIO_0 == which_sio) { HI_REG_READ16((GPIO7_VIR_BASE+0X400),tmp); tmp |= 0x08; HI_REG_WRITE16((GPIO7_VIR_BASE+0X400),tmp); HI_REG_WRITE16((GPIO7_VIR_BASE+0X20),0Xff); SIO_VBASE[SIO_0]=IO_ADDRESS(SIO_0_PHY_BASE); } else if(SIO_1 == which_sio) { HI_REG_READ16((GPIO7_VIR_BASE+0X400),tmp); tmp |= 0x04; HI_REG_WRITE16((GPIO7_VIR_BASE+0X400),tmp); HI_REG_WRITE16((GPIO7_VIR_BASE+0X10),0Xff); SIO_VBASE[SIO_1]=IO_ADDRESS(SIO_1_PHY_BASE); } } HI_REG_WRITE16(SIO_WORK_MODE(which_sio), SIO_MODE_I2S); HI_REG_WRITE16(SIO_I2S_CT_CLR(which_sio), 0xffff); HI_REG_WRITE16(SIO_I2S_CT_SET(which_sio), 0xb1cf);////SIO in slave mode b1bf .modify the tx fifo watermark b1cf tmp = ahbFreqDectect(); tmp = (tmp/(32*8000))-1; HI_REG_WRITE16(SIO_ICD_SET(which_sio),tmp); SioInitialized[which_sio] = 1; } return 0;}static irqreturn_t sio_0_int(int irq, void *dev_id ,struct pt_regs * regs){ int i,handled; unsigned short buf_left[8],buf_right[8]; for(i=0;i<3;i++) { HI_REG_READ16(SIO_LEFT_RD(0),buf_left[i]); HI_REG_READ16(SIO_RIGHT_RD(0),buf_right[i]); } for(i=0;i<3;i++) { HI_REG_WRITE16(SIO_LEFT_TD(0),buf_left[i]); HI_REG_WRITE16(SIO_RIGHT_TD(0),buf_right[i]); } HI_REG_WRITE16(SIO_INTR_CLR(0),0x03); handled = 1; return IRQ_RETVAL(handled);}static irqreturn_t sio_1_int(int irq, void *dev_id ,struct pt_regs * regs){ int i,handled; unsigned short buf_left[8],buf_right[8]; for(i=0;i<3;i++) { HI_REG_READ16(SIO_LEFT_RD(1),buf_left[i]); HI_REG_READ16(SIO_RIGHT_RD(1),buf_right[i]); } for(i=0;i<3;i++) { HI_REG_WRITE16(SIO_LEFT_TD(1),buf_left[i]); HI_REG_WRITE16(SIO_RIGHT_TD(1),buf_right[i]); } HI_REG_WRITE16(SIO_INTR_CLR(1),0x03); handled = 1; return IRQ_RETVAL(handled);}/* 设置 SIO 工作参数 * 参数cmd: 控制命令 * 参数arg: 命令参数 */static int hi_sio_0_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ unsigned int ahbFreqtmp; unsigned int result; int ret; switch (cmd) { case GET_SIO_SAMPLERATE: put_user(samplerate_record_0,(unsigned int *)arg); break; case GET_SIO_TX_BITWIDTH: HI_REG_READ16(SIO_I2S_CT_SET(0), result); result &= 0x01;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -