www.pudn.com > uda1380-s3c2410-linux-rev.zip.zip > uda1380.c
/****** * * FileName: uda1380.c * * Version: 0.10 * * Date: 2005-03-20 * * Copyright (C) 2005 Shenzhen Newenjoy Technology Co,.LTD. * * Designed by Yinshenk < Email: yinshenk@21cn.com > * * Philips UDA1380 Audio drive device for Samsuang S3C2410 Linux 4.8.XX * configure. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License. * *****************************************************************************/ #include#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "2410addr.h" #include "2410lib.h" #include "def.h" #include "2410iic.h" #include "uda1380.h" /* * Samsung S3C2410 core and Philips UDA1380 Audio Hardware circuit * connecter description (From S3C2410 to UDA1380 ). * * I2C Bus circuit connect part: * IIC_SDA/GPE15 ---> L3Data * IIC_SCL/GPE14 ---> L3Clock * * I2S Bus circuirt connect part: * IIS_SDO/IIS_SDI/GPE4 ---> DATA1 * IIS_SDI/nSS0/GPE3 ---> DATA0 * CDCLK/GPE2 ---> SYSCLK * IIS_SCLK/GPE1 ---> BCKI * IIS_LRCK/GPE0 ---> WSI * * other: * L3MODE ---> (GND) * SEL_L3_IIC ---> (VCC) */ #undef DEBUG #ifdef DEBUG #define DPRINTK( x... ) printk( ##x ) #else #define DPRINTK( x... ) #endif /* * This drive device program value and paremeter description. *****************************************************************************/ #define UDA1380_ADDR 0x30 // Philips UDA1380 Audio device address. #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 #define AUDIO_NBFRAGS_DEFAULT 8 #define AUDIO_FRAGSIZE_DEFAULT 8192 #define S_CLOCK_FREQ 384 // frequency #define PCM_ABS(a) (a < 0 ? -a : a) /* define audio managment struction */ 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; /* define audio data stream managment struction */ 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; #define NEXT_BUF(_s_,_b_) { \ (_s_)->_b_##_idx++; \ (_s_)->_b_##_idx %= (_s_)->nbfrags; \ (_s_)->_b_ = (_s_)->buffers + (_s_)->_b_##_idx; } 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; // /***** S.Yin insert program begin 2005-3-20 **********/ static char _iicData[IICBUFSIZE]; static volatile int _iicDataCount; static volatile int _iicStatus; static volatile int _iicControl; static int _iicPt; U32 uda1380_data; static int delayLoopCount = FCLK/10000/10; /* digital master volume control register */ #define DEF_VOLUME 20 unsigned long rtc_r_GPECON; // Port E control register. unsigned long rtc_r_GPEUP; // Port E pull-up disable register. unsigned long r_IICCON; // I2C Bus control register. unsigned long r_IICSTAT; // I2C Bus status register. unsigned long r_IICADD; // I2C Bus address register. unsigned long r_IICDS; // I2C Bus Tx/Rx data shift register. #define rGPECON (*(volatile unsigned long *)rtc_r_GPECON) //Port E control #define rGPEUP (*(volatile unsigned long *)rtc_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 /* Philips UDA1380 synchronization or Initialization infomation */ static struct uda1380_reg_info { unsigned short num; // Reigisters. unsigned short default_value; // parameter. } uda1380_reg_info[] = { { REG0, REG0_EN_ADC|REG0_EN_DAC|REG0_EN_INT|REG0_EN_DEC|REG0_SC_384FS|REG0_ADC_USE_SYSCLK|REG0_DAC_USE_SYSCLK }, { I2S_REG, (FMT_I2S << I2S_REG_SFORO_SHIFT)|(FMT_I2S << I2S_REG_SFORI_SHIFT | (1<< 6)) }, { PWR_REG, PWR_REG_PON_DAC|PWR_REG_PON_BIAS }, { MASTER_VOL_REG, 0x4040 }, { MIXER_VOL_REG, 0xaa }, { MBT_REG, 0xaa}, { MMCDM_REG, 0x0 }, { MIXER_CTL_REG, 0x70 }, { DEC_VOL_REG, 0xaa }, { DPM_REG, 0xaa }, { DEC_ADC_REG, 0xaa }, { DEC_AGC_REG, 0xaa }, }; /** S.Yin insert program end 2005-03-20 ****/ /* * function program part of the Philips UDA1380 audio. *****************************************************************************/ /***** S.Yin insert program begin 2005-03-20 **********/ /** * I2C Bus control register address set. */ int rtc_address_map(void) { rtc_r_GPECON=(unsigned long)__ioremap(0x56000040,4,0); // rtc_r_GPEUP =(unsigned long)__ioremap(0x56000048,4,0); // r_IICCON =(unsigned long)__ioremap(0x54000000,4,0); // r_IICSTAT =(unsigned long)__ioremap(0x54000004,4,0); // r_IICADD =(unsigned long)__ioremap(0x54000008,4,0); // r_IICDS =(unsigned long)__ioremap(0x5400000c,4,0); // return 0; } /** * Delay control. * time=0: adjust the Delay function by WatchDog timer. * time>0: the number of loop time resolution of time is 100us. */ void Delay(int time) { int i, adjust = 0; // define value. if ( time == 0 ) { // time = 200; // reset paremeter. adjust = 1; // delayLoopCount = 400; // //PCLK/1M,Watch-dog disable,1/64,interrupt disable,reset disable rWTCON = ((PCLK/1000000-1)<<8)|(2<<3); // rWTDAT = 0xffff; //for first update rWTCNT = 0xffff; //resolution=64us @any PCLK rWTCON = ((PCLK/1000000-1)<<8)|(2<<3)|(1<<5); //Watch-dog timer start } for ( ; time > 0; time-- ) // Delay. for ( i = 0; i < delayLoopCount; i++ ); // if ( adjust == 1 ) { // loop counter. rWTCON = ((PCLK/1000000-1)<<8)|(2<<3); // // Watch-dog timer stop i = 0xffff - rWTCNT; // 1count->64us, 200*400 cycle runtime = 64*i us delayLoopCount = 8000000/(i*64); // 200*400:64*i=1*x:100 -> x=80000*100/(64*i) } } /** * Check ACK and Pending bit. */ int AckPoll( U8 slvAddr ) { int i; // define value. while ( 1 ) { // rIICDS = slvAddr; // Write slave address to rIICDS. rIICSTAT = 0xf0; // Write 0xf0 (M/T start) to rIICSTAT. rIICCON = 0xe0; // Resumes IIC operation. Delay(2); // Wait until stop condtion is in effect. for ( i = 0; i < 1000; i ++ ) { // _iicStatus = rIICSTAT; // To check if _iicStatus is changed if ( !(_iicStatus & 0x1 )) { // CHeck ACK flag received. // printk("ACK is received................\n"); return 0; // Normal return when ACK is received. } if ( i > 256 ) // Check time over. return 5; // Error return. } } return 2; // Error return. } /** * I2C bus write an slave address to client device. */ int WriteInitByte( U8 slvAddr ) { int i; // define value. rIICDS = slvAddr; // Write slave address to rIICDS. for ( i = 0; i < 20; i ++ ); // rIICSTAT = 0xf0; // Write 0xf0 (M/T start) to rIICSTAT. Delay(2); // Delay. The data of the rIICDS is transmitted. for ( i = 0; i < 2000; i ++ ) { // The data of the rIICDS is shifted to SDA, and error check. _iicControl = rIICCON; // if (( _iicControl & 0x10 ) > 0 ) // ACK period and then interrupt is pending. return 0; // Normal out. if ( i > 1000 ) // Timeout. return 1; // Stop or Error return. } return 2; // Error return. } /** * I2C Bus write an data to client device ( Master Transmitted Mode Operation ). */ int _Wr2410Iic( U8 slvAddr, U8 addr, U32 wdata ) { int i; // define value. /* Configured. Initialize. */ _iicData[0] = addr; // Set register address. _iicData[1] = wdata / 256; // Set data high tybe. _iicData[2] = wdata & 0xff; // Set data low tybe. _iicPt = 0; // Define data pointer. _iicDataCount = 3; // Set received data counter. /* IIC-Bus send initialize tybe (device address) */ if ( WriteInitByte( slvAddr ) > 0 ) // return 1; // /* IIC-Bus send register address and data */ while ( _iicDataCount ) { // rIICDS = _iicData[_iicPt]; // Write slave address to rIICDS. for ( i = 0; i < 20; i ++ ); // rIICCON = 0xe0; // CLear pending bit to resumes IIC operation. _iicPt += 1; // Counter pointer. _iicDataCount -= 1; // Data counter. Delay(2); // Delay. The data of the rIICDS is transmitted. for ( i = 0; i < 1000; i ++ ) { // Error status process. _iicControl = rIICCON; // if (( _iicControl & 0x10 ) > 0 ) // ACK period and then interrupt is pending. break; // normal out. if ( i > 256 ) // Timeout. return 2; // Stop or Error return. } } rIICSTAT = 0xd0; // Write 0xd0 (M/T STOP) to rIICSTAT. rIICCON = 0xe0; // Clear pending to resumes IIC operation. Delay(50); // Wait until stop condtion is in effect. /* Write completed oparation check. */ i = AckPoll( slvAddr ); // Check ACK flag bit. rIICSTAT = 0xd0; // Master Tx condition, Stop(Write), Output Enable rIICCON = 0xe0; // Resumes IIC operation. Delay(2); // Wait until stop condtion is in effect. return i; // return. } /** * I2C Bus read an data from client device( Master receive mode ). * (program is not normal or have error) */ int _Rd2410Iic( U8 slvAddr, U8 addr ) { int i, readControl; // define value. /* Read oparation initialize */ _iicPt = 0; // define pointer. _iicDataCount = 1 * 2; // Data counter (word). if ( _iicDataCount > IICBUFSIZE ) // Check data counter. return 1; // Error return. /* IIC-Bus send initialize tybe */ if ( WriteInitByte( slvAddr ) > 0 ) // return 2; // /* IIC-Bus send register address */ rIICDS = addr; // Write slave address to rIICDS. for ( i = 0; i < 10; i ++ ); // rIICCON = 0xe0; // Clear pending bit. Delay(10); // The data of the IICDS (slave address) is transmitted, Delay. for ( i = 0; i < 2000; i ++ ) { // Error status process. _iicControl = rIICCON; // if (( _iicControl & 0x10 ) > 0 ) // ACK period and then interrupt is pending. break; // Normal out. if ( i > 1000 ) // Timeout. return 3; // Error return, or STOP. } /* IIC-Bus send slave address */ rIICDS = slvAddr | 0x01; // Write register address to rIICDS.. for ( i = 0; i < 20; i ++ ); // rIICSTAT = 0xb0; // Writ 0xb0 (M/R, START) to rIICSTAT. Delay(2); // The data of the IICDS (slave address) is transmitted, Delay. for ( i = 0; i < 2000; i ++ ) { // Error status process. _iicControl = rIICCON; // if (( _iicControl & 0x10 ) > 0 ) // ACK period and then interrupt is pending. break; // Normal out. if ( i > 1000 ) // Timeout. return 4; // Error return, or STOP. } /* IIC-Bus read data */ readControl = 1000; // Timeout contrl counter. while ( _iicDataCount ) { // Data number completed.. _iicControl = rIICCON; // if (( _iicControl & 0x10 ) > 0 ) { // ACK period and then interrupt is pending. _iicData[_iicPt] = rIICDS; // Read a new data from rIICDS. Delay(2); // The data of the IICDS (slave address) is transmitted, Delay. rIICCON = 0xe0; //Clear pending bit. Resumes IIC operation with ACK _iicPt += 1; // Data pointer. _iicDataCount -= 1; // Data counter. readControl = 1000; // Timeout control register reset. } if ( (readControl--) <= 0 ) // Check timeout. return 5; // Error return, orSTOP. } rIICSTAT = 0xd0; // Writ 0xb0 (M/R, STOP) to rIICSTAT. rIICCON = 0xe0; // Clear pending bit. Delay(2); // Wait until stop condtion is in effect. /* Read completed oparation check. */ i = AckPoll( slvAddr ); // Check ACK flag bit. rIICSTAT = 0xd0; // Master Tx condition, Stop(Write), Output Enable rIICCON = 0xe0; // Resumes IIC operation. Delay(2); // Wait until stop condtion is in effect. return i; // return. } /***** S.Yin insrt program end 2005-03-20 *****/ /***** S.Yin close this part program begin 2005-03-20 ***** static void uda1341_l3_address(u8 data) { int i; int flags; local_irq_save(flags); write_gpio_bit(GPIO_L3MODE, 0); write_gpio_bit(GPIO_L3DATA, 0); write_gpio_bit(GPIO_L3CLOCK, 1); udelay(1); for (i = 0; i < 8; i++) { if (data & 0x1) { write_gpio_bit(GPIO_L3CLOCK, 0); udelay(1); write_gpio_bit(GPIO_L3DATA, 1); udelay(1); write_gpio_bit(GPIO_L3CLOCK, 1); udelay(1); } else { write_gpio_bit(GPIO_L3CLOCK, 0); udelay(1); write_gpio_bit(GPIO_L3DATA, 0); udelay(1); write_gpio_bit(GPIO_L3CLOCK, 1); udelay(1); } data >>= 1; } write_gpio_bit(GPIO_L3MODE, 1); udelay(1); local_irq_restore(flags); } static void uda1341_l3_data(u8 data) { int i; int flags; local_irq_save(flags); write_gpio_bit(GPIO_L3MODE, 1); udelay(1); write_gpio_bit(GPIO_L3MODE, 0); udelay(1); write_gpio_bit(GPIO_L3MODE, 1); for (i = 0; i < 8; i++) { if (data & 0x1) { write_gpio_bit(GPIO_L3CLOCK, 0); udelay(1); write_gpio_bit(GPIO_L3DATA, 1); udelay(1); write_gpio_bit(GPIO_L3CLOCK, 1); udelay(1); } else { write_gpio_bit(GPIO_L3CLOCK, 0); udelay(1); write_gpio_bit(GPIO_L3DATA, 0); udelay(1); write_gpio_bit(GPIO_L3CLOCK, 1); udelay(1); } data >>= 1; } write_gpio_bit(GPIO_L3MODE, 1); write_gpio_bit(GPIO_L3MODE, 0); udelay(1); write_gpio_bit(GPIO_L3MODE, 1); local_irq_restore(flags); } ***** S.Yin close this part program end 2005-03-20 *****/ /** * Clear Audio data stream buffer. */ static void audio_clear_buf(audio_stream_t * s) { int frag; // define value. DPRINTK( "audio_clear_buf\n" ); // pinter. s3c2410_dma_flush_all( s->dma_ch ); // DMA if ( s->buffers ) { // If buffer have data... for ( frag = 0; frag < s->nbfrags; frag++ ) { // if ( !s->buffers[frag].master ) // continue; // If Buffer have data, then consistent_free(s->buffers[frag].start, s->buffers[frag].master, s->buffers[frag].dma_addr); // Free DMA buffer. } kfree(s->buffers); // Free Buffer. s->buffers = NULL; // Buffer clear. } s->buf_idx = 0; // Buffer pointer. s->buf = NULL; // Buffer clear. } /** * Set audio data stream buffer. */ static int audio_setup_buf(audio_stream_t * s) { int frag; // define value. int dmasize = 0; // char *dmabuf = 0; // dma_addr_t dmaphys = 0; // /* Set buffer point and size.*/ if (s->buffers) // If buffer have data, then. return -EBUSY; // return busy status. 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 ); /* Move data to buffer. */ 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; // counter. dmaphys += s->fragsize; // dmasize -= s->fragsize; // } s->buf_idx = 0; // s->buf = &s->buffers[0]; // return 0; // normal return. /* Error process */ err: printk(AUDIO_NAME ": unable to allocate audio memory\n "); audio_clear_buf(s); // return -ENOMEM; // } /** * DMA request server. */ static void audio_dmaout_done_callback(void *buf_id, int size) { audio_buf_t *b = (audio_buf_t *) buf_id; // 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); // s3c2410_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 smdk2410_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; // } s3c2410_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 smdk2410_audio_read(struct file *file, char *buffer, size_t count, loff_t * ppos) { return 0; // } static unsigned int smdk2410_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 smdk2410_audio_llseek(struct file *file, loff_t offset, int origin) { return -ESPIPE; // } static int smdk2410_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long 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; // /***** S.Yin insert program begin 2005-03-20 *****/ uda1380_volume = 63 - (((val & 0xff) + 1) * 63) / 100; _Wr2410Iic( 0x30, MASTER_VOL_REG, uda1380_volume ); /***** S.Yin insert program end 2005-03-20 *****/ /***** S.Yin close this part program begin 2005-03-20 uda1341_l3_address(UDA1380_ADDR + UDA1341_REG_DATA); uda1341_l3_data(uda1380_volume); ***** S.Yin close this part program end 2005-03-20 *****/ break; // case SOUND_MIXER_READ_VOLUME: // val = ((63 - uda1380_volume) * 100) / 63; // val |= val << 8; // return put_user(val, (long *) arg); // 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; // unsigned long tmpval; // unsigned long tmpval384; // unsigned long tmpval384min = 0xffff; // 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; // } } DPRINTK("prescaler addr= %d\n", prescaler); // return prescaler; // } static long audio_set_dsp_speed(long 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 smdk2410_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 smdk2410_mixer_ioctl(inode, file, cmd, arg); } return 0; // } static int smdk2410_audio_open(struct inode *inode, struct file *file) { 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); // } MOD_INC_USE_COUNT; // return 0; // } static int smdk2410_mixer_open(struct inode *inode, struct file *file) { MOD_INC_USE_COUNT; // return 0; // } static int smdk2410_audio_release(struct inode *inode, struct file *file) { DPRINTK("audio_release\n"); // switch (file->f_flags & O_ACCMODE) { // Read buffer operation. case O_RDONLY: // case O_RDWR: // if (audio_rd_refcount == 1) // audio_rd_refcount = 0; // } switch (file->f_flags & O_ACCMODE) { // Write buffer operation. case O_WRONLY: // case O_RDWR: // if (audio_wr_refcount == 1) { // audio_sync(file); // audio_clear_buf(&output_stream); // audio_wr_refcount = 0; // } // } // MOD_DEC_USE_COUNT; // return 0; // } static int smdk2410_mixer_release(struct inode *inode, struct file *file) { MOD_DEC_USE_COUNT; // return 0; // } /* * Philips UDA1380 driver interface description, and initialize operation, * exit operation for module server. ****************************************************************************/ /** * Philips UDA1380 audio device interface description. */ static struct file_operations smdk2410_audio_fops = { llseek: smdk2410_audio_llseek, write: smdk2410_audio_write, read: smdk2410_audio_read, poll: smdk2410_audio_poll, ioctl: smdk2410_audio_ioctl, open: smdk2410_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 }; /** * Philips UDA1380 audio device initialize. */ static void init_uda1380( void ) { int flags; // define value. int i,a; // uda1380_volume = 0; // Master volume. local_irq_save(flags); // keep. write_gpio_bit(GPIO_L3CLOCK, 1); // I/O Port set. write_gpio_bit(GPIO_L3DATA, 1); // local_irq_restore(flags); // restore. /***** S.Yin insert program begin 2005-03-20 **********/ _Wr2410Iic( UDA1380_ADDR, 0x7f, 0xFFFF ); // reset operation. Delay(50); // Delay 10ms. a = 0; for ( i = 0; i < ARRAY_SIZE( uda1380_reg_info ); i++ ) {// a += _Wr2410Iic( UDA1380_ADDR, uda1380_reg_info[i].num, uda1380_reg_info[i].default_value ); // Delay(50); // Delay 10ms. } if ( a > 0 ) // printk( "Philips UDA1380 initialization fail ....\n" ); /** S.Yin insert program end 2005-03-20 ****/ /***** S.Yin close program begin 2005-03-20 * uda1341_l3_address(UDA1380_ADDR + UDA1341_REG_STATUS); // uda1341_l3_data(SC_384fs | IF_MSB); // set 384 system clock, MSB uda1341_l3_data(0xC1); // uda1341_l3_address(UDA1380_ADDR + UDA1341_REG_DATA); // uda1341_l3_data(uda1380_volume); // maximum volume uda1341_l3_data(uda_sampling); //NO_DE_EMPHASIS and no muting ***** S.Yin insert program end 2005-03-20 ****/ } /** * Samsung S3C2410 I2C Bus device initialize. */ static void init_iic_bus(void) { unsigned int save_E, save_PE; // define value. rtc_address_map(); // Set I2C Bus control register. save_E = rGPECON; // Keep. save_PE = rGPEUP; // rGPEUP |= 0xc000; // Pull-up disable rGPECON |= 0xa00000; // GPE15:IICSDA , GPE14:IICSCL rIICCON = 0; // Clear rIICSTAT = 0; // rIICADD = 0x10; // S3C2410 slave address=[7:1] rIICCON = 0xe0; // Enable ACK, Prescaler IICCLK=PCLK/512, // Enable interrupt, Transmit clock value // Tx clock=IICCLK/512 rIICSTAT = 0xd0; // to get to a sane state (also generates a STOP condition) rGPEUP = save_PE; // restore rGPECON = save_E; // } /** * Samsung S3C2410 I2S Bus device initialize. */ static void init_s3c2410_iis_bus(void) { IISCON = 0; // Clear register. IISMOD = 0; // IISFIFOC = 0; // IISPSR = (IISPSR_A( iispsr_value( S_CLOCK_FREQ,44100 )) | IISPSR_B(iispsr_value(S_CLOCK_FREQ, 44100))); // 44 KHz , 384fs IISCON = (IISCON_TX_DMA // Transmit DMA service request |IISCON_RX_IDLE // Receive Channel idle |IISCON_PRESCALE); // IIS Prescaler Enable IISMOD = (IISMOD_SEL_MA // Master mode | IISMOD_SEL_TX // Transmit | IISMOD_CH_RIGHT // Low for left channel | IISMOD_FMT_MSB // 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); // Transmit FIFO enable IISCON |= IISCON_EN; // IIS enable(start) } /** * Samsung S3C2410 DMA device initialize for audio server. */ static int __init audio_init_dma(audio_stream_t * s, char *desc) { return s3c2410_request_dma("I2SSDO", s->dma_ch, audio_dmaout_done_callback, NULL); } /** * Philips UDA1380 audio device close. */ static int s3c2410_uda1380_close(void) { _Wr2410Iic( UDA1380_ADDR, 0x0, 0x0 ); // reset operation. Delay(50); // Delay 10ms. _Wr2410Iic( UDA1380_ADDR, 0x02, 0x0 ); // reset operation. Delay(50); // Delay 10ms. return 0; // return. } /** * Clear Samsung S3C2410 DMA device. */ static int audio_clear_dma(audio_stream_t * s) { s3c2410_free_dma(s->dma_ch); // Free DMA device. return 0; // return. } /** * Philips UDA1380 Audio driver initialize. */ int __init s3c2410_uda1380_init(void) { unsigned long flags; // Define valuse. local_irq_save(flags); // keep. /***** Initialize Samsung S3C2410 I/O port. *****/ set_gpio_ctrl(GPIO_L3CLOCK); // GPB 4: L3CLOCK, OUTPUT set_gpio_ctrl(GPIO_L3DATA); // GPB 3: L3DATA, OUTPUT set_gpio_ctrl(GPIO_L3MODE); // GPB 2: L3MODE, OUTPUT set_gpio_ctrl(GPIO_E3 | GPIO_PULLUP_EN | GPIO_MODE_I2SSDI); // GPE 3: I2SSDI set_gpio_ctrl(GPIO_E0 | GPIO_PULLUP_EN | GPIO_MODE_I2SSDI); // GPE 0: I2SLRCK set_gpio_ctrl(GPIO_E1 | GPIO_PULLUP_EN | GPIO_MODE_I2SSCLK); // GPE 1: I2SSCLK set_gpio_ctrl(GPIO_E2 | GPIO_PULLUP_EN | GPIO_MODE_CDCLK); // GPE 2: CDCLK set_gpio_ctrl(GPIO_E4 | GPIO_PULLUP_EN | GPIO_MODE_I2SSDO); // GPE 4: I2SSDO local_irq_restore(flags); // restore. /**** Samsung S3C2410 I2C bus and I2S bus, DMA device, UDA1380 Initialize. *****/ int init_iic_bus(); // I2C bus initialization. init_uda1380(); // uda1380 initialization. init_s3c2410_iis_bus(); // I2S bus initialization. output_stream.dma_ch = DMA_CH2; // DMA device initialization. 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; // return busy. } /***** Register Philips UDA1380 Audio device *****/ audio_dev_dsp = register_sound_dsp(&smdk2410_audio_fops, -1); audio_dev_mixer = register_sound_mixer(&smdk2410_mixer_fops, -1); printk(AUDIO_NAME_VERBOSE " device initialized.\n"); // Complete note... return 0; // normal return. } /** * Drive device EXIT oparation. */ void __exit s3c2410_uda1380_exit(void) { s3c2410_uda1380_close(); // Clear UDA1380 control register. unregister_sound_dsp(audio_dev_dsp); // Release audio drive interfase. unregister_sound_mixer(audio_dev_mixer); // Release Mixer device interfase. audio_clear_dma(&output_stream); // Release DMA server. printk(AUDIO_NAME_VERBOSE " device unloaded\n"); // Release note... } module_init(s3c2410_uda1380_init); module_exit(s3c2410_uda1380_exit); MODULE_AUTHOR( "Yinshenk" ); MODULE_DESCRIPTION( "Philips UDA1380 CODEC driver" ); MODULE_LICENSE("GPL");