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

📄 i810_audio.c

📁 iis s3c2410-uda1341语音系统的 开发
💻 C
📖 第 1 页 / 共 5 页
字号:
	 * (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->userfragsize = dmabuf->ossfragsize;	dmabuf->userfrags = dmabuf->dmasize/dmabuf->ossfragsize;	memset(dmabuf->rawbuf, 0, dmabuf->dmasize);	if(dmabuf->ossmaxfrags == 4) {		fragint = 8;		dmabuf->fragshift = 2;	} else if (dmabuf->ossmaxfrags == 8) {		fragint = 4;		dmabuf->fragshift = 3;	} else if (dmabuf->ossmaxfrags == 16) {		fragint = 2;		dmabuf->fragshift = 4;	} else {		fragint = 1;		dmabuf->fragshift = 5;	}	/*	 *	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=virt_to_bus(dmabuf->rawbuf+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);		outb(2, state->card->iobase+c->port+OFF_CR);   /* reset DMA machine */		outl(virt_to_bus(&c->sg[0]), state->card->iobase+c->port+OFF_BDBAR);		outb(0, state->card->iobase+c->port+OFF_CIV);		outb(0, state->card->iobase+c->port+OFF_LVI);		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;		port = state->card->iobase;	if(rec)		port += dmabuf->read_channel->port;	else		port += dmabuf->write_channel->port;	/* 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_{dac,adc}, things will operate properly	 */	if (!dmabuf->enable && dmabuf->ready) {		if(rec && dmabuf->count < dmabuf->dmasize &&		   (dmabuf->trigger & PCM_ENABLE_INPUT))		{			outb((inb(port+OFF_CIV)+1)&31, port+OFF_LVI);			__start_adc(state);			while( !(inb(port + OFF_CR) & ((1<<4) | (1<<2))) ) ;		} else if (!rec && dmabuf->count &&			   (dmabuf->trigger & PCM_ENABLE_OUTPUT))		{			outb((inb(port+OFF_CIV)+1)&31, port+OFF_LVI);			__start_dac(state);			while( !(inb(port + OFF_CR) & ((1<<4) | (1<<2))) ) ;		}	}	/* swptr - 1 is the tail of our transfer */	x = (dmabuf->dmasize + dmabuf->swptr - 1) % dmabuf->dmasize;	x /= dmabuf->fragsize;	outb(x, 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;	int diff;	/* error handling and process wake up for DAC */	if (dmabuf->enable == ADC_RUNNING) {		/* update hardware pointer */		hwptr = i810_get_dma_addr(state, 1);		diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;#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((inb(state->card->iobase + PI_CIV) & 31) !=			   (inb(state->card->iobase + PI_LVI) & 31)) {				printk(KERN_WARNING "i810_audio: DMA overrun on read\n");				dmabuf->error++;			}		}		if (dmabuf->count > dmabuf->userfragsize)			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);		diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;#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((inb(state->card->iobase + PO_CIV) & 31) !=			   (inb(state->card->iobase + PO_LVI) & 31)) {				printk(KERN_WARNING "i810_audio: DMA overrun on write\n");				printk("i810_audio: CIV %d, LVI %d, hwptr %x, "					"count %d\n",					inb(state->card->iobase + PO_CIV) & 31,					inb(state->card->iobase + PO_LVI) & 31,					dmabuf->hwptr, dmabuf->count);				dmabuf->error++;			}		}		if (dmabuf->count < (dmabuf->dmasize-dmabuf->userfragsize))			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;	free -= (dmabuf->hwptr % dmabuf->fragsize);	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;	avail -= (dmabuf->hwptr % dmabuf->fragsize);	if(avail < 0)		return(0);	return(avail);}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;	}	add_wait_queue(&dmabuf->wait, &wait);	for (;;) {		spin_lock_irqsave(&state->card->lock, flags);		i810_update_ptr(state);		count = dmabuf->count;		spin_unlock_irqrestore(&state->card->lock, flags);		if (count <= 0)			break;		/* 		 * 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.		 */		if(!dmabuf->enable) {			dmabuf->trigger = PCM_ENABLE_OUTPUT;			i810_update_lvi(state,0);		}                if (signal_pending(current) && signals_allowed) {                        break;                }		/* 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(TASK_INTERRUPTIBLE);		/*		 * 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 = card->iobase;		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 = inw(port + OFF_PICB);		else			status = inw(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			printk("COMP %d ", dmabuf->hwptr /					dmabuf->fragsize);#endif		}		if(status & (DMA_INT_LVI | DMA_INT_DCH))		{			/* wake_up() unconditionally on LVI and DCH */			i810_update_ptr(state);			wake_up(&dmabuf->wait);#ifdef DEBUG_INTERRUPTS			if(status & DMA_INT_LVI)				printk("LVI ");			if(status & DMA_INT_DCH)				printk("DCH -");#endif			if(dmabuf->enable & DAC_RUNNING)				count = dmabuf->count;			else				count = dmabuf->dmasize - dmabuf->count;			if(count > 0) {				outb(inb(port+OFF_CR) | 1, port+OFF_CR);#ifdef DEBUG_INTERRUPTS				printk(" CONTINUE ");#endif			} else {				if (dmabuf->enable & DAC_RUNNING)					__stop_dac(state);				if (dmabuf->enable & ADC_RUNNING)					__stop_adc(state);				dmabuf->enable = 0;				wake_up(&dmabuf->wait);#ifdef DEBUG_INTERRUPTS				printk(" STOP ");#endif			}		}		if(card->pci_id == PCI_DEVICE_ID_SI_7012)			outw(status & DMA_INT_MASK, port + OFF_PICB);		else			outw(status & DMA_INT_MASK, port + OFF_SR);	}#ifdef DEBUG_INTERRUPTS	printk(")\n");#endif}static void i810_interrupt(int irq, void *dev_id, struct pt_regs *regs){	struct i810_card *card = (struct i810_card *)dev_id;	u32 status;	spin_lock(&card->lock);	status = inl(card->iobase + GLOB_STA);	if(!(status & INT_MASK)) 	{		spin_unlock(&card->lock);		return;  /* not for us */	}	if(status & (INT_PO|INT_PI|INT_MC))		i810_channel_interrupt(card); 	/* clear 'em */	outl(status & INT_MASK, card->iobase + GLOB_STA);	spin_unlock(&card->lock);}/* in this loop, dmabuf.count signifies the amount of data that is   waiting to be copied to the user's buffer.  It is filled by the dma   machine and drained by this loop. */static ssize_t i810_read(struct file *file, char *buffer, size_t count, loff_t *ppos){	struct i810_state *state = (struct i810_state *)file->private_data;	struct i810_card *card=state ? state->card : 0;	struct dmabuf *dmabuf = &state->dmabuf;	ssize_t ret;	unsigned long flags;	unsigned int swptr;	int cnt;        DECLARE_WAITQUEUE(waita, current);#ifdef DEBUG2	printk("i810_audio: i810_read called, count = %d\n", count);#endif	if (ppos != &file->f_pos)		return -ESPIPE;

⌨️ 快捷键说明

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