📄 azt3328.c
字号:
}static irqreturn_tsnd_azf3328_interrupt(int irq, void *dev_id){ struct snd_azf3328 *chip = dev_id; u8 status, which; static unsigned long irq_count; status = snd_azf3328_codec_inb(chip, IDX_IO_IRQSTATUS); /* fast path out, to ease interrupt sharing */ if (!(status & (IRQ_PLAYBACK|IRQ_RECORDING|IRQ_MPU401|IRQ_TIMER))) return IRQ_NONE; /* must be interrupt for another device */ snd_azf3328_dbgplay("Interrupt %ld!\nIDX_IO_PLAY_FLAGS %04x, IDX_IO_PLAY_IRQTYPE %04x, IDX_IO_IRQSTATUS %04x\n", irq_count, snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS), snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE), status); if (status & IRQ_TIMER) { /* snd_azf3328_dbgplay("timer %ld\n", inl(chip->codec_port+IDX_IO_TIMER_VALUE) & TIMER_VALUE_MASK); */ if (chip->timer) snd_timer_interrupt(chip->timer, chip->timer->sticks); /* ACK timer */ spin_lock(&chip->reg_lock); snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x07); spin_unlock(&chip->reg_lock); snd_azf3328_dbgplay("azt3328: timer IRQ\n"); } if (status & IRQ_PLAYBACK) { spin_lock(&chip->reg_lock); which = snd_azf3328_codec_inb(chip, IDX_IO_PLAY_IRQTYPE); /* ack all IRQ types immediately */ snd_azf3328_codec_outb(chip, IDX_IO_PLAY_IRQTYPE, which); spin_unlock(&chip->reg_lock); if (chip->pcm && chip->playback_substream) { snd_pcm_period_elapsed(chip->playback_substream); snd_azf3328_dbgplay("PLAY period done (#%x), @ %x\n", which, inl(chip->codec_port+IDX_IO_PLAY_DMA_CURRPOS)); } else snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n"); if (which & IRQ_PLAY_SOMETHING) snd_azf3328_dbgplay("azt3328: unknown play IRQ type occurred, please report!\n"); } if (status & IRQ_RECORDING) { spin_lock(&chip->reg_lock); which = snd_azf3328_codec_inb(chip, IDX_IO_REC_IRQTYPE); /* ack all IRQ types immediately */ snd_azf3328_codec_outb(chip, IDX_IO_REC_IRQTYPE, which); spin_unlock(&chip->reg_lock); if (chip->pcm && chip->capture_substream) { snd_pcm_period_elapsed(chip->capture_substream); snd_azf3328_dbgplay("REC period done (#%x), @ %x\n", which, inl(chip->codec_port+IDX_IO_REC_DMA_CURRPOS)); } else snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n"); if (which & IRQ_REC_SOMETHING) snd_azf3328_dbgplay("azt3328: unknown rec IRQ type occurred, please report!\n"); } /* MPU401 has less critical IRQ requirements * than timer and playback/recording, right? */ if (status & IRQ_MPU401) { snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data); /* hmm, do we have to ack the IRQ here somehow? * If so, then I don't know how... */ snd_azf3328_dbgplay("azt3328: MPU401 IRQ\n"); } irq_count++; return IRQ_HANDLED;}/*****************************************************************/static const struct snd_pcm_hardware snd_azf3328_playback ={ /* FIXME!! Correct? */ .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID, .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE, .rates = SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT, .rate_min = 4000, .rate_max = 66200, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = 65536, .period_bytes_min = 64, .period_bytes_max = 65536, .periods_min = 1, .periods_max = 1024, /* FIXME: maybe that card actually has a FIFO? * Hmm, it seems newer revisions do have one, but we still don't know * its size... */ .fifo_size = 0,};static const struct snd_pcm_hardware snd_azf3328_capture ={ /* FIXME */ .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP_VALID, .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE, .rates = SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT, .rate_min = 4000, .rate_max = 66200, .channels_min = 1, .channels_max = 2, .buffer_bytes_max = 65536, .period_bytes_min = 64, .period_bytes_max = 65536, .periods_min = 1, .periods_max = 1024, .fifo_size = 0,};static unsigned int snd_azf3328_fixed_rates[] = { 4000, 4800, 5512, 6620, 8000, 9600, 11025, 13240, 16000, 22050, 32000, 44100, 48000, 66200 };static struct snd_pcm_hw_constraint_list snd_azf3328_hw_constraints_rates = { .count = ARRAY_SIZE(snd_azf3328_fixed_rates), .list = snd_azf3328_fixed_rates, .mask = 0,};/*****************************************************************/static intsnd_azf3328_playback_open(struct snd_pcm_substream *substream){ struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; snd_azf3328_dbgcallenter(); chip->playback_substream = substream; runtime->hw = snd_azf3328_playback; snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &snd_azf3328_hw_constraints_rates); snd_azf3328_dbgcallleave(); return 0;}static intsnd_azf3328_capture_open(struct snd_pcm_substream *substream){ struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; snd_azf3328_dbgcallenter(); chip->capture_substream = substream; runtime->hw = snd_azf3328_capture; snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &snd_azf3328_hw_constraints_rates); snd_azf3328_dbgcallleave(); return 0;}static intsnd_azf3328_playback_close(struct snd_pcm_substream *substream){ struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); snd_azf3328_dbgcallenter(); chip->playback_substream = NULL; snd_azf3328_dbgcallleave(); return 0;}static intsnd_azf3328_capture_close(struct snd_pcm_substream *substream){ struct snd_azf3328 *chip = snd_pcm_substream_chip(substream); snd_azf3328_dbgcallenter(); chip->capture_substream = NULL; snd_azf3328_dbgcallleave(); return 0;}/******************************************************************/static struct snd_pcm_ops snd_azf3328_playback_ops = { .open = snd_azf3328_playback_open, .close = snd_azf3328_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_azf3328_hw_params, .hw_free = snd_azf3328_hw_free, .prepare = snd_azf3328_playback_prepare, .trigger = snd_azf3328_playback_trigger, .pointer = snd_azf3328_playback_pointer};static struct snd_pcm_ops snd_azf3328_capture_ops = { .open = snd_azf3328_capture_open, .close = snd_azf3328_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_azf3328_hw_params, .hw_free = snd_azf3328_hw_free, .prepare = snd_azf3328_capture_prepare, .trigger = snd_azf3328_capture_trigger, .pointer = snd_azf3328_capture_pointer};static int __devinitsnd_azf3328_pcm(struct snd_azf3328 *chip, int device){ struct snd_pcm *pcm; int err; snd_azf3328_dbgcallenter(); if ((err = snd_pcm_new(chip->card, "AZF3328 DSP", device, 1, 1, &pcm)) < 0) return err; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_azf3328_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_azf3328_capture_ops); pcm->private_data = chip; pcm->info_flags = 0; strcpy(pcm->name, chip->card->shortname); chip->pcm = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), 64*1024, 64*1024); snd_azf3328_dbgcallleave(); return 0;}/******************************************************************/#ifdef SUPPORT_JOYSTICKstatic int __devinitsnd_azf3328_config_joystick(struct snd_azf3328 *chip, int dev){ struct gameport *gp; struct resource *r; if (!joystick[dev]) return -ENODEV; if (!(r = request_region(0x200, 8, "AZF3328 gameport"))) { printk(KERN_WARNING "azt3328: cannot reserve joystick ports\n"); return -EBUSY; } chip->gameport = gp = gameport_allocate_port(); if (!gp) { printk(KERN_ERR "azt3328: cannot allocate memory for gameport\n"); release_and_free_resource(r); return -ENOMEM; } gameport_set_name(gp, "AZF3328 Gameport"); gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci)); gameport_set_dev_parent(gp, &chip->pci->dev); gp->io = 0x200; gameport_set_port_data(gp, r); snd_azf3328_io2_outb(chip, IDX_IO2_LEGACY_ADDR, snd_azf3328_io2_inb(chip, IDX_IO2_LEGACY_ADDR) | LEGACY_JOY); gameport_register_port(chip->gameport); return 0;}static voidsnd_azf3328_free_joystick(struct snd_azf3328 *chip){ if (chip->gameport) { struct resource *r = gameport_get_port_data(chip->gameport); gameport_unregister_port(chip->gameport); chip->gameport = NULL; /* disable gameport */ snd_azf3328_io2_outb(chip, IDX_IO2_LEGACY_ADDR, snd_azf3328_io2_inb(chip, IDX_IO2_LEGACY_ADDR) & ~LEGACY_JOY); release_and_free_resource(r); }}#elsestatic inline intsnd_azf3328_config_joystick(struct snd_azf3328 *chip, int dev) { return -ENOSYS; }static inline voidsnd_azf3328_free_joystick(struct snd_azf3328 *chip) { }#endif/******************************************************************/static intsnd_azf3328_free(struct snd_azf3328 *chip){ if (chip->irq < 0) goto __end_hw; /* reset (close) mixer */ snd_azf3328_mixer_set_mute(chip, IDX_MIXER_PLAY_MASTER, 1); /* first mute master volume */ snd_azf3328_mixer_outw(chip, IDX_MIXER_RESET, 0x0000); /* interrupt setup - mask everything (FIXME!) */ /* well, at least we know how to disable the timer IRQ */ snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0x00); synchronize_irq(chip->irq);__end_hw: snd_azf3328_free_joystick(chip); if (chip->irq >= 0) free_irq(chip->irq, chip); pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip); return 0;}static intsnd_azf3328_dev_free(struct snd_device *device){ struct snd_azf3328 *chip = device->device_data; return snd_azf3328_free(chip);}/******************************************************************//*** NOTE: the physical timer resolution actually is 1024000 ticks per second, *** but announcing those attributes to user-space would make programs *** configure the timer to a 1 tick value, resulting in an absolutely fatal *** timer IRQ storm. *** Thus I chose to announce a down-scaled virtual timer to the outside and *** calculate real timer countdown values internally. *** (the scale factor can be set via module parameter "seqtimer_scaling"). ***/static intsnd_azf3328_timer_start(struct snd_timer *timer){ struct snd_azf3328 *chip; unsigned long flags; unsigned int delay; snd_azf3328_dbgcallenter(); chip = snd_timer_chip(timer); delay = ((timer->sticks * seqtimer_scaling) - 1) & TIMER_VALUE_MASK; if (delay < 49) { /* uhoh, that's not good, since user-space won't know about * this timing tweak * (we need to do it to avoid a lockup, though) */ snd_azf3328_dbgtimer("delay was too low (%d)!\n", delay); delay = 49; /* minimum time is 49 ticks */ } snd_azf3328_dbgtimer("setting timer countdown value %d, add COUNTDOWN|IRQ\n", delay); delay |= TIMER_ENABLE_COUNTDOWN | TIMER_ENABLE_IRQ; spin_lock_irqsave(&chip->reg_lock, flags); snd_azf3328_codec_outl(chip, IDX_IO_TIMER_VALUE, delay); spin_unlock_irqrestore(&chip->reg_lock, flags); snd_azf3328_dbgcallleave(); return 0;}static intsnd_azf3328_timer_stop(struct snd_timer *timer){ struct snd_azf3328 *chip; unsigned long flags; snd_azf3328_dbgcallenter(); chip = snd_timer_chip(timer); spin_lock_irqsave(&chip->reg_lock, flags); /* disable timer countdown and interrupt */ /* FIXME: should we write TIMER_ACK_IRQ here? */ snd_azf3328_codec_outb(chip, IDX_IO_TIMER_VALUE + 3, 0); spin_unlock_irqrestore(&chip->reg_lock, flags); snd_azf3328_dbgcallleave(); return 0;}static intsnd_azf3328_timer_precise_resolution(struct snd_timer *timer, unsigned long *num, unsigned long *den){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -