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

📄 i810_audio.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 5 页
字号:
	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_page_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 (!dmabuf->enable && dmabuf->ready) {		if (!(dmabuf->trigger & trigger))			return;		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;	unsigned long tmo;	int count;	if (!dmabuf->ready)		return 0;	if(dmabuf->mapped) {		stop_dac(state);		return 0;	}	spin_lock_irqsave(&state->card->lock, flags);	fill_partial_frag(dmabuf);	/* 	 * This will make sure that our LVI is correct, that our	 * pointer is updated, and that the DAC is running.  We	 * have to force the setting of dmabuf->trigger to avoid	 * any possible deadlocks.	 */	dmabuf->trigger = PCM_ENABLE_OUTPUT;	__i810_update_lvi(state, 0);	spin_unlock_irqrestore(&state->card->lock, flags);	add_wait_queue(&dmabuf->wait, &wait);	for (;;) {		spin_lock_irqsave(&state->card->lock, flags);		i810_update_ptr(state);		count = dmabuf->count;		/* It seems that we have to set the current state to		 * TASK_INTERRUPTIBLE every time to make the process		 * really go to sleep.  This also has to be *after* the		 * update_ptr() call because update_ptr is likely to		 * do a wake_up() which will unset this before we ever		 * try to sleep, resuling in a tight loop in this code		 * instead of actually sleeping and waiting for an		 * interrupt to wake us up!		 */		__set_current_state(signals_allowed ?				    TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);		spin_unlock_irqrestore(&state->card->lock, flags);		if (count <= 0)			break;                if (signal_pending(current) && signals_allowed) {                        break;                }		/*		 * set the timeout to significantly longer than it *should*		 * take for the DAC to drain the DMA buffer		 */		tmo = (count * HZ) / (dmabuf->rate);		if (!schedule_timeout(tmo >= 2 ? tmo : 2)){			printk(KERN_ERR "i810_audio: drain_dac, dma timeout?\n");			count = 0;			break;		}	}	set_current_state(TASK_RUNNING);	remove_wait_queue(&dmabuf->wait, &wait);	if(count > 0 && signal_pending(current) && signals_allowed)		return -ERESTARTSYS;	stop_dac(state);	return 0;}static void i810_channel_interrupt(struct i810_card *card){	int i, count;	#ifdef DEBUG_INTERRUPTS	printk("CHANNEL ");#endif	for(i=0;i<NR_HW_CH;i++)	{		struct i810_state *state = card->states[i];		struct i810_channel *c;		struct dmabuf *dmabuf;		unsigned long port;		u16 status;				if(!state)			continue;		if(!state->dmabuf.ready)			continue;		dmabuf = &state->dmabuf;		if(dmabuf->enable & DAC_RUNNING) {			c=dmabuf->write_channel;		} else if(dmabuf->enable & ADC_RUNNING) {			c=dmabuf->read_channel;		} else	/* This can occur going from R/W to close */			continue;				port = c->port;		if(card->pci_id == PCI_DEVICE_ID_SI_7012)			status = I810_IOREADW(card, port + OFF_PICB);		else			status = I810_IOREADW(card, port + OFF_SR);#ifdef DEBUG_INTERRUPTS		printk("NUM %d PORT %X IRQ ( ST%d ", c->num, c->port, status);#endif		if(status & DMA_INT_COMPLETE)		{			/* only wake_up() waiters if this interrupt signals			 * us being beyond a userfragsize of data open or			 * available, and i810_update_ptr() does that for			 * us			 */			i810_update_ptr(state);#ifdef DEBUG_INTERRUPTS

⌨️ 快捷键说明

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