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

📄 vwsnd.c

📁 iis s3c2410-uda1341语音系统的 开发
💻 C
📖 第 1 页 / 共 5 页
字号:
	/* 4. Put the conversion resources into standby. */	ad1843_write_bits(lith, &ad1843_PDNI, 0);	later = jiffies + HZ / 2;	/* roughly half a second */	DBGDO(shut_up++);	while (ad1843_read_bits(lith, &ad1843_PDNO)) {		if (jiffies > later) {			printk(KERN_ERR			       "vwsnd audio: AD1843 won't power up\n");			return -EIO;		}		schedule();	}	DBGDO(shut_up--);	/* 5. Power up the clock generators and enable clock output pins. */	ad1843_write_multi(lith, 2, &ad1843_C1EN, 1, &ad1843_C2EN, 1);	/* 6. Configure conversion resources while they are in standby. */ 	/* DAC1 uses clock 1 as source, ADC uses clock 2.  Always. */	ad1843_write_multi(lith, 3,			   &ad1843_DA1C, 1,			   &ad1843_ADLC, 2,			   &ad1843_ADRC, 2);	/* 7. Enable conversion resources. */	ad1843_write_bits(lith, &ad1843_ADTLK, 1);	ad1843_write_multi(lith, 5,			   &ad1843_ANAEN, 1,			   &ad1843_AAMEN, 1,			   &ad1843_DA1EN, 1,			   &ad1843_ADLEN, 1,			   &ad1843_ADREN, 1);	/* 8. Configure conversion resources while they are enabled. */	ad1843_write_bits(lith, &ad1843_DA1C, 1);	/* Unmute all channels. */	ad1843_set_outsrc(lith,			  (SOUND_MASK_PCM | SOUND_MASK_LINE |			   SOUND_MASK_MIC | SOUND_MASK_CD));	ad1843_write_multi(lith, 2, &ad1843_LDA1AM, 0, &ad1843_RDA1AM, 0);	/* Set default recording source to Line In and set	 * mic gain to +20 dB.	 */	ad1843_set_recsrc(lith, SOUND_MASK_LINE);	ad1843_write_multi(lith, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1);	/* Set Speaker Out level to +/- 4V and unmute it. */	ad1843_write_multi(lith, 2, &ad1843_HPOS, 1, &ad1843_HPOM, 0);	return 0;}/*****************************************************************************//* PCM I/O */#define READ_INTR_MASK  (LI_INTR_COMM1_TRIG | LI_INTR_COMM1_OVERFLOW)#define WRITE_INTR_MASK (LI_INTR_COMM2_TRIG | LI_INTR_COMM2_UNDERFLOW)typedef enum vwsnd_port_swstate {	/* software state */	SW_OFF,	SW_INITIAL,	SW_RUN,	SW_DRAIN,} vwsnd_port_swstate_t;typedef enum vwsnd_port_hwstate {	/* hardware state */	HW_STOPPED,	HW_RUNNING,} vwsnd_port_hwstate_t;/* * These flags are read by ISR, but only written at baseline. */typedef enum vwsnd_port_flags {	DISABLED = 1 << 0,	ERFLOWN  = 1 << 1,		/* overflown or underflown */	HW_BUSY  = 1 << 2,} vwsnd_port_flags_t;/* * vwsnd_port is the per-port data structure.  Each device has two * ports, one for input and one for output. * * Locking: * *	port->lock protects: hwstate, flags, swb_[iu]_avail. * *	devc->io_sema protects: swstate, sw_*, swb_[iu]_idx. * *	everything else is only written by open/release or *	pcm_{setup,shutdown}(), which are serialized by a *	combination of devc->open_sema and devc->io_sema. */typedef struct vwsnd_port {	spinlock_t	lock;	wait_queue_head_t queue;	vwsnd_port_swstate_t swstate;	vwsnd_port_hwstate_t hwstate;	vwsnd_port_flags_t flags;	int		sw_channels;	int		sw_samplefmt;	int		sw_framerate;	int		sample_size;	int		frame_size;	unsigned int	zero_word;	/* zero for the sample format */	int		sw_fragshift;	int		sw_fragcount;	int		sw_subdivshift;	unsigned int	hw_fragshift;	unsigned int	hw_fragsize;	unsigned int	hw_fragcount;	int		hwbuf_size;	unsigned long	hwbuf_paddr;	unsigned long	hwbuf_vaddr;	caddr_t		hwbuf;		/* hwbuf == hwbuf_vaddr */	int		hwbuf_max;	/* max bytes to preload */	caddr_t		swbuf;	unsigned int	swbuf_size;	/* size in bytes */	unsigned int	swb_u_idx;	/* index of next user byte */	unsigned int	swb_i_idx;	/* index of next intr byte */	unsigned int	swb_u_avail;	/* # bytes avail to user */	unsigned int	swb_i_avail;	/* # bytes avail to intr */	dma_chan_t	chan;	/* Accounting */	int		byte_count;	int		frag_count;	int		MSC_offset;} vwsnd_port_t;/* vwsnd_dev is the per-device data structure. */typedef struct vwsnd_dev {	struct vwsnd_dev *next_dev;	int		audio_minor;	/* minor number of audio device */	int		mixer_minor;	/* minor number of mixer device */	struct semaphore open_sema;	struct semaphore io_sema;	struct semaphore mix_sema;	mode_t		open_mode;	wait_queue_head_t open_wait;	lithium_t	lith;	vwsnd_port_t	rport;	vwsnd_port_t	wport;} vwsnd_dev_t;static vwsnd_dev_t *vwsnd_dev_list;	/* linked list of all devices */static atomic_t vwsnd_use_count = ATOMIC_INIT(0);# define INC_USE_COUNT (atomic_inc(&vwsnd_use_count))# define DEC_USE_COUNT (atomic_dec(&vwsnd_use_count))# define IN_USE        (atomic_read(&vwsnd_use_count) != 0)/* * Lithium can only DMA multiples of 32 bytes.  Its DMA buffer may * be up to 8 Kb.  This driver always uses 8 Kb. * * Memory bug workaround -- I'm not sure what's going on here, but * somehow pcm_copy_out() was triggering segv's going on to the next * page of the hw buffer.  So, I make the hw buffer one size bigger * than we actually use.  That way, the following page is allocated * and mapped, and no error.  I suspect that something is broken * in Cobalt, but haven't really investigated.  HBO is the actual * size of the buffer, and HWBUF_ORDER is what we allocate. */#define HWBUF_SHIFT 13#define HWBUF_SIZE (1 << HWBUF_SHIFT)# define HBO         (HWBUF_SHIFT > PAGE_SHIFT ? HWBUF_SHIFT - PAGE_SHIFT : 0)# define HWBUF_ORDER (HBO + 1)		/* next size bigger */#define MIN_SPEED 4000#define MAX_SPEED 49000#define MIN_FRAGSHIFT			(DMACHUNK_SHIFT + 1)#define MAX_FRAGSHIFT			(PAGE_SHIFT)#define MIN_FRAGSIZE			(1 << MIN_FRAGSHIFT)#define MAX_FRAGSIZE			(1 << MAX_FRAGSHIFT)#define MIN_FRAGCOUNT(fragsize)		3#define MAX_FRAGCOUNT(fragsize)		(32 * PAGE_SIZE / (fragsize))#define DEFAULT_FRAGSHIFT		12#define DEFAULT_FRAGCOUNT		16#define DEFAULT_SUBDIVSHIFT		0/* * The software buffer (swbuf) is a ring buffer shared between user * level and interrupt level.  Each level owns some of the bytes in * the buffer, and may give bytes away by calling swb_inc_{u,i}(). * User level calls _u for user, and interrupt level calls _i for * interrupt. * * port->swb_{u,i}_avail is the number of bytes available to that level. * * port->swb_{u,i}_idx is the index of the first available byte in the * buffer. * * Each level calls swb_inc_{u,i}() to atomically increment its index, * recalculate the number of bytes available for both sides, and * return the number of bytes available.  Since each side can only * give away bytes, the other side can only increase the number of * bytes available to this side.  Each side updates its own index * variable, swb_{u,i}_idx, so no lock is needed to read it. * * To query the number of bytes available, call swb_inc_{u,i} with an * increment of zero. */static __inline__ unsigned int __swb_inc_u(vwsnd_port_t *port, int inc){	if (inc) {		port->swb_u_idx += inc;		port->swb_u_idx %= port->swbuf_size;		port->swb_u_avail -= inc;		port->swb_i_avail += inc;	}	return port->swb_u_avail;}static __inline__ unsigned int swb_inc_u(vwsnd_port_t *port, int inc){	unsigned long flags;	unsigned int ret;	spin_lock_irqsave(&port->lock, flags);	{		ret = __swb_inc_u(port, inc);	}	spin_unlock_irqrestore(&port->lock, flags);	return ret;}static __inline__ unsigned int __swb_inc_i(vwsnd_port_t *port, int inc){	if (inc) {		port->swb_i_idx += inc;		port->swb_i_idx %= port->swbuf_size;		port->swb_i_avail -= inc;		port->swb_u_avail += inc;	}	return port->swb_i_avail;}static __inline__ unsigned int swb_inc_i(vwsnd_port_t *port, int inc){	unsigned long flags;	unsigned int ret;	spin_lock_irqsave(&port->lock, flags);	{		ret = __swb_inc_i(port, inc);	}	spin_unlock_irqrestore(&port->lock, flags);	return ret;}/* * pcm_setup - this routine initializes all port state after * mode-setting ioctls have been done, but before the first I/O is * done. * * Locking: called with devc->io_sema held. * * Returns 0 on success, -errno on failure. */static int pcm_setup(vwsnd_dev_t *devc,		     vwsnd_port_t *rport,		     vwsnd_port_t *wport){	vwsnd_port_t *aport = rport ? rport : wport;	int sample_size;	unsigned int zero_word;	DBGEV("(devc=0x%p, rport=0x%p, wport=0x%p)\n", devc, rport, wport);	ASSERT(aport != NULL);	if (aport->swbuf != NULL)		return 0;	switch (aport->sw_samplefmt) {	case AFMT_MU_LAW:		sample_size = 1;		zero_word = 0xFFFFFFFF ^ 0x80808080;		break;	case AFMT_A_LAW:		sample_size = 1;		zero_word = 0xD5D5D5D5 ^ 0x80808080;		break;	case AFMT_U8:		sample_size = 1;		zero_word = 0x80808080;		break;	case AFMT_S8:		sample_size = 1;		zero_word = 0x00000000;		break;	case AFMT_S16_LE:		sample_size = 2;		zero_word = 0x00000000;		break;	default:		sample_size = 0;	/* prevent compiler warning */		zero_word = 0;		ASSERT(0);	}	aport->sample_size  = sample_size;	aport->zero_word    = zero_word;	aport->frame_size   = aport->sw_channels * aport->sample_size;	aport->hw_fragshift = aport->sw_fragshift - aport->sw_subdivshift;	aport->hw_fragsize  = 1 << aport->hw_fragshift;	aport->hw_fragcount = aport->sw_fragcount << aport->sw_subdivshift;	ASSERT(aport->hw_fragsize >= MIN_FRAGSIZE);	ASSERT(aport->hw_fragsize <= MAX_FRAGSIZE);	ASSERT(aport->hw_fragcount >= MIN_FRAGCOUNT(aport->hw_fragsize));	ASSERT(aport->hw_fragcount <= MAX_FRAGCOUNT(aport->hw_fragsize));	if (rport) {		int hwfrags, swfrags;		rport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE;		hwfrags = rport->hwbuf_max >> aport->hw_fragshift;		swfrags = aport->hw_fragcount - hwfrags;		if (swfrags < 2)			swfrags = 2;		rport->swbuf_size = swfrags * aport->hw_fragsize;		DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags);		DBGPV("read hwbuf_max = %d, swbuf_size = %d\n",		     rport->hwbuf_max, rport->swbuf_size);	}	if (wport) {		int hwfrags, swfrags;		int total_bytes = aport->hw_fragcount * aport->hw_fragsize;		wport->hwbuf_max = aport->hwbuf_size - DMACHUNK_SIZE;		if (wport->hwbuf_max > total_bytes)			wport->hwbuf_max = total_bytes;		hwfrags = wport->hwbuf_max >> aport->hw_fragshift;		DBGPV("hwfrags = %d\n", hwfrags);		swfrags = aport->hw_fragcount - hwfrags;		if (swfrags < 2)			swfrags = 2;		wport->swbuf_size = swfrags * aport->hw_fragsize;		DBGPV("hwfrags = %d, swfrags = %d\n", hwfrags, swfrags);		DBGPV("write hwbuf_max = %d, swbuf_size = %d\n",		     wport->hwbuf_max, wport->swbuf_size);	}	aport->swb_u_idx    = 0;	aport->swb_i_idx    = 0;	aport->byte_count   = 0;	/*	 * Is this a Cobalt bug?  We need to make this buffer extend	 * one page further than we actually use -- somehow memcpy	 * causes an exceptoin otherwise.  I suspect there's a bug in	 * Cobalt (or somewhere) where it's generating a fault on a	 * speculative load or something.  Obviously, I haven't taken	 * the time to track it down.	 */	aport->swbuf        = vmalloc(aport->swbuf_size + PAGE_SIZE);	if (!aport->swbuf)		return -ENOMEM;	if (rport && wport) {		ASSERT(aport == rport);		ASSERT(wport->swbuf == NULL);		/* One extra page - see comment above. */		wport->swbuf = vmalloc(aport->swbuf_size + PAGE_SIZE);		if (!wport->swbuf) {			vfree(aport->swbuf);			aport->swbuf = NULL;			return -ENOMEM;		}		wport->sample_size  = rport->sample_size;		wport->zero_word    = rport->zero_word;		wport->frame_size   = rport->frame_size;		wport->hw_fragshift = rport->hw_fragshift;		wport->hw_fragsize  = rport->hw_fragsize;		wport->hw_fragcount = rport->hw_fragcount;		wport->swbuf_size   = rport->swbuf_size;		wport->hwbuf_max    = rport->hwbuf_max;		wport->swb_u_idx    = rport->swb_u_idx;		wport->swb_i_idx    = rport->swb_i_idx;		wport->byte_count   = rport->byte_count;	}	if (rport) {		rport->swb_u_avail = 0;		rport->swb_i_avail = rport->swbuf_size;		rport->swstate = SW_RUN;		li_setup_dma(&rport->chan,			     &li_comm1,			     &devc->lith,			     rport->hwbuf_paddr,			     HWBUF_SHIFT,			     rport->hw_fragshift,			     rport->sw_channels,			     rport->sample_size);		ad1843_setup_adc(&devc->lith,				 rport->sw_framerate,				 rport->sw_samplefmt,				 rport->sw_channels);		li_enable_interrupts(&devc->lith, READ_INTR_MASK);		if (!(rport->flags & DISABLED)) {			ustmsc_t ustmsc;			rport->hwstate = HW_RUNNING;			li_activate_dma(&rport->chan);			li_read_USTMSC(&rport->chan, &ustmsc);			rport->MSC_offset = ustmsc.msc;		}	}	if (wport) {		if (wport->hwbuf_max > wport->swbuf_size)			wport->hwbuf_max = wport->swbuf_size;		wport->flags &= ~ERFLOWN;		wport->swb_u_avail = wport->swbuf_size;		wport->swb_i_avail = 0;		wport->swstate = SW_RUN;		li_setup_dma(&wport->chan,			     &li_comm2,			     &devc->lith,			     wport->hwbuf_paddr,			     HWBUF_SHIFT,			     wport->hw_fragshift,			     wport->sw_channels,

⌨️ 快捷键说明

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