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

📄 cmipci.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
#endif/* * driver data */struct cmipci_pcm {	struct snd_pcm_substream *substream;	u8 running;		/* dac/adc running? */	u8 fmt;			/* format bits */	u8 is_dac;	u8 needs_silencing;	unsigned int dma_size;	/* in frames */	unsigned int shift;	unsigned int ch;	/* channel (0/1) */	unsigned int offset;	/* physical address of the buffer */};/* mixer elements toggled/resumed during ac3 playback */struct cmipci_mixer_auto_switches {	const char *name;	/* switch to toggle */	int toggle_on;		/* value to change when ac3 mode */};static const struct cmipci_mixer_auto_switches cm_saved_mixer[] = {	{"PCM Playback Switch", 0},	{"IEC958 Output Switch", 1},	{"IEC958 Mix Analog", 0},	// {"IEC958 Out To DAC", 1}, // no longer used	{"IEC958 Loop", 0},};#define CM_SAVED_MIXERS		ARRAY_SIZE(cm_saved_mixer)struct cmipci {	struct snd_card *card;	struct pci_dev *pci;	unsigned int device;	/* device ID */	int irq;	unsigned long iobase;	unsigned int ctrl;	/* FUNCTRL0 current value */	struct snd_pcm *pcm;		/* DAC/ADC PCM */	struct snd_pcm *pcm2;	/* 2nd DAC */	struct snd_pcm *pcm_spdif;	/* SPDIF */	int chip_version;	int max_channels;	unsigned int can_ac3_sw: 1;	unsigned int can_ac3_hw: 1;	unsigned int can_multi_ch: 1;	unsigned int do_soft_ac3: 1;	unsigned int spdif_playback_avail: 1;	/* spdif ready? */	unsigned int spdif_playback_enabled: 1;	/* spdif switch enabled? */	int spdif_counter;	/* for software AC3 */	unsigned int dig_status;	unsigned int dig_pcm_status;	struct snd_pcm_hardware *hw_info[3]; /* for playbacks */	int opened[2];	/* open mode */	struct mutex open_mutex;	unsigned int mixer_insensitive: 1;	struct snd_kcontrol *mixer_res_ctl[CM_SAVED_MIXERS];	int mixer_res_status[CM_SAVED_MIXERS];	struct cmipci_pcm channel[2];	/* ch0 - DAC, ch1 - ADC or 2nd DAC */	/* external MIDI */	struct snd_rawmidi *rmidi;#ifdef SUPPORT_JOYSTICK	struct gameport *gameport;#endif	spinlock_t reg_lock;#ifdef CONFIG_PM	unsigned int saved_regs[0x20];	unsigned char saved_mixers[0x20];#endif};/* read/write operations for dword register */static inline void snd_cmipci_write(struct cmipci *cm, unsigned int cmd, unsigned int data){	outl(data, cm->iobase + cmd);}static inline unsigned int snd_cmipci_read(struct cmipci *cm, unsigned int cmd){	return inl(cm->iobase + cmd);}/* read/write operations for word register */static inline void snd_cmipci_write_w(struct cmipci *cm, unsigned int cmd, unsigned short data){	outw(data, cm->iobase + cmd);}static inline unsigned short snd_cmipci_read_w(struct cmipci *cm, unsigned int cmd){	return inw(cm->iobase + cmd);}/* read/write operations for byte register */static inline void snd_cmipci_write_b(struct cmipci *cm, unsigned int cmd, unsigned char data){	outb(data, cm->iobase + cmd);}static inline unsigned char snd_cmipci_read_b(struct cmipci *cm, unsigned int cmd){	return inb(cm->iobase + cmd);}/* bit operations for dword register */static int snd_cmipci_set_bit(struct cmipci *cm, unsigned int cmd, unsigned int flag){	unsigned int val, oval;	val = oval = inl(cm->iobase + cmd);	val |= flag;	if (val == oval)		return 0;	outl(val, cm->iobase + cmd);	return 1;}static int snd_cmipci_clear_bit(struct cmipci *cm, unsigned int cmd, unsigned int flag){	unsigned int val, oval;	val = oval = inl(cm->iobase + cmd);	val &= ~flag;	if (val == oval)		return 0;	outl(val, cm->iobase + cmd);	return 1;}/* bit operations for byte register */static int snd_cmipci_set_bit_b(struct cmipci *cm, unsigned int cmd, unsigned char flag){	unsigned char val, oval;	val = oval = inb(cm->iobase + cmd);	val |= flag;	if (val == oval)		return 0;	outb(val, cm->iobase + cmd);	return 1;}static int snd_cmipci_clear_bit_b(struct cmipci *cm, unsigned int cmd, unsigned char flag){	unsigned char val, oval;	val = oval = inb(cm->iobase + cmd);	val &= ~flag;	if (val == oval)		return 0;	outb(val, cm->iobase + cmd);	return 1;}/* * PCM interface *//* * calculate frequency */static unsigned int rates[] = { 5512, 11025, 22050, 44100, 8000, 16000, 32000, 48000 };static unsigned int snd_cmipci_rate_freq(unsigned int rate){	unsigned int i;	if (rate > 48000)		rate /= 2;	for (i = 0; i < ARRAY_SIZE(rates); i++) {		if (rates[i] == rate)			return i;	}	snd_BUG();	return 0;}#ifdef USE_VAR48KRATE/* * Determine PLL values for frequency setup, maybe the CMI8338 (CMI8738???) * does it this way .. maybe not.  Never get any information from C-Media about * that <werner@suse.de>. */static int snd_cmipci_pll_rmn(unsigned int rate, unsigned int adcmult, int *r, int *m, int *n){	unsigned int delta, tolerance;	int xm, xn, xr;	for (*r = 0; rate < CM_MAXIMUM_RATE/adcmult; *r += (1<<5))		rate <<= 1;	*n = -1;	if (*r > 0xff)		goto out;	tolerance = rate*CM_TOLERANCE_RATE;	for (xn = (1+2); xn < (0x1f+2); xn++) {		for (xm = (1+2); xm < (0xff+2); xm++) {			xr = ((CM_REFFREQ_XIN/adcmult) * xm) / xn;			if (xr < rate)				delta = rate - xr;			else				delta = xr - rate;			/*			 * If we found one, remember this,			 * and try to find a closer one			 */			if (delta < tolerance) {				tolerance = delta;				*m = xm - 2;				*n = xn - 2;			}		}	}out:	return (*n > -1);}/* * Program pll register bits, I assume that the 8 registers 0xf8 upto 0xff * are mapped onto the 8 ADC/DAC sampling frequency which can be choosen * at the register CM_REG_FUNCTRL1 (0x04). * Problem: other ways are also possible (any information about that?) */static void snd_cmipci_set_pll(struct cmipci *cm, unsigned int rate, unsigned int slot){	unsigned int reg = CM_REG_PLL + slot;	/*	 * Guess that this programs at reg. 0x04 the pos 15:13/12:10	 * for DSFC/ASFC (000 upto 111).	 */	/* FIXME: Init (Do we've to set an other register first before programming?) */	/* FIXME: Is this correct? Or shouldn't the m/n/r values be used for that? */	snd_cmipci_write_b(cm, reg, rate>>8);	snd_cmipci_write_b(cm, reg, rate&0xff);	/* FIXME: Setup (Do we've to set an other register first to enable this?) */}#endif /* USE_VAR48KRATE */static int snd_cmipci_hw_params(struct snd_pcm_substream *substream,				struct snd_pcm_hw_params *hw_params){	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}static int snd_cmipci_playback2_hw_params(struct snd_pcm_substream *substream,					  struct snd_pcm_hw_params *hw_params){	struct cmipci *cm = snd_pcm_substream_chip(substream);	if (params_channels(hw_params) > 2) {		mutex_lock(&cm->open_mutex);		if (cm->opened[CM_CH_PLAY]) {			mutex_unlock(&cm->open_mutex);			return -EBUSY;		}		/* reserve the channel A */		cm->opened[CM_CH_PLAY] = CM_OPEN_PLAYBACK_MULTI;		mutex_unlock(&cm->open_mutex);	}	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}static void snd_cmipci_ch_reset(struct cmipci *cm, int ch){	int reset = CM_RST_CH0 << (cm->channel[ch].ch);	snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | reset);	snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~reset);	udelay(10);}static int snd_cmipci_hw_free(struct snd_pcm_substream *substream){	return snd_pcm_lib_free_pages(substream);}/* */static unsigned int hw_channels[] = {1, 2, 4, 6, 8};static struct snd_pcm_hw_constraint_list hw_constraints_channels_4 = {	.count = 3,	.list = hw_channels,	.mask = 0,};static struct snd_pcm_hw_constraint_list hw_constraints_channels_6 = {	.count = 4,	.list = hw_channels,	.mask = 0,};static struct snd_pcm_hw_constraint_list hw_constraints_channels_8 = {	.count = 5,	.list = hw_channels,	.mask = 0,};static int set_dac_channels(struct cmipci *cm, struct cmipci_pcm *rec, int channels){	if (channels > 2) {		if (!cm->can_multi_ch || !rec->ch)			return -EINVAL;		if (rec->fmt != 0x03) /* stereo 16bit only */			return -EINVAL;	}	if (cm->can_multi_ch) {		spin_lock_irq(&cm->reg_lock);		if (channels > 2) {			snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG);			snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC);		} else {			snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG);			snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC);		}		if (channels == 8)			snd_cmipci_set_bit(cm, CM_REG_EXT_MISC, CM_CHB3D8C);		else			snd_cmipci_clear_bit(cm, CM_REG_EXT_MISC, CM_CHB3D8C);		if (channels == 6) {			snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C);			snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C);		} else {			snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C);			snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C);		}		if (channels == 4)			snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);		else			snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);		spin_unlock_irq(&cm->reg_lock);	}	return 0;}/* * prepare playback/capture channel * channel to be used must have been set in rec->ch. */static int snd_cmipci_pcm_prepare(struct cmipci *cm, struct cmipci_pcm *rec,				 struct snd_pcm_substream *substream){	unsigned int reg, freq, val;	unsigned int period_size;	struct snd_pcm_runtime *runtime = substream->runtime;	rec->fmt = 0;	rec->shift = 0;	if (snd_pcm_format_width(runtime->format) >= 16) {		rec->fmt |= 0x02;		if (snd_pcm_format_width(runtime->format) > 16)			rec->shift++; /* 24/32bit */	}	if (runtime->channels > 1)		rec->fmt |= 0x01;	if (rec->is_dac && set_dac_channels(cm, rec, runtime->channels) < 0) {		snd_printd("cannot set dac channels\n");		return -EINVAL;	}	rec->offset = runtime->dma_addr;	/* buffer and period sizes in frame */	rec->dma_size = runtime->buffer_size << rec->shift;	period_size = runtime->period_size << rec->shift;	if (runtime->channels > 2) {		/* multi-channels */		rec->dma_size = (rec->dma_size * runtime->channels) / 2;		period_size = (period_size * runtime->channels) / 2;	}	spin_lock_irq(&cm->reg_lock);	/* set buffer address */	reg = rec->ch ? CM_REG_CH1_FRAME1 : CM_REG_CH0_FRAME1;	snd_cmipci_write(cm, reg, rec->offset);	/* program sample counts */	reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2;	snd_cmipci_write_w(cm, reg, rec->dma_size - 1);	snd_cmipci_write_w(cm, reg + 2, period_size - 1);	/* set adc/dac flag */	val = rec->ch ? CM_CHADC1 : CM_CHADC0;	if (rec->is_dac)		cm->ctrl &= ~val;	else		cm->ctrl |= val;	snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);	//snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl);	/* set sample rate */	freq = snd_cmipci_rate_freq(runtime->rate);	val = snd_cmipci_read(cm, CM_REG_FUNCTRL1);	if (rec->ch) {		val &= ~CM_DSFC_MASK;		val |= (freq << CM_DSFC_SHIFT) & CM_DSFC_MASK;	} else {		val &= ~CM_ASFC_MASK;		val |= (freq << CM_ASFC_SHIFT) & CM_ASFC_MASK;	}	snd_cmipci_write(cm, CM_REG_FUNCTRL1, val);	//snd_printd("cmipci: functrl1 = %08x\n", val);	/* set format */	val = snd_cmipci_read(cm, CM_REG_CHFORMAT);

⌨️ 快捷键说明

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