⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 uda1380.c

📁 这个源码相信对很多用arm开发板开发的人会有用的
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Philips UDA1380 Audio Device Driver for S3C2410 Linux * * Copyright (C) 2002 MIZI Research, Inc. * ChangeLog: * 30-April-2005  Port from kernel2.4.18 to linux2.6.11.1  * for MatrixV(based on s3c2410) (Dexiang Huang,GuangDong Linux Center) * 29-Dec-2005	make it work for recording(Dexiang Huang,GuangDong Linux Center) * 29-Dec-2005	make it work to support Full-Duplex.(Dexiang Huang,GuangDong Linux Center) */#include <linux/module.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 <asm/uaccess.h>#include <asm/io.h>#include <asm/hardware.h>#include <asm/semaphore.h>#include <asm/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>#undef  DEBUG#ifdef DEBUG#define DPRINTK( x... )  printk( ##x )#else#define DPRINTK( x... )#endif#define S3C2410_IISREG(x)   ((x)+S3C24XX_VA_IIS)#define S3C2410_SBC_IISCON  S3C2410_IISREG(0X00)#define S3C2410_SBC_IISMOD  S3C2410_IISREG(0x04)#define S3C2410_SBC_IISPSR  S3C2410_IISREG(0x08)#define S3C2410_SBC_IISFCON	S3C2410_IISREG(0x0c)#define S3C2410_SBC_IISFIFO S3C2410_IISREG(0x10)static void init_s3c2410_iis_bus_rx(void);static void init_s3c2410_iis_bus_tx(void);static void init_s3c2410_iis_bus_txrx(void);#define DEF_VOLUME     65/* UDA1380 Register bits */#define UDA1380_ADDR		0x14#define UDA1380_REG_DATA0	(UDA1380_ADDR + 0)#define UDA1380_REG_STATUS	(UDA1380_ADDR + 2)#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	44100#define AUDIO_NBFRAGS_DEFAULT	16#define AUDIO_FRAGSIZE_DEFAULT	8192#define S_CLOCK_FREQ	384#define PCM_ABS(a) (a < 0 ? -a : a)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; /* input */#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;/* * For the dma chanel user. This is added in the 2.6 kernel. */static struct s3c2410_dma_client s3c2410_dma_client_out ={	.name ="I2SSDO",};static struct s3c2410_dma_client s3c2410_dma_client_in={	.name ="I2SSDI",};/*Registers of uda1380(only the used)*/enum uda1380_registers {    EVAMODE_CLK = 0,    IISBUS = 0x01,    POWERCTL = 0x02,    ALG_MIXER =0x03,    HP_AMP = 0x04,    MASTER_VOL_CTL = 0x10,    MIXER_VOLUME_CTL = 0x11,    MODE_BASS_TREBLE = 0x12,    MASTER_MUTE_DEEMP_MUTE = 0x13,    MIXER_SILENCE_DETECTOR = 0x14,    DECIMATOR_VOLUME_CONTROL = 0x20,    PGA_MUTE = 0x21,    ADC = 0x22,    AGC = 0x23,    RESTORE_L3DEFAULT = 0x7f,    INTERPOLATION_FILTER_STATUS = 0x18,    DECIMATE_STATUS = 0x28};#define FIN_NEW	12000000#define GET_PCLK_NEW	0#define DMA_CH1		1#define DMA_CH2		2#ifndef __ASSEMBLY__#define UData(Data)	((unsigned long) (Data))#else#define UData(Data)	(Data)#endif#define FInsrt(Value, Field)	(UData (Value) << FShft (Field))#define fIISPSR_A       Fld(5, 5)       /* Prescaler Control A */#define IISPSR_A(x)     FInsrt((x), fIISPSR_A)#define fIISPSR_B       Fld(5, 0)       /* Prescaler Control B */#define IISPSR_B(x)     FInsrt((x), fIISPSR_B)  #define fPLL_MDIV               Fld(8,12)#define fPLL_PDIV               Fld(6,4)#define fPLL_SDIV               Fld(2,0)#define Fld(Size, Shft) (((Size) << 16) + (Shft))#define FShft(Field)    ((Field) & 0x0000FFFF)#define FAlnMsk(Field)  ((UData (1) << FSize (Field)) - 1)#define FSize(Field)    ((Field) >> 16)#define FExtr(Data, Field) ((UData (Data) >> FShft (Field)) & FAlnMsk (Field))#define GET_MDIV(x)     FExtr(x, fPLL_MDIV)#define GET_PDIV(x)     FExtr(x, fPLL_PDIV)#define GET_SDIV(x)     FExtr(x, fPLL_SDIV)/* Functions for the callback when dma transfered done */static void audio_dmaout_done_callback(s3c2410_dma_chan_t  *r_value,void *buf_id, 					    int size,s3c2410_dma_buffresult_t result);static void audio_dmain_done_callback(s3c2410_dma_chan_t  *r_value,void *buf_id,						    int size,s3c2410_dma_buffresult_t result);extern int s3c2410_uda_codec_write(unsigned char adr, unsigned short value);	//defined in the driver/i2c/chips/uda1380.c/*write to the register of uda1380 bu CPU, controlled by iic*/static void wrcodec(u8 ctlreg, u16 val){    int rcnt = 0;    /* The codec is a write only device, with a 16-bit control/data    * word.  Although it is written as two bytes on the I2C,the    * format is actually 7 bits of register and 9 bits of data.    * The ls bit of the first byte is the ms bit of    *  the data.    */    while ((s3c2410_uda_codec_write(ctlreg, val)) && (rcnt < 50))	rcnt++;    if (rcnt >= 50)	printk("timeout uda1380_wrcodec: reg:%x val:%x\n", ctlreg, val);    udelay(10);}static void audio_clear_buf(audio_stream_t * s){    DPRINTK("audio_clear_buf\n");    s3c2410_dma_ctrl(s->dma_ch, S3C2410_DMAOP_FLUSH);    if (s->buffers)     {	int frag;	for (frag = 0; frag < s->nbfrags; frag++) 	{	    if (!s->buffers[frag].master)		continue;	    /* To free the dma buffer */ 	    dma_free_coherent(NULL,s->buffers[frag].master, 			    s->buffers[frag].start, 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 	    {		/* To allocate the coherent address buffer for dma transfer*/ 		dmabuf=dma_alloc_coherent(NULL,dmasize,&dmaphys,GFP_KERNEL|GFP_DMA);  		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 %d\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(s3c2410_dma_chan_t  *r_value, void *buf_id,					    int size, s3c2410_dma_buffresult_t result){    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(s3c2410_dma_chan_t  *r_value, void *buf_id, 					int size, s3c2410_dma_buffresult_t result){    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);		} /* using when write */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);	/* To enqueue the dma task */ 	s3c2410_dma_enqueue(s->dma_ch,(void *)b,b->dma_addr,b->size);	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 (!access_ok(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 	{

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -