📄 ymfpci.c
字号:
} spin_unlock_irqrestore(&unit->reg_lock, flags); set_current_state(TASK_RUNNING); remove_wait_queue(&ypcm->dmabuf.wait, &waita); /* * This function may take up to 4 seconds to reach this point * (32K circular buffer, 8000 Hz). User notices. */}/* Can just stop, without wait. Or can we? */static void ymf_stop_adc(struct ymf_state *state){ struct ymf_unit *unit = state->unit; unsigned long flags; spin_lock_irqsave(&unit->reg_lock, flags); ymf_capture_trigger(unit, &state->rpcm, 0); spin_unlock_irqrestore(&unit->reg_lock, flags);}/* * Hardware start management */static void ymfpci_hw_start(ymfpci_t *unit){ unsigned long flags; spin_lock_irqsave(&unit->reg_lock, flags); if (unit->start_count++ == 0) { ymfpci_writel(unit, YDSXGR_MODE, ymfpci_readl(unit, YDSXGR_MODE) | 3); unit->active_bank = ymfpci_readl(unit, YDSXGR_CTRLSELECT) & 1; } spin_unlock_irqrestore(&unit->reg_lock, flags);}static void ymfpci_hw_stop(ymfpci_t *unit){ unsigned long flags; long timeout = 1000; spin_lock_irqsave(&unit->reg_lock, flags); if (--unit->start_count == 0) { ymfpci_writel(unit, YDSXGR_MODE, ymfpci_readl(unit, YDSXGR_MODE) & ~3); while (timeout-- > 0) { if ((ymfpci_readl(unit, YDSXGR_STATUS) & 2) == 0) break; } } spin_unlock_irqrestore(&unit->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; for (idx = 0; idx < YDSXG_PLAYBACK_VOICES; 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); rvoice[0] = voice; if (voice2) { ymfpci_hw_start(codec); rvoice[1] = voice2; } return 0; } return -EBUSY; /* Your audio channel is open by someone else. */}static void ymfpci_voice_free(ymfpci_t *unit, ymfpci_voice_t *pvoice){ ymfpci_hw_stop(unit); pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0; pvoice->ypcm = NULL;}/* */static void ymf_pcm_interrupt(ymfpci_t *codec, ymfpci_voice_t *voice){ struct ymf_pcm *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 = &ypcm->dmabuf; spin_lock(&codec->reg_lock); if (ypcm->running) { YMFDBGI("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: runaway voice %d: hwptr %d=>%d dmasize %d\n", codec->dev_audio, voice->number, dmabuf->hwptr, pos, 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(KERN_ERR "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(KERN_ERR "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. */ YMFDBGI("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);}static void ymf_cap_interrupt(ymfpci_t *unit, struct ymf_capture *cap){ struct ymf_pcm *ypcm; int redzone; struct ymf_state *state; struct ymf_dmabuf *dmabuf; int pos, delta; int cnt; if ((ypcm = cap->ypcm) == NULL) { return; } if ((state = ypcm->state) == NULL) { ypcm->running = 0; // lock it return; } dmabuf = &ypcm->dmabuf; spin_lock(&unit->reg_lock); if (ypcm->running) { redzone = ymf_calc_lend(state->format.rate); redzone <<= (state->format.shift + 1); pos = cap->bank[unit->active_bank].start; // pos <<= state->format.shift; if (pos < 0 || pos >= dmabuf->dmasize) { /* ucode bug */ printk(KERN_ERR "ymfpci%d: runaway capture %d: hwptr %d=>%d dmasize %d\n", unit->dev_audio, ypcm->capture_bank_number, dmabuf->hwptr, pos, dmabuf->dmasize); pos = 0; } if (pos < dmabuf->hwptr) { delta = dmabuf->dmasize - dmabuf->hwptr; delta += pos; } else { delta = pos - dmabuf->hwptr; } dmabuf->hwptr = pos; cnt = dmabuf->count; cnt += delta; if (cnt + redzone > dmabuf->dmasize) { /* Overflow - bump swptr */ dmabuf->count = dmabuf->dmasize - redzone; dmabuf->swptr = dmabuf->hwptr + redzone; if (dmabuf->swptr >= dmabuf->dmasize) { dmabuf->swptr -= dmabuf->dmasize; } } else { dmabuf->count = cnt; } dmabuf->total_bytes += delta; if (dmabuf->count) { /* && is_sleeping XXX */ wake_up(&dmabuf->wait); } } spin_unlock(&unit->reg_lock);}static int ymf_playback_trigger(ymfpci_t *codec, struct ymf_pcm *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;}static void ymf_capture_trigger(ymfpci_t *codec, struct ymf_pcm *ypcm, int cmd){ u32 tmp; if (cmd != 0) { tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number); ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp); ypcm->running = 1; } else { tmp = ymfpci_readl(codec, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number); ymfpci_writel(codec, YDSXGR_MAPOFREC, tmp); ypcm->running = 0; }}static int ymfpci_pcm_voice_alloc(struct ymf_pcm *ypcm, int voices){ struct ymf_unit *unit; int err; unit = ypcm->state->unit; if (ypcm->voices[1] != NULL && voices < 2) { ymfpci_voice_free(unit, 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(unit, ypcm->voices[0]); ypcm->voices[0] = NULL; } if ((err = voice_alloc(unit, YMFPCI_PCM, 1, ypcm->voices)) < 0) return err; ypcm->voices[0]->ypcm = ypcm; ypcm->voices[1]->ypcm = ypcm; } else { if ((err = voice_alloc(unit, YMFPCI_PCM, 0, ypcm->voices)) < 0) return err; ypcm->voices[0]->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; 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->left_gain = bank->left_gain_end = 0x40000000; } else { bank->format |= 1; bank->right_gain = bank->right_gain_end = 0x40000000; } } else { if ((voice->number & 1) == 0) { bank->eff2_gain = bank->eff2_gain_end = 0x40000000; } else { bank->format |= 1; bank->eff3_gain = bank->eff3_gain_end = 0x40000000; } } } }}/* * XXX Capture channel allocation is entirely fake at the moment. * We use only one channel and mark it busy as required. */static int ymf_capture_alloc(struct ymf_unit *unit, int *pbank){ struct ymf_capture *cap; int cbank; cbank = 1; /* Only ADC slot is used for now. */ cap = &unit->capture[cbank]; if (cap->use) return -EBUSY; cap->use = 1; *pbank = cbank; return 0;}static int ymf_playback_prepare(struct ymf_state *state){ struct ymf_pcm *ypcm = &state->wpcm; int err, nvoice; if ((err = ymfpci_pcm_voice_alloc(ypcm, state->format.voices)) < 0) { /* Somebody started 32 mpg123's in parallel? */ printk(KERN_INFO "ymfpci%d: cannot allocate voice\n", state->unit->dev_audio); return err; } for (nvoice = 0; nvoice < state->format.voices; nvoice++) { ymf_pcm_init_voice(ypcm->voices[nvoice],
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -