via82cxxx_audio.c

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

C
2,515
字号
	 */	nonblock = 0;	if (nonblock) {		if (!mutex_trylock(&card->syscall_mutex))			return -EAGAIN;	} else {		if (mutex_lock_interruptible(&card->syscall_mutex))			return -ERESTARTSYS;	}	return 0;}/** *	via_stop_everything - Stop all audio operations *	@card: Private info for specified board * *	Stops all DMA operations and interrupts, and clear *	any pending status bits resulting from those operations. */static void via_stop_everything (struct via_info *card){	u8 tmp, new_tmp;	DPRINTK ("ENTER\n");	assert (card != NULL);	/*	 * terminate any existing operations on audio read/write channels	 */	via_chan_stop (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN);	via_chan_stop (card->baseaddr + VIA_BASE0_PCM_IN_CHAN);	via_chan_stop (card->baseaddr + VIA_BASE0_FM_OUT_CHAN);	if(card->sixchannel)		via_chan_stop (card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN);	/*	 * clear any existing stops / flags (sanity check mainly)	 */	via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN);	via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_IN_CHAN);	via_chan_status_clear (card->baseaddr + VIA_BASE0_FM_OUT_CHAN);	if(card->sixchannel)		via_chan_status_clear (card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN);	/*	 * clear any enabled interrupt bits	 */	tmp = inb (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN_TYPE);	new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL);	if (tmp != new_tmp)		outb (0, card->baseaddr + VIA_BASE0_PCM_OUT_CHAN_TYPE);	tmp = inb (card->baseaddr + VIA_BASE0_PCM_IN_CHAN_TYPE);	new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL);	if (tmp != new_tmp)		outb (0, card->baseaddr + VIA_BASE0_PCM_IN_CHAN_TYPE);	tmp = inb (card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE);	new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL);	if (tmp != new_tmp)		outb (0, card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE);	if(card->sixchannel)	{		tmp = inb (card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN_TYPE);		new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL);		if (tmp != new_tmp)			outb (0, card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN_TYPE);	}	udelay(10);	/*	 * clear any existing flags	 */	via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN);	via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_IN_CHAN);	via_chan_status_clear (card->baseaddr + VIA_BASE0_FM_OUT_CHAN);	DPRINTK ("EXIT\n");}/** *	via_set_rate - Set PCM rate for given channel *	@ac97: Pointer to generic codec info struct *	@chan: Private info for specified channel *	@rate: Desired PCM sample rate, in Khz * *	Sets the PCM sample rate for a channel. * *	Values for @rate are clamped to a range of 4000 Khz through 48000 Khz, *	due to hardware constraints. */static int via_set_rate (struct ac97_codec *ac97,			 struct via_channel *chan, unsigned rate){	struct via_info *card = ac97->private_data;	int rate_reg;	u32 dacp;	u32 mast_vol, phone_vol, mono_vol, pcm_vol;	u32 mute_vol = 0x8000;	/* The mute volume? -- Seems to work! */	DPRINTK ("ENTER, rate = %d\n", rate);	if (chan->rate == rate)		goto out;	if (card->locked_rate) {		chan->rate = 48000;		goto out;	}	if (rate > 48000)		rate = 48000;	if (rate < 4000) 		rate = 4000;	rate_reg = chan->is_record ? AC97_PCM_LR_ADC_RATE :			    AC97_PCM_FRONT_DAC_RATE;	/* Save current state */	dacp=via_ac97_read_reg(ac97, AC97_POWER_CONTROL);	mast_vol = via_ac97_read_reg(ac97, AC97_MASTER_VOL_STEREO);	mono_vol = via_ac97_read_reg(ac97, AC97_MASTER_VOL_MONO);	phone_vol = via_ac97_read_reg(ac97, AC97_HEADPHONE_VOL);	pcm_vol = via_ac97_read_reg(ac97, AC97_PCMOUT_VOL);	/* Mute - largely reduces popping */	via_ac97_write_reg(ac97, AC97_MASTER_VOL_STEREO, mute_vol);	via_ac97_write_reg(ac97, AC97_MASTER_VOL_MONO, mute_vol);	via_ac97_write_reg(ac97, AC97_HEADPHONE_VOL, mute_vol);       	via_ac97_write_reg(ac97, AC97_PCMOUT_VOL, mute_vol);	/* Power down the DAC */	via_ac97_write_reg(ac97, AC97_POWER_CONTROL, dacp|0x0200);        /* Set new rate */	via_ac97_write_reg (ac97, rate_reg, rate);	/* Power DAC back up */	via_ac97_write_reg(ac97, AC97_POWER_CONTROL, dacp);	udelay (200); /* reduces popping */	/* Restore volumes */	via_ac97_write_reg(ac97, AC97_MASTER_VOL_STEREO, mast_vol);	via_ac97_write_reg(ac97, AC97_MASTER_VOL_MONO, mono_vol);	via_ac97_write_reg(ac97, AC97_HEADPHONE_VOL, phone_vol);	via_ac97_write_reg(ac97, AC97_PCMOUT_VOL, pcm_vol);	/* the hardware might return a value different than what we	 * passed to it, so read the rate value back from hardware	 * to see what we came up with	 */	chan->rate = via_ac97_read_reg (ac97, rate_reg);	if (chan->rate == 0) {		card->locked_rate = 1;		chan->rate = 48000;		printk (KERN_WARNING PFX "Codec rate locked at 48Khz\n");	}out:	DPRINTK ("EXIT, returning rate %d Hz\n", chan->rate);	return chan->rate;}/**************************************************************** * * Channel-specific operations * * *//** *	via_chan_init_defaults - Initialize a struct via_channel *	@card: Private audio chip info *	@chan: Channel to be initialized * *	Zero @chan, and then set all static defaults for the structure. */static void via_chan_init_defaults (struct via_info *card, struct via_channel *chan){	memset (chan, 0, sizeof (*chan));	if(card->intmask)		chan->intmask = 0x23;	/* Turn on the IRQ bits */			if (chan == &card->ch_out) {		chan->name = "PCM-OUT";		if(card->sixchannel)		{			chan->iobase = card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN;			chan->is_multi = 1;			DPRINTK("Using multichannel for pcm out\n");		}		else			chan->iobase = card->baseaddr + VIA_BASE0_PCM_OUT_CHAN;	} else if (chan == &card->ch_in) {		chan->name = "PCM-IN";		chan->iobase = card->baseaddr + VIA_BASE0_PCM_IN_CHAN;		chan->is_record = 1;	} else if (chan == &card->ch_fm) {		chan->name = "PCM-OUT-FM";		chan->iobase = card->baseaddr + VIA_BASE0_FM_OUT_CHAN;	} else {		BUG();	}	init_waitqueue_head (&chan->wait);	chan->pcm_fmt = VIA_PCM_FMT_MASK;	chan->is_enabled = 1;	chan->frag_number = 0;        chan->frag_size = 0;	atomic_set(&chan->n_frags, 0);	atomic_set (&chan->hw_ptr, 0);}/** *      via_chan_init - Initialize PCM channel *      @card: Private audio chip info *      @chan: Channel to be initialized * *      Performs some of the preparations necessary to begin *      using a PCM channel. * *      Currently the preparations consist of *      setting the PCM channel to a known state. */static void via_chan_init (struct via_info *card, struct via_channel *chan){        DPRINTK ("ENTER\n");	/* bzero channel structure, and init members to defaults */        via_chan_init_defaults (card, chan);        /* stop any existing channel output */        via_chan_clear (card, chan);        via_chan_status_clear (chan->iobase);        via_chan_pcm_fmt (chan, 1);	DPRINTK ("EXIT\n");}/** *	via_chan_buffer_init - Initialize PCM channel buffer *	@card: Private audio chip info *	@chan: Channel to be initialized * *	Performs some of the preparations necessary to begin *	using a PCM channel. * *	Currently the preparations include allocating the *	scatter-gather DMA table and buffers, *	and passing the *	address of the DMA table to the hardware. * *	Note that special care is taken when passing the *	DMA table address to hardware, because it was found *	during driver development that the hardware did not *	always "take" the address. */static int via_chan_buffer_init (struct via_info *card, struct via_channel *chan){	int page, offset;	int i;	DPRINTK ("ENTER\n");	chan->intmask = 0;	if(card->intmask)		chan->intmask = 0x23;	/* Turn on the IRQ bits */			if (chan->sgtable != NULL) {		DPRINTK ("EXIT\n");		return 0;	}	/* alloc DMA-able memory for scatter-gather table */	chan->sgtable = pci_alloc_consistent (card->pdev,		(sizeof (struct via_sgd_table) * chan->frag_number),		&chan->sgt_handle);	if (!chan->sgtable) {		printk (KERN_ERR PFX "DMA table alloc fail, aborting\n");		DPRINTK ("EXIT\n");		return -ENOMEM;	}	memset ((void*)chan->sgtable, 0,		(sizeof (struct via_sgd_table) * chan->frag_number));	/* alloc DMA-able memory for scatter-gather buffers */	chan->page_number = (chan->frag_number * chan->frag_size) / PAGE_SIZE +			    (((chan->frag_number * chan->frag_size) % PAGE_SIZE) ? 1 : 0);	for (i = 0; i < chan->page_number; i++) {		chan->pgtbl[i].cpuaddr = pci_alloc_consistent (card->pdev, PAGE_SIZE,					      &chan->pgtbl[i].handle);		if (!chan->pgtbl[i].cpuaddr) {			chan->page_number = i;			goto err_out_nomem;		}#ifndef VIA_NDEBUG                memset (chan->pgtbl[i].cpuaddr, 0xBC, chan->frag_size);#endif#if 1                DPRINTK ("dmabuf_pg #%d (h=%lx, v2p=%lx, a=%p)\n",			i, (long)chan->pgtbl[i].handle,			virt_to_phys(chan->pgtbl[i].cpuaddr),			chan->pgtbl[i].cpuaddr);#endif	}	for (i = 0; i < chan->frag_number; i++) {		page = i / (PAGE_SIZE / chan->frag_size);		offset = (i % (PAGE_SIZE / chan->frag_size)) * chan->frag_size;		chan->sgtable[i].count = cpu_to_le32 (chan->frag_size | VIA_FLAG);		chan->sgtable[i].addr = cpu_to_le32 (chan->pgtbl[page].handle + offset);#if 1		DPRINTK ("dmabuf #%d (32(h)=%lx)\n",			 i,			 (long)chan->sgtable[i].addr);#endif	}	/* overwrite the last buffer information */	chan->sgtable[chan->frag_number - 1].count = cpu_to_le32 (chan->frag_size | VIA_EOL);	/* set location of DMA-able scatter-gather info table */	DPRINTK ("outl (0x%X, 0x%04lX)\n",		chan->sgt_handle, chan->iobase + VIA_PCM_TABLE_ADDR);	via_ac97_wait_idle (card);	outl (chan->sgt_handle, chan->iobase + VIA_PCM_TABLE_ADDR);	udelay (20);	via_ac97_wait_idle (card);	/* load no rate adaption, stereo 16bit, set up ring slots */	if(card->sixchannel)	{		if(!chan->is_multi)		{			outl (0xFFFFF | (0x3 << 20) | (chan->frag_number << 24), chan->iobase + VIA_PCM_STOPRATE);			udelay (20);			via_ac97_wait_idle (card);		}	}	DPRINTK ("inl (0x%lX) = %x\n",		chan->iobase + VIA_PCM_TABLE_ADDR,		inl(chan->iobase + VIA_PCM_TABLE_ADDR));	DPRINTK ("EXIT\n");	return 0;err_out_nomem:	printk (KERN_ERR PFX "DMA buffer alloc fail, aborting\n");	via_chan_buffer_free (card, chan);	DPRINTK ("EXIT\n");	return -ENOMEM;}/** *	via_chan_free - Release a PCM channel *	@card: Private audio chip info *	@chan: Channel to be released * *	Performs all the functions necessary to clean up *	an initialized channel. * *	Currently these functions include disabled any *	active DMA operations, setting the PCM channel *	back to a known state, and releasing any allocated *	sound buffers. */static void via_chan_free (struct via_info *card, struct via_channel *chan){	DPRINTK ("ENTER\n");	spin_lock_irq (&card->lock);	/* stop any existing channel output */	via_chan_status_clear (chan->iobase);	via_chan_stop (chan->iobase);	via_chan_status_clear (chan->iobase);	spin_unlock_irq (&card->lock);	synchronize_irq(card->pdev->irq);	DPRINTK ("EXIT\n");}static void via_chan_buffer_free (struct via_info *card, struct via_channel *chan){	int i;        DPRINTK ("ENTER\n");	/* zero location of DMA-able scatter-gather info table */	via_ac97_wait_idle(card);	outl (0, chan->iobase + VIA_PCM_TABLE_ADDR);	for (i = 0; i < chan->page_number; i++)		if (chan->pgtbl[i].cpuaddr) {			pci_free_consistent (card->pdev, PAGE_SIZE,					     chan->pgtbl[i].cpuaddr,					     chan->pgtbl[i].handle);			chan->pgtbl[i].cpuaddr = NULL;			chan->pgtbl[i].handle = 0;		}	chan->page_number = 0;	if (chan->sgtable) {		pci_free_consistent (card->pdev,			(sizeof (struct via_sgd_table) * chan->frag_number),			(void*)chan->sgtable, chan->sgt_handle);		chan->sgtable = NULL;	}	DPRINTK ("EXIT\n");}/** *	via_chan_pcm_fmt - Update PCM channel settings *	@chan: Channel to be updated *	@reset: Boolean.  If non-zero, channel will be reset *		to 8-bit mono mode. * *	Stores the settings of the current PCM format, *	8-bit or 16-bit, and mono/stereo, into the *	hardware settings for the specified channel. *	If @reset is non-zero, the channel is reset *	to 8-bit mono mode.  Otherwise, the channel *	is set to the values stored in the channel *	information struct @chan. */static void via_chan_pcm_fmt (struct via_channel *chan, int reset){	DPRINTK ("ENTER, pcm_fmt=0x%02X, reset=%s\n",		 chan->pcm_fmt, reset ? "yes" : "no");	assert (chan != NULL);	if (reset)	{		/* reset to 8-bit mono mode */		chan->pcm_fmt = 0;		chan->channels = 1;	}	/* enable interrupts on FLAG and EOL */	chan->pcm_fmt |= VIA_CHAN_TYPE_MASK;	/* if we are recording, enable recording fifo bit */	if (chan->is_record)		chan->pcm_fmt |= VIA_PCM_REC_FIFO;	/* set interrupt select bits where applicable (PCM in & out channels) */	if (!chan->is_record)		chan->pcm_fmt |= VIA_CHAN_TYPE_INT_SELECT;		DPRINTK("SET FMT - %02x %02x\n", chan->intmask , chan->is_multi);		if(chan->intmask)	{		u32 m;		/*		 *	Channel 0x4 is up to 6 x 16bit and has to be		 *	programmed differently 		 */		 				if(chan->is_multi)		{			u8 c = 0;						/*			 *	Load the type bit for num channels			 *	and 8/16bit			 */			 

⌨️ 快捷键说明

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