📄 emupcm.c
字号:
if (rpcm) *rpcm = NULL; if ((err = snd_pcm_new(emu->card, "emu10k1", device, 1, 0, &pcm)) < 0) return err; pcm->private_data = emu; pcm->private_free = snd_emu10k1_pcm_free; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_efx_playback_ops); pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; strcpy(pcm->name, "Multichannel Playback"); emu->pcm = pcm; for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0) return err; if (rpcm) *rpcm = pcm; return 0;}static snd_pcm_ops_t snd_emu10k1_capture_mic_ops = { .open = snd_emu10k1_capture_mic_open, .close = snd_emu10k1_capture_mic_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_emu10k1_capture_hw_params, .hw_free = snd_emu10k1_capture_hw_free, .prepare = snd_emu10k1_capture_prepare, .trigger = snd_emu10k1_capture_trigger, .pointer = snd_emu10k1_capture_pointer,};static void snd_emu10k1_pcm_mic_free(snd_pcm_t *pcm){ emu10k1_t *emu = pcm->private_data; emu->pcm_mic = NULL; snd_pcm_lib_preallocate_free_for_all(pcm);}int __devinit snd_emu10k1_pcm_mic(emu10k1_t * emu, int device, snd_pcm_t ** rpcm){ snd_pcm_t *pcm; int err; if (rpcm) *rpcm = NULL; if ((err = snd_pcm_new(emu->card, "emu10k1 mic", device, 0, 1, &pcm)) < 0) return err; pcm->private_data = emu; pcm->private_free = snd_emu10k1_pcm_mic_free; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_mic_ops); pcm->info_flags = 0; strcpy(pcm->name, "Mic Capture"); emu->pcm_mic = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024); if (rpcm) *rpcm = pcm; return 0;}static int snd_emu10k1_pcm_efx_voices_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo){ emu10k1_t *emu = snd_kcontrol_chip(kcontrol); int nefx = emu->audigy ? 64 : 32; uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; uinfo->count = nefx; uinfo->value.integer.min = 0; uinfo->value.integer.max = 1; return 0;}static int snd_emu10k1_pcm_efx_voices_mask_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ emu10k1_t *emu = snd_kcontrol_chip(kcontrol); int nefx = emu->audigy ? 64 : 32; int idx; spin_lock_irq(&emu->reg_lock); for (idx = 0; idx < nefx; idx++) ucontrol->value.integer.value[idx] = (emu->efx_voices_mask[idx / 32] & (1 << (idx % 32))) ? 1 : 0; spin_unlock_irq(&emu->reg_lock); return 0;}static int snd_emu10k1_pcm_efx_voices_mask_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ emu10k1_t *emu = snd_kcontrol_chip(kcontrol); unsigned int nval[2], bits; int nefx = emu->audigy ? 64 : 32; int nefxb = emu->audigy ? 7 : 6; int change, idx; nval[0] = nval[1] = 0; for (idx = 0, bits = 0; idx < nefx; idx++) if (ucontrol->value.integer.value[idx]) { nval[idx / 32] |= 1 << (idx % 32); bits++; } for (idx = 0; idx < nefxb; idx++) if (1 << idx == bits) break; if (idx >= nefxb) return -EINVAL; spin_lock_irq(&emu->reg_lock); change = (nval[0] != emu->efx_voices_mask[0]) || (nval[1] != emu->efx_voices_mask[1]); emu->efx_voices_mask[0] = nval[0]; emu->efx_voices_mask[1] = nval[1]; spin_unlock_irq(&emu->reg_lock); return change;}static snd_kcontrol_new_t snd_emu10k1_pcm_efx_voices_mask = { .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = "Captured FX8010 Outputs", .info = snd_emu10k1_pcm_efx_voices_mask_info, .get = snd_emu10k1_pcm_efx_voices_mask_get, .put = snd_emu10k1_pcm_efx_voices_mask_put};static snd_pcm_ops_t snd_emu10k1_capture_efx_ops = { .open = snd_emu10k1_capture_efx_open, .close = snd_emu10k1_capture_efx_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_emu10k1_capture_hw_params, .hw_free = snd_emu10k1_capture_hw_free, .prepare = snd_emu10k1_capture_prepare, .trigger = snd_emu10k1_capture_trigger, .pointer = snd_emu10k1_capture_pointer,};/* EFX playback */#define INITIAL_TRAM_SHIFT 14#define INITIAL_TRAM_POS(size) ((((size) / 2) - INITIAL_TRAM_SHIFT) - 1)static void snd_emu10k1_fx8010_playback_irq(emu10k1_t *emu, void *private_data){ snd_pcm_substream_t *substream = private_data; snd_pcm_period_elapsed(substream);}static void snd_emu10k1_fx8010_playback_tram_poke1(unsigned short *dst_left, unsigned short *dst_right, unsigned short *src, unsigned int count, unsigned int tram_shift){ // printk("tram_poke1: dst_left = 0x%p, dst_right = 0x%p, src = 0x%p, count = 0x%x\n", dst_left, dst_right, src, count); if ((tram_shift & 1) == 0) { while (count--) { *dst_left-- = *src++; *dst_right-- = *src++; } } else { while (count--) { *dst_right-- = *src++; *dst_left-- = *src++; } }}static void fx8010_pb_trans_copy(snd_pcm_substream_t *substream, snd_pcm_indirect_t *rec, size_t bytes){ emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; unsigned int tram_size = pcm->buffer_size; unsigned short *src = (unsigned short *)(substream->runtime->dma_area + rec->sw_data); unsigned int frames = bytes >> 2, count; unsigned int tram_pos = pcm->tram_pos; unsigned int tram_shift = pcm->tram_shift; while (frames > tram_pos) { count = tram_pos + 1; snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + tram_pos, (unsigned short *)emu->fx8010.etram_pages.area + tram_pos + tram_size / 2, src, count, tram_shift); src += count * 2; frames -= count; tram_pos = (tram_size / 2) - 1; tram_shift++; } snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages.area + tram_pos, (unsigned short *)emu->fx8010.etram_pages.area + tram_pos + tram_size / 2, src, frames, tram_shift); tram_pos -= frames; pcm->tram_pos = tram_pos; pcm->tram_shift = tram_shift;}static int snd_emu10k1_fx8010_playback_transfer(snd_pcm_substream_t *substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; snd_pcm_indirect_playback_transfer(substream, &pcm->pcm_rec, fx8010_pb_trans_copy); return 0;}static int snd_emu10k1_fx8010_playback_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_fx8010_playback_hw_free(snd_pcm_substream_t * substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; unsigned int i; for (i = 0; i < pcm->channels; i++) snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, 0); snd_pcm_lib_free_pages(substream); return 0;}static int snd_emu10k1_fx8010_playback_prepare(snd_pcm_substream_t * substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; unsigned int i; // printk("prepare: etram_pages = 0x%p, dma_area = 0x%x, buffer_size = 0x%x (0x%x)\n", emu->fx8010.etram_pages, runtime->dma_area, runtime->buffer_size, runtime->buffer_size << 2); memset(&pcm->pcm_rec, 0, sizeof(pcm->pcm_rec)); pcm->pcm_rec.hw_buffer_size = pcm->buffer_size * 2; /* byte size */ pcm->pcm_rec.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); pcm->tram_shift = 0; snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_running, 0, 0); /* reset */ snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); /* reset */ snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_size, 0, runtime->buffer_size); snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_ptr, 0, 0); /* reset ptr number */ snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_count, 0, runtime->period_size); snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_tmpcount, 0, runtime->period_size); for (i = 0; i < pcm->channels; i++) snd_emu10k1_ptr_write(emu, TANKMEMADDRREGBASE + 0x80 + pcm->etram[i], 0, (TANKMEMADDRREG_READ|TANKMEMADDRREG_ALIGN) + i * (runtime->buffer_size / pcm->channels)); return 0;}static int snd_emu10k1_fx8010_playback_trigger(snd_pcm_substream_t * substream, int cmd){ emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; int result = 0; spin_lock(&emu->reg_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: /* follow thru */ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:#ifdef EMU10K1_SET_AC3_IEC958 { int i; for (i = 0; i < 3; i++) { unsigned int bits; bits = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT | SPCS_NOTAUDIODATA; snd_emu10k1_ptr_write(emu, SPCS0 + i, 0, bits); } }#endif result = snd_emu10k1_fx8010_register_irq_handler(emu, snd_emu10k1_fx8010_playback_irq, pcm->gpr_running, substream, &pcm->irq); if (result < 0) goto __err; snd_emu10k1_fx8010_playback_transfer(substream); /* roll the ball */ snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 1); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: snd_emu10k1_fx8010_unregister_irq_handler(emu, pcm->irq); pcm->irq = NULL; snd_emu10k1_ptr_write(emu, emu->gpr_base + pcm->gpr_trigger, 0, 0); pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); pcm->tram_shift = 0; break; default: result = -EINVAL; break; } __err: spin_unlock(&emu->reg_lock); return result;}static snd_pcm_uframes_t snd_emu10k1_fx8010_playback_pointer(snd_pcm_substream_t * substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; size_t ptr; /* byte pointer */ if (!snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_trigger, 0)) return 0; ptr = snd_emu10k1_ptr_read(emu, emu->gpr_base + pcm->gpr_ptr, 0) << 2; return snd_pcm_indirect_playback_pointer(substream, &pcm->pcm_rec, ptr);}static snd_pcm_hardware_t snd_emu10k1_fx8010_playback ={ .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_48000, .rate_min = 48000, .rate_max = 48000, .channels_min = 1, .channels_max = 1, .buffer_bytes_max = (128*1024), .period_bytes_min = 1024, .period_bytes_max = (128*1024), .periods_min = 1, .periods_max = 1024, .fifo_size = 0,};static int snd_emu10k1_fx8010_playback_open(snd_pcm_substream_t * substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; runtime->hw = snd_emu10k1_fx8010_playback; runtime->hw.channels_min = runtime->hw.channels_max = pcm->channels; runtime->hw.period_bytes_max = (pcm->buffer_size * 2) / 2; spin_lock_irq(&emu->reg_lock); if (pcm->valid == 0) { spin_unlock_irq(&emu->reg_lock); return -ENODEV; } pcm->opened = 1; spin_unlock_irq(&emu->reg_lock); return 0;}static int snd_emu10k1_fx8010_playback_close(snd_pcm_substream_t * substream){ emu10k1_t *emu = snd_pcm_substream_chip(substream); snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; spin_lock_irq(&emu->reg_lock); pcm->opened = 0; spin_unlock_irq(&emu->reg_lock); return 0;}static snd_pcm_ops_t snd_emu10k1_fx8010_playback_ops = { .open = snd_emu10k1_fx8010_playback_open, .close = snd_emu10k1_fx8010_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_emu10k1_fx8010_playback_hw_params, .hw_free = snd_emu10k1_fx8010_playback_hw_free, .prepare = snd_emu10k1_fx8010_playback_prepare, .trigger = snd_emu10k1_fx8010_playback_trigger, .pointer = snd_emu10k1_fx8010_playback_pointer, .ack = snd_emu10k1_fx8010_playback_transfer,};static void snd_emu10k1_pcm_efx_free(snd_pcm_t *pcm){ emu10k1_t *emu = pcm->private_data; emu->pcm_efx = NULL; snd_pcm_lib_preallocate_free_for_all(pcm);}int __devinit snd_emu10k1_pcm_efx(emu10k1_t * emu, int device, snd_pcm_t ** rpcm){ snd_pcm_t *pcm; snd_kcontrol_t *kctl; int err; if (rpcm) *rpcm = NULL; if ((err = snd_pcm_new(emu->card, "emu10k1 efx", device, 8, 1, &pcm)) < 0) return err; pcm->private_data = emu; pcm->private_free = snd_emu10k1_pcm_efx_free; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_emu10k1_fx8010_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_emu10k1_capture_efx_ops); pcm->info_flags = 0; strcpy(pcm->name, "Multichannel Capture/PT Playback"); emu->pcm_efx = pcm; if (rpcm) *rpcm = pcm; /* EFX capture - record the "FXBUS2" channels, by default we connect the EXTINs * to these */ /* emu->efx_voices_mask[0] = FXWC_DEFAULTROUTE_C | FXWC_DEFAULTROUTE_A; */ if (emu->audigy) { emu->efx_voices_mask[0] = 0; emu->efx_voices_mask[1] = 0xffff; } else { emu->efx_voices_mask[0] = 0xffff0000; emu->efx_voices_mask[1] = 0; } kctl = snd_ctl_new1(&snd_emu10k1_pcm_efx_voices_mask, emu); if (!kctl) return -ENOMEM; kctl->id.device = device; snd_ctl_add(emu->card, kctl); snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -