📄 emufx.c
字号:
case EMU10K1_GRP_TRANSLATION_BASS: snd_runtime_check((ctl->count % 5) == 0 && (ctl->count / 5) == ctl->vcount, change = -EIO; goto __error); for (j = 0; j < 5; j++) snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, bass_table[val][j]); break; case EMU10K1_GRP_TRANSLATION_TREBLE: snd_runtime_check((ctl->count % 5) == 0 && (ctl->count / 5) == ctl->vcount, change = -EIO; goto __error); for (j = 0; j < 5; j++) snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[j * ctl->vcount + i], 0, treble_table[val][j]); break; case EMU10K1_GPR_TRANSLATION_ONOFF: snd_emu10k1_ptr_write(emu, emu->gpr_base + ctl->gpr[i], 0, onoff_table[val]); break; } } __error: spin_unlock_irqrestore(&emu->reg_lock, flags); return change;}/* * Interrupt handler */static void snd_emu10k1_fx8010_interrupt(emu10k1_t *emu){ snd_emu10k1_fx8010_irq_t *irq, *nirq; irq = emu->fx8010.irq_handlers; while (irq) { nirq = irq->next; /* irq ptr can be removed from list */ if (snd_emu10k1_ptr_read(emu, emu->gpr_base + irq->gpr_running, 0) & 0xffff0000) { if (irq->handler) irq->handler(emu, irq->private_data); snd_emu10k1_ptr_write(emu, emu->gpr_base + irq->gpr_running, 0, 1); } irq = nirq; }}static int snd_emu10k1_fx8010_register_irq_handler(emu10k1_t *emu, snd_fx8010_irq_handler_t *handler, unsigned char gpr_running, void *private_data, snd_emu10k1_fx8010_irq_t **r_irq){ snd_emu10k1_fx8010_irq_t *irq; unsigned long flags; snd_runtime_check(emu, return -EINVAL); snd_runtime_check(handler, return -EINVAL); irq = kmalloc(sizeof(*irq), GFP_KERNEL); if (irq == NULL) return -ENOMEM; irq->handler = handler; irq->gpr_running = gpr_running; irq->private_data = private_data; irq->next = NULL; spin_lock_irqsave(&emu->fx8010.irq_lock, flags); if (emu->fx8010.irq_handlers == NULL) { emu->fx8010.irq_handlers = irq; emu->dsp_interrupt = snd_emu10k1_fx8010_interrupt; snd_emu10k1_intr_enable(emu, INTE_FXDSPENABLE); } else { irq->next = emu->fx8010.irq_handlers; emu->fx8010.irq_handlers = irq; } spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags); if (r_irq) *r_irq = irq; return 0;}static int snd_emu10k1_fx8010_unregister_irq_handler(emu10k1_t *emu, snd_emu10k1_fx8010_irq_t *irq){ snd_emu10k1_fx8010_irq_t *tmp; unsigned long flags; snd_runtime_check(irq, return -EINVAL); spin_lock_irqsave(&emu->fx8010.irq_lock, flags); if ((tmp = emu->fx8010.irq_handlers) == irq) { emu->fx8010.irq_handlers = tmp->next; if (emu->fx8010.irq_handlers == NULL) { snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE); emu->dsp_interrupt = NULL; } } else { while (tmp && tmp->next != irq) tmp = tmp->next; if (tmp) tmp->next = tmp->next->next; } spin_unlock_irqrestore(&emu->fx8010.irq_lock, flags); kfree(irq); return 0;}/* * PCM streams */#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 = snd_magic_cast(snd_pcm_substream_t, private_data, return); 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 snd_emu10k1_fx8010_playback_tram_poke(emu10k1_t *emu, unsigned int *tram_pos, unsigned int *tram_shift, unsigned int tram_size, unsigned short *src, unsigned int frames){ unsigned int count; while (frames > *tram_pos) { count = *tram_pos + 1; snd_emu10k1_fx8010_playback_tram_poke1((unsigned short *)emu->fx8010.etram_pages + *tram_pos, (unsigned short *)emu->fx8010.etram_pages + *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 + *tram_pos, (unsigned short *)emu->fx8010.etram_pages + *tram_pos + tram_size / 2, src, frames, *tram_shift++); *tram_pos -= frames;}static int snd_emu10k1_fx8010_playback_transfer(snd_pcm_substream_t *substream, snd_pcm_uframes_t frames){ 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]; snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr; snd_pcm_sframes_t diff = appl_ptr - pcm->appl_ptr; snd_pcm_uframes_t buffer_size = pcm->buffer_size / 2; if (diff) { if (diff < -(snd_pcm_sframes_t) (runtime->boundary / 2)) diff += runtime->boundary; pcm->sw_ready += diff; } pcm->sw_ready += frames; pcm->appl_ptr = appl_ptr + frames; while (pcm->hw_ready < buffer_size && pcm->sw_ready > 0) { size_t hw_to_end = buffer_size - pcm->hw_data; size_t sw_to_end = (runtime->buffer_size << 2) - pcm->sw_data; size_t tframes = buffer_size - pcm->hw_ready; if (pcm->sw_ready < tframes) tframes = pcm->sw_ready; if (hw_to_end < tframes) tframes = hw_to_end; if (sw_to_end < tframes) tframes = sw_to_end; snd_emu10k1_fx8010_playback_tram_poke(emu, &pcm->tram_pos, &pcm->tram_shift, pcm->buffer_size, (unsigned short *)(runtime->dma_area + (pcm->sw_data << 2)), tframes); pcm->hw_data += tframes; if (pcm->hw_data == buffer_size) pcm->hw_data = 0; pcm->sw_data += tframes; if (pcm->sw_data == runtime->buffer_size) pcm->sw_data = 0; pcm->hw_ready += tframes; pcm->sw_ready -= tframes; } 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]; 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]; 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); pcm->sw_data = pcm->sw_io = pcm->sw_ready = 0; pcm->hw_data = pcm->hw_io = pcm->hw_ready = 0; pcm->tram_pos = INITIAL_TRAM_POS(pcm->buffer_size); pcm->tram_shift = 0; pcm->appl_ptr = 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]; unsigned long flags; int result = 0; spin_lock_irqsave(&emu->reg_lock, flags); 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, 0); /* 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_irqrestore(&emu->reg_lock, flags); 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_pcm_runtime_t *runtime = substream->runtime; snd_emu10k1_fx8010_pcm_t *pcm = &emu->fx8010.pcm[substream->number]; size_t ptr; snd_pcm_sframes_t frames; 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); frames = ptr - pcm->hw_io; if (frames < 0) frames += runtime->buffer_size; pcm->hw_io = ptr; pcm->hw_ready -= frames; pcm->sw_io += frames; if (pcm->sw_io > runtime->buffer_size) pcm->sw_io -= runtime->buffer_size; snd_emu10k1_fx8010_playback_transfer(substream, 0); return pcm->sw_io;}static int snd_emu10k1_fx8010_playback_copy(snd_pcm_substream_t *substream, int channel, snd_pcm_uframes_t hwoff, void *src, snd_pcm_uframes_t frames){ snd_pcm_runtime_t *runtime = substream->runtime; size_t hwoffb = hwoff << 2; size_t bytes = frames << 2; char *hwbuf = runtime->dma_area + hwoffb; if (copy_from_user(hwbuf, src, bytes)) return -EFAULT; spin_lock_irq(&runtime->lock); snd_emu10k1_fx8010_playback_transfer(substream, frames); spin_unlock_irq(&runtime->lock); return 0;}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(&emu->reg_lock); if (pcm->valid == 0) { spin_unlock(&emu->reg_lock); return -ENODEV; } pcm->opened = 1; spin_unlock(&emu->reg_lock); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -