📄 cs46xx_lib.c
字号:
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 void snd_cs46xx_pb_trans_copy(snd_pcm_substream_t *substream, snd_pcm_indirect_t *rec, size_t bytes){ snd_pcm_runtime_t *runtime = substream->runtime; cs46xx_pcm_t * cpcm = runtime->private_data; memcpy(cpcm->hw_buf.area + rec->hw_data, runtime->dma_area + rec->sw_data, bytes);}static int snd_cs46xx_playback_transfer(snd_pcm_substream_t *substream){ snd_pcm_runtime_t *runtime = substream->runtime; cs46xx_pcm_t * cpcm = runtime->private_data; snd_pcm_indirect_playback_transfer(substream, &cpcm->pcm_rec, snd_cs46xx_pb_trans_copy); return 0;}static void snd_cs46xx_cp_trans_copy(snd_pcm_substream_t *substream, snd_pcm_indirect_t *rec, size_t bytes){ cs46xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; memcpy(runtime->dma_area + rec->sw_data, chip->capt.hw_buf.area + rec->hw_data, bytes);}static int snd_cs46xx_capture_transfer(snd_pcm_substream_t *substream){ cs46xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_indirect_capture_transfer(substream, &chip->capt.pcm_rec, snd_cs46xx_cp_trans_copy); 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 = substream->runtime->private_data; 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 = substream->runtime->private_data;#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; return snd_pcm_indirect_playback_pointer(substream, &cpcm->pcm_rec, ptr);}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; return snd_pcm_indirect_capture_pointer(substream, &chip->capt.pcm_rec, ptr);}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 = substream->runtime->private_data; 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 spin_lock(&chip->reg_lock); 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); } spin_unlock(&chip->reg_lock);#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 spin_lock(&chip->reg_lock); { unsigned int tmp; tmp = snd_cs46xx_peek(chip, BA1_PCTL); tmp &= 0x0000ffff; snd_cs46xx_poke(chip, BA1_PCTL, tmp); } spin_unlock(&chip->reg_lock);#endif break; default: result = -EINVAL; break; } return result;}static int snd_cs46xx_capture_trigger(snd_pcm_substream_t * substream, int cmd){ cs46xx_t *chip = snd_pcm_substream_chip(substream); unsigned int tmp; int result = 0; spin_lock(&chip->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: tmp = snd_cs46xx_peek(chip, BA1_CCTL); tmp &= 0xffff0000; snd_cs46xx_poke(chip, BA1_CCTL, chip->capt.ctl | tmp); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: tmp = snd_cs46xx_peek(chip, BA1_CCTL); tmp &= 0xffff0000; snd_cs46xx_poke(chip, BA1_CCTL, tmp); break; default: result = -EINVAL; break; } spin_unlock(&chip->reg_lock); return result;}#ifdef CONFIG_SND_CS46XX_NEW_DSPstatic int _cs46xx_adjust_sample_rate (cs46xx_t *chip, cs46xx_pcm_t *cpcm, int sample_rate) { /* If PCMReaderSCB and SrcTaskSCB not created yet ... */ if ( cpcm->pcm_channel == NULL) { cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate, cpcm, cpcm->hw_buf.addr,cpcm->pcm_channel_id); if (cpcm->pcm_channel == NULL) { snd_printk(KERN_ERR "cs46xx: failed to create virtual PCM channel\n"); return -ENOMEM; } cpcm->pcm_channel->sample_rate = sample_rate; } else /* if sample rate is changed */ if ((int)cpcm->pcm_channel->sample_rate != sample_rate) { int unlinked = cpcm->pcm_channel->unlinked; cs46xx_dsp_destroy_pcm_channel (chip,cpcm->pcm_channel); if ( (cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, sample_rate, cpcm, cpcm->hw_buf.addr, cpcm->pcm_channel_id)) == NULL) { snd_printk(KERN_ERR "cs46xx: failed to re-create virtual PCM channel\n"); return -ENOMEM; } if (!unlinked) cs46xx_dsp_pcm_link (chip,cpcm->pcm_channel); cpcm->pcm_channel->sample_rate = sample_rate; } return 0;}#endifstatic int snd_cs46xx_playback_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params){ snd_pcm_runtime_t *runtime = substream->runtime; cs46xx_pcm_t *cpcm; int err;#ifdef CONFIG_SND_CS46XX_NEW_DSP cs46xx_t *chip = snd_pcm_substream_chip(substream); int sample_rate = params_rate(hw_params); int period_size = params_period_bytes(hw_params);#endif cpcm = runtime->private_data;#ifdef CONFIG_SND_CS46XX_NEW_DSP snd_assert (sample_rate != 0, return -ENXIO); down (&chip->spos_mutex); if (_cs46xx_adjust_sample_rate (chip,cpcm,sample_rate)) { up (&chip->spos_mutex); return -ENXIO; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -