www.pudn.com > 2410audio.rar > 2410audio.c


/*
 * Philips UDA1380 Audio Device Driver for S3C2410 Linux
 *
 * Copyright (C) 2002 MIZI Research, Inc.
 *
 */

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#include 
#include 
#include 
#include 
#include 
#include 

#include "khead.h"
#include "2410audio.h"
#define UDA1380
#define PDEBUG
#undef DEBUG
//#define DEBUG
#ifdef DEBUG
#define DPRINTK( x... )  printk( ##x )
#else
#define DPRINTK( x... )
#endif


/* UDA1380 Register bits */
#define UDA1380_ADDR		0x14

#define UDA1380_REG_DATA	0x0
#define UDA1380_REG_STATUS	0x2

#define SC_512fs		(0x0 << 4)
#define SC_384fs		(0x1 << 4)
#define SC_256fs		(0x2 << 4)

#define IF_IIS			(0x0 << 1)
#define IF_LSB_16		(0x1 << 1)
#define IF_LSB_18		(0x2 << 1)
#define IF_LSB_20		(0x3 << 1)
#define IF_MSB			(0x4 << 1)

#define MUTE		        (0x1 << 2)

#define NO_DE_EMPHASIS		(0x10 << 3)
#define DE_EMPHASIS_32		(0x11 << 3)
#define DE_EMPHASIS_441		(0x12 << 3)
#define DE_EMPHASIS_48		(0x13 << 3)

#define GPIO_L3CLOCK            (GPIO_MODE_OUT | GPIO_PULLUP_DIS | GPIO_B4)
#define GPIO_L3DATA             (GPIO_MODE_OUT | GPIO_PULLUP_DIS | GPIO_B3)
#define GPIO_L3MODE             (GPIO_MODE_OUT | GPIO_PULLUP_DIS | GPIO_B2)

#define AUDIO_NAME		"UDA1380"
#define AUDIO_NAME_VERBOSE	"UDA1380 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	22050 //8000

#define AUDIO_NBFRAGS_DEFAULT	8 //16
#define AUDIO_FRAGSIZE_DEFAULT	8192

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

#define IICBUFSIZE 0x20
#define BIT_IIC        (0x1<<27)

#define rGPECON    (*(volatile U32 *)r_GPECON) //Port E control
#define rGPEUP     (*(volatile U32 *)r_GPEUP) //Pull-up control E
#define rIICCON  (*(volatile unsigned char *)r_IICCON) //IIC control
#define rIICSTAT (*(volatile unsigned char *)r_IICSTAT) //IIC status
#define rIICADD  (*(volatile unsigned char *)r_IICADD) //IIC address
#define rIICDS   (*(volatile unsigned char *)r_IICDS) //IIC data shift

#define WRDATA      (1)
#define POLLACK     (2)
#define RDDATA      (3)
#define SETRDADDR   (4)

#define Uart_Printf PDEBUG
extern int uda1380_init(void);
#define U8 unsigned char
#define U32 unsigned int
static unsigned char _iicData[IICBUFSIZE];
static volatile int _iicDataCount;
static volatile int _iicStatus;
static volatile int _iicMode;
static int _iicPt;

typedef struct {
	int size;		/* buffer size */
	char *start;		/* point to actual buffer */
	dma_addr_t dma_addr;	/* physical buffer address */
	struct semaphore sem;	/* down before touching the buffer */
	int master;		/* owner for buffer allocation, contain size when true */
} audio_buf_t;

typedef struct {
	audio_buf_t *buffers;	/* pointer to audio buffer structures */
	audio_buf_t *buf;	/* current buffer used by read/write */
	u_int buf_idx;		/* index for the pointer above */
	u_int fragsize;		/* fragment i.e. buffer size */
	u_int nbfrags;		/* nbr of fragments */
	dmach_t dma_ch;		/* DMA channel (channel2 for audio) */
} audio_stream_t;

static audio_stream_t output_stream;
static audio_stream_t input_stream;
 
#define NEXT_BUF(_s_,_b_) { \
        (_s_)->_b_##_idx++; \
        (_s_)->_b_##_idx %= (_s_)->nbfrags; \
        (_s_)->_b_ = (_s_)->buffers + (_s_)->_b_##_idx; }

extern int uda1380_init(void);
static u_int audio_rate;
static int audio_channels;
static int audio_fmt;
static u_int audio_fragsize;
static u_int audio_nbfrags;


static int audio_rd_refcount;
static int audio_wr_refcount;
#define audio_active		(audio_rd_refcount | audio_wr_refcount)

static int audio_dev_dsp;
static int audio_dev_mixer;
static int audio_mix_modcnt;

static int uda1380_volume;
static u8 uda_sampling;

U32 r_GPECON,r_GPEUP;
U32 r_IICCON,r_IICSTAT,r_IICADD,r_IICDS;
//       SMDK2410 IIC configuration
//  GPE15=IICSDA, GPE14=IICSCL
//  "Interrupt mode" for IIC block
//=================================================================== 
int address_map(void)
{
	r_GPECON	=__ioremap(0x56000040,4,0);
	r_GPEUP		=__ioremap(0x56000048,4,0);
	r_IICCON	=__ioremap(0x54000000,4,0);
	r_IICSTAT	=__ioremap(0x54000004,4,0);
	r_IICADD	=__ioremap(0x54000008,4,0);
	r_IICDS		=__ioremap(0x5400000c,4,0);

	return 0;
}

void Delay(int times)
{
	udelay(1000);
}
int Test_Iic2(void)
{
    unsigned int i,j,save_E,save_PE;
        unsigned int upll, uclk, camclk,camclk_div;
    static U32 data;
    static U32 rdata;
    static U32 wdata;
	address_map();

    save_E   = rGPECON;
    save_PE  = rGPEUP;

    rGPEUP  |= 0xc000;                  //Pull-up disable
    rGPECON |= 0xa00000;                //GPE15:IICSDA , GPE14:IICSCL    
      //Enable ACK, Prescaler IICCLK=PCLK/16, Enable interrupt, Transmit clock value Tx clock=IICCLK/16
    rIICCON  = (1<<7) | (0<<6) | (1<<5) | (0xf);

    rIICADD  = 0x10;                    //2410 slave address = [7:1]
    rIICSTAT = 0x10;                    //IIC bus data output enable(Rx/Tx)

#if 1
        wdata = 0xaa55;
      	i=0x11;	
        _Wr24C080(0x30,(U8)i,wdata);
	printk("the date write is: %x\n",wdata);

       _Rd24C080(0x31,(U8)i,&(rdata)); 
    	printk("the read data is: %x \n",rdata);
#endif
    rGPEUP  = save_PE;
    rGPECON = save_E;
	return 0;
}

//**************[ _Wr24C080 ]*****************************************
void _Wr24C080(U32 slvAddr,U32 addr,U32 wdata)
{
    _iicMode      = WRDATA;
    _iicPt        = 0;
    _iicData[0]   = (U8)addr;
    _iicData[1]   = wdata/256;
    _iicData[2]   = wdata;
    _iicDataCount = 3;
    Uart_Printf("MSB is: %2x \n",_iicData[1]);
    Uart_Printf("LSB is: %2x \n",_iicData[2]);
    rIICDS        = slvAddr&0xfe;            //0x30
      //Master Tx mode, Start(Write), IIC-bus data output enable
      //Bus arbitration sucessful, Address as slave status flag Cleared,
      //Address zero status flag cleared, Last received bit is 0
    rIICSTAT      = 0xf0;      
      //Clearing the pending bit isn't needed because the pending bit has been cleared.
    while(_iicDataCount!=-1)
       Run_IicPoll();

    _iicMode = POLLACK;
    while(1)
    {
        rIICDS     =slvAddr&0xfe;
        _iicStatus = 0x100;             //To check if _iicStatus is changed 
        rIICSTAT   = 0xf0;              //Master Tx, Start, Output Enable, Sucessful, Cleared, Cleared, 0
        rIICCON    = 0xaf;              //Resumes IIC operation. 
	while(_iicStatus==0x100)  
            Run_IicPoll();
              
        if(!(_iicStatus & 0x1))
	PDEBUG("ACK is received\n");
            break;                      //When ACK is received
    }
    rIICSTAT = 0xd0;                    //Master Tx condition, Stop(Write), Output Enable
    rIICCON  = 0xaf;                    //Resumes IIC operation. 
    Delay(1);                           //Wait until stop condtion is in effect.
      //Write is completed.
}
        
//************************[ _Rd24C080 ]********************************
void _Rd24C080(U32 slvAddr,U32 addr,U32 *rdata)
{
	PDEBUG("read---->0\n");
    _iicMode      = SETRDADDR;
    _iicPt        = 0;
    _iicData[0]   = (U8)addr;
    _iicDataCount = 1;
	PDEBUG("read---->1\n");
    
    rIICDS   = slvAddr&0xfe;			//0x31
    rIICSTAT = 0xf0;                    //MasTx,Start  
      //Clearing the pending bit isn't needed because the pending bit has been cleared.
	PDEBUG("read---->2\n");
    while(_iicDataCount!=-1)
        Run_IicPoll();
	
    _iicMode      = RDDATA;
    _iicPt        = 0;
    _iicDataCount = 2;
    
    rIICDS   = slvAddr|0x01;
    rIICSTAT = 0xb0;                    //Master Rx,Start
    rIICCON  = 0xaf;                    //Resumes IIC operation.  
     while(_iicDataCount!=-1)
        Run_IicPoll();

    *rdata = _iicData[1]*256+_iicData[2];
//    Uart_Printf("the read date is: %2x \n",_iicData[1]);
//    Uart_Printf("the read date is: %2x \n",_iicData[2]);
	
}
//**********************[ Run_IicPoll ]*********************************
void Run_IicPoll(void)
{
    if(rIICCON & 0x10)                  //Tx/Rx Interrupt Enable
       IicPoll();
}       
    
//**********************[IicPoll ]**************************************
void IicPoll(void)
{
    U32 iicSt,i;
    iicSt = rIICSTAT; 
    if(iicSt & 0x8){
	PDEBUG("bus arbitration is failed\n");
			}   //When bus arbitration is failed.
    if(iicSt & 0x4){
	PDEBUG("matched\n");
	}                   //When a slave address is matched with IICADD
    if(iicSt & 0x2){
	PDEBUG("slave address 0000000b\n");
	}                   //When a slave address is 0000000b
    if(iicSt & 0x1){
	PDEBUG("Ack isn't received\n");
	}                   //When ACK isn't received

    switch(_iicMode)
    {
        case POLLACK:
		PDEBUG("poll--ACK\n");
            _iicStatus = iicSt;
            break;

        case RDDATA:
	//	PDEBUG("read--ACK\n");
            if((_iicDataCount--)==0)
            {
                _iicData[_iicPt++] = rIICDS;
            
                rIICSTAT = 0x90;                //Stop MasRx condition 
                rIICCON  = 0xaf;                //Resumes IIC operation.
                Delay(1);                       //Wait until stop condtion is in effect.
                                                //Too long time... 
                                                //The pending bit will not be set after issuing stop condition.
                break;    
            }      
            _iicData[_iicPt++] = rIICDS;
                        //The last data has to be read with no ack.
            if((_iicDataCount)==0)
                rIICCON = 0x2f;                 //Resumes IIC operation with NOACK.  
            else 
                rIICCON = 0xaf;                 //Resumes IIC operation with ACK
            break;

        case WRDATA:
	//	PDEBUG("write--ACK\n");
            if((_iicDataCount--)==0)
            {
                rIICSTAT = 0xd0;                //stop MasTx condition 
                rIICCON  = 0xaf;                //resumes IIC operation.
                Delay(1);                       //wait until stop condtion is in effect.
                       //The pending bit will not be set after issuing stop condition.
                break;    
            }
		PDEBUG("poll--->write\n");
            rIICDS = _iicData[_iicPt++];        //_iicData[0] has dummy.
            for(i=0;i<10;i++);                  //for setup time until rising edge of IICSCL
            rIICCON = 0xaf;                     //resumes IIC operation.
            break;

        case SETRDADDR:
//            Uart_Printf("IicPoll() SETRADDR [S%d]\n",_iicDataCount);
	//	PDEBUG("SETRDADDR\n");
            if((_iicDataCount--)==0)
            {
                break;                  //IIC operation is stopped because of IICCON[4]    
            }
            rIICDS = _iicData[_iicPt++];
            for(i=0;i<10;i++);          //for setup time until rising edge of IICSCL
            rIICCON = 0xaf;             //resumes IIC operation.
            break;

        default:
            break;      
    }
}

unsigned char command[] = {
	0x00, 0x0F, 0x02, //Sysclock
	//0x00, 0x0F, 0x32,	//WSPLL 44.1KHz
	0x01, 0x00, 0x00,
	0x02, 0xaf, 0xff,	//line in
	0x03, 0x00, 0x00,
	0x04, 0x02, 0x02,   //default
	0x10, 0x00, 0x00,
	0x11, 0x00, 0x00,
	0x12, 0x00, 0x00,
	0x13, 0x00, 0x00,
	0x14, 0x00, 0x00,
	0x20, 0x00, 0x00,
	0x21, 0x00, 0x00,
	//0x22, 0x00, 0x00, 	//line-in
	0x22, 0x00, 0x0C,		//mic
	0x23, 0x00, 0x00
};
int uda1380_init(void)
{
    unsigned int i,j,save_E,save_PE;
    static U32 data;
    static U32 rdata;
    static U32 wdata;
	address_map();
    save_E   = rGPECON;
    save_PE  = rGPEUP;
    rGPEUP  |= 0xc000;                  //Pull-up disable
    rGPECON |= 0xa0000000;                //GPE15:IICSDA , GPE14:IICSCL    
      //Enable ACK, Prescaler IICCLK=PCLK/16, Enable interrupt, Transmit clock value Tx clock=IICCLK/16
    rIICCON  = (1<<7) | (0<<6) | (1<<5) | (0xf);

    rIICADD  = 0x10;                    //2410 slave address = [7:1]
    rIICSTAT = 0x10;     
    Test_Iic2();               //IIC bus data output enable(Rx/Tx)
#if 0
        wdata = 0xba65;
      	i=0x10;	
        _Wr24C080(0x30,(U8)i,wdata);
	printk("the date write is: %x\n",wdata);
       _Rd24C080(0x31,(U8)i,&(rdata)); 
    	printk("the read data is: %x \n",rdata);
#endif

{
	int ic;
	unsigned int msb, lsb, data, reg;
	for (ic = 0; ic < 42; ic += 3) {
		reg = command[ic];
		msb = command[ic + 1];
		lsb = command[ic + 2];
		data = msb << 8 | lsb;
        	_Wr24C080(0x30,(U8)reg,data);

	}
	printk("Init uda1380 finished.\n");
}
    rGPEUP  = save_PE;
    rGPECON = save_E;

	return 0;
}

static void audio_clear_buf(audio_stream_t * s)
{
    	DPRINTK("audio_clear_buf\n");

	s3c2440_dma_flush_all(s->dma_ch);

	if (s->buffers) {
		int frag;

		for (frag = 0; frag < s->nbfrags; frag++) {
			if (!s->buffers[frag].master)
				continue;
			consistent_free(s->buffers[frag].start,
					s->buffers[frag].master,
					s->buffers[frag].dma_addr);
		}
		kfree(s->buffers);
		s->buffers = NULL;
	}

	s->buf_idx = 0;
	s->buf = NULL;
}

static int audio_setup_buf(audio_stream_t * s)
{
	int frag;
	int dmasize = 0;
	char *dmabuf = 0;
	dma_addr_t dmaphys = 0;

	if (s->buffers)
		return -EBUSY;

	s->nbfrags = audio_nbfrags;
	s->fragsize = audio_fragsize;

	s->buffers = (audio_buf_t *)
	    kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL);
	if (!s->buffers)
		goto err;
	memset(s->buffers, 0, sizeof(audio_buf_t) * s->nbfrags);

	for (frag = 0; frag < s->nbfrags; frag++) {
		audio_buf_t *b = &s->buffers[frag];

		if (!dmasize) {
			dmasize = (s->nbfrags - frag) * s->fragsize;
			do {
				dmabuf = consistent_alloc(GFP_KERNEL|GFP_DMA,
							  dmasize, &dmaphys);
				if (!dmabuf) 
				    	dmasize -= s->fragsize;
			} while (!dmabuf && dmasize);
			if (!dmabuf)
				goto err;
			b->master = dmasize;
		}

		b->start = dmabuf;
		b->dma_addr = dmaphys;
		sema_init(&b->sem, 1);
		DPRINTK("buf %d: start %p dma %p\n", frag, b->start,
			b->dma_addr);

		dmabuf += s->fragsize;
		dmaphys += s->fragsize;
		dmasize -= s->fragsize;
	}

	s->buf_idx = 0;
	s->buf = &s->buffers[0];
	return 0;

      err:
	printk(AUDIO_NAME ": unable to allocate audio memory\n ");
	audio_clear_buf(s);
	return -ENOMEM;
}

static void audio_dmaout_done_callback(void *buf_id, int size)
{
	audio_buf_t *b = (audio_buf_t *) buf_id;

	DPRINTK("DMA-OUT%d\n", size);
	up(&b->sem);
	wake_up(&b->sem.wait);
}

static void audio_dmain_done_callback(void *buf_id, int size)
{
        audio_buf_t *b = (audio_buf_t *) buf_id;

       	DPRINTK("DMA-IN%d\n", size);
	b->size = size;
        up(&b->sem);
        wake_up(&b->sem.wait);
}

static int audio_sync(struct file *file)
{
	audio_stream_t *s = &output_stream;
	audio_buf_t *b = s->buf;

	DPRINTK("audio_sync\n");

	if (!s->buffers)
		return 0;

	if (b->size != 0) {
		down(&b->sem);
		s3c2440_dma_queue_buffer(s->dma_ch, (void *) b,
					 b->dma_addr, b->size, DMA_BUF_WR);
		b->size = 0;
		NEXT_BUF(s, buf);
	}

	b = s->buffers + ((s->nbfrags + s->buf_idx - 1) % s->nbfrags);
	if (down_interruptible(&b->sem))
		return -EINTR;
	up(&b->sem);

	return 0;
}

static inline int copy_from_user_mono_stereo(char *to, const char *from, int count)
{
	u_int *dst = (u_int *)to;
	const char *end = from + count;

	if (verify_area(VERIFY_READ, from, count))
		return -EFAULT;

	if ((int)from & 0x2) {
		u_int v;
		__get_user(v, (const u_short *)from); from += 2;
		*dst++ = v | (v << 16);
	}

	while (from < end-2) {
		u_int v, x, y;
		__get_user(v, (const u_int *)from); from += 4;
		x = v << 16;
		x |= x >> 16;
		y = v >> 16;
		y |= y << 16;
		*dst++ = x;
		*dst++ = y;
	}

	if (from < end) {
		u_int v;
		__get_user(v, (const u_short *)from);
		*dst = v | (v << 16);
	}

	return 0;
}


static ssize_t smdk2440_audio_write(struct file *file, const char *buffer, 
				    size_t count, loff_t * ppos)
{
	const char *buffer0 = buffer;
	audio_stream_t *s = &output_stream;
	int chunksize, ret = 0;

	DPRINTK("audio_write : start count=%d\n", count);

	switch (file->f_flags & O_ACCMODE) {
	  	case O_WRONLY:
	  	case O_RDWR:
			break;
	  	default:
		  	return -EPERM;
	}

	if (!s->buffers && audio_setup_buf(s))
		return -ENOMEM;

	count &= ~0x03;

	while (count > 0) {
		audio_buf_t *b = s->buf;

		if (file->f_flags & O_NONBLOCK) {
			ret = -EAGAIN;
			if (down_trylock(&b->sem))
				break;
		} else {
			ret = -ERESTARTSYS;
			if (down_interruptible(&b->sem))
				break;
		}

		if (audio_channels == 2) {
			chunksize = s->fragsize - b->size;
			if (chunksize > count)
				chunksize = count;
			DPRINTK("write %d to %d\n", chunksize, s->buf_idx);
			if (copy_from_user(b->start + b->size, buffer, chunksize)) {
				up(&b->sem);
				return -EFAULT;
			}
			b->size += chunksize;
		} else {
			chunksize = (s->fragsize - b->size) >> 1;

			if (chunksize > count)
				chunksize = count;
			DPRINTK("write %d to %d\n", chunksize*2, s->buf_idx);
			if (copy_from_user_mono_stereo(b->start + b->size, 
				    		       buffer, chunksize)) {
				up(&b->sem);
				return -EFAULT;
			}

			b->size += chunksize*2;
		}

		buffer += chunksize;
		count -= chunksize;
		if (b->size < s->fragsize) {
			up(&b->sem);
			break;
		}

		s3c2440_dma_queue_buffer(s->dma_ch, (void *) b,
					   b->dma_addr, b->size, DMA_BUF_WR);
		b->size = 0;
		NEXT_BUF(s, buf);
	}

	if ((buffer - buffer0))
		ret = buffer - buffer0;

	DPRINTK("audio_write : end count=%d\n\n", ret);

	return ret;
}


static ssize_t smdk2440_mixer_read(struct file *file, char *buffer, 
                                        size_t count, loff_t * ppos)
{
        char *buffer0 = buffer;
        audio_stream_t *s = &input_stream;
        int chunksize, ret = 0;

        DPRINTK("audio_read: count=%d\n", count);

        switch (file->f_flags & O_ACCMODE) {
        case O_RDONLY:
        case O_RDWR:
                break;
        default:
                return -EPERM;
        }

        if (!s->buffers && audio_setup_buf(s))
                return -ENOMEM;

        count &= ~0x03;

        while (count > 0) {
        	audio_buf_t *b = s->buf;

                /* Wait for a buffer to become full */
                if (file->f_flags & O_NONBLOCK) {
                        ret = -EAGAIN;
                        if (down_trylock(&b->sem))
                                break;
                } else {
                        ret = -ERESTARTSYS;
                        if (down_interruptible(&b->sem))
                                break;
                }

                /* Grab data from the current buffer */
                chunksize = b->size;
                if (chunksize > count)
                        chunksize = count;//************add*******
                DPRINTK("read %d from %d\n", chunksize,
                                s->buf_idx);
                if (copy_to_user
                    (buffer, b->start + s->fragsize - b->size,
                     chunksize)) {
                        up(&b->sem);
                        return -EFAULT;
                }

                b->size -= chunksize;
                buffer += chunksize;
                count -= chunksize;
                if (b->size > 0) {
                        up(&b->sem);
                        break;
                }

                /* Make current buffer available for DMA again */
                DPRINTK("b->dma_addr %x\n",b->dma_addr);
                s3c2440_dma_queue_buffer(s->dma_ch, (void *) b,
                                         b->dma_addr, s->fragsize,
                                         DMA_BUF_RD);
                NEXT_BUF(s, buf);
        }

        if ((buffer - buffer0))
                ret = buffer - buffer0;
        DPRINTK("audio_read: return=%d\n", ret);
        return ret;

}


static unsigned int smdk2440_audio_poll(struct file *file, 
					struct poll_table_struct *wait)
{
	unsigned int mask = 0;
	int i;

	DPRINTK("audio_poll(): mode=%s\n",
		(file->f_mode & FMODE_WRITE) ? "w" : "");

	if (file->f_mode & FMODE_WRITE) {
		if (!output_stream.buffers && audio_setup_buf(&output_stream))
			return -ENOMEM;
		poll_wait(file, &output_stream.buf->sem.wait, wait);

		for (i = 0; i < output_stream.nbfrags; i++) {
			if (atomic_read(&output_stream.buffers[i].sem.count) > 0)
				mask |= POLLOUT | POLLWRNORM;
		}
	}

	DPRINTK("audio_poll() returned mask of %s\n",
		(mask & POLLOUT) ? "w" : "");

	return mask;
}


static loff_t smdk2440_audio_llseek(struct file *file, loff_t offset, 
				    int origin)
{
            return -ESPIPE;
}


static int smdk2440_mixer_ioctl(struct inode *inode, struct file *file, 
                                unsigned int cmd, U32 arg)
{
	int ret;
	long val = 0;
	switch (cmd) {
		case SOUND_MIXER_INFO:
		{
			mixer_info info;
			strncpy(info.id, "UDA1380", sizeof(info.id));
			strncpy(info.name,"Philips UDA1380", sizeof(info.name));
			info.modify_counter = audio_mix_modcnt;
			return copy_to_user((void *)arg, &info, sizeof(info));
		}
	
		case SOUND_OLD_MIXER_INFO:
		{
			_old_mixer_info info;
			strncpy(info.id, "UDA1380", sizeof(info.id));
			strncpy(info.name,"Philips UDA1380", sizeof(info.name));
			return copy_to_user((void *)arg, &info, sizeof(info));
		}

		case SOUND_MIXER_READ_STEREODEVS:
			return put_user(0, (long *) arg);

		case SOUND_MIXER_READ_CAPS:
			val = SOUND_CAP_EXCL_INPUT;
			return put_user(val, (long *) arg);
		case SOUND_MIXER_WRITE_VOLUME:
			ret = get_user(val, (long *) arg);
			if (ret)
				return ret;
			val &= 0xFF;	
			uda1380_volume = (val << 8) | val;
			_Wr24C080(0x30,0x10,(unsigned int)uda1380_volume);
			break;
		case SOUND_MIXER_READ_VOLUME:
			val = uda1380_volume;
			val &= 0xFF;
			return put_user(val, (long *) arg);
		case SOUND_MIXER_VOLUME:
			ret = get_user(val, (long *) arg);
			if (ret)
				return ret;
			_Wr24C080(0x30,0x10,(unsigned int)val);
			break;
		default:
			DPRINTK("mixer ioctl %u unknown\n", cmd);
			return -ENOSYS;
	}			
	audio_mix_modcnt++;
	return 0;
}


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

 	tmpval384 = s3c2440_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;
                }
        }

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

        return prescaler;
}

static long audio_set_dsp_speed(long val)
{
	printk("val = %ld\n",val);
	switch (val) {
		case 48000:
		case 44100:
			IISPSR = (IISPSR_A(iispsr_value(S_CLOCK_FREQ, 44100)) 
				| IISPSR_B(iispsr_value(S_CLOCK_FREQ, 44100)));
			break;
		case 22050:
			IISPSR = (IISPSR_A(iispsr_value(S_CLOCK_FREQ, 22050)) 
				| IISPSR_B(iispsr_value(S_CLOCK_FREQ, 22050)));
			break;
		case 11025:
			IISPSR = (IISPSR_A(iispsr_value(S_CLOCK_FREQ, 11025)) 
				| IISPSR_B(iispsr_value(S_CLOCK_FREQ, 11025)));
			break;
		case 8000:
			IISPSR = (IISPSR_A(iispsr_value(S_CLOCK_FREQ, 8000)) 
				| IISPSR_B(iispsr_value(S_CLOCK_FREQ, 8000)));
			break;
		default:
			return -1;
	}

	audio_rate = val;
	
	return audio_rate;
}

static int smdk2440_audio_ioctl(struct inode *inode, struct file *file, 
                                uint cmd, ulong arg)
{
	long val;

	switch (cmd) {
	  	case SNDCTL_DSP_SETFMT:
			get_user(val, (long *) arg);
		  	if (val & AUDIO_FMT_MASK) {
			    	audio_fmt = val;
			    	break;
		  	} else
				return -EINVAL;

	  	case SNDCTL_DSP_CHANNELS:
	  	case SNDCTL_DSP_STEREO:
		  	get_user(val, (long *) arg);
		  	if (cmd == SNDCTL_DSP_STEREO)
			  	val = val ? 2 : 1;
		  	if (val != 1 && val != 2)
			  	return -EINVAL;
		  	audio_channels = val;
		  	break;

	  	case SOUND_PCM_READ_CHANNELS:
		  	put_user(audio_channels, (long *) arg);
		 	break;

	  	case SNDCTL_DSP_SPEED:
		  	get_user(val, (long *) arg);
		  	val = audio_set_dsp_speed(val);
            if (val < 0) 
				return -EINVAL;
		  	put_user(val, (long *) arg);
		  	break;

	  	case SOUND_PCM_READ_RATE:
		  	put_user(audio_rate, (long *) arg);
		  	break;

	  	case SNDCTL_DSP_GETFMTS:
		  	put_user(AUDIO_FMT_MASK, (long *) arg);
		  	break;

	  	case SNDCTL_DSP_GETBLKSIZE:
		  	put_user(audio_fragsize, (long *) arg);
		  	break;

	  	case SNDCTL_DSP_SETFRAGMENT:
		  	if (output_stream.buffers)
			  	return -EBUSY;
		  	get_user(val, (long *) arg);
		  	audio_fragsize = 1 << (val & 0xFFFF);
		  	if (audio_fragsize < 16)
			  	audio_fragsize = 16;
		  	if (audio_fragsize > 16384)
			  	audio_fragsize = 16384;
		  	audio_nbfrags = (val >> 16) & 0x7FFF;
			if (audio_nbfrags < 2)
				audio_nbfrags = 2;
		  	if (audio_nbfrags * audio_fragsize > 128 * 1024)
			  	audio_nbfrags = 128 * 1024 / audio_fragsize;
		  	if (audio_setup_buf(&output_stream))
			  	return -ENOMEM;
		  	break;

	  	case SNDCTL_DSP_SYNC:
		  	return audio_sync(file);

	  	case SNDCTL_DSP_GETOSPACE:
		{
			audio_stream_t *s = &output_stream;
			audio_buf_info *inf = (audio_buf_info *) arg;
			int err = verify_area(VERIFY_WRITE, inf, sizeof(*inf));
			int i;
			int frags = 0, bytes = 0;

			if (err)
				return err;
			for (i = 0; i < s->nbfrags; i++) {
				if (atomic_read(&s->buffers[i].sem.count) > 0) {
					if (s->buffers[i].size == 0) frags++;
					bytes += s->fragsize - s->buffers[i].size;
				}
			}
			put_user(frags, &inf->fragments);
			put_user(s->nbfrags, &inf->fragstotal);
			put_user(s->fragsize, &inf->fragsize);
			put_user(bytes, &inf->bytes);
			break;
		}

	  	case SNDCTL_DSP_RESET:
		  	switch (file->f_flags & O_ACCMODE) {
		    		case O_RDONLY:
		    		case O_RDWR:
		    	}
		  	switch (file->f_flags & O_ACCMODE) {
		    		case O_WRONLY:
		    		case O_RDWR:
			    		audio_clear_buf(&output_stream);
		    	}
		  	return 0;

	 	case SNDCTL_DSP_POST:
	      	case SNDCTL_DSP_SUBDIVIDE:
	      	case SNDCTL_DSP_NONBLOCK:
	      	case SNDCTL_DSP_GETCAPS:
	      	case SNDCTL_DSP_GETTRIGGER:
	      	case SNDCTL_DSP_SETTRIGGER:
	      	case SNDCTL_DSP_GETIPTR:
	      	case SNDCTL_DSP_GETOPTR:
	      	case SNDCTL_DSP_MAPINBUF:
	      	case SNDCTL_DSP_MAPOUTBUF:
	      	case SNDCTL_DSP_SETSYNCRO:
	      	case SNDCTL_DSP_SETDUPLEX:
		  	return -ENOSYS;
	  	default:
		  	return smdk2440_mixer_ioctl(inode, file, cmd, arg);
	}

	return 0;
}


static int smdk2440_audio_open(struct inode *inode, struct file *file)
{
#if 0
	int cold = !audio_active;

	DPRINTK("audio_open\n");

	if ((file->f_flags & O_ACCMODE) == O_RDONLY) {
		if (audio_rd_refcount || audio_wr_refcount)
			return -EBUSY;
		audio_rd_refcount++;
	} else if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
		if (audio_wr_refcount)
			return -EBUSY;
		audio_wr_refcount++;
	} else if ((file->f_flags & O_ACCMODE) == O_RDWR) {
		if (audio_rd_refcount || audio_wr_refcount)
			return -EBUSY;
		audio_rd_refcount++;
		audio_wr_refcount++;
	} else
		return -EINVAL;

	if (cold) {
		audio_rate = AUDIO_RATE_DEFAULT;
		audio_channels = AUDIO_CHANNELS_DEFAULT;
		audio_fragsize = AUDIO_FRAGSIZE_DEFAULT;
		audio_nbfrags = AUDIO_NBFRAGS_DEFAULT;
		audio_clear_buf(&output_stream);
        audio_clear_buf(&input_stream);
 		//         audio_power_on();
	}
#endif
	MOD_INC_USE_COUNT;

	return 0;
}


static int smdk2440_mixer_open(struct inode *inode, struct file *file)
{
	MOD_INC_USE_COUNT;
	return 0;
}


static int smdk2440_audio_release(struct inode *inode, struct file *file)
{
	DPRINTK("audio_release\n");

	switch (file->f_flags & O_ACCMODE) {
	  	case O_RDONLY:
	  	case O_RDWR:
		  	if (audio_rd_refcount == 1)
			  	audio_rd_refcount = 0;
	}

	switch (file->f_flags & O_ACCMODE) {
	  	case O_WRONLY:
	  	case O_RDWR:
		  	if (audio_wr_refcount == 1) {
			    	audio_sync(file);
			    	audio_clear_buf(&output_stream);
			    	audio_wr_refcount = 0;
		    	}
	  	}
#if 0
	if (!audio_active)
		audio_power_off();
#endif
	MOD_DEC_USE_COUNT;

	return 0;
}


static int smdk2440_mixer_release(struct inode *inode, struct file *file)
{
	MOD_DEC_USE_COUNT;
	return 0;
}


static struct file_operations smdk2440_audio_fops = {
	llseek:		smdk2440_audio_llseek,
	write:		smdk2440_audio_write,
	poll:		smdk2440_audio_poll,
	ioctl:		smdk2440_audio_ioctl,
	open:		smdk2440_audio_open,
	release:	smdk2440_audio_release
};

static struct file_operations smdk2440_mixer_fops = {
	ioctl:		smdk2440_mixer_ioctl,
	open:		smdk2440_mixer_open,
	read:		smdk2440_mixer_read,
	release:	smdk2440_mixer_release
};

static void init_s3c2440_iis_bus(void)
{
        IISCON = 0;
        IISMOD = 0;
        IISFIFOC = 0;

    	/* 44 KHz , 384fs */
	IISPSR = (IISPSR_A(iispsr_value(S_CLOCK_FREQ, 44100)) 
		| IISPSR_B(iispsr_value(S_CLOCK_FREQ, 44100)));

	IISCON = (IISCON_TX_DMA		/* Transmit DMA service request */
		|IISCON_RX_DMA		/* Receive Channel idle */
		|IISCON_PRESCALE);	/* IIS Prescaler Enable */

 	IISMOD = (IISMOD_SEL_MA         /* Master mode */
                | IISMOD_SEL_TX         /* Transmit */
		| IISMOD_SEL_RX         /* Transmit */
		| IISMOD_CH_RIGHT       /* Low for left channel */
		| IISMOD_FMT_IIS        /* MSB-justified format */
		| IISMOD_BIT_16         /* Serial data bit/channel is 16 bit */
		| IISMOD_FREQ_384       /* Master clock freq = 384 fs */
		| IISMOD_SFREQ_32	);     /* 32 fs */

	IISFIFOC = (IISFCON_TX_DMA      /* Transmit FIFO access mode: DMA */
		| IISFCON_TX_EN
		| IISFCON_RX_DMA
		| IISFCON_RX_EN		);       /* Transmit FIFO enable */

	IISCON |= IISCON_EN;		/* IIS enable(start) */
}

static int __init audio_init_dma(audio_stream_t * s, char *desc)
{
	if(s->dma_ch == 1)
	{
	//	printk("s->dma_ch = %d\n",s->dma_ch);
		return s3c2440_request_dma("I2SSDI", s->dma_ch, NULL, audio_dmain_done_callback);
	}
	else
	{
	//	printk("s->dma_ch = %d\n",s->dma_ch);
		return s3c2440_request_dma("I2SSDO", s->dma_ch, audio_dmaout_done_callback, NULL);
	}
}

static int audio_clear_dma(audio_stream_t * s)
{
	s3c2440_free_dma(s->dma_ch);

	return 0;
}

int init_module1380(void)
{
	U32 flags;
        int cold = !audio_active;
	local_irq_save(flags);
	
	/* GPE 3: I2SSDI */
	set_gpio_ctrl(GPIO_E3 | GPIO_PULLUP_EN | GPIO_MODE_I2SSDI);
	/* GPE 0: I2SLRCK */
	set_gpio_ctrl(GPIO_E0 | GPIO_PULLUP_EN | GPIO_MODE_I2SSDI);
	/* GPE 1: I2SSCLK */
	set_gpio_ctrl(GPIO_E1 | GPIO_PULLUP_EN | GPIO_MODE_I2SSCLK);
	/* GPE 2: CDCLK */
	set_gpio_ctrl(GPIO_E2 | GPIO_PULLUP_EN | GPIO_MODE_CDCLK);
	/* GPE 4: I2SSDO */
	set_gpio_ctrl(GPIO_E4 | GPIO_PULLUP_EN | GPIO_MODE_I2SSDO);
	

	local_irq_restore(flags);
	uda1380_init();
	init_s3c2440_iis_bus();
	output_stream.dma_ch = DMA_CH2;
	input_stream.dma_ch = DMA_CH1;

	if (audio_init_dma(&output_stream, "UDA1380 out")) {
		audio_clear_dma(&output_stream);
		printk( KERN_WARNING AUDIO_NAME_VERBOSE 
			": unable to get DMA channels\n" );
		return -EBUSY;
	}
       if (audio_init_dma(&input_stream, "UDA1380 in")) {
               audio_clear_dma(&input_stream);
               printk( KERN_WARNING AUDIO_NAME_VERBOSE
                       ": unable to get DMA channels\n" );
               return -EBUSY;
       }

	audio_dev_dsp = register_sound_dsp(&smdk2440_audio_fops, -1);
	audio_dev_mixer = register_sound_mixer(&smdk2440_mixer_fops, -1);

	printk(AUDIO_NAME_VERBOSE " initialized\n");

        if (cold) {
                audio_rate = AUDIO_RATE_DEFAULT;
                audio_channels = AUDIO_CHANNELS_DEFAULT;
                audio_fragsize = AUDIO_FRAGSIZE_DEFAULT;
                audio_nbfrags = AUDIO_NBFRAGS_DEFAULT;
                audio_clear_buf(&output_stream);
	        audio_clear_buf(&input_stream);
        }

	return 0;
}

void cleanup_module(void)
{
	unregister_sound_dsp(audio_dev_dsp);
	unregister_sound_mixer(audio_dev_mixer);
	audio_clear_dma(&input_stream);
	audio_clear_dma(&output_stream);
	printk(AUDIO_NAME_VERBOSE " unloaded\n");
}

module_init(init_module1380);
module_exit(cleanup_module);