📄 ymfpci.c
字号:
#if 0 if (file->f_flags & O_NONBLOCK) { /* * XXX Our mistake is to attach DMA buffer to state * rather than to some per-device structure. * Cannot skip waiting, can only make it shorter. */ }#endif while (ypcm->running) { spin_unlock_irqrestore(&unit->reg_lock, flags); set_current_state(TASK_UNINTERRUPTIBLE); schedule(); spin_lock_irqsave(&unit->reg_lock, flags); } spin_unlock_irqrestore(&unit->reg_lock, flags); set_current_state(TASK_RUNNING); remove_wait_queue(&state->dmabuf.wait, &waita); /* * This function may take up to 4 seconds to reach this point * (32K circular buffer, 8000 Hz). User notices. */}/* * Hardware start management */static void ymfpci_hw_start(ymfpci_t *codec){ unsigned long flags; spin_lock_irqsave(&codec->reg_lock, flags); if (codec->start_count++ == 0) { ymfpci_writel(codec, YDSXGR_MODE, 3); codec->active_bank = ymfpci_readl(codec, YDSXGR_CTRLSELECT) & 1; } spin_unlock_irqrestore(&codec->reg_lock, flags);}static void ymfpci_hw_stop(ymfpci_t *codec){ unsigned long flags; long timeout = 1000; spin_lock_irqsave(&codec->reg_lock, flags); if (--codec->start_count == 0) { ymfpci_writel(codec, YDSXGR_MODE, 0); while (timeout-- > 0) { if ((ymfpci_readl(codec, YDSXGR_STATUS) & 2) == 0) break; } } spin_unlock_irqrestore(&codec->reg_lock, flags);}/* * Playback voice management */static int voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice){ ymfpci_voice_t *voice, *voice2; int idx; *rvoice = NULL; for (idx = 0; idx < 64; idx += pair ? 2 : 1) { voice = &codec->voices[idx]; voice2 = pair ? &codec->voices[idx+1] : NULL; if (voice->use || (voice2 && voice2->use)) continue; voice->use = 1; if (voice2) voice2->use = 1; switch (type) { case YMFPCI_PCM: voice->pcm = 1; if (voice2) voice2->pcm = 1; break; case YMFPCI_SYNTH: voice->synth = 1; break; case YMFPCI_MIDI: voice->midi = 1; break; } ymfpci_hw_start(codec); if (voice2) ymfpci_hw_start(codec); *rvoice = voice; return 0; } return -ENOMEM;}static int ymfpci_voice_alloc(ymfpci_t *codec, ymfpci_voice_type_t type, int pair, ymfpci_voice_t **rvoice){ unsigned long flags; int result; spin_lock_irqsave(&codec->voice_lock, flags); for (;;) { result = voice_alloc(codec, type, pair, rvoice); if (result == 0 || type != YMFPCI_PCM) break; /* TODO: synth/midi voice deallocation */ break; } spin_unlock_irqrestore(&codec->voice_lock, flags); return result; }static int ymfpci_voice_free(ymfpci_t *codec, ymfpci_voice_t *pvoice){ unsigned long flags; ymfpci_hw_stop(codec); spin_lock_irqsave(&codec->voice_lock, flags); pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; pvoice->ypcm = NULL; pvoice->interrupt = NULL; spin_unlock_irqrestore(&codec->voice_lock, flags); return 0;}/* * PCM part */static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice){ ymfpci_pcm_t *ypcm; int redzone; int pos, delta, swptr; int played, distance; struct ymf_state *state; struct ymf_dmabuf *dmabuf; char silence; if ((ypcm = voice->ypcm) == NULL) { return; } if ((state = ypcm->state) == NULL) { ypcm->running = 0; // lock it return; } dmabuf = &state->dmabuf; spin_lock(&codec->reg_lock); if (ypcm->running) {/* P3 */ /** printk("ymfpci: %d, intr bank %d count %d start 0x%x:%x\n", voice->number, codec->active_bank, dmabuf->count, voice->bank[0].start, voice->bank[1].start); **/ silence = (ymf_pcm_format_width(state->format.format) == 16) ? 0 : 0x80; /* We need actual left-hand-side redzone size here. */ redzone = ymf_calc_lend(state->format.rate); redzone <<= (state->format.shift + 1); swptr = dmabuf->swptr; pos = voice->bank[codec->active_bank].start; pos <<= state->format.shift; if (pos < 0 || pos >= dmabuf->dmasize) { /* ucode bug */ printk(KERN_ERR "ymfpci%d: %d: runaway: hwptr %d dmasize %d\n", codec->dev_audio, voice->number, dmabuf->hwptr, dmabuf->dmasize); pos = 0; } if (pos < dmabuf->hwptr) { delta = dmabuf->dmasize - dmabuf->hwptr; memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta); delta += pos; memset(dmabuf->rawbuf, silence, pos); } else { delta = pos - dmabuf->hwptr; memset(dmabuf->rawbuf + dmabuf->hwptr, silence, delta); } dmabuf->hwptr = pos; if (dmabuf->count == 0) { printk("ymfpci%d: %d: strain: hwptr %d\n", codec->dev_audio, voice->number, dmabuf->hwptr); ymf_playback_trigger(codec, ypcm, 0); } if (swptr <= pos) { distance = pos - swptr; } else { distance = dmabuf->dmasize - (swptr - pos); } if (distance < redzone) { /* * hwptr inside redzone => DMA ran out of samples. */ if (delta < dmabuf->count) { /* * Lost interrupt or other screwage. */ printk("ymfpci%d: %d: lost: delta %d" " hwptr %d swptr %d distance %d count %d\n", codec->dev_audio, voice->number, delta, dmabuf->hwptr, swptr, distance, dmabuf->count); } else { /* * Normal end of DMA. */// printk("ymfpci%d: %d: done: delta %d"// " hwptr %d swptr %d distance %d count %d\n",// codec->dev_audio, voice->number, delta,// dmabuf->hwptr, swptr, distance, dmabuf->count); } played = dmabuf->count; if (ypcm->running) { ymf_playback_trigger(codec, ypcm, 0); } } else { /* * hwptr is chipping away towards a remote swptr. * Calculate other distance and apply it to count. */ if (swptr >= pos) { distance = swptr - pos; } else { distance = dmabuf->dmasize - (pos - swptr); } if (distance < dmabuf->count) { played = dmabuf->count - distance; } else { played = 0; } } dmabuf->total_bytes += played; dmabuf->count -= played; if (dmabuf->count < dmabuf->dmasize / 2) { wake_up(&dmabuf->wait); } } spin_unlock(&codec->reg_lock);}#if HAVE_RECORDstatic void ymfpci_pcm_capture_interrupt(snd_pcm_subchn_t *substream){ snd_pcm_runtime_t *runtime = substream->runtime; ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, runtime->private_data, ); ymfpci_t *codec = ypcm->codec; u32 pos, delta; spin_lock(&codec->reg_lock); if (ypcm->running) { pos = codec->bank_capture[ypcm->capture_bank_number][codec->active_bank]->start << ypcm->shift_offset; if (pos < ypcm->last_pos) // <-- dmabuf->hwptr delta = pos + (ypcm->buffer_size - ypcm->last_pos); else delta = pos - ypcm->last_pos; ypcm->frag_pos += delta; ypcm->last_pos = pos; while (ypcm->frag_pos >= ypcm->frag_size) { ypcm->frag_pos -= ypcm->frag_size; // printk("done - active_bank = 0x%x, start = 0x%x\n", codec->active_bank, voice->bank[codec->active_bank].start); spin_unlock(&codec->reg_lock); snd_pcm_transfer_done(substream); spin_lock(&codec->reg_lock); } } spin_unlock(&codec->reg_lock);}#endifstatic int ymf_playback_trigger(ymfpci_t *codec, ymfpci_pcm_t *ypcm, int cmd){ if (ypcm->voices[0] == NULL) { return -EINVAL; } if (cmd != 0) { codec->ctrl_playback[ypcm->voices[0]->number + 1] = virt_to_bus(ypcm->voices[0]->bank); if (ypcm->voices[1] != NULL) codec->ctrl_playback[ypcm->voices[1]->number + 1] = virt_to_bus(ypcm->voices[1]->bank); ypcm->running = 1; } else { codec->ctrl_playback[ypcm->voices[0]->number + 1] = 0; if (ypcm->voices[1] != NULL) codec->ctrl_playback[ypcm->voices[1]->number + 1] = 0; ypcm->running = 0; } return 0;}#if HAVE_RECORDstatic int ymfpci_capture_trigger(void *private_data, snd_pcm_subchn_t * substream, int cmd){ unsigned long flags; ymfpci_t *codec = snd_magic_cast(ymfpci_t, private_data, -ENXIO); ymfpci_pcm_t *ypcm = snd_magic_cast(ymfpci_pcm_t, substream->runtime->private_data, -ENXIO); int result = 0; u32 tmp; spin_lock_irqsave(&codec->reg_lock, flags); if (cmd == SND_PCM_TRIGGER_GO) { tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number); ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp); ypcm->running = 1; } else if (cmd == SND_PCM_TRIGGER_STOP) { tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number); ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp); ypcm->running = 0; } else { result = -EINVAL; } spin_unlock_irqrestore(&codec->reg_lock, flags); return result;}#endifstatic int ymfpci_pcm_voice_alloc(ymfpci_pcm_t *ypcm, int voices){ int err; if (ypcm->voices[1] != NULL && voices < 2) { ymfpci_voice_free(ypcm->codec, ypcm->voices[1]); ypcm->voices[1] = NULL; } if (voices == 1 && ypcm->voices[0] != NULL) return 0; /* already allocated */ if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL) return 0; /* already allocated */ if (voices > 1) { if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) { ymfpci_voice_free(ypcm->codec, ypcm->voices[0]); ypcm->voices[0] = NULL; } } err = ymfpci_voice_alloc(ypcm->codec, YMFPCI_PCM, voices > 1, &ypcm->voices[0]); if (err < 0) return err; ypcm->voices[0]->ypcm = ypcm; ypcm->voices[0]->interrupt = ymf_pcm_interrupt; if (voices > 1) { ypcm->voices[1] = &ypcm->codec->voices[ypcm->voices[0]->number + 1]; ypcm->voices[1]->ypcm = ypcm; } return 0;}static void ymf_pcm_init_voice(ymfpci_voice_t *voice, int stereo, int rate, int w_16, unsigned long addr, unsigned int end, int spdif){ u32 format; u32 delta = ymfpci_calc_delta(rate); u32 lpfQ = ymfpci_calc_lpfQ(rate); u32 lpfK = ymfpci_calc_lpfK(rate); ymfpci_playback_bank_t *bank; int nbank; format = (stereo ? 0x00010000 : 0) | (w_16 ? 0 : 0x80000000); if (stereo) end >>= 1; if (w_16) end >>= 1;/* P3 */ // printk("ymf_pcm_init_voice: %d: Rate %d Format 0x%08x Delta 0x%x End 0x%x\n",// voice->number, rate, format, delta, end); for (nbank = 0; nbank < 2; nbank++) { bank = &voice->bank[nbank]; bank->format = format; bank->loop_default = 0; /* 0-loops forever, otherwise count */ bank->base = addr; bank->loop_start = 0; bank->loop_end = end; bank->loop_frac = 0; bank->eg_gain_end = 0x40000000; bank->lpfQ = lpfQ; bank->status = 0; bank->num_of_frames = 0; bank->loop_count = 0; bank->start = 0; bank->start_frac = 0; bank->delta = bank->delta_end = delta; bank->lpfK = bank->lpfK_end = lpfK; bank->eg_gain = 0x40000000; bank->lpfD1 = bank->lpfD2 = 0; bank->left_gain = bank->right_gain = bank->left_gain_end = bank->right_gain_end = bank->eff1_gain = bank->eff2_gain = bank->eff3_gain = bank->eff1_gain_end = bank->eff2_gain_end = bank->eff3_gain_end = 0; if (!stereo) { if (!spdif) { bank->left_gain = bank->right_gain = bank->left_gain_end = bank->right_gain_end = 0x40000000; } else { bank->eff2_gain = bank->eff2_gain_end = bank->eff3_gain = bank->eff3_gain_end = 0x40000000; } } else { if (!spdif) { if ((voice->number & 1) == 0) { bank->format |= 1; bank->left_gain = bank->left_gain_end = 0x40000000; } else { bank->right_gain = bank->right_gain_end = 0x40000000; } } else { if ((voice->number & 1) == 0) { bank->format |= 1; bank->eff2_gain = bank->eff2_gain_end = 0x40000000; } else { bank->eff3_gain = bank->eff3_gain_end = 0x40000000; } } } }}/* * XXX Use new cache coherent PCI DMA routines instead of virt_to_bus. */static int ymf_playback_prepare(ymfpci_t *codec, struct ymf_state *state){ ymfpci_pcm_t *ypcm = &state->ypcm; int err, nvoice; if ((err = ymfpci_pcm_voice_alloc(ypcm, state->format.voices)) < 0) { /* Cannot be unless we leak voices in ymf_release! */ printk(KERN_ERR "ymfpci%d: cannot allocate voice!\n", codec->dev_audio); return err; } for (nvoice = 0; nvoice < state->format.voices; nvoice++) { ymf_pcm_init_voice(ypcm->voices[nvoice], state->format.voices == 2, state->format.rate, ymf_pcm_format_width(state->format.format) == 16, virt_to_bus(state->dmabuf.rawbuf), state->dmabuf.dmasize,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -