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

📄 i810_audio.c

📁 宋宝华的《Linux设备驱动开发详解》第一版的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
	if(card->pci_id == PCI_DEVICE_ID_SI_7012)		I810_IOWRITEB( I810_IOREADB(card, PI_PICB), card, PI_PICB );	else		I810_IOWRITEB( I810_IOREADB(card, PI_SR), card, PI_SR );	I810_IOWRITEL( I810_IOREADL(card, GLOB_STA) & INT_PI, card, GLOB_STA);}static void stop_adc(struct i810_state *state){	struct i810_card *card = state->card;	unsigned long flags;	spin_lock_irqsave(&card->lock, flags);	__stop_adc(state);	spin_unlock_irqrestore(&card->lock, flags);}static inline void __start_adc(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	if (dmabuf->count < dmabuf->dmasize && dmabuf->ready && !dmabuf->enable &&	    (dmabuf->trigger & PCM_ENABLE_INPUT)) {		dmabuf->enable |= ADC_RUNNING;		// Interrupt enable, LVI enable, DMA enable		I810_IOWRITEB(0x10 | 0x04 | 0x01, state->card, PI_CR);	}}static void start_adc(struct i810_state *state){	struct i810_card *card = state->card;	unsigned long flags;	spin_lock_irqsave(&card->lock, flags);	__start_adc(state);	spin_unlock_irqrestore(&card->lock, flags);}/* stop playback (lock held) */static inline void __stop_dac(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	struct i810_card *card = state->card;	dmabuf->enable &= ~DAC_RUNNING;	I810_IOWRITEB(0, card, PO_CR);	// wait for the card to acknowledge shutdown	while( I810_IOREADB(card, PO_CR) != 0 ) ;	// now clear any latent interrupt bits (like the halt bit)	if(card->pci_id == PCI_DEVICE_ID_SI_7012)		I810_IOWRITEB( I810_IOREADB(card, PO_PICB), card, PO_PICB );	else		I810_IOWRITEB( I810_IOREADB(card, PO_SR), card, PO_SR );	I810_IOWRITEL( I810_IOREADL(card, GLOB_STA) & INT_PO, card, GLOB_STA);}static void stop_dac(struct i810_state *state){	struct i810_card *card = state->card;	unsigned long flags;	spin_lock_irqsave(&card->lock, flags);	__stop_dac(state);	spin_unlock_irqrestore(&card->lock, flags);}	static inline void __start_dac(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable &&	    (dmabuf->trigger & PCM_ENABLE_OUTPUT)) {		dmabuf->enable |= DAC_RUNNING;		// Interrupt enable, LVI enable, DMA enable		I810_IOWRITEB(0x10 | 0x04 | 0x01, state->card, PO_CR);	}}static void start_dac(struct i810_state *state){	struct i810_card *card = state->card;	unsigned long flags;	spin_lock_irqsave(&card->lock, flags);	__start_dac(state);	spin_unlock_irqrestore(&card->lock, flags);}#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT)#define DMABUF_MINORDER 1/* allocate DMA buffer, playback and recording buffer should be allocated separately */static int alloc_dmabuf(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	void *rawbuf= NULL;	int order, size;	struct page *page, *pend;	/* If we don't have any oss frag params, then use our default ones */	if(dmabuf->ossmaxfrags == 0)		dmabuf->ossmaxfrags = 4;	if(dmabuf->ossfragsize == 0)		dmabuf->ossfragsize = (PAGE_SIZE<<DMABUF_DEFAULTORDER)/dmabuf->ossmaxfrags;	size = dmabuf->ossfragsize * dmabuf->ossmaxfrags;	if(dmabuf->rawbuf && (PAGE_SIZE << dmabuf->buforder) == size)		return 0;	/* alloc enough to satisfy the oss params */	for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) {		if ( (PAGE_SIZE<<order) > size )			continue;		if ((rawbuf = pci_alloc_consistent(state->card->pci_dev,						   PAGE_SIZE << order,						   &dmabuf->dma_handle)))			break;	}	if (!rawbuf)		return -ENOMEM;#ifdef DEBUG	printk("i810_audio: allocated %ld (order = %d) bytes at %p\n",	       PAGE_SIZE << order, order, rawbuf);#endif	dmabuf->ready  = dmabuf->mapped = 0;	dmabuf->rawbuf = rawbuf;	dmabuf->buforder = order;		/* now mark the pages as reserved; otherwise remap_pfn_range doesn't do what we want */	pend = virt_to_page(rawbuf + (PAGE_SIZE << order) - 1);	for (page = virt_to_page(rawbuf); page <= pend; page++)		SetPageReserved(page);	return 0;}/* free DMA buffer */static void dealloc_dmabuf(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	struct page *page, *pend;	if (dmabuf->rawbuf) {		/* undo marking the pages as reserved */		pend = virt_to_page(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1);		for (page = virt_to_page(dmabuf->rawbuf); page <= pend; page++)			ClearPageReserved(page);		pci_free_consistent(state->card->pci_dev, PAGE_SIZE << dmabuf->buforder,				    dmabuf->rawbuf, dmabuf->dma_handle);	}	dmabuf->rawbuf = NULL;	dmabuf->mapped = dmabuf->ready = 0;}static int prog_dmabuf(struct i810_state *state, unsigned rec){	struct dmabuf *dmabuf = &state->dmabuf;	struct i810_channel *c;	struct sg_item *sg;	unsigned long flags;	int ret;	unsigned fragint;	int i;	spin_lock_irqsave(&state->card->lock, flags);	if(dmabuf->enable & DAC_RUNNING)		__stop_dac(state);	if(dmabuf->enable & ADC_RUNNING)		__stop_adc(state);	dmabuf->total_bytes = 0;	dmabuf->count = dmabuf->error = 0;	dmabuf->swptr = dmabuf->hwptr = 0;	spin_unlock_irqrestore(&state->card->lock, flags);	/* allocate DMA buffer, let alloc_dmabuf determine if we are already	 * allocated well enough or if we should replace the current buffer	 * (assuming one is already allocated, if it isn't, then allocate it).	 */	if ((ret = alloc_dmabuf(state)))		return ret;	/* FIXME: figure out all this OSS fragment stuff */	/* I did, it now does what it should according to the OSS API.  DL */	/* We may not have realloced our dmabuf, but the fragment size to	 * fragment number ratio may have changed, so go ahead and reprogram	 * things	 */	dmabuf->dmasize = PAGE_SIZE << dmabuf->buforder;	dmabuf->numfrag = SG_LEN;	dmabuf->fragsize = dmabuf->dmasize/dmabuf->numfrag;	dmabuf->fragsamples = dmabuf->fragsize >> 1;	dmabuf->fragshift = ffs(dmabuf->fragsize) - 1;	dmabuf->userfragsize = dmabuf->ossfragsize;	dmabuf->userfrags = dmabuf->dmasize/dmabuf->ossfragsize;	memset(dmabuf->rawbuf, 0, dmabuf->dmasize);	if(dmabuf->ossmaxfrags == 4) {		fragint = 8;	} else if (dmabuf->ossmaxfrags == 8) {		fragint = 4;	} else if (dmabuf->ossmaxfrags == 16) {		fragint = 2;	} else {		fragint = 1;	}	/*	 *	Now set up the ring 	 */	if(dmabuf->read_channel)		c = dmabuf->read_channel;	else		c = dmabuf->write_channel;	while(c != NULL) {		sg=&c->sg[0];		/*		 *	Load up 32 sg entries and take an interrupt at half		 *	way (we might want more interrupts later..) 		 */	  		for(i=0;i<dmabuf->numfrag;i++)		{			sg->busaddr=(u32)dmabuf->dma_handle+dmabuf->fragsize*i;			// the card will always be doing 16bit stereo			sg->control=dmabuf->fragsamples;			if(state->card->pci_id == PCI_DEVICE_ID_SI_7012)				sg->control <<= 1;			sg->control|=CON_BUFPAD;			// set us up to get IOC interrupts as often as needed to			// satisfy numfrag requirements, no more			if( ((i+1) % fragint) == 0) {				sg->control|=CON_IOC;			}			sg++;		}		spin_lock_irqsave(&state->card->lock, flags);		I810_IOWRITEB(2, state->card, c->port+OFF_CR);   /* reset DMA machine */		while( I810_IOREADB(state->card, c->port+OFF_CR) & 0x02 ) ;		I810_IOWRITEL((u32)state->card->chandma +		    c->num*sizeof(struct i810_channel),		    state->card, c->port+OFF_BDBAR);		CIV_TO_LVI(state->card, c->port, 0);		spin_unlock_irqrestore(&state->card->lock, flags);		if(c != dmabuf->write_channel)			c = dmabuf->write_channel;		else			c = NULL;	}		/* set the ready flag for the dma buffer */	dmabuf->ready = 1;#ifdef DEBUG	printk("i810_audio: prog_dmabuf, sample rate = %d, format = %d,\n\tnumfrag = %d, "	       "fragsize = %d dmasize = %d\n",	       dmabuf->rate, dmabuf->fmt, dmabuf->numfrag,	       dmabuf->fragsize, dmabuf->dmasize);#endif	return 0;}static void __i810_update_lvi(struct i810_state *state, int rec){	struct dmabuf *dmabuf = &state->dmabuf;	int x, port;	int trigger;	int count, fragsize;	void (*start)(struct i810_state *);	count = dmabuf->count;	if (rec) {		port = dmabuf->read_channel->port;		trigger = PCM_ENABLE_INPUT;		start = __start_adc;		count = dmabuf->dmasize - count;	} else {		port = dmabuf->write_channel->port;		trigger = PCM_ENABLE_OUTPUT;		start = __start_dac;	}	/* Do not process partial fragments. */	fragsize = dmabuf->fragsize;	if (count < fragsize)		return;	/* if we are currently stopped, then our CIV is actually set to our	 * *last* sg segment and we are ready to wrap to the next.  However,	 * if we set our LVI to the last sg segment, then it won't wrap to	 * the next sg segment, it won't even get a start.  So, instead, when	 * we are stopped, we set both the LVI value and also we increment	 * the CIV value to the next sg segment to be played so that when	 * we call start, things will operate properly.  Since the CIV can't	 * be written to directly for this purpose, we set the LVI to CIV + 1	 * temporarily.  Once the engine has started we set the LVI to its	 * final value.	 */	if (!dmabuf->enable && dmabuf->ready) {		if (!(dmabuf->trigger & trigger))			return;		CIV_TO_LVI(state->card, port, 1);		start(state);		while (!(I810_IOREADB(state->card, port + OFF_CR) & ((1<<4) | (1<<2))))			;	}	/* MASKP2(swptr, fragsize) - 1 is the tail of our transfer */	x = MODULOP2(MASKP2(dmabuf->swptr, fragsize) - 1, dmabuf->dmasize);	x >>= dmabuf->fragshift;	I810_IOWRITEB(x, state->card, port + OFF_LVI);}static void i810_update_lvi(struct i810_state *state, int rec){	struct dmabuf *dmabuf = &state->dmabuf;	unsigned long flags;	if(!dmabuf->ready)		return;	spin_lock_irqsave(&state->card->lock, flags);	__i810_update_lvi(state, rec);	spin_unlock_irqrestore(&state->card->lock, flags);}/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */static void i810_update_ptr(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	unsigned hwptr;	unsigned fragmask, dmamask;	int diff;	fragmask = MASKP2(~0, dmabuf->fragsize);	dmamask = MODULOP2(~0, dmabuf->dmasize);	/* error handling and process wake up for ADC */	if (dmabuf->enable == ADC_RUNNING) {		/* update hardware pointer */		hwptr = i810_get_dma_addr(state, 1) & fragmask;		diff = (hwptr - dmabuf->hwptr) & dmamask;#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP)		printk("ADC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);#endif		dmabuf->hwptr = hwptr;		dmabuf->total_bytes += diff;		dmabuf->count += diff;		if (dmabuf->count > dmabuf->dmasize) {			/* buffer underrun or buffer overrun */			/* this is normal for the end of a read */			/* only give an error if we went past the */			/* last valid sg entry */			if (GET_CIV(state->card, PI_BASE) !=			    GET_LVI(state->card, PI_BASE)) {				printk(KERN_WARNING "i810_audio: DMA overrun on read\n");				dmabuf->error++;			}		}		if (diff)			wake_up(&dmabuf->wait);	}	/* error handling and process wake up for DAC */	if (dmabuf->enable == DAC_RUNNING) {		/* update hardware pointer */		hwptr = i810_get_dma_addr(state, 0) & fragmask;		diff = (hwptr - dmabuf->hwptr) & dmamask;#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP)		printk("DAC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);#endif		dmabuf->hwptr = hwptr;		dmabuf->total_bytes += diff;		dmabuf->count -= diff;		if (dmabuf->count < 0) {			/* buffer underrun or buffer overrun */			/* this is normal for the end of a write */			/* only give an error if we went past the */			/* last valid sg entry */			if (GET_CIV(state->card, PO_BASE) !=			    GET_LVI(state->card, PO_BASE)) {				printk(KERN_WARNING "i810_audio: DMA overrun on write\n");				printk("i810_audio: CIV %d, LVI %d, hwptr %x, "					"count %d\n",					GET_CIV(state->card, PO_BASE),					GET_LVI(state->card, PO_BASE),					dmabuf->hwptr, dmabuf->count);				dmabuf->error++;			}		}		if (diff)			wake_up(&dmabuf->wait);	}}static inline int i810_get_free_write_space(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	int free;	i810_update_ptr(state);	// catch underruns during playback	if (dmabuf->count < 0) {		dmabuf->count = 0;		dmabuf->swptr = dmabuf->hwptr;	}	free = dmabuf->dmasize - dmabuf->count;	if(free < 0)		return(0);	return(free);}static inline int i810_get_available_read_data(struct i810_state *state){	struct dmabuf *dmabuf = &state->dmabuf;	int avail;	i810_update_ptr(state);	// catch overruns during record	if (dmabuf->count > dmabuf->dmasize) {		dmabuf->count = dmabuf->dmasize;		dmabuf->swptr = dmabuf->hwptr;	}	avail = dmabuf->count;	if(avail < 0)		return(0);	return(avail);}static inline void fill_partial_frag(struct dmabuf *dmabuf){	unsigned fragsize;	unsigned swptr, len;	fragsize = dmabuf->fragsize;	swptr = dmabuf->swptr;	len = fragsize - MODULOP2(dmabuf->swptr, fragsize);	if (len == fragsize)		return;	memset(dmabuf->rawbuf + swptr, '\0', len);	dmabuf->swptr = MODULOP2(swptr + len, dmabuf->dmasize);	dmabuf->count += len;}static int drain_dac(struct i810_state *state, int signals_allowed){	DECLARE_WAITQUEUE(wait, current);	struct dmabuf *dmabuf = &state->dmabuf;	unsigned long flags;

⌨️ 快捷键说明

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