📄 emupcm.c
字号:
if (epcm->voices[1]) { snd_emu10k1_voice_free(epcm->emu, epcm->voices[1]); epcm->voices[1] = NULL; } if (epcm->voices[0]) { snd_emu10k1_voice_free(epcm->emu, epcm->voices[0]); epcm->voices[0] = NULL; } if (epcm->memblk) { snd_emu10k1_free_pages(emu, epcm->memblk); epcm->memblk = NULL; epcm->start_addr = 0; } snd_pcm_lib_free_pages(substream); return 0;}static int snd_emu10k1_efx_playback_hw_free(snd_pcm_substream_t * substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; emu10k1_pcm_t *epcm; int i; if (runtime->private_data == NULL) return 0; epcm = runtime->private_data; if (epcm->extra) { snd_emu10k1_voice_free(epcm->emu, epcm->extra); epcm->extra = NULL; } for (i=0; i < NUM_EFX_PLAYBACK; i++) { if (epcm->voices[i]) { snd_emu10k1_voice_free(epcm->emu, epcm->voices[i]); epcm->voices[i] = NULL; } } if (epcm->memblk) { snd_emu10k1_free_pages(emu, epcm->memblk); epcm->memblk = NULL; epcm->start_addr = 0; } snd_pcm_lib_free_pages(substream); return 0;}static int snd_emu10k1_playback_prepare(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 start_addr, end_addr; start_addr = epcm->start_addr; end_addr = snd_pcm_lib_period_bytes(substream); if (runtime->channels == 2) { start_addr >>= 1; end_addr >>= 1; } end_addr += start_addr; snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra, start_addr, end_addr, NULL); start_addr = epcm->start_addr; end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream); snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0], start_addr, end_addr, &emu->pcm_mixer[substream->number]); if (epcm->voices[1]) snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[1], start_addr, end_addr, &emu->pcm_mixer[substream->number]); return 0;}static int snd_emu10k1_efx_playback_prepare(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 start_addr, end_addr; unsigned int channel_size; int i; start_addr = epcm->start_addr; end_addr = epcm->start_addr + snd_pcm_lib_buffer_bytes(substream); /* * the kX driver leaves some space between voices */ channel_size = ( end_addr - start_addr ) / NUM_EFX_PLAYBACK; snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra, start_addr, start_addr + (channel_size / 2), NULL); /* only difference with the master voice is we use it for the pointer */ snd_emu10k1_pcm_init_voice(emu, 1, 0, epcm->voices[0], start_addr, start_addr + channel_size, &emu->efx_pcm_mixer[0]); start_addr += channel_size; for (i = 1; i < NUM_EFX_PLAYBACK; i++) { snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[i], start_addr, start_addr + channel_size, &emu->efx_pcm_mixer[i]); start_addr += channel_size; } return 0;}static snd_pcm_hardware_t snd_emu10k1_efx_playback ={ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_NONINTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_48000, .rate_min = 48000, .rate_max = 48000, .channels_min = NUM_EFX_PLAYBACK, .channels_max = NUM_EFX_PLAYBACK, .buffer_bytes_max = (64*1024), .period_bytes_min = 64, .period_bytes_max = (64*1024), .periods_min = 2, .periods_max = 2, .fifo_size = 0,};static int snd_emu10k1_capture_hw_params(snd_pcm_substream_t * substream, snd_pcm_hw_params_t * hw_params){ return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}static int snd_emu10k1_capture_hw_free(snd_pcm_substream_t * substream){ return snd_pcm_lib_free_pages(substream);}static int snd_emu10k1_capture_prepare(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; int idx; /* zeroing the buffer size will stop capture */ 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; } snd_emu10k1_ptr_write(emu, epcm->capture_ba_reg, 0, runtime->dma_addr); epcm->capture_bufsize = snd_pcm_lib_buffer_bytes(substream); epcm->capture_bs_val = 0; for (idx = 0; idx < 31; idx++) { if (capture_period_sizes[idx] == epcm->capture_bufsize) { epcm->capture_bs_val = idx + 1; break; } } if (epcm->capture_bs_val == 0) { snd_BUG(); 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, int extra, emu10k1_voice_t *evoice){ snd_pcm_runtime_t *runtime; unsigned int voice, stereo, i, ccis, cra = 64, cs, sample; if (evoice == NULL) return; runtime = evoice->epcm->substream->runtime; voice = evoice->number; stereo = (!extra && runtime->channels == 2); sample = snd_pcm_format_width(runtime->format) == 16 ? 0 : 0x80808080; ccis = emu10k1_ccis(stereo, sample == 0); // set cs to 2 * number of cache registers beside the invalidated cs = (sample == 0) ? (32-ccis) : (64-ccis+1) >> 1; if (cs > 16) cs = 16; for (i = 0; i < cs; i++) { snd_emu10k1_ptr_write(emu, CD0 + i, voice, sample); if (stereo) { snd_emu10k1_ptr_write(emu, CD0 + i, voice + 1, sample); } } // reset cache snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice, 0); snd_emu10k1_ptr_write(emu, CCR_READADDRESS, voice, cra); if (stereo) { 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); if (stereo) { snd_emu10k1_ptr_write(emu, CCR_CACHEINVALIDSIZE, voice+1, ccis); }}static void snd_emu10k1_playback_prepare_voice(emu10k1_t *emu, emu10k1_voice_t *evoice, int master, int extra, emu10k1_pcm_mixer_t *mix){ snd_pcm_substream_t *substream; snd_pcm_runtime_t *runtime; unsigned int attn, vattn; unsigned int voice, tmp; if (evoice == NULL) /* skip second voice for mono */ return; substream = evoice->epcm->substream; runtime = substream->runtime; voice = evoice->number; attn = extra ? 0 : 0x00ff; tmp = runtime->channels == 2 ? (master ? 1 : 2) : 0; vattn = mix != NULL ? (mix->attn[tmp] << 16) : 0; snd_emu10k1_ptr_write(emu, IFATN, voice, attn); snd_emu10k1_ptr_write(emu, VTFT, voice, vattn | 0xffff); snd_emu10k1_ptr_write(emu, CVCF, voice, vattn | 0xffff); snd_emu10k1_ptr_write(emu, DCYSUSV, voice, 0x7f7f); snd_emu10k1_voice_clear_loop_stop(emu, voice);} 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; unsigned int voice, pitch, pitch_target; if (evoice == NULL) /* skip second voice for mono */ return; substream = evoice->epcm->substream; runtime = substream->runtime; voice = evoice->number; pitch = snd_emu10k1_rate_to_pitch(runtime->rate) >> 8; pitch_target = emu10k1_calc_pitch_target(runtime->rate); snd_emu10k1_ptr_write(emu, PTRX_PITCHTARGET, voice, pitch_target); if (master || evoice->epcm->type == PLAYBACK_EFX) snd_emu10k1_ptr_write(emu, CPF_CURRENTPITCH, voice, pitch_target); snd_emu10k1_ptr_write(emu, IP, voice, pitch); if (extra) snd_emu10k1_voice_intr_enable(emu, voice);}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; emu10k1_pcm_mixer_t *mix; 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, 1, epcm->extra); /* do we need this? */ snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[0]); /* follow thru */ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: mix = &emu->pcm_mixer[substream->number]; snd_emu10k1_playback_prepare_voice(emu, epcm->voices[0], 1, 0, mix); snd_emu10k1_playback_prepare_voice(emu, epcm->voices[1], 0, 0, mix); snd_emu10k1_playback_prepare_voice(emu, epcm->extra, 1, 1, NULL); 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; spin_lock(&emu->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: // hmm this should cause full and half full interrupt to be raised? 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 int snd_emu10k1_efx_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 i; int result = 0; spin_lock(&emu->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: // prepare voices for (i = 0; i < NUM_EFX_PLAYBACK; i++) { snd_emu10k1_playback_invalidate_cache(emu, 0, epcm->voices[i]); } snd_emu10k1_playback_invalidate_cache(emu, 1, epcm->extra); /* follow thru */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -