📄 ca0106_main.c
字号:
{ return snd_pcm_lib_free_pages(substream);}/* prepare playback callback */static int snd_ca0106_pcm_prepare_playback(snd_pcm_substream_t *substream){ ca0106_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; ca0106_pcm_t *epcm = runtime->private_data; int channel = epcm->channel_id; u32 *table_base = (u32 *)(emu->buffer.area+(8*16*channel)); u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size); u32 hcfg_mask = HCFG_PLAYBACK_S32_LE; u32 hcfg_set = 0x00000000; u32 hcfg; u32 reg40_mask = 0x30000 << (channel<<1); u32 reg40_set = 0; u32 reg40; /* FIXME: Depending on mixer selection of SPDIF out or not, select the spdif rate or the DAC rate. */ u32 reg71_mask = 0x03030000 ; /* Global. Set SPDIF rate. We only support 44100 to spdif, not to DAC. */ u32 reg71_set = 0; u32 reg71; int i; //snd_printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, periods=%u, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, runtime->periods, frames_to_bytes(runtime, 1)); //snd_printk("dma_addr=%x, dma_area=%p, table_base=%p\n",runtime->dma_addr, runtime->dma_area, table_base); //snd_printk("dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",emu->buffer.addr, emu->buffer.area, emu->buffer.bytes); /* Rate can be set per channel. */ /* reg40 control host to fifo */ /* reg71 controls DAC rate. */ switch (runtime->rate) { case 44100: reg40_set = 0x10000 << (channel<<1); reg71_set = 0x01010000; break; case 48000: reg40_set = 0; reg71_set = 0; break; case 96000: reg40_set = 0x20000 << (channel<<1); reg71_set = 0x02020000; break; case 192000: reg40_set = 0x30000 << (channel<<1); reg71_set = 0x03030000; break; default: reg40_set = 0; reg71_set = 0; break; } /* Format is a global setting */ /* FIXME: Only let the first channel accessed set this. */ switch (runtime->format) { case SNDRV_PCM_FORMAT_S16_LE: hcfg_set = 0; break; case SNDRV_PCM_FORMAT_S32_LE: hcfg_set = HCFG_PLAYBACK_S32_LE; break; default: hcfg_set = 0; break; } hcfg = inl(emu->port + HCFG) ; hcfg = (hcfg & ~hcfg_mask) | hcfg_set; outl(hcfg, emu->port + HCFG); reg40 = snd_ca0106_ptr_read(emu, 0x40, 0); reg40 = (reg40 & ~reg40_mask) | reg40_set; snd_ca0106_ptr_write(emu, 0x40, 0, reg40); reg71 = snd_ca0106_ptr_read(emu, 0x71, 0); reg71 = (reg71 & ~reg71_mask) | reg71_set; snd_ca0106_ptr_write(emu, 0x71, 0, reg71); /* FIXME: Check emu->buffer.size before actually writing to it. */ for(i=0; i < runtime->periods; i++) { table_base[i*2]=runtime->dma_addr+(i*period_size_bytes); table_base[(i*2)+1]=period_size_bytes<<16; } snd_ca0106_ptr_write(emu, PLAYBACK_LIST_ADDR, channel, emu->buffer.addr+(8*16*channel)); snd_ca0106_ptr_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19); snd_ca0106_ptr_write(emu, PLAYBACK_LIST_PTR, channel, 0); snd_ca0106_ptr_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr); snd_ca0106_ptr_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes snd_ca0106_ptr_write(emu, PLAYBACK_POINTER, channel, 0); snd_ca0106_ptr_write(emu, 0x07, channel, 0x0); snd_ca0106_ptr_write(emu, 0x08, channel, 0); snd_ca0106_ptr_write(emu, PLAYBACK_MUTE, 0x0, 0x0); /* Unmute output */#if 0 snd_ca0106_ptr_write(emu, SPCS0, 0, SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT ); }#endif return 0;}/* prepare capture callback */static int snd_ca0106_pcm_prepare_capture(snd_pcm_substream_t *substream){ ca0106_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; ca0106_pcm_t *epcm = runtime->private_data; int channel = epcm->channel_id; //printk("prepare:channel_number=%d, rate=%d, format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, frames_to_bytes=%d\n",channel, runtime->rate, runtime->format, runtime->channels, runtime->buffer_size, runtime->period_size, frames_to_bytes(runtime, 1)); snd_ca0106_ptr_write(emu, 0x13, channel, 0); snd_ca0106_ptr_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr); snd_ca0106_ptr_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size)<<16); // buffer size in bytes snd_ca0106_ptr_write(emu, CAPTURE_POINTER, channel, 0); return 0;}/* trigger_playback callback */static int snd_ca0106_pcm_trigger_playback(snd_pcm_substream_t *substream, int cmd){ ca0106_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime; ca0106_pcm_t *epcm; int channel; int result = 0; struct list_head *pos; snd_pcm_substream_t *s; u32 basic = 0; u32 extended = 0; int running=0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: running=1; break; case SNDRV_PCM_TRIGGER_STOP: default: running=0; break; } snd_pcm_group_for_each(pos, substream) { s = snd_pcm_group_substream_entry(pos); runtime = s->runtime; epcm = runtime->private_data; channel = epcm->channel_id; //snd_printk("channel=%d\n",channel); epcm->running = running; basic |= (0x1<<channel); extended |= (0x10<<channel); snd_pcm_trigger_done(s, substream); } //snd_printk("basic=0x%x, extended=0x%x\n",basic, extended); 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) | (extended)); snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0)|(basic)); break; case SNDRV_PCM_TRIGGER_STOP: snd_ca0106_ptr_write(emu, BASIC_INTERRUPT, 0, snd_ca0106_ptr_read(emu, BASIC_INTERRUPT, 0) & ~(basic)); snd_ca0106_ptr_write(emu, EXTENDED_INT_MASK, 0, snd_ca0106_ptr_read(emu, EXTENDED_INT_MASK, 0) & ~(extended)); break; default: result = -EINVAL; break; } return result;}/* trigger_capture callback */static int snd_ca0106_pcm_trigger_capture(snd_pcm_substream_t *substream, int cmd){ ca0106_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; ca0106_pcm_t *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(snd_pcm_substream_t *substream){ ca0106_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; ca0106_pcm_t *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(snd_pcm_substream_t *substream){ ca0106_t *emu = snd_pcm_substream_chip(substream); snd_pcm_runtime_t *runtime = substream->runtime; ca0106_pcm_t *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 snd_pcm_ops_t 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 snd_pcm_ops_t 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 snd_pcm_ops_t 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 snd_pcm_ops_t 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 snd_pcm_ops_t 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 snd_pcm_ops_t 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 snd_pcm_ops_t 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 snd_pcm_ops_t 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(ac97_t *ac97, unsigned short reg){ ca0106_t *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(ac97_t *ac97, unsigned short reg, unsigned short val){ ca0106_t *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(ca0106_t *chip){ ac97_bus_t *pbus; ac97_template_t ac97; int err; static ac97_bus_ops_t 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; return snd_ac97_mixer(pbus, &ac97, &chip->ac97);}static int snd_ca0106_free(ca0106_t *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 if (chip->res_port) { release_resource(chip->res_port); kfree_nocheck(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(snd_device_t *device){ ca0106_t *chip = device->device_data; return snd_ca0106_free(chip);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -