📄 emupcm.c
字号:
return 0; epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); if (epcm->extra) { snd_emu10k1_voice_free(epcm->emu, epcm->extra); epcm->extra = NULL; } 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_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 = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); unsigned int start_addr, end_addr; start_addr = epcm->start_addr; end_addr = snd_pcm_lib_period_bytes(substream); if (runtime->channels == 2) end_addr >>= 1; end_addr += start_addr; snd_emu10k1_pcm_init_voice(emu, 1, 1, epcm->extra, start_addr, end_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); if (epcm->voices[1]) snd_emu10k1_pcm_init_voice(emu, 0, 0, epcm->voices[1], start_addr, end_addr); return 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 = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); int idx; 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: 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, 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 = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); unsigned long flags; int result = 0; // printk("trigger - emu10k1 = 0x%x, cmd = %i, pointer = %i\n", (int)emu, cmd, substream->ops->pointer(substream)); spin_lock_irqsave(&emu->reg_lock, flags); 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_irqrestore(&emu->reg_lock, flags); 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 = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); unsigned long flags; int result = 0; // printk("trigger - emu10k1 = %p, cmd = %i, pointer = %i\n", emu, cmd, substream->ops->pointer(substream)); spin_lock_irqsave(&emu->reg_lock, flags); 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: 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; 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: snd_emu10k1_ptr_write(emu, FXWC, 0, 0); break; default: break; } break; default: result = -EINVAL; } spin_unlock_irqrestore(&emu->reg_lock, flags); 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 = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); 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;#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 = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return -ENXIO); unsigned int ptr; if (!epcm->running) return 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(snd_card_t *card, snd_kcontrol_t *kctl, int activate){ snd_runtime_check(kctl != NULL, return); if (activate) kctl->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; else kctl->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &kctl->id);}static void snd_emu10k1_pcm_mixer_notify(snd_card_t *card, emu10k1_pcm_mixer_t *mix, int activate){ snd_emu10k1_pcm_mixer_notify1(card, mix->ctl_send_routing, activate); snd_emu10k1_pcm_mixer_notify1(card, mix->ctl_send_volume, activate); snd_emu10k1_pcm_mixer_notify1(card, mix->ctl_attn, activate);}static void snd_emu10k1_pcm_free_substream(snd_pcm_runtime_t *runtime){ emu10k1_pcm_t *epcm = snd_magic_cast(emu10k1_pcm_t, runtime->private_data, return); if (epcm) snd_magic_kfree(epcm);}static int snd_emu10k1_playback_open(snd_pcm_substream_t * substream){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -