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

📄 cmpci.c

📁 iis s3c2410-uda1341语音系统的 开发
💻 C
📖 第 1 页 / 共 5 页
字号:
		// the HW only support 16-bit stereo		fmts |= CM_CFMT_16BIT << CM_CFMT_DACSHIFT;		fmts |= CM_CFMT_16BIT << CM_CFMT_ADCSHIFT;		fmts |= CM_CFMT_STEREO << CM_CFMT_DACSHIFT;		fmts |= CM_CFMT_STEREO << CM_CFMT_ADCSHIFT;				set_fmt_unlocked(s, fmtm, fmts);		set_adc_rate_unlocked(s, s->ratedac);	    }	    if (s->speakers > 2)		maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~0x04, 0);	    s->curr_channels = channels;	} else {	    if (s->status & DO_MULTI_CH_HW) {		maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 3, ~0x80, 0);		maskb(s->iobase + CODEC_CMI_CHFORMAT + 3, ~0xa0, 0);		maskb(s->iobase + CODEC_CMI_LEGACY_CTRL + 1, ~0x80, 0);	    } else if (s->status & DO_DUAL_DAC) {		maskb(s->iobase + CODEC_CMI_MISC_CTRL + 2, ~0x80, 0);	    }	    // N4SPK3D, enable 4 speaker mode (analog duplicate)	    if (s->speakers > 2)		maskb(s->iobase + CODEC_CMI_MISC_CTRL + 3, ~0, 0x04);	    s->status &= ~DO_MULTI_CH;	    s->curr_channels = s->fmt & (CM_CFMT_STEREO << CM_CFMT_DACSHIFT) ? 2 : 1;	}	spin_unlock_irqrestore(&s->lock, flags);	return s->curr_channels;}/* --------------------------------------------------------------------- */#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT)#define DMABUF_MINORDER 1static void dealloc_dmabuf(struct dmabuf *db){	struct page *pstart, *pend;		if (db->rawbuf) {		/* undo marking the pages as reserved */		pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);		for (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++)			mem_map_unreserve(pstart);		free_pages((unsigned long)db->rawbuf, db->buforder);	}	db->rawbuf = NULL;	db->mapped = db->ready = 0;}/* Ch1 is used for playback, Ch0 is used for recording */static int prog_dmabuf(struct cm_state *s, unsigned rec){	struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac;	unsigned rate = rec ? s->rateadc : s->ratedac;	int order;	unsigned bytepersec;	unsigned bufs;	struct page *pstart, *pend;	unsigned char fmt;	unsigned long flags;	fmt = s->fmt;	if (rec) {		stop_adc(s);		fmt >>= CM_CFMT_ADCSHIFT;	} else {		stop_dac(s);		fmt >>= CM_CFMT_DACSHIFT;	}	fmt &= CM_CFMT_MASK;	db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;	if (!db->rawbuf) {		db->ready = db->mapped = 0;		for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)			if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order)))				break;		if (!db->rawbuf)			return -ENOMEM;		db->buforder = order;		db->rawphys = virt_to_bus(db->rawbuf);		if ((db->rawphys ^ (db->rawphys + (PAGE_SIZE << db->buforder) - 1)) & ~0xffff)			printk(KERN_DEBUG "cmpci: DMA buffer crosses 64k boundary: busaddr 0x%lx  size %ld\n", 			       (long) db->rawphys, PAGE_SIZE << db->buforder);		if ((db->rawphys + (PAGE_SIZE << db->buforder) - 1) & ~0xffffff)			printk(KERN_DEBUG "cmpci: DMA buffer beyond 16MB: busaddr 0x%lx  size %ld\n", 			       (long) db->rawphys, PAGE_SIZE << db->buforder);		/* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */		pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);		for (pstart = virt_to_page(db->rawbuf); pstart <= pend; pstart++)			mem_map_reserve(pstart);	}	bytepersec = rate << sample_shift[fmt];	bufs = PAGE_SIZE << db->buforder;	if (db->ossfragshift) {		if ((1000 << db->ossfragshift) < bytepersec)			db->fragshift = ld2(bytepersec/1000);		else			db->fragshift = db->ossfragshift;	} else {		db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1));		if (db->fragshift < 3)			db->fragshift = 3;	}	db->numfrag = bufs >> db->fragshift;	while (db->numfrag < 4 && db->fragshift > 3) {		db->fragshift--;		db->numfrag = bufs >> db->fragshift;	}	db->fragsize = 1 << db->fragshift;	if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag)		db->numfrag = db->ossmaxfrags; 	/* to make fragsize >= 4096 */	db->fragsamples = db->fragsize >> sample_shift[fmt];	db->dmasize = db->numfrag << db->fragshift;	db->dmasamples = db->dmasize >> sample_shift[fmt];	memset(db->rawbuf, (fmt & CM_CFMT_16BIT) ? 0 : 0x80, db->dmasize);	spin_lock_irqsave(&s->lock, flags);	if (rec) {		if (s->status & DO_DUAL_DAC)		    set_dmadac1(s, db->rawphys, db->dmasize >> sample_shift[fmt]);		else		    set_dmaadc(s, db->rawphys, db->dmasize >> sample_shift[fmt]);		/* program sample counts */		set_countdac(s, db->fragsamples);	} else {		set_dmadac(s, db->rawphys, db->dmasize >> sample_shift[fmt]);		/* program sample counts */		set_countdac(s, db->fragsamples);	}	spin_unlock_irqrestore(&s->lock, flags);	db->ready = 1;	return 0;}static inline void clear_advance(struct cm_state *s){	unsigned char c = (s->fmt & (CM_CFMT_16BIT << CM_CFMT_DACSHIFT)) ? 0 : 0x80;	unsigned char *buf = s->dma_dac.rawbuf;	unsigned char *buf1 = s->dma_adc.rawbuf;	unsigned bsize = s->dma_dac.dmasize;	unsigned bptr = s->dma_dac.swptr;	unsigned len = s->dma_dac.fragsize;	if (bptr + len > bsize) {		unsigned x = bsize - bptr;		memset(buf + bptr, c, x);		if (s->status & DO_DUAL_DAC)			memset(buf1 + bptr, c, x);		bptr = 0;		len -= x;	}	memset(buf + bptr, c, len);	if (s->status & DO_DUAL_DAC)		memset(buf1 + bptr, c, len);}/* call with spinlock held! */static void cm_update_ptr(struct cm_state *s){	unsigned hwptr;	int diff;	/* update ADC pointer */	if (s->dma_adc.ready) {	    if (s->status & DO_DUAL_DAC) {		hwptr = get_dmaadc(s) % s->dma_adc.dmasize;		diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize;		s->dma_adc.hwptr = hwptr;		s->dma_adc.total_bytes += diff;		if (s->dma_adc.mapped) {			s->dma_adc.count += diff;			if (s->dma_adc.count >= (signed)s->dma_adc.fragsize)				wake_up(&s->dma_adc.wait);		} else {			s->dma_adc.count -= diff;			if (s->dma_adc.count <= 0) {				pause_adc(s);				s->dma_adc.error++;			} else if (s->dma_adc.count <= (signed)s->dma_adc.fragsize && !s->dma_adc.endcleared) {				clear_advance(s);				s->dma_adc.endcleared = 1;			}			if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize)				wake_up(&s->dma_adc.wait);		}	    } else {		hwptr = get_dmaadc(s) % s->dma_adc.dmasize;		diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % s->dma_adc.dmasize;		s->dma_adc.hwptr = hwptr;		s->dma_adc.total_bytes += diff;		s->dma_adc.count += diff;		if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) 			wake_up(&s->dma_adc.wait);		if (!s->dma_adc.mapped) {			if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) {				pause_adc(s);				s->dma_adc.error++;			}		}	    }	}	/* update DAC pointer */	if (s->dma_dac.ready) {		hwptr = get_dmadac(s) % s->dma_dac.dmasize;		diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize;		s->dma_dac.hwptr = hwptr;		s->dma_dac.total_bytes += diff;		if (s->dma_dac.mapped) {			s->dma_dac.count += diff;			if (s->dma_dac.count >= (signed)s->dma_dac.fragsize)				wake_up(&s->dma_dac.wait);		} else {			s->dma_dac.count -= diff;			if (s->dma_dac.count <= 0) {				pause_dac(s);				s->dma_dac.error++;			} else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) {				clear_advance(s);				s->dma_dac.endcleared = 1;			}			if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize)				wake_up(&s->dma_dac.wait);		}	}}#ifdef CONFIG_SOUND_CMPCI_MIDI/* hold spinlock for the following! */static void cm_handle_midi(struct cm_state *s){	unsigned char ch;	int wake;	wake = 0;	while (!(inb(s->iomidi+1) & 0x80)) {		ch = inb(s->iomidi);		if (s->midi.icnt < MIDIINBUF) {			s->midi.ibuf[s->midi.iwr] = ch;			s->midi.iwr = (s->midi.iwr + 1) % MIDIINBUF;			s->midi.icnt++;		}		wake = 1;	}	if (wake)		wake_up(&s->midi.iwait);	wake = 0;	while (!(inb(s->iomidi+1) & 0x40) && s->midi.ocnt > 0) {		outb(s->midi.obuf[s->midi.ord], s->iomidi);		s->midi.ord = (s->midi.ord + 1) % MIDIOUTBUF;		s->midi.ocnt--;		if (s->midi.ocnt < MIDIOUTBUF-16)			wake = 1;	}	if (wake)		wake_up(&s->midi.owait);}#endifstatic void cm_interrupt(int irq, void *dev_id, struct pt_regs *regs){        struct cm_state *s = (struct cm_state *)dev_id;	unsigned int intsrc, intstat;	unsigned char mask = 0;		/* fastpath out, to ease interrupt sharing */	intsrc = inl(s->iobase + CODEC_CMI_INT_STATUS);	if (!(intsrc & 0x80000000))		return;	spin_lock(&s->lock);	intstat = inb(s->iobase + CODEC_CMI_INT_HLDCLR + 2);	/* acknowledge interrupt */	if (intsrc & CM_INT_CH0)		mask |= 1;	if (intsrc & CM_INT_CH1)		mask |= 2;	outb(intstat & ~mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2);	outb(intstat | mask, s->iobase + CODEC_CMI_INT_HLDCLR + 2);	cm_update_ptr(s);#ifdef CONFIG_SOUND_CMPCI_MIDI	cm_handle_midi(s);#endif	spin_unlock(&s->lock);}#ifdef CONFIG_SOUND_CMPCI_MIDIstatic void cm_midi_timer(unsigned long data){	struct cm_state *s = (struct cm_state *)data;	unsigned long flags;		spin_lock_irqsave(&s->lock, flags);	cm_handle_midi(s);	spin_unlock_irqrestore(&s->lock, flags);	s->midi.timer.expires = jiffies+1;	add_timer(&s->midi.timer);}#endif/* --------------------------------------------------------------------- */static const char invalid_magic[] = KERN_CRIT "cmpci: invalid magic value\n";#ifdef CONFIG_SOUND_CMPCI	/* support multiple chips */#define VALIDATE_STATE(s)#else#define VALIDATE_STATE(s)                         \({                                                \	if (!(s) || (s)->magic != CM_MAGIC) { \		printk(invalid_magic);            \		return -ENXIO;                    \	}                                         \})#endif/* --------------------------------------------------------------------- */#define MT_4          1#define MT_5MUTE      2#define MT_4MUTEMONO  3#define MT_6MUTE      4#define MT_5MUTEMONO  5static const struct {	unsigned left;	unsigned right;	unsigned type;	unsigned rec;	unsigned play;} mixtable[SOUND_MIXER_NRDEVICES] = {	[SOUND_MIXER_CD]     = { DSP_MIX_CDVOLIDX_L,     DSP_MIX_CDVOLIDX_R,     MT_5MUTE,     0x04, 0x02 },	[SOUND_MIXER_LINE]   = { DSP_MIX_LINEVOLIDX_L,   DSP_MIX_LINEVOLIDX_R,   MT_5MUTE,     0x10, 0x08 },	[SOUND_MIXER_MIC]    = { DSP_MIX_MICVOLIDX,      DSP_MIX_MICVOLIDX,      MT_5MUTEMONO, 0x01, 0x01 },	[SOUND_MIXER_SYNTH]  = { DSP_MIX_FMVOLIDX_L,  	 DSP_MIX_FMVOLIDX_R,     MT_5MUTE,     0x40, 0x00 },	[SOUND_MIXER_VOLUME] = { DSP_MIX_MASTERVOLIDX_L, DSP_MIX_MASTERVOLIDX_R, MT_5MUTE,     0x00, 0x00 },	[SOUND_MIXER_PCM]    = { DSP_MIX_VOICEVOLIDX_L,  DSP_MIX_VOICEVOLIDX_R,  MT_5MUTE,     0x00, 0x00 },	[SOUND_MIXER_SPEAKER]= { DSP_MIX_SPKRVOLIDX,	 DSP_MIX_SPKRVOLIDX,	 MT_5MUTEMONO, 0x01, 0x01 }};static const unsigned char volidx[SOUND_MIXER_NRDEVICES] = {	[SOUND_MIXER_CD]     = 1,	[SOUND_MIXER_LINE]   = 2,	[SOUND_MIXER_MIC]    = 3,	[SOUND_MIXER_SYNTH]  = 4,	[SOUND_MIXER_VOLUME] = 5,	[SOUND_MIXER_PCM]    = 6,	[SOUND_MIXER_SPEAKER]= 7};static unsigned mixer_recmask(struct cm_state *s){	int i, j, k;	j = rdmixer(s, DSP_MIX_ADCMIXIDX_L);	j &= 0x7f;	for (k = i = 0; i < SOUND_MIXER_NRDEVICES; i++)		if (j & mixtable[i].rec)			k |= 1 << i;	return k;}static int mixer_ioctl(struct cm_state *s, unsigned int cmd, unsigned long arg){	unsigned long flags;	int i, val, j;	unsigned char l, r, rl, rr;	VALIDATE_STATE(s);        if (cmd == SOUND_MIXER_INFO) {		mixer_info info;		strncpy(info.id, "cmpci", sizeof(info.id));		strncpy(info.name, "C-Media PCI", sizeof(info.name));		info.modify_counter = s->mix.modcnt;		if (copy_to_user((void *)arg, &info, sizeof(info)))			return -EFAULT;		return 0;	}	if (cmd == SOUND_OLD_MIXER_INFO) {		_old_mixer_info info;		strncpy(info.id, "cmpci", sizeof(info.id));		strncpy(info.name, "C-Media cmpci", sizeof(info.name));		if (copy_to_user((void *)arg, &info, sizeof(info)))			return -EFAULT;		return 0;	}	if (cmd == OSS_GETVERSION)		return put_user(SOUND_VERSION, (int *)arg);	if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int))                return -EINVAL;        if (_IOC_DIR(cmd) == _IOC_READ) {                switch (_IOC_NR(cmd)) {                case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */			return put_user(mixer_recmask(s), (int *)arg);			                case SOUND_MIXER_OUTSRC: /* Arg contains a bit for each recording source */			return put_user(mixer_recmask(s), (int *)arg);//need fix			                case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)				if (mixtable[i].type)					val |= 1 << i;			return put_user(val, (int *)arg);                case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)				if (mixtable[i].rec)					val |= 1 << i;			return put_user(val, (int *)arg);			                case SOUND_MIXER_OUTMASK: /* Arg contains a bit for each supported recording source */			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)				if (mixtable[i].play)					val |= 1 << i;			return put_user(val, (int *)arg);			                 case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */			for (val = i = 0; i < SOUND_MIXER_NRDEVICES; i++)				if (mixtable[i].type && mixtable[i].type != MT_4MUTEMONO)					val |= 1 << i;			return put_user(val, (int *)arg);			                case SOUND_MIXER_CAPS:			return put_user(0, (int *)arg);		default:			i = _IOC_NR(cmd);                        if (i >= SOUND_MIXER_NRDEVICES || !mixtable[i].type)                                return -EINVAL;			if (!volidx[i])				return -EINVAL;			return put_user(s->mix.vol[volidx[i]-1], (int *)arg);		}	}        if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) 		return -EINVAL;	s->mix.modcnt++;	switch (_IOC_NR(cmd)) {	case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */		if (get_user(val, (int *)arg))			return -EFAULT;

⌨️ 快捷键说明

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