📄 ca0106_main.c
字号:
return result;}/* trigger_capture callback */static int snd_ca0106_pcm_trigger_capture(struct snd_pcm_substream *substream, int cmd){ struct snd_ca0106 *emu = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_ca0106_pcm *epcm = runtime->private_data; int channel = epcm->channel_id; int result = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) | (0x110000<<channel)); snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0)|(0x100<<channel)); epcm->running = 1; break; case SNDRV_PCM_TRIGGER_STOP: snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<<channel)); snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) & ~(0x110000<<channel)); epcm->running = 0; break; default: result = -EINVAL; break; } return result;}/* pointer_playback callback */static snd_pcm_uframes_tsnd_ca0106_pcm_pointer_playback(struct snd_pcm_substream *substream){ struct snd_ca0106 *emu = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_ca0106_pcm *epcm = runtime->private_data; snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0; int channel = epcm->channel_id; if (!epcm->running) return 0; ptr3 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel); ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel); ptr4 = snd_ca0106_ptr_read(emu, PLAYBACK_LIST_PTR, channel); if (ptr3 != ptr4) ptr1 = snd_ca0106_ptr_read(emu, PLAYBACK_POINTER, channel); ptr2 = bytes_to_frames(runtime, ptr1); ptr2+= (ptr4 >> 3) * runtime->period_size; ptr=ptr2; if (ptr >= runtime->buffer_size) ptr -= runtime->buffer_size; //printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate); return ptr;}/* pointer_capture callback */static snd_pcm_uframes_tsnd_ca0106_pcm_pointer_capture(struct snd_pcm_substream *substream){ struct snd_ca0106 *emu = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_ca0106_pcm *epcm = runtime->private_data; snd_pcm_uframes_t ptr, ptr1, ptr2 = 0; int channel = channel=epcm->channel_id; if (!epcm->running) return 0; ptr1 = snd_ca0106_ptr_read(emu, CAPTURE_POINTER, channel); ptr2 = bytes_to_frames(runtime, ptr1); ptr=ptr2; if (ptr >= runtime->buffer_size) ptr -= runtime->buffer_size; //printk("ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n", ptr1, ptr2, ptr, (int)runtime->buffer_size, (int)runtime->period_size, (int)runtime->frame_bits, (int)runtime->rate); return ptr;}/* operators */static struct snd_pcm_ops snd_ca0106_playback_front_ops = { .open = snd_ca0106_pcm_open_playback_front, .close = snd_ca0106_pcm_close_playback, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ca0106_pcm_hw_params_playback, .hw_free = snd_ca0106_pcm_hw_free_playback, .prepare = snd_ca0106_pcm_prepare_playback, .trigger = snd_ca0106_pcm_trigger_playback, .pointer = snd_ca0106_pcm_pointer_playback,};static struct snd_pcm_ops snd_ca0106_capture_0_ops = { .open = snd_ca0106_pcm_open_0_capture, .close = snd_ca0106_pcm_close_capture, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ca0106_pcm_hw_params_capture, .hw_free = snd_ca0106_pcm_hw_free_capture, .prepare = snd_ca0106_pcm_prepare_capture, .trigger = snd_ca0106_pcm_trigger_capture, .pointer = snd_ca0106_pcm_pointer_capture,};static struct snd_pcm_ops snd_ca0106_capture_1_ops = { .open = snd_ca0106_pcm_open_1_capture, .close = snd_ca0106_pcm_close_capture, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ca0106_pcm_hw_params_capture, .hw_free = snd_ca0106_pcm_hw_free_capture, .prepare = snd_ca0106_pcm_prepare_capture, .trigger = snd_ca0106_pcm_trigger_capture, .pointer = snd_ca0106_pcm_pointer_capture,};static struct snd_pcm_ops snd_ca0106_capture_2_ops = { .open = snd_ca0106_pcm_open_2_capture, .close = snd_ca0106_pcm_close_capture, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ca0106_pcm_hw_params_capture, .hw_free = snd_ca0106_pcm_hw_free_capture, .prepare = snd_ca0106_pcm_prepare_capture, .trigger = snd_ca0106_pcm_trigger_capture, .pointer = snd_ca0106_pcm_pointer_capture,};static struct snd_pcm_ops snd_ca0106_capture_3_ops = { .open = snd_ca0106_pcm_open_3_capture, .close = snd_ca0106_pcm_close_capture, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ca0106_pcm_hw_params_capture, .hw_free = snd_ca0106_pcm_hw_free_capture, .prepare = snd_ca0106_pcm_prepare_capture, .trigger = snd_ca0106_pcm_trigger_capture, .pointer = snd_ca0106_pcm_pointer_capture,};static struct snd_pcm_ops snd_ca0106_playback_center_lfe_ops = { .open = snd_ca0106_pcm_open_playback_center_lfe, .close = snd_ca0106_pcm_close_playback, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ca0106_pcm_hw_params_playback, .hw_free = snd_ca0106_pcm_hw_free_playback, .prepare = snd_ca0106_pcm_prepare_playback, .trigger = snd_ca0106_pcm_trigger_playback, .pointer = snd_ca0106_pcm_pointer_playback, };static struct snd_pcm_ops snd_ca0106_playback_unknown_ops = { .open = snd_ca0106_pcm_open_playback_unknown, .close = snd_ca0106_pcm_close_playback, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ca0106_pcm_hw_params_playback, .hw_free = snd_ca0106_pcm_hw_free_playback, .prepare = snd_ca0106_pcm_prepare_playback, .trigger = snd_ca0106_pcm_trigger_playback, .pointer = snd_ca0106_pcm_pointer_playback, };static struct snd_pcm_ops snd_ca0106_playback_rear_ops = { .open = snd_ca0106_pcm_open_playback_rear, .close = snd_ca0106_pcm_close_playback, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ca0106_pcm_hw_params_playback, .hw_free = snd_ca0106_pcm_hw_free_playback, .prepare = snd_ca0106_pcm_prepare_playback, .trigger = snd_ca0106_pcm_trigger_playback, .pointer = snd_ca0106_pcm_pointer_playback, };static unsigned short snd_ca0106_ac97_read(struct snd_ac97 *ac97, unsigned short reg){ struct snd_ca0106 *emu = ac97->private_data; unsigned long flags; unsigned short val; spin_lock_irqsave(&emu->emu_lock, flags); outb(reg, emu->port + AC97ADDRESS); val = inw(emu->port + AC97DATA); spin_unlock_irqrestore(&emu->emu_lock, flags); return val;}static void snd_ca0106_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val){ struct snd_ca0106 *emu = ac97->private_data; unsigned long flags; spin_lock_irqsave(&emu->emu_lock, flags); outb(reg, emu->port + AC97ADDRESS); outw(val, emu->port + AC97DATA); spin_unlock_irqrestore(&emu->emu_lock, flags);}static int snd_ca0106_ac97(struct snd_ca0106 *chip){ struct snd_ac97_bus *pbus; struct snd_ac97_template ac97; int err; static struct snd_ac97_bus_ops ops = { .write = snd_ca0106_ac97_write, .read = snd_ca0106_ac97_read, }; if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0) return err; pbus->no_vra = 1; /* we don't need VRA */ memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; ac97.scaps = AC97_SCAP_NO_SPDIF; return snd_ac97_mixer(pbus, &ac97, &chip->ac97);}static int snd_ca0106_free(struct snd_ca0106 *chip){ if (chip->res_port != NULL) { /* avoid access to already used hardware */ // disable interrupts snd_ca0106_ptr_write(chip, BASIC_INTERRUPT, 0, 0); outl(0, chip->port + INTE); snd_ca0106_ptr_write(chip, EXTENDED_INT_MASK, 0, 0); udelay(1000); // disable audio //outl(HCFG_LOCKSOUNDCACHE, chip->port + HCFG); outl(0, chip->port + HCFG); /* FIXME: We need to stop and DMA transfers here. * But as I am not sure how yet, we cannot from the dma pages. * So we can fix: snd-malloc: Memory leak? pages not freed = 8 */ } // release the data#if 1 if (chip->buffer.area) snd_dma_free_pages(&chip->buffer);#endif // release the i/o port release_and_free_resource(chip->res_port); // release the irq if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); pci_disable_device(chip->pci); kfree(chip); return 0;}static int snd_ca0106_dev_free(struct snd_device *device){ struct snd_ca0106 *chip = device->device_data; return snd_ca0106_free(chip);}static irqreturn_t snd_ca0106_interrupt(int irq, void *dev_id, struct pt_regs *regs){ unsigned int status; struct snd_ca0106 *chip = dev_id; int i; int mask; unsigned int stat76; struct snd_ca0106_channel *pchannel; status = inl(chip->port + IPR); if (! status) return IRQ_NONE; stat76 = snd_ca0106_ptr_read(chip, EXTENDED_INT, 0); //snd_printk("interrupt status = 0x%08x, stat76=0x%08x\n", status, stat76); //snd_printk("ptr=0x%08x\n",snd_ca0106_ptr_read(chip, PLAYBACK_POINTER, 0)); mask = 0x11; /* 0x1 for one half, 0x10 for the other half period. */ for(i = 0; i < 4; i++) { pchannel = &(chip->playback_channels[i]); if (stat76 & mask) {/* FIXME: Select the correct substream for period elapsed */ if(pchannel->use) { snd_pcm_period_elapsed(pchannel->epcm->substream); //printk(KERN_INFO "interrupt [%d] used\n", i); } } //printk(KERN_INFO "channel=%p\n",pchannel); //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number); mask <<= 1; } mask = 0x110000; /* 0x1 for one half, 0x10 for the other half period. */ for(i = 0; i < 4; i++) { pchannel = &(chip->capture_channels[i]); if (stat76 & mask) {/* FIXME: Select the correct substream for period elapsed */ if(pchannel->use) { snd_pcm_period_elapsed(pchannel->epcm->substream); //printk(KERN_INFO "interrupt [%d] used\n", i); } } //printk(KERN_INFO "channel=%p\n",pchannel); //printk(KERN_INFO "interrupt stat76[%d] = %08x, use=%d, channel=%d\n", i, stat76, pchannel->use, pchannel->number); mask <<= 1; } snd_ca0106_ptr_write(chip, EXTENDED_INT, 0, stat76); if (chip->midi.dev_id && (status & (chip->midi.ipr_tx|chip->midi.ipr_rx))) { if (chip->midi.interrupt) chip->midi.interrupt(&chip->midi, status); else chip->midi.interrupt_disable(&chip->midi, chip->midi.tx_enable | chip->midi.rx_enable); } // acknowledge the interrupt if necessary outl(status, chip->port+IPR); return IRQ_HANDLED;}static int __devinit snd_ca0106_pcm(struct snd_ca0106 *emu, int device, struct snd_pcm **rpcm){ struct snd_pcm *pcm; struct snd_pcm_substream *substream; int err; if (rpcm) *rpcm = NULL; if ((err = snd_pcm_new(emu->card, "ca0106", device, 1, 1, &pcm)) < 0) return err; pcm->private_data = emu; switch (device) { case 0: snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_front_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_0_ops); break; case 1: snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_rear_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_1_ops); break; case 2: snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_center_lfe_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_2_ops); break; case 3: snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ca0106_playback_unknown_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ca0106_capture_3_ops); break; } pcm->info_flags = 0; pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX; strcpy(pcm->name, "CA0106"); 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, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0) /* FIXME: 32*1024 for sound buffer, between 32and64 for Periods table. */ return err; } for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) { if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0) return err; } if (rpcm) *rpcm = pcm; return 0;}static unsigned int spi_dac_init[] = { 0x00ff, 0x02ff, 0x0400, 0x0520, 0x0600, 0x08ff, 0x0aff, 0x0cff, 0x0eff, 0x10ff, 0x1200, 0x1400, 0x1480, 0x1800, 0x1aff,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -