Sound Card(UDA1341) Driver for Linux2.6.xx+ARM9

来源:互联网 发布:知乎 fade 编辑:程序博客网 时间:2024/05/21 14:45

/*
  *  UDA1341TS by chaozang(cz) <zangchao.cn@gmail.com>
  *  From 2013-01-26  To 2013-01-30
  */

----------------------------------------------------------driver---------------------------------------------------------------

#include <linux/module.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/interrupt.h>
#include <linux/errno.h>
#include <linux/sound.h>
#include <linux/soundcard.h>

#include <linux/pm.h>
#include <linux/clk.h>
#include <linux/platform_device.h>

#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/semaphore.h>
#include <asm/dma.h>
#include <asm/arch/dma.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-iis.h>
#include <asm/arch/regs-clock.h>
#include <linux/dma-mapping.h>
#include <asm/dma-mapping.h>
#include <asm/arch/hardware.h>
#include <asm/arch/map.h>

static volatile int waitq_condition = 0;
static DECLARE_WAIT_QUEUE_HEAD(waitq);

#define I2SSDI_DMA1 (010<<24) 
#define I2SSDO_DMA2 (000<<24)
#define DMAON (1<<1)

#define S3C2410_DCON_HANDSHAKE (1<<31)
#define S3C2410_DCON_SYNC_PCLK (0<<30)
#define S3C2410_DCON_INTREQ (1<<29)
#define S3C2410_DCON_TSZUNIT (0<<28)
#define S3C2410_DCON_SSERVE (0<<27)
#define S3C2410_DCON_HWTRIG (1<<23)

#define HALFWORD (1<<20)
#define IISFIFO 0x55000010

#define SRCONAHB (0<<1)
#define SRCONAPB (1<<1)
#define ADDRINC (0<<0)
#define ADDRFIX (1<<0)

#define DMASTOP (1<<2)
#define DMAON (1<<1)
#define DMAOFF (0<<1)
#define DMASW_TRIG (1<<0)

#define AUDIO_RATE_DEFAULT 44100

/* UDA1341 Register bits */
#define UDA1341_ADDR 0x14

#define UDA1341_REG_DATA0 (UDA1341_ADDR + 0)
#define UDA1341_REG_STATUS (UDA1341_ADDR + 2)

/* status control */
#define STAT0 (0x00)
#define STAT0_RST (1 << 6)
#define STAT0_SC_MASK (3 << 4)
#define STAT0_SC_512FS (0 << 4)
#define STAT0_SC_384FS (1 << 4)
#define STAT0_SC_256FS (2 << 4)
#define STAT0_IF_MASK (7 << 1)
#define STAT0_IF_I2S (0 << 1)
#define STAT0_IF_LSB16 (1 << 1)
#define STAT0_IF_LSB18 (2 << 1)
#define STAT0_IF_LSB20 (3 << 1)
#define STAT0_IF_MSB (4 << 1)
#define STAT0_IF_LSB16MSB (5 << 1)
#define STAT0_IF_LSB18MSB (6 << 1)
#define STAT0_IF_LSB20MSB (7 << 1)
#define STAT0_DC_FILTER (1 << 0)
#define STAT0_DC_NO_FILTER (0 << 0)

#define STAT1 (0x80)
#define STAT1_DAC_GAIN (1 << 6) /* gain of DAC */
#define STAT1_ADC_GAIN (1 << 5) /* gain of ADC */
#define STAT1_ADC_POL (1 << 4) /* polarity of ADC */
#define STAT1_DAC_POL (1 << 3) /* polarity of DAC */
#define STAT1_DBL_SPD (1 << 2) /* double speed playback */
#define STAT1_ADC_ON (1 << 1) /* ADC powered */
#define STAT1_DAC_ON (1 << 0) /* DAC powered */

/* data0 direct control */
#define DATA0 (0x00)
#define DATA0_VOLUME_MASK (0x3f)
#define DATA0_VOLUME(x) (x)

#define DATA1 (0x40)
#define DATA1_BASS(x) ((x) << 2)
#define DATA1_BASS_MASK (15 << 2)
#define DATA1_TREBLE(x) ((x))
#define DATA1_TREBLE_MASK (3)

#define DATA2 (0x80)
#define DATA2_PEAKAFTER (0x1 << 5)
#define DATA2_DEEMP_NONE (0x0 << 3)
#define DATA2_DEEMP_32KHz (0x1 << 3)
#define DATA2_DEEMP_44KHz (0x2 << 3)
#define DATA2_DEEMP_48KHz (0x3 << 3)
#define DATA2_MUTE (0x1 << 2)
#define DATA2_FILTER_FLAT (0x0 << 0)
#define DATA2_FILTER_MIN (0x1 << 0)
#define DATA2_FILTER_MAX (0x3 << 0)
/* data0 extend control */
#define EXTADDR(n) (0xc0 | (n))
#define EXTDATA(d) (0xe0 | (d))

#define EXT0 0
#define EXT0_CH1_GAIN(x) (x)
#define EXT1 1
#define EXT1_CH2_GAIN(x) (x)
#define EXT2 2
#define EXT2_MIC_GAIN_MASK (7 << 2)
#define EXT2_MIC_GAIN(x) ((x) << 2)
#define EXT2_MIXMODE_DOUBLEDIFF (0)
#define EXT2_MIXMODE_CH1 (1)
#define EXT2_MIXMODE_CH2 (2)
#define EXT2_MIXMODE_MIX (3)
#define EXT4 4
#define EXT4_AGC_ENABLE (1 << 4)
#define EXT4_INPUT_GAIN_MASK (3)
#define EXT4_INPUT_GAIN(x) ((x) & 3)
#define EXT5 5
#define EXT5_INPUT_GAIN(x) ((x) >> 2)
#define EXT6 6
#define EXT6_AGC_CONSTANT_MASK (7 << 2)
#define EXT6_AGC_CONSTANT(x) ((x) << 2)
#define EXT6_AGC_LEVEL_MASK (3)
#define EXT6_AGC_LEVEL(x) (x)

#define AUDIO_NAME "UDA1341"
#define AUDIO_NAME_VERBOSE "UDA1341 audio driver"

#define AUDIO_FMT_MASK (AFMT_S16_LE)
#define AUDIO_FMT_DEFAULT (AFMT_S16_LE)

#define AUDIO_CHANNELS_DEFAULT 2
#define AUDIO_RATE_DEFAULT 44100

#define AUDIO_NBFRAGS_DEFAULT 8
#define AUDIO_FRAGSIZE_DEFAULT 8192

#define S_CLOCK_FREQ 384
#define PCM_ABS(a) (a < 0 ? -a : a)

static u_int audio_rate;

struct iis_regs {
 unsigned long iiscon;
 unsigned long iismod;
 unsigned long iispsr;
 unsigned long iisfcon;
 unsigned long iisfifo;
};

static struct iis_regs *piis_regs_base;

struct dma_regs {
 unsigned long disrc;
 unsigned long disrcc;
 unsigned long didst;
 unsigned long didstc;
 unsigned long dcon;
 unsigned long dstat;
 unsigned long dcsrc;
 unsigned long dcdst;
 unsigned long dmasktrig;
};

static struct dma_regs *pdma_regs_in_base;
static struct dma_regs *pdma_regs_out_base;

static struct clk *iis_clock;

static int audio_dev_dsp;
static int audio_dev_mixer;

static int iispsr_value(int s_bit_clock, int sample_rate)
{
 int i, prescaler = 0;
 unsigned long tmpval;
 unsigned long tmpval384;
 unsigned long tmpval384min = 0xffff;

 tmpval384 = clk_get_rate(iis_clock) / s_bit_clock;
// tmpval384 = s3c2410_get_bus_clk(GET_PCLK) / s_bit_clock;

 for (i = 0; i < 32; i++) {
  tmpval = tmpval384/(i+1);
  if (PCM_ABS((sample_rate - tmpval)) < tmpval384min) {
   tmpval384min = PCM_ABS((sample_rate - tmpval));
   prescaler = i;
  }
 }

 printk("prescaler = %d\n", prescaler);

 return prescaler;
}

static long audio_set_dsp_speed(long val)
{
 unsigned int prescaler;
 prescaler=(IISPSR_A(iispsr_value(S_CLOCK_FREQ, val))
   | IISPSR_B(iispsr_value(S_CLOCK_FREQ, val)));
 __raw_writel(prescaler, piis_regs_base + S3C2410_IISPSR);

 printk("audio_set_dsp_speed:%ld prescaler:%i\n",val,prescaler);
 return (audio_rate = val);
}

static void czuda1341_l3_address(u8 data)
{
 printk("driver: czuda1341_l3_address in ok\n");

 int i;
 unsigned long flags;
 local_irq_save(flags);
 s3c2410_gpio_setpin(S3C2410_GPB2,0); //GPB 2: L3MODE
 s3c2410_gpio_setpin(S3C2410_GPB4,1);// GPB 4: L3CLOCK
 udelay(1);
 for (i = 0; i < 8; i++) {
  if (data & 0x1) {
   s3c2410_gpio_setpin(S3C2410_GPB4,0);
   s3c2410_gpio_setpin(S3C2410_GPB3,1);//set GPB 3: L3DATA 1
   udelay(1);
   s3c2410_gpio_setpin(S3C2410_GPB4,1);
  } else {
   s3c2410_gpio_setpin(S3C2410_GPB4,0);
   s3c2410_gpio_setpin(S3C2410_GPB3,0);//set GPB 3: L3DATA 0
   udelay(1);
   s3c2410_gpio_setpin(S3C2410_GPB4,1);
  }
  data >>= 1;
 }
 s3c2410_gpio_setpin(S3C2410_GPB2,1);//GPB 2: L3MODE
 s3c2410_gpio_setpin(S3C2410_GPB4,1);// GPB 4: L3CLOCK
 local_irq_restore(flags);
 printk("driver: czuda1341_l3_address out ok\n");
}

static void czuda1341_l3_data(u8 data)
{
 printk("driver: czuda1341_l3_data in ok\n");

 int i;
 unsigned long flags;
 local_irq_save(flags);
 udelay(1);
 for (i = 0; i < 8; i++) {
  if (data & 0x1) {
   s3c2410_gpio_setpin(S3C2410_GPB4,0);
   s3c2410_gpio_setpin(S3C2410_GPB3,1);
   udelay(1);
   s3c2410_gpio_setpin(S3C2410_GPB4,1);
  } else {
   s3c2410_gpio_setpin(S3C2410_GPB4,0);
   s3c2410_gpio_setpin(S3C2410_GPB3,0);
   udelay(1);
   s3c2410_gpio_setpin(S3C2410_GPB4,1);
  }
  data >>= 1;
 }
 local_irq_restore(flags);
 printk("driver: czuda1341_l3_data out ok\n");
}

static irqreturn_t cz_dma_irqhandler(int irq, void *dev_id)

 printk("driver: cz_dma_irqhandler in ok\n");

 waitq_condition = 1;
 wake_up_interruptible(&waitq);
 
 printk("driver: cz_dma_irqhandler out ok\n");
 return IRQ_HANDLED;
}

#if 0
static const struct file_operations czuda1341_fops = {
 .owner  = THIS_MODULE,
 .read  = czdma_read,
 .write  = czdma_write,
 //.open  = czdma_open,
 //.release = czdma_release,
 //.ioctl      = czdma_ioctl,
};

static  int major;
static struct class *pczuda1341_cls;
static struct class_device *pczuda1341_cls_dev;
#endif

static int cz_dmainit(int in,unsigned long addr,unsigned int size)
{
 printk("driver: cz_dmainit in ok\n");
 unsigned long flags;

 if(in)
  {
   //init dma_in, from uda1341 to mem, recorded
   local_irq_restore(flags); 
   request_irq(IRQ_DMA1, cz_dma_irqhandler, IRQF_DISABLED, "IRQ_DMA1", "dma1");
   local_irq_save(flags);
   printk("driver: cz_dmainit if request_irq ok\n");
   pdma_regs_in_base->disrc = addr;//from;
   pdma_regs_in_base->didst = IISFIFO;
   pdma_regs_in_base->disrcc = SRCONAPB|ADDRINC;//src(uda1341) is on ph bus
   pdma_regs_in_base->didstc = (0<<2)|SRCONAHB|ADDRINC;//dst(mem) is on sys bus

   pdma_regs_in_base->dcon = S3C2410_DCON_HANDSHAKE|S3C2410_DCON_SYNC_PCLK|S3C2410_DCON_INTREQ|S3C2410_DCON_TSZUNIT|S3C2410_DCON_SSERVE|I2SSDI_DMA1|\
                        S3C2410_DCON_HWTRIG|(1<<22)|HALFWORD|(size<<0);
   pdma_regs_in_base->dmasktrig = DMAON;//turn on DMA1
   printk("driver: cz_dmainit if DMAON ok\n");
  }
 else
  {
   //init dma_out, from mem to uda1341, play
   local_irq_restore(flags); 
   request_irq(IRQ_DMA2, cz_dma_irqhandler, IRQF_DISABLED, "IRQ_DMA2", "dma2");
   local_irq_save(flags);
   printk("driver: cz_dmainit else request_irq ok\n");
   
   pdma_regs_out_base->disrc = IISFIFO;
   pdma_regs_out_base->didst = addr;//to;
   pdma_regs_out_base->disrcc = SRCONAHB|ADDRINC;//src(mem) is on on sys bus
   pdma_regs_out_base->didstc = (0<<2)|SRCONAPB|ADDRINC;//dst(uda1341) is on  ph bus
   pdma_regs_out_base->dcon = S3C2410_DCON_HANDSHAKE|S3C2410_DCON_SYNC_PCLK|S3C2410_DCON_INTREQ|\
                 S3C2410_DCON_TSZUNIT|S3C2410_DCON_SSERVE|I2SSDO_DMA2|\
                        S3C2410_DCON_HWTRIG|(1<<22)|HALFWORD|(size<<0);
   pdma_regs_out_base->dmasktrig = DMAON;//turn on DMA2
   printk("driver: cz_dmainit else DMAON ok\n");
  }
  
 return 0;
}

static void init_s3c2410_iis_bus_rx(void){
 printk("driver: init_s3c2410_iis_bus_rx in ok\n");
 unsigned int iiscon = 0, iismod= 0, iisfcon= 0;

 iiscon |= (1<<1);//S3C2410_IISCON_PSCEN; // Enable prescaler
 iiscon |= (1<<0);//S3C2410_IISCON_IISEN; // Enable interface

 // iismod |= S3C2410_IISMOD_MASTER; // Set interface to Master Mode
 iismod |= (0<<5);//S3C2410_IISMOD_LR_LLOW; // Low for left channel
 iismod |= (1<<4);//S3C2410_IISMOD_MSB; // IIS format
 iismod |= (1<<3);//S3C2410_IISMOD_16BIT; // Serial data bit/channel is 16 bit
 iismod |= (1<<2);//S3C2410_IISMOD_384FS; // Master clock freq = 384 fs
 iismod |= (1<<0);//S3C2410_IISMOD_32FS; // 32 fs

 iisfcon |= (1<<14);//S3C2410_IISFCON_RXDMA; //Set RX FIFO acces mode to DMA
 iisfcon |= (1<<15);//S3C2410_IISFCON_TXDMA; //Set RX FIFO acces mode to DMA

 iiscon |= (1<<4);//S3C2410_IISCON_RXDMAEN; //Enable RX DMA service request
 iiscon |= (1<<3);//S3C2410_IISCON_TXIDLE; //Set TX channel idle

 iismod |= (1<<6);//S3C2410_IISMOD_RXMODE; //Set RX Mode
 iisfcon|= (1<<12);//S3C2410_IISFCON_RXENABLE; //Enable RX Fifo

 //setup the prescaler
 printk("driver: init_s3c2410_iis_bus_rx audio_rate ok\n");
 
 audio_set_dsp_speed(audio_rate);
 
 printk("driver: init_s3c2410_iis_bus_rx audio_set_dsp_speed ok\n");
 //iiscon has to be set last - it enables the interface
 __raw_writel(iismod, piis_regs_base + S3C2410_IISMOD);
 __raw_writel(iisfcon, piis_regs_base + S3C2410_IISFCON);
 __raw_writel(iiscon, piis_regs_base + S3C2410_IISCON);
 printk("driver: init_s3c2410_iis_bus_rx out ok\n");
}

static int cz_audio_read(struct file *file, char *buffer,
       size_t count, loff_t * ppos)
{
// uda1341 to iis to mem
 printk("driver: cz_audio_read in ok\n");

 cz_dmainit(1, (unsigned long)buffer, count);
 printk("driver: cz_audio_read cz_dmainit ok\n");

 waitq_condition = 0;
 wait_event_interruptible(waitq, waitq_condition);
 printk("driver: cz_audio_read wait_event_interruptible ok\n");

 return 0;
}

static void init_s3c2410_iis_bus_tx(void)
{
 printk("driver: init_s3c2410_iis_bus_tx in ok\n");

 unsigned int iiscon = 0, iismod= 0, iisfcon= 0;

//Kill everything...
 printk("driver: init_s3c2410_iis_bus_tx in");

 iowrite32(0, piis_regs_base + S3C2410_IISPSR);
 printk("driver: init_s3c2410_iis_bus_tx S3C2410_IISPSR");
 iowrite32(0, piis_regs_base + S3C2410_IISCON);
 iowrite32(0, piis_regs_base + S3C2410_IISMOD);
 iowrite32(0, piis_regs_base + S3C2410_IISFCON);
#if 0
 __raw_writel(0, iis_base + S3C2410_IISPSR);
 printk("driver: init_s3c2410_iis_bus_tx S3C2410_IISPSR");
 __raw_writel(0, iis_base + S3C2410_IISCON);
 __raw_writel(0, iis_base + S3C2410_IISMOD);
 __raw_writel(0, iis_base + S3C2410_IISFCON);
#endif
 clk_enable(iis_clock);


 iiscon = iismod = iisfcon = 0;

//Setup basic stuff
 iiscon |= S3C2410_IISCON_PSCEN; // Enable prescaler
 iiscon |= S3C2410_IISCON_IISEN; // Enable interface

// iismod |= S3C2410_IISMOD_MASTER; // Set interface to Master Mode
 iismod |= S3C2410_IISMOD_LR_LLOW; // Low for left channel
 iismod |= S3C2410_IISMOD_MSB; // MSB format
 iismod |= S3C2410_IISMOD_16BIT; // Serial data bit/channel is 16 bit
 iismod |= S3C2410_IISMOD_384FS; // Master clock freq = 384 fs
 iismod |= S3C2410_IISMOD_32FS; // 32 fs

 iisfcon|= S3C2410_IISFCON_RXDMA; //Set RX FIFO acces mode to DMA
 iisfcon|= S3C2410_IISFCON_TXDMA; //Set TX FIFO acces mode to DMA

 iiscon |= S3C2410_IISCON_TXDMAEN; //Enable TX DMA service request
 iiscon |= S3C2410_IISCON_RXIDLE; //Set RX channel idle
 iismod |= S3C2410_IISMOD_TXMODE; //Set TX Mode
 iisfcon|= S3C2410_IISFCON_TXENABLE; //Enable TX Fifo


//setup the prescaler

 audio_set_dsp_speed(audio_rate);

printk("driver: init_s3c2410_iis_bus_tx audio_rate ok\n");

//iiscon has to be set last - it enables the interface
 //printk("driver: init_s3c2410_iis_bus_tx audio_set_dsp_speed");

 iowrite32(iismod, piis_regs_base + S3C2410_IISMOD);
 printk("driver: init_s3c2410_iis_bus_tx S3C2410_IISMOD");
 iowrite32(iisfcon, piis_regs_base + S3C2410_IISFCON);
 iowrite32(iiscon, piis_regs_base + S3C2410_IISCON);
 printk("driver: init_s3c2410_iis_bus_tx out ok\n");

#if 0
 __raw_writel(iismod, iis_base + S3C2410_IISMOD);
 printk("driver: init_s3c2410_iis_bus_tx S3C2410_IISMOD");
 __raw_writel(iisfcon, iis_base + S3C2410_IISFCON);
 __raw_writel(iiscon, iis_base + S3C2410_IISCON);
#endif
}

static ssize_t cz_audio_write(struct file *file, const char *buffer,
        size_t count, loff_t * ppos)
{
// mem to iis to uda1341
 printk("driver: cz_audio_write in ok\n");

 cz_dmainit(0, (unsigned long)buffer, count);
 printk("driver: cz_audio_write cz_dmainit ok\n");

 waitq_condition = 0;
 wait_event_interruptible(waitq, waitq_condition);
 printk("driver: cz_audio_write wait_event_interruptible ok\n");

 return 0;
}

static int cz_audio_open(struct inode *inode, struct file *file)
{
 printk("driver: cz_audio_open in ok\n");

 if ((file->f_mode & FMODE_WRITE)){
  printk("driver: cz_audio_open FMODE_WRITE ok\n");
  audio_rate = AUDIO_RATE_DEFAULT;
  init_s3c2410_iis_bus_tx();
  printk("driver: cz_audio_open init_s3c2410_iis_bus_tx ok\n");
  //audio_clear_buf(&output_stream);
 }
 if ((file->f_mode & FMODE_READ)){
  printk("driver: cz_audio_open FMODE_READ ok\n");
  audio_rate = AUDIO_RATE_DEFAULT;
  init_s3c2410_iis_bus_rx();
  printk("driver: cz_audio_open init_s3c2410_iis_bus_rx ok\n");
  //audio_clear_buf(&input_stream);
 }

 return 0;
}

static struct file_operations smdk2410_audio_fops = {
 //llseek: smdk2410_audio_llseek,
 write: cz_audio_write,
 read: cz_audio_read,
 //poll: smdk2410_audio_poll,
 //ioctl: smdk2410_audio_ioctl,
 open: cz_audio_open,
 //release: smdk2410_audio_release
};

static struct file_operations smdk2410_mixer_fops = {
 //ioctl: smdk2410_mixer_ioctl,
 //open: smdk2410_mixer_open,
 //release: smdk2410_mixer_release
};

static int cz_uda1341_init(void) { 
 printk("driver: cz_uda1341_init in ok\n");
 unsigned long flags;
 
 iis_clock = clk_get(NULL, "iis");
 clk_enable(iis_clock);

 piis_regs_base     = ioremap(0x55000000,SZ_1M);
 
 pdma_regs_in_base  = ioremap(0x4B000040,SZ_1M);//DMA1
 pdma_regs_out_base = ioremap(0x4B000080,SZ_1M);//DMA2
 printk("driver: cz_uda1341_init pdma_regs_out_base ok\n");

 //iis & l3 pin set
 local_irq_save(flags);
 s3c2410_gpio_cfgpin(S3C2410_GPB4, S3C2410_GPB4_OUTP);  // GPB 4: L3CLOCK, OUTPUT
 s3c2410_gpio_pullup(S3C2410_GPB4,1);
 s3c2410_gpio_cfgpin(S3C2410_GPB3,S3C2410_GPB3_OUTP);   //GPB 3: L3DATA, OUTPUT
 s3c2410_gpio_cfgpin(S3C2410_GPB2,S3C2410_GPB2_OUTP);   //GPB 2: L3MODE, OUTPUT
 s3c2410_gpio_pullup(S3C2410_GPB2,1);
 s3c2410_gpio_cfgpin(S3C2410_GPE3,S3C2410_GPE3_I2SSDI); //GPE 3: I2SSDI
 s3c2410_gpio_pullup(S3C2410_GPE3,0);
 s3c2410_gpio_cfgpin(S3C2410_GPE0,S3C2410_GPE0_I2SLRCK);//GPE 0: I2SLRCK
 s3c2410_gpio_pullup(S3C2410_GPE0,0);
 s3c2410_gpio_cfgpin(S3C2410_GPE1,S3C2410_GPE1_I2SSCLK);//GPE 1: I2SSCLK
 s3c2410_gpio_pullup(S3C2410_GPE1,0);
 s3c2410_gpio_cfgpin(S3C2410_GPE2,S3C2410_GPE2_CDCLK);  //GPE 2: CDCLK
 s3c2410_gpio_pullup(S3C2410_GPE2,0);
 s3c2410_gpio_cfgpin(S3C2410_GPE4,S3C2410_GPE4_I2SSDO); //GPE 4: I2SSDO
 s3c2410_gpio_pullup(S3C2410_GPE4,0);
 local_irq_restore(flags);
 
 printk("driver: cz_uda1341_init local_irq_restore ok\n");

 //init_s3c2410_iis_bus
 __raw_writel(0, piis_regs_base + S3C2410_IISPSR);
 __raw_writel(0, piis_regs_base + S3C2410_IISCON);
 __raw_writel(0, piis_regs_base + S3C2410_IISMOD);
 __raw_writel(0, piis_regs_base + S3C2410_IISFCON);
 printk("driver: cz_uda1341_init __raw_writel ok\n");

 //init_uda1341
 local_irq_save(flags);
 s3c2410_gpio_setpin(S3C2410_GPB2,1);//L3MODE=1
 s3c2410_gpio_setpin(S3C2410_GPB4,1);//L3CLOCK=1
 local_irq_restore(flags);
 //uda1341 chip address is : 000101xx
 //xx = 00 means DATA0 mode;   xx = 01 means DATA1 mode;
 //xx = 10 means STATUS mode; xx = 11 means not used;
 //STATUS mode selected
 czuda1341_l3_address(UDA1341_REG_STATUS);//0x14+2 = 0x16 = 000101 10
 //0x40 = 0100 0000, STATUS control 1/2
 czuda1341_l3_data(0x40 | STAT0_SC_384FS | STAT0_IF_MSB|STAT0_DC_FILTER); // reset; 384fs; MSB-justified; DC-filtering
 //0x80 = 1000 0000, STATUS control 2/2
 czuda1341_l3_data(STAT1 | STAT1_ADC_ON | STAT1_DAC_ON);//adc on; dac on
    //DATA0 mode selected
 czuda1341_l3_address(UDA1341_REG_DATA0);//0x14+0 = 000101 00
 //DATA0-> bit7:bit6=00
 czuda1341_l3_data(DATA0 |DATA0_VOLUME(0x0)); // maximum volume
 //DATA1-> bit7:bit6=01
 czuda1341_l3_data(DATA1 |DATA1_BASS(0)| DATA1_TREBLE(0));//BB = 0; TR = 0
 //DATA2-> bit7:bit6=10
 czuda1341_l3_data((DATA2 |DATA2_DEEMP_NONE) &~(DATA2_MUTE));//DE = 0; MT = 0, no mute
 //EXTADDR:0xc0=11000  000, means extended address(EA) mode selected
 //EXT2:2=0  010, means MS2:MS1:MS0:MM1:MM0 registers  selected
 czuda1341_l3_data(EXTADDR(EXT2));
 //EXTDATA:0xe0=111  00000, means extended data(ED) mode selected
 //EXT2_MIC_GAIN(0x6)=0x6<<2=0110<<2=0  110  00, means MS2:MS1:MS0=110, means MIC AMPLIFIER GAIN = +27dB
 //EXT2_MIXMODE_CH1=01, means MM1:MM0=01, means input channel 1 select(input channel 2 off) 
 czuda1341_l3_data(EXTDATA(EXT2_MIC_GAIN(0x6)) | EXT2_MIXMODE_CH1);
 printk("driver: cz_uda1341_init czuda1341_l3_data ok\n");

 audio_dev_dsp = register_sound_dsp(&smdk2410_audio_fops, -1);
 audio_dev_mixer = register_sound_mixer(&smdk2410_mixer_fops, -1);
 printk("driver: cz_uda1341_init audio_dev_mixer ok\n");

#if 0
 major = register_chrdev(0, "czuda1341", &czuda1341_fops);
 pczuda1341_cls = class_create(THIS_MODULE, "czuda1341_cls");
 pczuda1341_cls_dev = class_device_create(pczuda1341_cls, NULL, MKDEV(major, 0), NULL, "czuda1341_cls_dev");
#endif

 return 0;
}

static void  cz_uda1341_exit(void) {
 printk("driver: cz_uda1341_exit in ok\n");
 
 unregister_sound_dsp(audio_dev_dsp);
 unregister_sound_mixer(audio_dev_mixer);
 //audio_clear_dma(&output_stream,&s3c2410iis_dma_out);
 //audio_clear_dma(&input_stream,&s3c2410iis_dma_in); /* input */
 
 iounmap(pdma_regs_out_base);
 iounmap(pdma_regs_in_base);
 iounmap(piis_regs_base);
 
 clk_disable(iis_clock);

}

module_init(cz_uda1341_init);
module_exit(cz_uda1341_exit);
MODULE_AUTHOR("chaozang(cz)<zangchao.cn@gmail.com>");
MODULE_DESCRIPTION("TQ2440 uda1341 sound driver");
MODULE_LICENSE("GPL");

----------------------------------------------------test commands----------------------------------------------------------

#cat xxx.wav > /dev/dsp

#cat /dev/dsp > sound.bin

#cat sound.bin > /dev/dsp

-----------------------------------------------------------

Contact: zangchao.cn@gmail.com

 

原创粉丝点击