/*
* 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