📄 emupcm.c
字号:
epcm->capture_bs_val++; } if (epcm->type == CAPTURE_AC97ADC) { epcm->capture_cr_val = emu->audigy ? A_ADCCR_LCHANENABLE : ADCCR_LCHANENABLE; if (runtime->channels > 1) epcm->capture_cr_val |= emu->audigy ? A_ADCCR_RCHANENABLE : ADCCR_RCHANENABLE; epcm->capture_cr_val |= emu->audigy ? snd_emu10k1_audigy_capture_rate_reg(runtime->rate) : snd_emu10k1_capture_rate_reg(runtime->rate); } return 0;}static void snd_emu10k1_playback_invalidate_cache(emu10k1_t *emu, emu10k1_voice_t *evoice){ snd_pcm_runtime_t *runtime; unsigned int voice, i, ccis, cra = 64, cs, sample; if (evoice == NULL) return; runtime = evoice->epcm->substream->runtime; voice = evoice->number; sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080; if (runtime->channels > 1) { ccis = 28; cs = 4; } else { ccis = 30; cs = 2; } if (sample == 0) /* 16-bit */ ccis *= 2; for (i = 0; i < cs; i++) snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample); // reset cache snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0); snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra); if (runtime->channels > 1) { snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice + 1, 0); snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice + 1, cra); } // fill cache snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, ccis);}static void snd_emu10k1_playback_trigger_voice(emu10k1_t *emu, emu10k1_voice_t *evoice, int master, int extra){ snd_pcm_substream_t *substream; snd_pcm_runtime_t *runtime; emu10k1_pcm_mixer_t *mix; unsigned int voice, pitch, pitch_target, tmp; unsigned int attn; if (evoice == NULL) /* skip second voice for mono */ return; substream = evoice->epcm->substream; runtime = substream->runtime; mix = &emu->pcm_mixer[substream->number]; voice = evoice->number; pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8; pitch_target = emu10k1_calc_pitch_target(runtime->rate); attn = extra ? 0 : 0x00ff; tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0; snd_emu10k1_ptr_write(emu, IFATN, voice, attn); snd_emu10k1_ptr_write(emu, VTFT, voice, (mix->attn[tmp] << 16) | 0xffff); snd_emu10k1_ptr_write(emu, CVCF, voice, (mix->attn[tmp] << 16) | 0xffff); snd_emu10k1_voice_clear_loop_stop(emu, voice); if (extra) snd_emu10k1_voice_intr_enable(emu, voice); snd_emu10k1_ptr_write(emu, DCYSUSV, voice, 0x7f7f); snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target); if (master) snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, pitch_target); snd_emu10k1_ptr_write(emu, IP, voice, pitch);}static void snd_emu10k1_playback_stop_voice(emu10k1_t *emu, emu10k1_voice_t *evoice){ unsigned int voice; if (evoice == NULL) return; voice = evoice->number; snd_emu10k1_voice_intr_disable(emu, voice); snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, 0); snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, 0); snd_emu10k1_ptr_write(emu, IFATN, voice, 0xffff); snd_emu10k1_ptr_write(emu, VTFT, voice, 0xffff); snd_emu10k1_ptr_write(emu, CVCF, voice, 0xffff); snd_emu10k1_ptr_write(emu, IP, voice, 0);}static int snd_emu10k1_playback_trigger(snd_pcm_substream_t * substream, int cmd){ emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; emu10k1_pcm_t *epcm = runtime->private_data; int result = 0; // printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream)); spin_lock(&emu->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: snd_emu10k1_playback_invalidate_cache(emu, epcm->extra); /* do we need this? */ snd_emu10k1_playback_invalidate_cache(emu, epcm->voices[0]); /* follow thru */ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: snd_emu10k1_playback_trigger_voice(emu, epcm->voices[0], 1, 0); snd_emu10k1_playback_trigger_voice(emu, epcm->voices[1], 0, 0); snd_emu10k1_playback_trigger_voice(emu, epcm->extra, 1, 1); epcm->running = 1; break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: epcm->running = 0; snd_emu10k1_playback_stop_voice(emu, epcm->voices[0]); snd_emu10k1_playback_stop_voice(emu, epcm->voices[1]); snd_emu10k1_playback_stop_voice(emu, epcm->extra); break; default: result = -EINVAL; break; } spin_unlock(&emu->reg_lock); return result;}static int snd_emu10k1_capture_trigger(snd_pcm_substream_t * substream, int cmd){ emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; emu10k1_pcm_t *epcm = runtime->private_data; int result = 0; // printk("trigger - emu10k1 = %p, cmd = %i, pointer = %i\n", emu, cmd, substream->ops->pointer(substream)); spin_lock(&emu->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: outl(epcm->capture_ipr, emu->port + IPR); snd_emu10k1_intr_enable(emu, epcm->capture_inte); // printk("adccr = 0x%x, adcbs = 0x%x\n", epcm->adccr, epcm->adcbs); switch (epcm->type) { case CAPTURE_AC97ADC: snd_emu10k1_ptr_write(emu, ADCCR, 0, epcm->capture_cr_val); break; case CAPTURE_EFX: if (emu->audigy) { snd_emu10k1_ptr_write(emu, A_FXWC1, 0, epcm->capture_cr_val); snd_emu10k1_ptr_write(emu, A_FXWC2, 0, epcm->capture_cr_val2); } else snd_emu10k1_ptr_write(emu, FXWC, 0, epcm->capture_cr_val); break; default: break; } snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, epcm->capture_bs_val); epcm->running = 1; epcm->first_ptr = 1; break; case SNDRV_PCM_TRIGGER_STOP: epcm->running = 0; snd_emu10k1_intr_disable(emu, epcm->capture_inte); outl(epcm->capture_ipr, emu->port + IPR); snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, 0); switch (epcm->type) { case CAPTURE_AC97ADC: snd_emu10k1_ptr_write(emu, ADCCR, 0, 0); break; case CAPTURE_EFX: if (emu->audigy) { snd_emu10k1_ptr_write(emu, A_FXWC1, 0, 0); snd_emu10k1_ptr_write(emu, A_FXWC2, 0, 0); } else snd_emu10k1_ptr_write(emu, FXWC, 0, 0); break; default: break; } break; default: result = -EINVAL; } spin_unlock(&emu->reg_lock); return result;}static snd_pcm_uframes_t snd_emu10k1_playback_pointer(snd_pcm_substream_t * substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; emu10k1_pcm_t *epcm = runtime->private_data; unsigned int ptr; if (!epcm->running) return 0; ptr = snd_emu10k1_ptr_read(emu, CCCA, epcm->voices[0]->number) & 0x00ffffff;#if 0 /* Perex's code */ ptr += runtime->buffer_size; ptr -= epcm->ccca_start_addr; ptr %= runtime->buffer_size;#else /* EMU10K1 Open Source code from Creative */ if (ptr < epcm->ccca_start_addr) ptr += runtime->buffer_size - epcm->ccca_start_addr; else { ptr -= epcm->ccca_start_addr; if (ptr >= runtime->buffer_size) ptr -= runtime->buffer_size; }#endif // printk("ptr = 0x%x, buffer_size = 0x%x, period_size = 0x%x\n", ptr, runtime->buffer_size, runtime->period_size); return ptr;}static snd_pcm_uframes_t snd_emu10k1_capture_pointer(snd_pcm_substream_t * substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; emu10k1_pcm_t *epcm = runtime->private_data; unsigned int ptr; if (!epcm->running) return 0; if (epcm->first_ptr) { udelay(50); // hack, it takes awhile until capture is started epcm->first_ptr = 0; } ptr = snd_emu10k1_ptr_read(emu, epcm->capture_idx_reg, 0) & 0x0000ffff; return bytes_to_frames(runtime, ptr);}/* * Playback support device description */static snd_pcm_hardware_t snd_emu10k1_playback ={ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, .rate_min = 4000, .rate_max = 48000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (128*1024), .period_bytes_min = 64, .period_bytes_max = (128*1024), .periods_min = 1, .periods_max = 1024, .fifo_size = 0,};/* * Capture support device description */static snd_pcm_hardware_t snd_emu10k1_capture ={ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_8000_48000, .rate_min = 8000, .rate_max = 48000, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = (64*1024), .period_bytes_min = 384, .period_bytes_max = (64*1024), .periods_min = 2, .periods_max = 2, .fifo_size = 0,};/* * */static void snd_emu10k1_pcm_mixer_notify1(emu10k1_t *emu, snd_kcontrol_t *kctl, int idx, int activate){ snd_ctl_elem_id_t id; snd_runtime_check(kctl != NULL, return); if (activate) kctl->vd[idx].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; else kctl->vd[idx].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_ctl_notify(emu->card, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, snd_ctl_build_ioff(&id, kctl, idx));}static void snd_emu10k1_pcm_mixer_notify(emu10k1_t *emu, int idx, int activate){ snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_send_routing, idx, activate); snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_send_volume, idx, activate); snd_emu10k1_pcm_mixer_notify1(emu, emu->ctl_attn, idx, activate);}static void snd_emu10k1_pcm_free_substream(snd_pcm_runtime_t *runtime){ emu10k1_pcm_t *epcm = runtime->private_data; kfree(epcm);}static int snd_emu10k1_playback_open(snd_pcm_substream_t * substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); emu10k1_pcm_t *epcm; emu10k1_pcm_mixer_t *mix; snd_pcm_runtime_t *runtime = substream->runtime; int i, err; epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL); if (epcm == NULL) return -ENOMEM; epcm->emu = emu; epcm->type = PLAYBACK_EMUVOICE; epcm->substream = substream; runtime->private_data = epcm; runtime->private_free = snd_emu10k1_pcm_free_substream; runtime->hw = snd_emu10k1_playback; if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) { kfree(epcm); return err; } if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0) { kfree(epcm); return err; } mix = &emu->pcm_mixer[substream->number]; for (i = 0; i < 4; i++) mix->send_routing[0][i] = mix->send_routing[1][i] = mix->send_routing[2][i] = i; memset(&mix->send_volume, 0, sizeof(mix->send_volume)); mix->send_volume[0][0] = mix->send_volume[0][1] = mix->send_volume[1][0] = mix->send_volume[2][1] = 255; mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff; mix->epcm = epcm; snd_emu10k1_pcm_mixer_notify(emu, substream->number, 1); return 0;}static int snd_emu10k1_playback_close(snd_pcm_substream_t * substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); emu10k1_pcm_mixer_t *mix = &emu->pcm_mixer[substream->number]; mix->epcm = NULL; snd_emu10k1_pcm_mixer_notify(emu, substream->number, 0); return 0;}static int snd_emu10k1_capture_open(snd_pcm_substream_t * substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; emu10k1_pcm_t *epcm; epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL); if (epcm == NULL) return -ENOMEM; epcm->emu = emu; epcm->type = CAPTURE_AC97ADC; epcm->substream = substream; epcm->capture_ipr = IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL; epcm->capture_inte = INTE_ADCBUFENABLE; epcm->capture_ba_reg = ADCBA; epcm->capture_bs_reg = ADCBS; epcm->capture_idx_reg = emu->audigy ? A_ADCIDX : ADCIDX; runtime->private_data = epcm; runtime->private_free = snd_emu10k1_pcm_free_substream; runtime->hw = snd_emu10k1_capture; emu->capture_interrupt = snd_emu10k1_pcm_ac97adc_interrupt; emu->pcm_capture_substream = substream; snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes); snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_capture_rates); return 0;}static int snd_emu10k1_capture_close(snd_pcm_substream_t * substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); emu->capture_interrupt = NULL; emu->pcm_capture_substream = NULL; return 0;}static int snd_emu10k1_capture_mic_open(snd_pcm_substream_t * substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); emu10k1_pcm_t *epcm; snd_pcm_runtime_t *runtime = substream->runtime; epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL); if (epcm == NULL) return -ENOMEM; epcm->emu = emu; epcm->type = CAPTURE_AC97MIC; epcm->substream = substream; epcm->capture_ipr = IPR_MICBUFFULL|IPR_MICBUFHALFFULL; epcm->capture_inte = INTE_MICBUFENABLE; epcm->capture_ba_reg = MICBA; epcm->capture_bs_reg = MICBS; epcm->capture_idx_reg = emu->audigy ? A_MICIDX : MICIDX; substream->runtime->private_data = epcm; substream->runtime->private_free = snd_emu10k1_pcm_free_substream; runtime->hw = snd_emu10k1_capture; runtime->hw.rates = SNDRV_PCM_RATE_8000; runtime->hw.rate_min = runtime->hw.rate_max = 8000; runtime->hw.channels_min = 1; emu->capture_mic_interrupt = snd_emu10k1_pcm_ac97mic_interrupt; emu->pcm_capture_mic_substream = substream; snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, &hw_constraints_capture_period_sizes); return 0;}static int snd_emu10k1_capture_mic_close(snd_pcm_substream_t * substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); emu->capture_interrupt = NULL; emu->pcm_capture_mic_substream = NULL; return 0;}static int snd_emu10k1_capture_efx_open(snd_pcm_substream_t * substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); emu10k1_pcm_t *epcm; snd_pcm_runtime_t *runtime = substream->runtime; int nefx = emu->audigy ? 64 : 32; int idx; epcm = kcalloc(1, sizeof(*epcm), GFP_KERNEL); if (epcm == NULL) return -ENOMEM; epcm->emu = emu; epcm->type = CAPTURE_EFX; epcm->substream = substream; epcm->capture_ipr = IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL; epcm->capture_inte = INTE_EFXBUFENABLE; epcm->capture_ba_reg = FXBA; epcm->capture_bs_reg = FXBS; epcm->capture_idx_reg = FXIDX; substream->runtime->private_data = epcm; substream->runtime->private_free = snd_emu10k1_pcm_free_substream; runtime->hw = snd_emu10k1_capture; runtime->hw.rates = SNDRV_PCM_RATE_48000; runtime->hw.rate_min = runtime->hw.rate_max = 48000; spin_lock_irq(&emu->reg_lock); runtime->hw.channels_min = runtime->hw.channels_max = 0; for (idx = 0; idx < nefx; idx++) { if (emu->efx_voices_mask[idx/32] & (1 << (idx%32))) { runtime->hw.channels_min++; runtime->hw.channels_max++; } } epcm->capture_cr_val = emu->efx_voices_mask[0]; epcm->capture_cr_val2 = emu->efx_voices_mask[1]; spin_unlock_irq(&emu->reg_lock); emu->capture_efx_interrupt = snd_emu10k1_pcm_efx_interrupt;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -