au1550_ac97.c

来自「linux 内核源代码」· C语言 代码 · 共 2,130 行 · 第 1/4 页

C
2,130
字号
		ac97_config |= PSC_AC97CFG_TXSLOT_ENA(3);		ac97_config |= PSC_AC97CFG_TXSLOT_ENA(4);	}	au_writel(ac97_config, PSC_AC97CFG);	au_sync();	ac97_config |= PSC_AC97CFG_DE_ENABLE;	au_writel(ac97_config, PSC_AC97CFG);	au_sync();	/* Wait for Device ready.	*/	do {		stat = au_readl(PSC_AC97STAT);		au_sync();	} while ((stat & PSC_AC97STAT_DR) == 0);}static voidset_recv_slots(int num_channels){	u32	ac97_config, stat;	ac97_config = au_readl(PSC_AC97CFG);	au_sync();	ac97_config &= ~(PSC_AC97CFG_RXSLOT_MASK | PSC_AC97CFG_DE_ENABLE);	au_writel(ac97_config, PSC_AC97CFG);	au_sync();	/* Always enable slots 3 and 4 (stereo). Slot 6 is	 * optional Mic ADC, which we don't support yet.	 */	ac97_config |= PSC_AC97CFG_RXSLOT_ENA(3);	ac97_config |= PSC_AC97CFG_RXSLOT_ENA(4);	au_writel(ac97_config, PSC_AC97CFG);	au_sync();	ac97_config |= PSC_AC97CFG_DE_ENABLE;	au_writel(ac97_config, PSC_AC97CFG);	au_sync();	/* Wait for Device ready.	*/	do {		stat = au_readl(PSC_AC97STAT);		au_sync();	} while ((stat & PSC_AC97STAT_DR) == 0);}/* Hold spinlock for both start_dac() and start_adc() calls */static voidstart_dac(struct au1550_state *s){	struct dmabuf  *db = &s->dma_dac;	if (!db->stopped)		return;	set_xmit_slots(db->num_channels);	au_writel(PSC_AC97PCR_TC, PSC_AC97PCR);	au_sync();	au_writel(PSC_AC97PCR_TS, PSC_AC97PCR);	au_sync();	au1xxx_dbdma_start(db->dmanr);	db->stopped = 0;}static voidstart_adc(struct au1550_state *s){	struct dmabuf  *db = &s->dma_adc;	int	i;	if (!db->stopped)		return;	/* Put two buffers on the ring to get things started.	*/	for (i=0; i<2; i++) {		au1xxx_dbdma_put_dest(db->dmanr, db->nextIn, db->dma_fragsize);		db->nextIn += db->dma_fragsize;		if (db->nextIn >= db->rawbuf + db->dmasize)			db->nextIn -= db->dmasize;	}	set_recv_slots(db->num_channels);	au1xxx_dbdma_start(db->dmanr);	au_writel(PSC_AC97PCR_RC, PSC_AC97PCR);	au_sync();	au_writel(PSC_AC97PCR_RS, PSC_AC97PCR);	au_sync();	db->stopped = 0;}static intprog_dmabuf(struct au1550_state *s, struct dmabuf *db){	unsigned user_bytes_per_sec;	unsigned        bufs;	unsigned        rate = db->sample_rate;	if (!db->rawbuf) {		db->ready = db->mapped = 0;		db->buforder = 5;	/* 32 * PAGE_SIZE */		db->rawbuf = kmalloc((PAGE_SIZE << db->buforder), GFP_KERNEL);		if (!db->rawbuf)			return -ENOMEM;	}	db->cnt_factor = 1;	if (db->sample_size == 8)		db->cnt_factor *= 2;	if (db->num_channels == 1)		db->cnt_factor *= 2;	db->cnt_factor *= db->src_factor;	db->count = 0;	db->dma_qcount = 0;	db->nextIn = db->nextOut = db->rawbuf;	db->user_bytes_per_sample = (db->sample_size>>3) * db->num_channels;	db->dma_bytes_per_sample = 2 * ((db->num_channels == 1) ?					2 : db->num_channels);	user_bytes_per_sec = rate * db->user_bytes_per_sample;	bufs = PAGE_SIZE << db->buforder;	if (db->ossfragshift) {		if ((1000 << db->ossfragshift) < user_bytes_per_sec)			db->fragshift = ld2(user_bytes_per_sec/1000);		else			db->fragshift = db->ossfragshift;	} else {		db->fragshift = ld2(user_bytes_per_sec / 100 /				    (db->subdivision ? db->subdivision : 1));		if (db->fragshift < 3)			db->fragshift = 3;	}	db->fragsize = 1 << db->fragshift;	db->dma_fragsize = db->fragsize * db->cnt_factor;	db->numfrag = bufs / db->dma_fragsize;	while (db->numfrag < 4 && db->fragshift > 3) {		db->fragshift--;		db->fragsize = 1 << db->fragshift;		db->dma_fragsize = db->fragsize * db->cnt_factor;		db->numfrag = bufs / db->dma_fragsize;	}	if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)		db->numfrag = db->ossmaxfrags;	db->dmasize = db->dma_fragsize * db->numfrag;	memset(db->rawbuf, 0, bufs);	pr_debug("prog_dmabuf: rate=%d, samplesize=%d, channels=%d\n",	    rate, db->sample_size, db->num_channels);	pr_debug("prog_dmabuf: fragsize=%d, cnt_factor=%d, dma_fragsize=%d\n",	    db->fragsize, db->cnt_factor, db->dma_fragsize);	pr_debug("prog_dmabuf: numfrag=%d, dmasize=%d\n", db->numfrag, db->dmasize);	db->ready = 1;	return 0;}static intprog_dmabuf_adc(struct au1550_state *s){	stop_adc(s);	return prog_dmabuf(s, &s->dma_adc);}static intprog_dmabuf_dac(struct au1550_state *s){	stop_dac(s);	return prog_dmabuf(s, &s->dma_dac);}static void dac_dma_interrupt(int irq, void *dev_id){	struct au1550_state *s = (struct au1550_state *) dev_id;	struct dmabuf  *db = &s->dma_dac;	u32	ac97c_stat;	spin_lock(&s->lock);	ac97c_stat = au_readl(PSC_AC97STAT);	if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE))		pr_debug("AC97C status = 0x%08x\n", ac97c_stat);	db->dma_qcount--;	if (db->count >= db->fragsize) {		if (au1xxx_dbdma_put_source(db->dmanr, db->nextOut,							db->fragsize) == 0) {			err("qcount < 2 and no ring room!");		}		db->nextOut += db->fragsize;		if (db->nextOut >= db->rawbuf + db->dmasize)			db->nextOut -= db->dmasize;		db->count -= db->fragsize;		db->total_bytes += db->dma_fragsize;		db->dma_qcount++;	}	/* wake up anybody listening */	if (waitqueue_active(&db->wait))		wake_up(&db->wait);	spin_unlock(&s->lock);}static void adc_dma_interrupt(int irq, void *dev_id){	struct	au1550_state *s = (struct au1550_state *)dev_id;	struct	dmabuf  *dp = &s->dma_adc;	u32	obytes;	char	*obuf;	spin_lock(&s->lock);	/* Pull the buffer from the dma queue.	*/	au1xxx_dbdma_get_dest(dp->dmanr, (void *)(&obuf), &obytes);	if ((dp->count + obytes) > dp->dmasize) {		/* Overrun. Stop ADC and log the error		*/		spin_unlock(&s->lock);		stop_adc(s);		dp->error++;		err("adc overrun");		return;	}	/* Put a new empty buffer on the destination DMA.	*/	au1xxx_dbdma_put_dest(dp->dmanr, dp->nextIn, dp->dma_fragsize);	dp->nextIn += dp->dma_fragsize;	if (dp->nextIn >= dp->rawbuf + dp->dmasize)		dp->nextIn -= dp->dmasize;	dp->count += obytes;	dp->total_bytes += obytes;	/* wake up anybody listening	*/	if (waitqueue_active(&dp->wait))		wake_up(&dp->wait);	spin_unlock(&s->lock);}static loff_tau1550_llseek(struct file *file, loff_t offset, int origin){	return -ESPIPE;}static intau1550_open_mixdev(struct inode *inode, struct file *file){	file->private_data = &au1550_state;	return 0;}static intau1550_release_mixdev(struct inode *inode, struct file *file){	return 0;}static intmixdev_ioctl(struct ac97_codec *codec, unsigned int cmd,                        unsigned long arg){	return codec->mixer_ioctl(codec, cmd, arg);}static intau1550_ioctl_mixdev(struct inode *inode, struct file *file,			       unsigned int cmd, unsigned long arg){	struct au1550_state *s = (struct au1550_state *)file->private_data;	struct ac97_codec *codec = s->codec;	return mixdev_ioctl(codec, cmd, arg);}static /*const */ struct file_operations au1550_mixer_fops = {	owner:THIS_MODULE,	llseek:au1550_llseek,	ioctl:au1550_ioctl_mixdev,	open:au1550_open_mixdev,	release:au1550_release_mixdev,};static intdrain_dac(struct au1550_state *s, int nonblock){	unsigned long   flags;	int             count, tmo;	if (s->dma_dac.mapped || !s->dma_dac.ready || s->dma_dac.stopped)		return 0;	for (;;) {		spin_lock_irqsave(&s->lock, flags);		count = s->dma_dac.count;		spin_unlock_irqrestore(&s->lock, flags);		if (count <= s->dma_dac.fragsize)			break;		if (signal_pending(current))			break;		if (nonblock)			return -EBUSY;		tmo = 1000 * count / (s->no_vra ?				      48000 : s->dma_dac.sample_rate);		tmo /= s->dma_dac.dma_bytes_per_sample;		au1550_delay(tmo);	}	if (signal_pending(current))		return -ERESTARTSYS;	return 0;}static inline u8 S16_TO_U8(s16 ch){	return (u8) (ch >> 8) + 0x80;}static inline s16 U8_TO_S16(u8 ch){	return (s16) (ch - 0x80) << 8;}/* * Translates user samples to dma buffer suitable for AC'97 DAC data: *     If mono, copy left channel to right channel in dma buffer. *     If 8 bit samples, cvt to 16-bit before writing to dma buffer. *     If interpolating (no VRA), duplicate every audio frame src_factor times. */static inttranslate_from_user(struct dmabuf *db, char* dmabuf, char* userbuf,							       int dmacount){	int             sample, i;	int             interp_bytes_per_sample;	int             num_samples;	int             mono = (db->num_channels == 1);	char            usersample[12];	s16             ch, dmasample[6];	if (db->sample_size == 16 && !mono && db->src_factor == 1) {		/* no translation necessary, just copy		*/		if (copy_from_user(dmabuf, userbuf, dmacount))			return -EFAULT;		return dmacount;	}	interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor;	num_samples = dmacount / interp_bytes_per_sample;	for (sample = 0; sample < num_samples; sample++) {		if (copy_from_user(usersample, userbuf,				   db->user_bytes_per_sample)) {			return -EFAULT;		}		for (i = 0; i < db->num_channels; i++) {			if (db->sample_size == 8)				ch = U8_TO_S16(usersample[i]);			else				ch = *((s16 *) (&usersample[i * 2]));			dmasample[i] = ch;			if (mono)				dmasample[i + 1] = ch;	/* right channel */		}		/* duplicate every audio frame src_factor times		*/		for (i = 0; i < db->src_factor; i++)			memcpy(dmabuf, dmasample, db->dma_bytes_per_sample);		userbuf += db->user_bytes_per_sample;		dmabuf += interp_bytes_per_sample;	}	return num_samples * interp_bytes_per_sample;}/* * Translates AC'97 ADC samples to user buffer: *     If mono, send only left channel to user buffer. *     If 8 bit samples, cvt from 16 to 8 bit before writing to user buffer. *     If decimating (no VRA), skip over src_factor audio frames. */static inttranslate_to_user(struct dmabuf *db, char* userbuf, char* dmabuf,							     int dmacount){	int             sample, i;	int             interp_bytes_per_sample;	int             num_samples;	int             mono = (db->num_channels == 1);	char            usersample[12];	if (db->sample_size == 16 && !mono && db->src_factor == 1) {		/* no translation necessary, just copy		*/		if (copy_to_user(userbuf, dmabuf, dmacount))			return -EFAULT;		return dmacount;	}	interp_bytes_per_sample = db->dma_bytes_per_sample * db->src_factor;	num_samples = dmacount / interp_bytes_per_sample;	for (sample = 0; sample < num_samples; sample++) {		for (i = 0; i < db->num_channels; i++) {			if (db->sample_size == 8)				usersample[i] =					S16_TO_U8(*((s16 *) (&dmabuf[i * 2])));			else				*((s16 *) (&usersample[i * 2])) =					*((s16 *) (&dmabuf[i * 2]));		}		if (copy_to_user(userbuf, usersample,				 db->user_bytes_per_sample)) {			return -EFAULT;		}		userbuf += db->user_bytes_per_sample;		dmabuf += interp_bytes_per_sample;	}	return num_samples * interp_bytes_per_sample;}/* * Copy audio data to/from user buffer from/to dma buffer, taking care * that we wrap when reading/writing the dma buffer. Returns actual byte * count written to or read from the dma buffer. */static intcopy_dmabuf_user(struct dmabuf *db, char* userbuf, int count, int to_user){	char           *bufptr = to_user ? db->nextOut : db->nextIn;	char           *bufend = db->rawbuf + db->dmasize;	int             cnt, ret;	if (bufptr + count > bufend) {		int             partial = (int) (bufend - bufptr);		if (to_user) {			if ((cnt = translate_to_user(db, userbuf,						     bufptr, partial)) < 0)				return cnt;			ret = cnt;			if ((cnt = translate_to_user(db, userbuf + partial,						     db->rawbuf,						     count - partial)) < 0)				return cnt;			ret += cnt;		} else {			if ((cnt = translate_from_user(db, bufptr, userbuf,						       partial)) < 0)				return cnt;			ret = cnt;			if ((cnt = translate_from_user(db, db->rawbuf,						       userbuf + partial,						       count - partial)) < 0)				return cnt;			ret += cnt;		}	} else {		if (to_user)			ret = translate_to_user(db, userbuf, bufptr, count);		else			ret = translate_from_user(db, bufptr, userbuf, count);	}	return ret;}static ssize_tau1550_read(struct file *file, char *buffer, size_t count, loff_t *ppos){	struct au1550_state *s = (struct au1550_state *)file->private_data;	struct dmabuf  *db = &s->dma_adc;	DECLARE_WAITQUEUE(wait, current);	ssize_t         ret;	unsigned long   flags;	int             cnt, usercnt, avail;	if (db->mapped)		return -ENXIO;	if (!access_ok(VERIFY_WRITE, buffer, count))		return -EFAULT;	ret = 0;	count *= db->cnt_factor;	mutex_lock(&s->sem);	add_wait_queue(&db->wait, &wait);	while (count > 0) {		/* wait for samples in ADC dma buffer		*/		do {			spin_lock_irqsave(&s->lock, flags);			if (db->stopped)				start_adc(s);			avail = db->count;			if (avail <= 0)				__set_current_state(TASK_INTERRUPTIBLE);			spin_unlock_irqrestore(&s->lock, flags);			if (avail <= 0) {				if (file->f_flags & O_NONBLOCK) {					if (!ret)						ret = -EAGAIN;

⌨️ 快捷键说明

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