cs46xx_lib.c

来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 2,248 行 · 第 1/5 页

C
2,248
字号
		powerdown = 1;	}	/*	 *  We want to clear out the serial port FIFOs so we don't end up playing	 *  whatever random garbage happens to be in them.  We fill the sample FIFOS	 *  with zero (silence).	 */	snd_cs46xx_pokeBA0(chip, BA0_SERBWP, 0);	/*	 *  Fill all 256 sample FIFO locations.	 */	for (idx = 0; idx < 0xFF; idx++) {		/*		 *  Make sure the previous FIFO write operation has completed.		 */		if (cs46xx_wait_for_fifo(chip,1)) {			snd_printdd ("failed waiting for FIFO at addr (%02X)\n",idx);			if (powerdown)				snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);          			break;		}		/*		 *  Write the serial port FIFO index.		 */		snd_cs46xx_pokeBA0(chip, BA0_SERBAD, idx);		/*		 *  Tell the serial port to load the new value into the FIFO location.		 */		snd_cs46xx_pokeBA0(chip, BA0_SERBCM, SERBCM_WRC);	}	/*	 *  Now, if we powered up the devices, then power them back down again.	 *  This is kinda ugly, but should never happen.	 */	if (powerdown)		snd_cs46xx_pokeBA0(chip, BA0_CLKCR1, tmp);}static void snd_cs46xx_proc_start(cs46xx_t *chip){	int cnt;	/*	 *  Set the frame timer to reflect the number of cycles per frame.	 */	snd_cs46xx_poke(chip, BA1_FRMT, 0xadf);	/*	 *  Turn on the run, run at frame, and DMA enable bits in the local copy of	 *  the SP control register.	 */	snd_cs46xx_poke(chip, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN);	/*	 *  Wait until the run at frame bit resets itself in the SP control	 *  register.	 */	for (cnt = 0; cnt < 25; cnt++) {		udelay(50);		if (!(snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR))			break;	}	if (snd_cs46xx_peek(chip, BA1_SPCR) & SPCR_RUNFR)		snd_printk("SPCR_RUNFR never reset\n");}static void snd_cs46xx_proc_stop(cs46xx_t *chip){	/*	 *  Turn off the run, run at frame, and DMA enable bits in the local copy of	 *  the SP control register.	 */	snd_cs46xx_poke(chip, BA1_SPCR, 0);}/* *  Sample rate routines */#define GOF_PER_SEC 200static void snd_cs46xx_set_play_sample_rate(cs46xx_t *chip, unsigned int rate){	unsigned long flags;	unsigned int tmp1, tmp2;	unsigned int phiIncr;	unsigned int correctionPerGOF, correctionPerSec;	/*	 *  Compute the values used to drive the actual sample rate conversion.	 *  The following formulas are being computed, using inline assembly	 *  since we need to use 64 bit arithmetic to compute the values:	 *	 *  phiIncr = floor((Fs,in * 2^26) / Fs,out)	 *  correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) /         *                                   GOF_PER_SEC)         *  ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -M         *                       GOF_PER_SEC * correctionPerGOF	 *	 *  i.e.	 *	 *  phiIncr:other = dividend:remainder((Fs,in * 2^26) / Fs,out)	 *  correctionPerGOF:correctionPerSec =	 *      dividend:remainder(ulOther / GOF_PER_SEC)	 */	tmp1 = rate << 16;	phiIncr = tmp1 / 48000;	tmp1 -= phiIncr * 48000;	tmp1 <<= 10;	phiIncr <<= 10;	tmp2 = tmp1 / 48000;	phiIncr += tmp2;	tmp1 -= tmp2 * 48000;	correctionPerGOF = tmp1 / GOF_PER_SEC;	tmp1 -= correctionPerGOF * GOF_PER_SEC;	correctionPerSec = tmp1;	/*	 *  Fill in the SampleRateConverter control block.	 */	spin_lock_irqsave(&chip->reg_lock, flags);	snd_cs46xx_poke(chip, BA1_PSRC,	  ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));	snd_cs46xx_poke(chip, BA1_PPI, phiIncr);	spin_unlock_irqrestore(&chip->reg_lock, flags);}static void snd_cs46xx_set_capture_sample_rate(cs46xx_t *chip, unsigned int rate){	unsigned long flags;	unsigned int phiIncr, coeffIncr, tmp1, tmp2;	unsigned int correctionPerGOF, correctionPerSec, initialDelay;	unsigned int frameGroupLength, cnt;	/*	 *  We can only decimate by up to a factor of 1/9th the hardware rate.	 *  Correct the value if an attempt is made to stray outside that limit.	 */	if ((rate * 9) < 48000)		rate = 48000 / 9;	/*	 *  We can not capture at at rate greater than the Input Rate (48000).	 *  Return an error if an attempt is made to stray outside that limit.	 */	if (rate > 48000)		rate = 48000;	/*	 *  Compute the values used to drive the actual sample rate conversion.	 *  The following formulas are being computed, using inline assembly	 *  since we need to use 64 bit arithmetic to compute the values:	 *	 *     coeffIncr = -floor((Fs,out * 2^23) / Fs,in)	 *     phiIncr = floor((Fs,in * 2^26) / Fs,out)	 *     correctionPerGOF = floor((Fs,in * 2^26 - Fs,out * phiIncr) /	 *                                GOF_PER_SEC)	 *     correctionPerSec = Fs,in * 2^26 - Fs,out * phiIncr -	 *                          GOF_PER_SEC * correctionPerGOF	 *     initialDelay = ceil((24 * Fs,in) / Fs,out)	 *	 * i.e.	 *	 *     coeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in))	 *     phiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out)	 *     correctionPerGOF:correctionPerSec =	 * 	    dividend:remainder(ulOther / GOF_PER_SEC)	 *     initialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out)	 */	tmp1 = rate << 16;	coeffIncr = tmp1 / 48000;	tmp1 -= coeffIncr * 48000;	tmp1 <<= 7;	coeffIncr <<= 7;	coeffIncr += tmp1 / 48000;	coeffIncr ^= 0xFFFFFFFF;	coeffIncr++;	tmp1 = 48000 << 16;	phiIncr = tmp1 / rate;	tmp1 -= phiIncr * rate;	tmp1 <<= 10;	phiIncr <<= 10;	tmp2 = tmp1 / rate;	phiIncr += tmp2;	tmp1 -= tmp2 * rate;	correctionPerGOF = tmp1 / GOF_PER_SEC;	tmp1 -= correctionPerGOF * GOF_PER_SEC;	correctionPerSec = tmp1;	initialDelay = ((48000 * 24) + rate - 1) / rate;	/*	 *  Fill in the VariDecimate control block.	 */	spin_lock_irqsave(&chip->reg_lock, flags);	snd_cs46xx_poke(chip, BA1_CSRC,		((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));	snd_cs46xx_poke(chip, BA1_CCI, coeffIncr);	snd_cs46xx_poke(chip, BA1_CD,		(((BA1_VARIDEC_BUF_1 + (initialDelay << 2)) << 16) & 0xFFFF0000) | 0x80);	snd_cs46xx_poke(chip, BA1_CPI, phiIncr);	spin_unlock_irqrestore(&chip->reg_lock, flags);	/*	 *  Figure out the frame group length for the write back task.  Basically,	 *  this is just the factors of 24000 (2^6*3*5^3) that are not present in	 *  the output sample rate.	 */	frameGroupLength = 1;	for (cnt = 2; cnt <= 64; cnt *= 2) {		if (((rate / cnt) * cnt) != rate)			frameGroupLength *= 2;	}	if (((rate / 3) * 3) != rate) {		frameGroupLength *= 3;	}	for (cnt = 5; cnt <= 125; cnt *= 5) {		if (((rate / cnt) * cnt) != rate) 			frameGroupLength *= 5;        }	/*	 * Fill in the WriteBack control block.	 */	spin_lock_irqsave(&chip->reg_lock, flags);	snd_cs46xx_poke(chip, BA1_CFG1, frameGroupLength);	snd_cs46xx_poke(chip, BA1_CFG2, (0x00800000 | frameGroupLength));	snd_cs46xx_poke(chip, BA1_CCST, 0x0000FFFF);	snd_cs46xx_poke(chip, BA1_CSPB, ((65536 * rate) / 24000));	snd_cs46xx_poke(chip, (BA1_CSPB + 4), 0x0000FFFF);	spin_unlock_irqrestore(&chip->reg_lock, flags);}/* *  PCM part */static int snd_cs46xx_playback_transfer(snd_pcm_substream_t *substream){	/* cs46xx_t *chip = snd_pcm_substream_chip(substream); */	snd_pcm_runtime_t *runtime = substream->runtime;	cs46xx_pcm_t * cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO);	snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;	snd_pcm_sframes_t diff = appl_ptr - cpcm->appl_ptr;	int buffer_size = runtime->period_size * CS46XX_FRAGS << cpcm->shift;	if (diff) {		if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))			diff += runtime->boundary;		cpcm->sw_ready += diff * (1 << cpcm->shift);		cpcm->appl_ptr = appl_ptr;	}	while (cpcm->hw_ready < buffer_size && 	       cpcm->sw_ready > 0) {		size_t hw_to_end = buffer_size - cpcm->hw_data;		size_t sw_to_end = cpcm->sw_bufsize - cpcm->sw_data;		size_t bytes = buffer_size - cpcm->hw_ready;		if (cpcm->sw_ready < (int)bytes)			bytes = cpcm->sw_ready;		if (hw_to_end < bytes)			bytes = hw_to_end;		if (sw_to_end < bytes)			bytes = sw_to_end;		memcpy(cpcm->hw_buf.area + cpcm->hw_data,		       runtime->dma_area + cpcm->sw_data,		       bytes);		cpcm->hw_data += bytes;		if ((int)cpcm->hw_data == buffer_size)			cpcm->hw_data = 0;		cpcm->sw_data += bytes;		if (cpcm->sw_data == cpcm->sw_bufsize)			cpcm->sw_data = 0;		cpcm->hw_ready += bytes;		cpcm->sw_ready -= bytes;	}	return 0;}static int snd_cs46xx_capture_transfer(snd_pcm_substream_t *substream){	cs46xx_t *chip = snd_pcm_substream_chip(substream);	snd_pcm_runtime_t *runtime = substream->runtime;	snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;	snd_pcm_sframes_t diff = appl_ptr - chip->capt.appl_ptr;	int buffer_size = runtime->period_size * CS46XX_FRAGS << chip->capt.shift;	if (diff) {		if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2))			diff += runtime->boundary;		chip->capt.sw_ready -= diff * (1 << chip->capt.shift);		chip->capt.appl_ptr = appl_ptr;	}	while (chip->capt.hw_ready > 0 && 	       chip->capt.sw_ready < (int)chip->capt.sw_bufsize) {		size_t hw_to_end = buffer_size - chip->capt.hw_data;		size_t sw_to_end = chip->capt.sw_bufsize - chip->capt.sw_data;		size_t bytes = chip->capt.sw_bufsize - chip->capt.sw_ready;		if (chip->capt.hw_ready < (int)bytes)			bytes = chip->capt.hw_ready;		if (hw_to_end < bytes)			bytes = hw_to_end;		if (sw_to_end < bytes)			bytes = sw_to_end;		memcpy(runtime->dma_area + chip->capt.sw_data,		       chip->capt.hw_buf.area + chip->capt.hw_data,		       bytes);		chip->capt.hw_data += bytes;		if ((int)chip->capt.hw_data == buffer_size)			chip->capt.hw_data = 0;		chip->capt.sw_data += bytes;		if (chip->capt.sw_data == chip->capt.sw_bufsize)			chip->capt.sw_data = 0;		chip->capt.hw_ready -= bytes;		chip->capt.sw_ready += bytes;	}	return 0;}static snd_pcm_uframes_t snd_cs46xx_playback_direct_pointer(snd_pcm_substream_t * substream){	cs46xx_t *chip = snd_pcm_substream_chip(substream);	size_t ptr;	cs46xx_pcm_t *cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO);	snd_assert (cpcm->pcm_channel,return -ENXIO);#ifdef CONFIG_SND_CS46XX_NEW_DSP	ptr = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 2) << 2);#else	ptr = snd_cs46xx_peek(chip, BA1_PBA);#endif	ptr -= cpcm->hw_buf.addr;	return ptr >> cpcm->shift;}static snd_pcm_uframes_t snd_cs46xx_playback_indirect_pointer(snd_pcm_substream_t * substream){	cs46xx_t *chip = snd_pcm_substream_chip(substream);	size_t ptr;	cs46xx_pcm_t *cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO);	ssize_t bytes;	int buffer_size = substream->runtime->period_size * CS46XX_FRAGS << cpcm->shift;#ifdef CONFIG_SND_CS46XX_NEW_DSP	snd_assert (cpcm->pcm_channel,return -ENXIO);	ptr = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 2) << 2);#else	ptr = snd_cs46xx_peek(chip, BA1_PBA);#endif	ptr -= cpcm->hw_buf.addr;	bytes = ptr - cpcm->hw_io;	if (bytes < 0)		bytes += buffer_size;	cpcm->hw_io = ptr;	cpcm->hw_ready -= bytes;	cpcm->sw_io += bytes;	if (cpcm->sw_io >= cpcm->sw_bufsize)		cpcm->sw_io -= cpcm->sw_bufsize;	snd_cs46xx_playback_transfer(substream);	return cpcm->sw_io >> cpcm->shift;}static snd_pcm_uframes_t snd_cs46xx_capture_direct_pointer(snd_pcm_substream_t * substream){	cs46xx_t *chip = snd_pcm_substream_chip(substream);	size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_buf.addr;	return ptr >> chip->capt.shift;}static snd_pcm_uframes_t snd_cs46xx_capture_indirect_pointer(snd_pcm_substream_t * substream){	cs46xx_t *chip = snd_pcm_substream_chip(substream);	size_t ptr = snd_cs46xx_peek(chip, BA1_CBA) - chip->capt.hw_buf.addr;	ssize_t bytes = ptr - chip->capt.hw_io;	int buffer_size = substream->runtime->period_size * CS46XX_FRAGS << chip->capt.shift;	if (bytes < 0)		bytes += buffer_size;	chip->capt.hw_io = ptr;	chip->capt.hw_ready += bytes;	chip->capt.sw_io += bytes;	if (chip->capt.sw_io >= chip->capt.sw_bufsize)		chip->capt.sw_io -= chip->capt.sw_bufsize;	snd_cs46xx_capture_transfer(substream);	return chip->capt.sw_io >> chip->capt.shift;}static int snd_cs46xx_playback_trigger(snd_pcm_substream_t * substream,				       int cmd){	cs46xx_t *chip = snd_pcm_substream_chip(substream);	/*snd_pcm_runtime_t *runtime = substream->runtime;*/	int result = 0;#ifdef CONFIG_SND_CS46XX_NEW_DSP	cs46xx_pcm_t *cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO);#else	spin_lock(&chip->reg_lock);#endif#ifdef CONFIG_SND_CS46XX_NEW_DSP	if (! cpcm->pcm_channel) {		return -ENXIO;	}#endif	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:	case SNDRV_PCM_TRIGGER_RESUME:#ifdef CONFIG_SND_CS46XX_NEW_DSP		/* magic value to unmute PCM stream  playback volume */		snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 				       SCBVolumeCtrl) << 2, 0x80008000);		if (cpcm->pcm_channel->unlinked)			cs46xx_dsp_pcm_link(chip,cpcm->pcm_channel);		if (substream->runtime->periods != CS46XX_FRAGS)			snd_cs46xx_playback_transfer(substream);#else		if (substream->runtime->periods != CS46XX_FRAGS)			snd_cs46xx_playback_transfer(substream);		{ unsigned int tmp;		tmp = snd_cs46xx_peek(chip, BA1_PCTL);		tmp &= 0x0000ffff;		snd_cs46xx_poke(chip, BA1_PCTL, chip->play_ctl | tmp);		}#endif		break;	case SNDRV_PCM_TRIGGER_STOP:	case SNDRV_PCM_TRIGGER_SUSPEND:#ifdef CONFIG_SND_CS46XX_NEW_DSP		/* magic mute channel */		snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 				       SCBVolumeCtrl) << 2, 0xffffffff);		if (!cpcm->pcm_channel->unlinked)			cs46xx_dsp_pcm_unlink(chip,cpcm->pcm_channel);#else		{ unsigned int tmp;		tmp = snd_cs46xx_peek(chip, BA1_PCTL);		tmp &= 0x0000ffff;		snd_cs46xx_poke(chip, BA1_PCTL, tmp);		}#endif		break;

⌨️ 快捷键说明

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