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

📄 cmipci.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 5 页
字号:
	unsigned long iobase;	unsigned int ctrl;	/* FUNCTRL0 current value */	snd_pcm_t *pcm;		/* DAC/ADC PCM */	snd_pcm_t *pcm2;	/* 2nd DAC */	snd_pcm_t *pcm_spdif;	/* SPDIF */	int chip_version;	int max_channels;	unsigned int has_dual_dac: 1;	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;	snd_pcm_hardware_t *hw_info[3]; /* for playbacks */	int opened[2];	/* open mode */	struct semaphore open_mutex;	int mixer_insensitive: 1;	snd_kcontrol_t *mixer_res_ctl[CM_SAVED_MIXERS];	int mixer_res_status[CM_SAVED_MIXERS];	opl3_t *opl3;	snd_hwdep_t *opl3hwdep;	cmipci_pcm_t channel[2];	/* ch0 - DAC, ch1 - ADC or 2nd DAC */	/* external MIDI */	snd_rawmidi_t *rmidi;#ifdef SUPPORT_JOYSTICK	struct gameport gameport;	struct resource *res_joystick;#endif	spinlock_t reg_lock;};/* read/write operations for dword register */inline static void snd_cmipci_write(cmipci_t *cm, unsigned int cmd, unsigned int data){	outl(data, cm->iobase + cmd);}inline static unsigned int snd_cmipci_read(cmipci_t *cm, unsigned int cmd){	return inl(cm->iobase + cmd);}/* read/write operations for word register */inline static void snd_cmipci_write_w(cmipci_t *cm, unsigned int cmd, unsigned short data){	outw(data, cm->iobase + cmd);}inline static unsigned short snd_cmipci_read_w(cmipci_t *cm, unsigned int cmd){	return inw(cm->iobase + cmd);}/* read/write operations for byte register */inline static void snd_cmipci_write_b(cmipci_t *cm, unsigned int cmd, unsigned char data){	outb(data, cm->iobase + cmd);}inline static unsigned char snd_cmipci_read_b(cmipci_t *cm, unsigned int cmd){	return inb(cm->iobase + cmd);}/* bit operations for dword register */static void snd_cmipci_set_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag){	unsigned int val;	val = inl(cm->iobase + cmd);	val |= flag;	outl(val, cm->iobase + cmd);}static void snd_cmipci_clear_bit(cmipci_t *cm, unsigned int cmd, unsigned int flag){	unsigned int val;	val = inl(cm->iobase + cmd);	val &= ~flag;	outl(val, cm->iobase + cmd);}#if 0 // not used/* bit operations for byte register */static void snd_cmipci_set_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag){	unsigned char val;	val = inb(cm->iobase + cmd);	val |= flag;	outb(val, cm->iobase + cmd);}static void snd_cmipci_clear_bit_b(cmipci_t *cm, unsigned int cmd, unsigned char flag){	unsigned char val;	val = inb(cm->iobase + cmd);	val &= ~flag;	outb(val, cm->iobase + cmd);}#endif/* * 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;	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(cmipci_t *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(snd_pcm_substream_t * substream,				snd_pcm_hw_params_t * hw_params){	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}static int snd_cmipci_playback2_hw_params(snd_pcm_substream_t * substream,					  snd_pcm_hw_params_t * hw_params){	cmipci_t *cm = snd_pcm_substream_chip(substream);	if (params_channels(hw_params) > 2) {		down(&cm->open_mutex);		if (cm->opened[CM_CH_PLAY]) {			up(&cm->open_mutex);			return -EBUSY;		}		/* reserve the channel A */		cm->opened[CM_CH_PLAY] = CM_OPEN_PLAYBACK_MULTI;		up(&cm->open_mutex);	}	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}static void snd_cmipci_ch_reset(cmipci_t *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(snd_pcm_substream_t * substream){	return snd_pcm_lib_free_pages(substream);}/* */static unsigned int hw_channels[] = {1, 2, 4, 5, 6};static snd_pcm_hw_constraint_list_t hw_constraints_channels_4 = {	.count = 3,	.list = hw_channels,	.mask = 0,};static snd_pcm_hw_constraint_list_t hw_constraints_channels_6 = {	.count = 5,	.list = hw_channels,	.mask = 0,};static int set_dac_channels(cmipci_t *cm, cmipci_pcm_t *rec, int channels){	if (channels > 2) {		if (! cm->can_multi_ch)			return -EINVAL;		if (rec->fmt != 0x03) /* stereo 16bit only */			return -EINVAL;		spin_lock_irq(&cm->reg_lock);		snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG);		snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC);		if (channels > 4) {			snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);			snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C);		} else {			snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C);			snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);		}		if (channels == 6) {			snd_cmipci_set_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C);			snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER);		} else {			snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C);			snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER);		}		spin_unlock_irq(&cm->reg_lock);	} else {		if (cm->can_multi_ch) {			spin_lock_irq(&cm->reg_lock);			snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_NXCHG);			snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D);			snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_CHB3D5C);			snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_CHB3D6C);			snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_ENCENTER);			snd_cmipci_clear_bit(cm, CM_REG_MISC_CTRL, CM_XCHGDAC);			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(cmipci_t *cm, cmipci_pcm_t *rec,				 snd_pcm_substream_t *substream){	unsigned int reg, freq, val;	snd_pcm_runtime_t *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;	rec->period_size = runtime->period_size << rec->shift;	if (runtime->channels > 2) {		/* multi-channels */		rec->dma_size = (rec->dma_size * runtime->channels) / 2;		rec->period_size = (rec->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, rec->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_ASFC_MASK;		val |= (freq << CM_ASFC_SHIFT) & CM_ASFC_MASK;	} else {		val &= ~CM_DSFC_MASK;		val |= (freq << CM_DSFC_SHIFT) & CM_DSFC_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);	if (rec->ch) {		val &= ~CM_CH1FMT_MASK;		val |= rec->fmt << CM_CH1FMT_SHIFT;	} else {		val &= ~CM_CH0FMT_MASK;		val |= rec->fmt << CM_CH0FMT_SHIFT;	}	snd_cmipci_write(cm, CM_REG_CHFORMAT, val);	//snd_printd("cmipci: chformat = %08x\n", val);	rec->running = 0;	spin_unlock_irq(&cm->reg_lock);	return 0;}/* * PCM trigger/stop */static int snd_cmipci_pcm_trigger(cmipci_t *cm, cmipci_pcm_t *rec,				 snd_pcm_substream_t *substream, int cmd){	unsigned int inthld, chen, reset, pause;	int result = 0;	inthld = CM_CH0_INT_EN << rec->ch;	chen = CM_CHEN0 << rec->ch;	reset = CM_RST_CH0 << rec->ch;	pause = CM_PAUSE0 << rec->ch;	spin_lock(&cm->reg_lock);	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:		rec->running = 1;		/* set interrupt */		snd_cmipci_set_bit(cm, CM_REG_INT_HLDCLR, inthld);		cm->ctrl |= chen;		/* enable channel */		snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);		//snd_printd("cmipci: functrl0 = %08x\n", cm->ctrl);		break;	case SNDRV_PCM_TRIGGER_STOP:		rec->running = 0;		/* disable interrupt */		snd_cmipci_clear_bit(cm, CM_REG_INT_HLDCLR, inthld);		/* reset */		cm->ctrl &= ~chen;		snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl | reset);		snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl & ~reset);		break;	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:		cm->ctrl |= pause;		snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);		break;	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:		cm->ctrl &= ~pause;		snd_cmipci_write(cm, CM_REG_FUNCTRL0, cm->ctrl);		break;	default:		result = -EINVAL;		break;	}	spin_unlock(&cm->reg_lock);	return result;}/* * return the current pointer

⌨️ 快捷键说明

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