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

📄 atiixp.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * auto-detection of codecs * * the IXP chip can generate interrupts for the non-existing codecs. * NEW_FRAME interrupt is used to make sure that the interrupt is generated * even if all three codecs are connected. */#define ALL_CODEC_NOT_READY \	    (ATI_REG_ISR_CODEC0_NOT_READY |\	     ATI_REG_ISR_CODEC1_NOT_READY |\	     ATI_REG_ISR_CODEC2_NOT_READY)#define CODEC_CHECK_BITS (ALL_CODEC_NOT_READY|ATI_REG_ISR_NEW_FRAME)static int snd_atiixp_codec_detect(atiixp_t *chip){	int timeout;	chip->codec_not_ready_bits = 0;	atiixp_write(chip, IER, CODEC_CHECK_BITS);	/* wait for the interrupts */	timeout = HZ / 10;	while (timeout-- > 0) {		do_delay();		if (chip->codec_not_ready_bits)			break;	}	atiixp_write(chip, IER, 0); /* disable irqs */	if ((chip->codec_not_ready_bits & ALL_CODEC_NOT_READY) == ALL_CODEC_NOT_READY) {		snd_printk(KERN_ERR "atiixp: no codec detected!\n");		return -ENXIO;	}	return 0;}/* * enable DMA and irqs */static int snd_atiixp_chip_start(atiixp_t *chip){	unsigned int reg;	/* set up spdif, enable burst mode */	reg = atiixp_read(chip, CMD);	reg |= 0x02 << ATI_REG_CMD_SPDF_THRESHOLD_SHIFT;	reg |= ATI_REG_CMD_BURST_EN;	atiixp_write(chip, CMD, reg);	reg = atiixp_read(chip, SPDF_CMD);	reg &= ~(ATI_REG_SPDF_CMD_LFSR|ATI_REG_SPDF_CMD_SINGLE_CH);	atiixp_write(chip, SPDF_CMD, reg);	/* clear all interrupt source */	atiixp_write(chip, ISR, 0xffffffff);	/* enable irqs */	atiixp_write(chip, IER,		     ATI_REG_IER_IO_STATUS_EN |		     ATI_REG_IER_IN_XRUN_EN |		     ATI_REG_IER_OUT_XRUN_EN |		     ATI_REG_IER_SPDF_XRUN_EN |		     ATI_REG_IER_SPDF_STATUS_EN);	return 0;}/* * disable DMA and IRQs */static int snd_atiixp_chip_stop(atiixp_t *chip){	/* clear interrupt source */	atiixp_write(chip, ISR, atiixp_read(chip, ISR));	/* disable irqs */	atiixp_write(chip, IER, 0);	return 0;}/* * PCM section *//* * pointer callback simplly reads XXX_DMA_DT_CUR register as the current * position.  when SG-buffer is implemented, the offset must be calculated * correctly... */static snd_pcm_uframes_t snd_atiixp_pcm_pointer(snd_pcm_substream_t *substream){	atiixp_t *chip = snd_pcm_substream_chip(substream);	snd_pcm_runtime_t *runtime = substream->runtime;	atiixp_dma_t *dma = (atiixp_dma_t *)runtime->private_data;	unsigned int curptr;	spin_lock(&chip->reg_lock);	curptr = readl(chip->remap_addr + dma->ops->dt_cur);	if (curptr < dma->buf_addr) {		snd_printdd("curptr = %x, base = %x\n", curptr, dma->buf_addr);		curptr = 0;	} else {		curptr -= dma->buf_addr;		if (curptr >= dma->buf_bytes) {			snd_printdd("curptr = %x, size = %x\n", curptr, dma->buf_bytes);			curptr = 0;		}	}	spin_unlock(&chip->reg_lock);	return bytes_to_frames(runtime, curptr);}/* * XRUN detected, and stop the PCM substream */static void snd_atiixp_xrun_dma(atiixp_t *chip, atiixp_dma_t *dma){	if (! dma->substream || ! dma->running)		return;	snd_printdd("atiixp: XRUN detected (DMA %d)\n", dma->ops->type);	snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN);}/* * the period ack.  update the substream. */static void snd_atiixp_update_dma(atiixp_t *chip, atiixp_dma_t *dma){	if (! dma->substream || ! dma->running)		return;	snd_pcm_period_elapsed(dma->substream);}/* set BUS_BUSY interrupt bit if any DMA is running *//* call with spinlock held */static void snd_atiixp_check_bus_busy(atiixp_t *chip){	unsigned int bus_busy;	if (atiixp_read(chip, CMD) & (ATI_REG_CMD_SEND_EN |				      ATI_REG_CMD_RECEIVE_EN |				      ATI_REG_CMD_SPDF_OUT_EN))		bus_busy = ATI_REG_IER_SET_BUS_BUSY;	else		bus_busy = 0;	atiixp_update(chip, IER, ATI_REG_IER_SET_BUS_BUSY, bus_busy);}/* common trigger callback * calling the lowlevel callbacks in it */static int snd_atiixp_pcm_trigger(snd_pcm_substream_t *substream, int cmd){	atiixp_t *chip = snd_pcm_substream_chip(substream);	atiixp_dma_t *dma = (atiixp_dma_t *)substream->runtime->private_data;	int err = 0;	snd_assert(dma->ops->enable_transfer && dma->ops->flush_dma, return -EINVAL);	spin_lock(&chip->reg_lock);	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:		dma->ops->enable_transfer(chip, 1);		dma->running = 1;		break;	case SNDRV_PCM_TRIGGER_STOP:		dma->ops->enable_transfer(chip, 0);		dma->running = 0;		break;	default:		err = -EINVAL;		break;	}	if (! err) {		snd_atiixp_check_bus_busy(chip);		if (cmd == SNDRV_PCM_TRIGGER_STOP) {			dma->ops->flush_dma(chip);			snd_atiixp_check_bus_busy(chip);		}	}	spin_unlock(&chip->reg_lock);	return err;}/* * lowlevel callbacks for each DMA type * * every callback is supposed to be called in chip->reg_lock spinlock *//* flush FIFO of analog OUT DMA */static void atiixp_out_flush_dma(atiixp_t *chip){	atiixp_write(chip, FIFO_FLUSH, ATI_REG_FIFO_OUT_FLUSH);}/* enable/disable analog OUT DMA */static void atiixp_out_enable_dma(atiixp_t *chip, int on){	unsigned int data;	data = atiixp_read(chip, CMD);	if (on) {		if (data & ATI_REG_CMD_OUT_DMA_EN)			return;		atiixp_out_flush_dma(chip);		data |= ATI_REG_CMD_OUT_DMA_EN;	} else		data &= ~ATI_REG_CMD_OUT_DMA_EN;	atiixp_write(chip, CMD, data);}/* start/stop transfer over OUT DMA */static void atiixp_out_enable_transfer(atiixp_t *chip, int on){	atiixp_update(chip, CMD, ATI_REG_CMD_SEND_EN,		      on ? ATI_REG_CMD_SEND_EN : 0);}/* enable/disable analog IN DMA */static void atiixp_in_enable_dma(atiixp_t *chip, int on){	atiixp_update(chip, CMD, ATI_REG_CMD_IN_DMA_EN,		      on ? ATI_REG_CMD_IN_DMA_EN : 0);}/* start/stop analog IN DMA */static void atiixp_in_enable_transfer(atiixp_t *chip, int on){	if (on) {		unsigned int data = atiixp_read(chip, CMD);		if (! (data & ATI_REG_CMD_RECEIVE_EN)) {			data |= ATI_REG_CMD_RECEIVE_EN;#if 0 /* FIXME: this causes the endless loop */			/* wait until slot 3/4 are finished */			while ((atiixp_read(chip, COUNTER) &				ATI_REG_COUNTER_SLOT) != 5)				;#endif			atiixp_write(chip, CMD, data);		}	} else		atiixp_update(chip, CMD, ATI_REG_CMD_RECEIVE_EN, 0);}/* flush FIFO of analog IN DMA */static void atiixp_in_flush_dma(atiixp_t *chip){	atiixp_write(chip, FIFO_FLUSH, ATI_REG_FIFO_IN_FLUSH);}/* enable/disable SPDIF OUT DMA */static void atiixp_spdif_enable_dma(atiixp_t *chip, int on){	atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_DMA_EN,		      on ? ATI_REG_CMD_SPDF_DMA_EN : 0);}/* start/stop SPDIF OUT DMA */static void atiixp_spdif_enable_transfer(atiixp_t *chip, int on){	unsigned int data;	data = atiixp_read(chip, CMD);	if (on)		data |= ATI_REG_CMD_SPDF_OUT_EN;	else		data &= ~ATI_REG_CMD_SPDF_OUT_EN;	atiixp_write(chip, CMD, data);}/* flush FIFO of SPDIF OUT DMA */static void atiixp_spdif_flush_dma(atiixp_t *chip){	int timeout;	/* DMA off, transfer on */	atiixp_spdif_enable_dma(chip, 0);	atiixp_spdif_enable_transfer(chip, 1);		timeout = 100;	do {		if (! (atiixp_read(chip, SPDF_DMA_DT_SIZE) & ATI_REG_DMA_FIFO_USED))			break;		udelay(1);	} while (timeout-- > 0);	atiixp_spdif_enable_transfer(chip, 0);}/* set up slots and formats for SPDIF OUT */static int snd_atiixp_spdif_prepare(snd_pcm_substream_t *substream){	atiixp_t *chip = snd_pcm_substream_chip(substream);	spin_lock_irq(&chip->reg_lock);	if (chip->spdif_over_aclink) {		unsigned int data;		/* enable slots 10/11 */		atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_CONFIG_MASK,			      ATI_REG_CMD_SPDF_CONFIG_01);		data = atiixp_read(chip, OUT_DMA_SLOT) & ~ATI_REG_OUT_DMA_SLOT_MASK;		data |= ATI_REG_OUT_DMA_SLOT_BIT(10) |			ATI_REG_OUT_DMA_SLOT_BIT(11);		data |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT;		atiixp_write(chip, OUT_DMA_SLOT, data);		atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_OUT,			      substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ?			      ATI_REG_CMD_INTERLEAVE_OUT : 0);	} else {		atiixp_update(chip, CMD, ATI_REG_CMD_SPDF_CONFIG_MASK, 0);		atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_SPDF, 0);	}	spin_unlock_irq(&chip->reg_lock);	return 0;}/* set up slots and formats for analog OUT */static int snd_atiixp_playback_prepare(snd_pcm_substream_t *substream){	atiixp_t *chip = snd_pcm_substream_chip(substream);	unsigned int data;	spin_lock_irq(&chip->reg_lock);	data = atiixp_read(chip, OUT_DMA_SLOT) & ~ATI_REG_OUT_DMA_SLOT_MASK;	switch (substream->runtime->channels) {	case 8:		data |= ATI_REG_OUT_DMA_SLOT_BIT(10) |			ATI_REG_OUT_DMA_SLOT_BIT(11);		/* fallthru */	case 6:		data |= ATI_REG_OUT_DMA_SLOT_BIT(7) |			ATI_REG_OUT_DMA_SLOT_BIT(8);		/* fallthru */	case 4:		data |= ATI_REG_OUT_DMA_SLOT_BIT(6) |			ATI_REG_OUT_DMA_SLOT_BIT(9);		/* fallthru */	default:		data |= ATI_REG_OUT_DMA_SLOT_BIT(3) |			ATI_REG_OUT_DMA_SLOT_BIT(4);		break;	}	/* set output threshold */	data |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT;	atiixp_write(chip, OUT_DMA_SLOT, data);	atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_OUT,		      substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ?		      ATI_REG_CMD_INTERLEAVE_OUT : 0);	/*	 * enable 6 channel re-ordering bit if needed	 */	atiixp_update(chip, 6CH_REORDER, ATI_REG_6CH_REORDER_EN,		      substream->runtime->channels >= 6 ? ATI_REG_6CH_REORDER_EN: 0);    	spin_unlock_irq(&chip->reg_lock);	return 0;}/* set up slots and formats for analog IN */static int snd_atiixp_capture_prepare(snd_pcm_substream_t *substream){	atiixp_t *chip = snd_pcm_substream_chip(substream);	spin_lock_irq(&chip->reg_lock);	atiixp_update(chip, CMD, ATI_REG_CMD_INTERLEAVE_IN,		      substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE ?		      ATI_REG_CMD_INTERLEAVE_IN : 0);	spin_unlock_irq(&chip->reg_lock);	return 0;}/* * hw_params - allocate the buffer and set up buffer descriptors */static int snd_atiixp_pcm_hw_params(snd_pcm_substream_t *substream,				   snd_pcm_hw_params_t *hw_params){	atiixp_t *chip = snd_pcm_substream_chip(substream);	atiixp_dma_t *dma = (atiixp_dma_t *)substream->runtime->private_data;	int err;	err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));	if (err < 0)		return err;	dma->buf_addr = substream->runtime->dma_addr;	dma->buf_bytes = params_buffer_bytes(hw_params);	err = atiixp_build_dma_packets(chip, dma, substream,				       params_periods(hw_params),				       params_period_bytes(hw_params));	if (err < 0)		return err;	if (dma->ac97_pcm_type >= 0) {		struct ac97_pcm *pcm = chip->pcms[dma->ac97_pcm_type];		/* PCM is bound to AC97 codec(s)		 * set up the AC97 codecs		 */		if (dma->pcm_open_flag) {			snd_ac97_pcm_close(pcm);			dma->pcm_open_flag = 0;		}		err = snd_ac97_pcm_open(pcm, params_rate(hw_params),					params_channels(hw_params),					pcm->r[0].slots);		if (err >= 0)			dma->pcm_open_flag = 1;	}	return err;}static int snd_atiixp_pcm_hw_free(snd_pcm_substream_t * substream){	atiixp_t *chip = snd_pcm_substream_chip(substream);	atiixp_dma_t *dma = (atiixp_dma_t *)substream->runtime->private_data;	if (dma->pcm_open_flag) {		struct ac97_pcm *pcm = chip->pcms[dma->ac97_pcm_type];		snd_ac97_pcm_close(pcm);		dma->pcm_open_flag = 0;	}	atiixp_clear_dma_packets(chip, dma, substream);	snd_pcm_lib_free_pages(substream);	return 0;}/* * pcm hardware definition, identical for all DMA types */static snd_pcm_hardware_t snd_atiixp_pcm_hw ={	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |				 SNDRV_PCM_INFO_BLOCK_TRANSFER |				 SNDRV_PCM_INFO_RESUME |				 SNDRV_PCM_INFO_MMAP_VALID),	.formats =		SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,	.rates =		SNDRV_PCM_RATE_48000,	.rate_min =		48000,	.rate_max =		48000,	.channels_min =		2,	.channels_max =		2,	.buffer_bytes_max =	256 * 1024,	.period_bytes_min =	32,	.period_bytes_max =	128 * 1024,	.periods_min =		2,	.periods_max =		ATI_MAX_DESCRIPTORS,};static int snd_atiixp_pcm_open(snd_pcm_substream_t *substream, atiixp_dma_t *dma, int pcm_type){	atiixp_t *chip = snd_pcm_substream_chip(substream);	snd_pcm_runtime_t *runtime = substream->runtime;	int err;	snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);	if (dma->opened)		return -EBUSY;	dma->substream = substream;	runtime->hw = snd_atiixp_pcm_hw;	dma->ac97_pcm_type = pcm_type;	if (pcm_type >= 0) {		runtime->hw.rates = chip->pcms[pcm_type]->rates;		snd_pcm_limit_hw_rates(runtime);	} else {		/* direct SPDIF */		runtime->hw.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;	}	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)		return err;	runtime->private_data = dma;	/* enable DMA bits */	spin_lock_irq(&chip->reg_lock);	dma->ops->enable_dma(chip, 1);	spin_unlock_irq(&chip->reg_lock);	dma->opened = 1;	return 0;}static int snd_atiixp_pcm_close(snd_pcm_substream_t *substream, atiixp_dma_t *dma){	atiixp_t *chip = snd_pcm_substream_chip(substream);	/* disable DMA bits */	snd_assert(dma->ops && dma->ops->enable_dma, return -EINVAL);	spin_lock_irq(&chip->reg_lock);	dma->ops->enable_dma(chip, 0);	spin_unlock_irq(&chip->reg_lock);	dma->substream = NULL;	dma->opened = 0;	return 0;}/* */static int snd_atiixp_playback_open(snd_pcm_substream_t *substream){	atiixp_t *chip = snd_pcm_substream_chip(substream);	int err;	down(&chip->open_mutex);	err = snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_PLAYBACK], 0);	up(&chip->open_mutex);	if (err < 0)		return err;	substream->runtime->hw.channels_max = chip->max_channels;	if (chip->max_channels > 2)		/* channels must be even */		snd_pcm_hw_constraint_step(substream->runtime, 0,					   SNDRV_PCM_HW_PARAM_CHANNELS, 2);	return 0;}static int snd_atiixp_playback_close(snd_pcm_substream_t *substream){	atiixp_t *chip = snd_pcm_substream_chip(substream);	int err;	down(&chip->open_mutex);	err = snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_PLAYBACK]);	up(&chip->open_mutex);	return err;}static int snd_atiixp_capture_open(snd_pcm_substream_t *substream){	atiixp_t *chip = snd_pcm_substream_chip(substream);	return snd_atiixp_pcm_open(substream, &chip->dmas[ATI_DMA_CAPTURE], 1);}static int snd_atiixp_capture_close(snd_pcm_substream_t *substream){	atiixp_t *chip = snd_pcm_substream_chip(substream);	return snd_atiixp_pcm_close(substream, &chip->dmas[ATI_DMA_CAPTURE]);}static int snd_atiixp_spdif_open(snd_pcm_substream_t *substream){	atiixp_t *chip = snd_pcm_substream_chip(substream);

⌨️ 快捷键说明

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