📄 cs46xx_lib.c
字号:
}/* * 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, snd_pcm_uframes_t frames){ cs46xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_sframes_t diff = runtime->control->appl_ptr - chip->play.appl_ptr; if (diff) { if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) diff += runtime->boundary; chip->play.sw_ready += diff << chip->play.shift; } chip->play.sw_ready += frames << chip->play.shift; chip->play.appl_ptr = runtime->control->appl_ptr + frames; while (chip->play.hw_ready < CS46XX_BUFFER_SIZE && chip->play.sw_ready > 0) { size_t hw_to_end = CS46XX_BUFFER_SIZE - chip->play.hw_data; size_t sw_to_end = chip->play.sw_bufsize - chip->play.sw_data; size_t bytes = CS46XX_BUFFER_SIZE - chip->play.hw_ready; if (chip->play.sw_ready < bytes) bytes = chip->play.sw_ready; if (hw_to_end < bytes) bytes = hw_to_end; if (sw_to_end < bytes) bytes = sw_to_end; memcpy(chip->play.hw_area + chip->play.hw_data, runtime->dma_area + chip->play.sw_data, bytes); chip->play.hw_data += bytes; if (chip->play.hw_data == CS46XX_BUFFER_SIZE) chip->play.hw_data = 0; chip->play.sw_data += bytes; if (chip->play.sw_data == chip->play.sw_bufsize) chip->play.sw_data = 0; chip->play.hw_ready += bytes; chip->play.sw_ready -= bytes; } return 0;}static int snd_cs46xx_capture_transfer(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames){ cs46xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_sframes_t diff = runtime->control->appl_ptr - chip->capt.appl_ptr; if (diff) { if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) diff += runtime->boundary; chip->capt.sw_ready -= diff << chip->capt.shift; } chip->capt.sw_ready -= frames << chip->capt.shift; chip->capt.appl_ptr = runtime->control->appl_ptr + frames; while (chip->capt.hw_ready > 0 && chip->capt.sw_ready < chip->capt.sw_bufsize) { size_t hw_to_end = CS46XX_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 < 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_area + chip->capt.hw_data, bytes); chip->capt.hw_data += bytes; if (chip->capt.hw_data == CS46XX_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 = snd_cs46xx_peek(chip, BA1_PBA) - chip->play.hw_addr; return ptr >> chip->play.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 = snd_cs46xx_peek(chip, BA1_PBA) - chip->play.hw_addr; ssize_t bytes = ptr - chip->play.hw_io; if (bytes < 0) bytes += CS46XX_BUFFER_SIZE; chip->play.hw_io = ptr; chip->play.hw_ready -= bytes; chip->play.sw_io += bytes; if (chip->play.sw_io > chip->play.sw_bufsize) chip->play.sw_io -= chip->play.sw_bufsize; snd_cs46xx_playback_transfer(substream, 0); return chip->play.sw_io >> chip->play.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_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_addr; ssize_t bytes = ptr - chip->capt.hw_io; if (bytes < 0) bytes += CS46XX_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, 0); return chip->capt.sw_io >> chip->capt.shift;}static int snd_cs46xx_playback_copy(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t hwoff, void *src, snd_pcm_uframes_t frames){ snd_pcm_runtime_t *runtime = substream->runtime; cs46xx_t *chip = snd_pcm_substream_chip(substream); size_t hwoffb = hwoff << chip->play.shift; size_t bytes = frames << chip->play.shift; char *hwbuf = runtime->dma_area + hwoffb; if (copy_from_user(hwbuf, src, bytes)) return -EFAULT; spin_lock_irq(&runtime->lock); snd_cs46xx_playback_transfer(substream, frames); spin_unlock_irq(&runtime->lock); return 0;} static int snd_cs46xx_capture_copy(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t hwoff, void *dst, snd_pcm_uframes_t frames){ snd_pcm_runtime_t *runtime = substream->runtime; cs46xx_t *chip = snd_pcm_substream_chip(substream); size_t hwoffb = hwoff << chip->capt.shift; size_t bytes = frames << chip->capt.shift; char *hwbuf = runtime->dma_area + hwoffb; if (copy_to_user(dst, hwbuf, bytes)) return -EFAULT; spin_lock_irq(&runtime->lock); snd_cs46xx_capture_transfer(substream, frames); spin_unlock_irq(&runtime->lock); return 0;} static int snd_cs46xx_playback_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: if (substream->runtime->periods != CS46XX_FRAGS) snd_cs46xx_playback_transfer(substream, 0); tmp = snd_cs46xx_peek(chip, BA1_PCTL); tmp &= 0x0000ffff; snd_cs46xx_poke(chip, BA1_PCTL, chip->play.ctl | tmp); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: tmp = snd_cs46xx_peek(chip, BA1_PCTL); tmp &= 0x0000ffff; snd_cs46xx_poke(chip, BA1_PCTL, tmp); break; default: result = -EINVAL; break; } spin_unlock(&chip->reg_lock); 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;}static int snd_cs46xx_playback_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params){ cs46xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; int err; if (params_periods(hw_params) == CS46XX_FRAGS) { if (runtime->dma_area != chip->play.hw_area) snd_pcm_lib_free_pages(substream); runtime->dma_area = chip->play.hw_area; runtime->dma_addr = chip->play.hw_addr; runtime->dma_bytes = chip->play.hw_size; substream->ops = &snd_cs46xx_playback_ops; } else { if (runtime->dma_area == chip->play.hw_area) { runtime->dma_area = NULL; runtime->dma_addr = 0; runtime->dma_bytes = 0; } if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0) return err; substream->ops = &snd_cs46xx_playback_indirect_ops; } return 0;}static int snd_cs46xx_playback_hw_free(snd_pcm_substream_t * substream){ cs46xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; if (runtime->dma_area != chip->play.hw_area) snd_pcm_lib_free_pages(substream); runtime->dma_area = NULL; runtime->dma_addr = 0; runtime->dma_bytes = 0; return 0;}static int snd_cs46xx_playback_prepare(snd_pcm_substream_t * substream){ unsigned int tmp; unsigned int pfie; cs46xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; pfie = snd_cs46xx_peek(chip, BA1_PFIE); pfie &= ~0x0000f03f; chip->play.shift = 2; if (runtime->channels == 1) { chip->play.shift--; pfie |= 0x00002000; } if (snd_pcm_format_width(runtime->format) == 8) { chip->play.shift--; pfie |= 0x00001000; } if (snd_pcm_format_unsigned(runtime->format)) pfie |= 0x00008000; if (snd_pcm_format_big_endian(runtime->format)) pfie |= 0x00004000; chip->play.sw_bufsize = snd_pcm_lib_buffer_bytes(substream); chip->play.sw_data = chip->play.sw_io = chip->play.sw_ready = 0; chip->play.hw_data = chip->play.hw_io = chip->play.hw_ready = 0; chip->play.appl_ptr = 0; snd_cs46xx_poke(chip, BA1_PBA, chip->play.hw_addr); tmp = snd_cs46xx_peek(chip, BA1_PDTC); tmp &= ~0x000003ff; tmp |= (4 << chip->play.shift) - 1; snd_cs46xx_poke(chip, BA1_PDTC, tmp); snd_cs46xx_poke(chip, BA1_PFIE, pfie); snd_cs46xx_set_play_sample_rate(chip, runtime->rate); return 0;}static int snd_cs46xx_capture_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params){ cs46xx_t *chip = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; int err;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -