📄 ad1889.c
字号:
static snd_pcm_uframes_tsnd_ad1889_playback_pointer(struct snd_pcm_substream *ss){ size_t ptr = 0; struct snd_ad1889 *chip = snd_pcm_substream_chip(ss); if (unlikely(!(chip->wave.reg & AD_DS_WSMC_WAEN))) return 0; ptr = ad1889_readl(chip, AD_DMA_WAVCA); ptr -= chip->wave.addr; snd_assert((ptr >= 0) && (ptr < chip->wave.size), return 0); return bytes_to_frames(ss->runtime, ptr);}/* Called in atomic context with IRQ disabled */static snd_pcm_uframes_tsnd_ad1889_capture_pointer(struct snd_pcm_substream *ss){ size_t ptr = 0; struct snd_ad1889 *chip = snd_pcm_substream_chip(ss); if (unlikely(!(chip->ramc.reg & AD_DS_RAMC_ADEN))) return 0; ptr = ad1889_readl(chip, AD_DMA_ADCCA); ptr -= chip->ramc.addr; snd_assert((ptr >= 0) && (ptr < chip->ramc.size), return 0); return bytes_to_frames(ss->runtime, ptr);}static struct snd_pcm_ops snd_ad1889_playback_ops = { .open = snd_ad1889_playback_open, .close = snd_ad1889_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ad1889_hw_params, .hw_free = snd_ad1889_hw_free, .prepare = snd_ad1889_playback_prepare, .trigger = snd_ad1889_playback_trigger, .pointer = snd_ad1889_playback_pointer, };static struct snd_pcm_ops snd_ad1889_capture_ops = { .open = snd_ad1889_capture_open, .close = snd_ad1889_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ad1889_hw_params, .hw_free = snd_ad1889_hw_free, .prepare = snd_ad1889_capture_prepare, .trigger = snd_ad1889_capture_trigger, .pointer = snd_ad1889_capture_pointer, };static irqreturn_tsnd_ad1889_interrupt(int irq, void *dev_id){ unsigned long st; struct snd_ad1889 *chip = dev_id; st = ad1889_readl(chip, AD_DMA_DISR); /* clear ISR */ ad1889_writel(chip, AD_DMA_DISR, st); st &= AD_INTR_MASK; if (unlikely(!st)) return IRQ_NONE; if (st & (AD_DMA_DISR_PMAI|AD_DMA_DISR_PTAI)) ad1889_debug("Unexpected master or target abort interrupt!\n"); if ((st & AD_DMA_DISR_WAVI) && chip->psubs) snd_pcm_period_elapsed(chip->psubs); if ((st & AD_DMA_DISR_ADCI) && chip->csubs) snd_pcm_period_elapsed(chip->csubs); return IRQ_HANDLED;}static int __devinitsnd_ad1889_pcm_init(struct snd_ad1889 *chip, int device, struct snd_pcm **rpcm){ int err; struct snd_pcm *pcm; if (rpcm) *rpcm = NULL; err = snd_pcm_new(chip->card, chip->card->driver, device, 1, 1, &pcm); if (err < 0) return err; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ad1889_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ad1889_capture_ops); pcm->private_data = chip; pcm->info_flags = 0; strcpy(pcm->name, chip->card->shortname); chip->pcm = pcm; chip->psubs = NULL; chip->csubs = NULL; err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), BUFFER_BYTES_MAX / 2, BUFFER_BYTES_MAX); if (err < 0) { snd_printk(KERN_ERR PFX "buffer allocation error: %d\n", err); return err; } if (rpcm) *rpcm = pcm; return 0;}static voidsnd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer){ struct snd_ad1889 *chip = entry->private_data; u16 reg; int tmp; reg = ad1889_readw(chip, AD_DS_WSMC); snd_iprintf(buffer, "Wave output: %s\n", (reg & AD_DS_WSMC_WAEN) ? "enabled" : "disabled"); snd_iprintf(buffer, "Wave Channels: %s\n", (reg & AD_DS_WSMC_WAST) ? "stereo" : "mono"); snd_iprintf(buffer, "Wave Quality: %d-bit linear\n", (reg & AD_DS_WSMC_WA16) ? 16 : 8); /* WARQ is at offset 12 */ tmp = (reg & AD_DS_WSMC_WARQ) ? (((reg & AD_DS_WSMC_WARQ >> 12) & 0x01) ? 12 : 18) : 4; tmp /= (reg & AD_DS_WSMC_WAST) ? 2 : 1; snd_iprintf(buffer, "Wave FIFO: %d %s words\n\n", tmp, (reg & AD_DS_WSMC_WAST) ? "stereo" : "mono"); snd_iprintf(buffer, "Synthesis output: %s\n", reg & AD_DS_WSMC_SYEN ? "enabled" : "disabled"); /* SYRQ is at offset 4 */ tmp = (reg & AD_DS_WSMC_SYRQ) ? (((reg & AD_DS_WSMC_SYRQ >> 4) & 0x01) ? 12 : 18) : 4; tmp /= (reg & AD_DS_WSMC_WAST) ? 2 : 1; snd_iprintf(buffer, "Synthesis FIFO: %d %s words\n\n", tmp, (reg & AD_DS_WSMC_WAST) ? "stereo" : "mono"); reg = ad1889_readw(chip, AD_DS_RAMC); snd_iprintf(buffer, "ADC input: %s\n", (reg & AD_DS_RAMC_ADEN) ? "enabled" : "disabled"); snd_iprintf(buffer, "ADC Channels: %s\n", (reg & AD_DS_RAMC_ADST) ? "stereo" : "mono"); snd_iprintf(buffer, "ADC Quality: %d-bit linear\n", (reg & AD_DS_RAMC_AD16) ? 16 : 8); /* ACRQ is at offset 4 */ tmp = (reg & AD_DS_RAMC_ACRQ) ? (((reg & AD_DS_RAMC_ACRQ >> 4) & 0x01) ? 12 : 18) : 4; tmp /= (reg & AD_DS_RAMC_ADST) ? 2 : 1; snd_iprintf(buffer, "ADC FIFO: %d %s words\n\n", tmp, (reg & AD_DS_RAMC_ADST) ? "stereo" : "mono"); snd_iprintf(buffer, "Resampler input: %s\n", reg & AD_DS_RAMC_REEN ? "enabled" : "disabled"); /* RERQ is at offset 12 */ tmp = (reg & AD_DS_RAMC_RERQ) ? (((reg & AD_DS_RAMC_RERQ >> 12) & 0x01) ? 12 : 18) : 4; tmp /= (reg & AD_DS_RAMC_ADST) ? 2 : 1; snd_iprintf(buffer, "Resampler FIFO: %d %s words\n\n", tmp, (reg & AD_DS_WSMC_WAST) ? "stereo" : "mono"); /* doc says LSB represents -1.5dB, but the max value (-94.5dB) suggests that LSB is -3dB, which is more coherent with the logarithmic nature of the dB scale */ reg = ad1889_readw(chip, AD_DS_WADA); snd_iprintf(buffer, "Left: %s, -%d dB\n", (reg & AD_DS_WADA_LWAM) ? "mute" : "unmute", ((reg & AD_DS_WADA_LWAA) >> 8) * 3); reg = ad1889_readw(chip, AD_DS_WADA); snd_iprintf(buffer, "Right: %s, -%d dB\n", (reg & AD_DS_WADA_RWAM) ? "mute" : "unmute", ((reg & AD_DS_WADA_RWAA) >> 8) * 3); reg = ad1889_readw(chip, AD_DS_WAS); snd_iprintf(buffer, "Wave samplerate: %u Hz\n", reg); reg = ad1889_readw(chip, AD_DS_RES); snd_iprintf(buffer, "Resampler samplerate: %u Hz\n", reg);}static void __devinitsnd_ad1889_proc_init(struct snd_ad1889 *chip){ struct snd_info_entry *entry; if (!snd_card_proc_new(chip->card, chip->card->driver, &entry)) snd_info_set_text_ops(entry, chip, snd_ad1889_proc_read);}static struct ac97_quirk ac97_quirks[] = { { .subvendor = 0x11d4, /* AD */ .subdevice = 0x1889, /* AD1889 */ .codec_id = AC97_ID_AD1819, .name = "AD1889", .type = AC97_TUNE_HP_ONLY }, { } /* terminator */};static void __devinitsnd_ad1889_ac97_xinit(struct snd_ad1889 *chip){ u16 reg; reg = ad1889_readw(chip, AD_AC97_ACIC); reg |= AD_AC97_ACIC_ACRD; /* Reset Disable */ ad1889_writew(chip, AD_AC97_ACIC, reg); ad1889_readw(chip, AD_AC97_ACIC); /* flush posted write */ udelay(10); /* Interface Enable */ reg |= AD_AC97_ACIC_ACIE; ad1889_writew(chip, AD_AC97_ACIC, reg); snd_ad1889_ac97_ready(chip); /* Audio Stream Output | Variable Sample Rate Mode */ reg = ad1889_readw(chip, AD_AC97_ACIC); reg |= AD_AC97_ACIC_ASOE | AD_AC97_ACIC_VSRM; ad1889_writew(chip, AD_AC97_ACIC, reg); ad1889_readw(chip, AD_AC97_ACIC); /* flush posted write */}static voidsnd_ad1889_ac97_bus_free(struct snd_ac97_bus *bus){ struct snd_ad1889 *chip = bus->private_data; chip->ac97_bus = NULL;}static voidsnd_ad1889_ac97_free(struct snd_ac97 *ac97){ struct snd_ad1889 *chip = ac97->private_data; chip->ac97 = NULL;}static int __devinitsnd_ad1889_ac97_init(struct snd_ad1889 *chip, const char *quirk_override){ int err; struct snd_ac97_template ac97; static struct snd_ac97_bus_ops ops = { .write = snd_ad1889_ac97_write, .read = snd_ad1889_ac97_read, }; /* doing that here, it works. */ snd_ad1889_ac97_xinit(chip); err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus); if (err < 0) return err; chip->ac97_bus->private_free = snd_ad1889_ac97_bus_free; memset(&ac97, 0, sizeof(ac97)); ac97.private_data = chip; ac97.private_free = snd_ad1889_ac97_free; ac97.pci = chip->pci; err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97); if (err < 0) return err; snd_ac97_tune_hardware(chip->ac97, ac97_quirks, quirk_override); return 0;}static intsnd_ad1889_free(struct snd_ad1889 *chip){ if (chip->irq < 0) goto skip_hw; spin_lock_irq(&chip->lock); ad1889_mute(chip); /* Turn off interrupt on count and zero DMA registers */ ad1889_channel_reset(chip, AD_CHAN_WAV | AD_CHAN_ADC); /* clear DISR. If we don't, we'd better jump off the Eiffel Tower */ ad1889_writel(chip, AD_DMA_DISR, AD_DMA_DISR_PTAI | AD_DMA_DISR_PMAI); ad1889_readl(chip, AD_DMA_DISR); /* flush, dammit! */ spin_unlock_irq(&chip->lock); synchronize_irq(chip->irq); if (chip->irq >= 0) free_irq(chip->irq, chip);skip_hw: if (chip->iobase) iounmap(chip->iobase); pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip); return 0;}static intsnd_ad1889_dev_free(struct snd_device *device) { struct snd_ad1889 *chip = device->device_data; return snd_ad1889_free(chip);}static int __devinitsnd_ad1889_init(struct snd_ad1889 *chip) { ad1889_writew(chip, AD_DS_CCS, AD_DS_CCS_CLKEN); /* turn on clock */ ad1889_readw(chip, AD_DS_CCS); /* flush posted write */ mdelay(10); /* enable Master and Target abort interrupts */ ad1889_writel(chip, AD_DMA_DISR, AD_DMA_DISR_PMAE | AD_DMA_DISR_PTAE); return 0;}static int __devinitsnd_ad1889_create(struct snd_card *card, struct pci_dev *pci, struct snd_ad1889 **rchip){ int err; struct snd_ad1889 *chip; static struct snd_device_ops ops = { .dev_free = snd_ad1889_dev_free, }; *rchip = NULL; if ((err = pci_enable_device(pci)) < 0) return err; /* check PCI availability (32bit DMA) */ if (pci_set_dma_mask(pci, DMA_32BIT_MASK) < 0 || pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK) < 0) { printk(KERN_ERR PFX "error setting 32-bit DMA mask.\n"); pci_disable_device(pci); return -ENXIO; } /* allocate chip specific data with zero-filled memory */ if ((chip = kzalloc(sizeof(*chip), GFP_KERNEL)) == NULL) { pci_disable_device(pci); return -ENOMEM; } chip->card = card; card->private_data = chip; chip->pci = pci; chip->irq = -1; /* (1) PCI resource allocation */ if ((err = pci_request_regions(pci, card->driver)) < 0) goto free_and_ret; chip->bar = pci_resource_start(pci, 0); chip->iobase = ioremap_nocache(chip->bar, pci_resource_len(pci, 0)); if (chip->iobase == NULL) { printk(KERN_ERR PFX "unable to reserve region.\n"); err = -EBUSY; goto free_and_ret; } pci_set_master(pci); spin_lock_init(&chip->lock); /* only now can we call ad1889_free */ if (request_irq(pci->irq, snd_ad1889_interrupt, IRQF_SHARED, card->driver, chip)) { printk(KERN_ERR PFX "cannot obtain IRQ %d\n", pci->irq); snd_ad1889_free(chip); return -EBUSY; } chip->irq = pci->irq; synchronize_irq(chip->irq); /* (2) initialization of the chip hardware */ if ((err = snd_ad1889_init(chip)) < 0) { snd_ad1889_free(chip); return err; } if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { snd_ad1889_free(chip); return err; } snd_card_set_dev(card, &pci->dev); *rchip = chip; return 0;free_and_ret: kfree(chip); pci_disable_device(pci); return err;}static int __devinitsnd_ad1889_probe(struct pci_dev *pci, const struct pci_device_id *pci_id){ int err; static int devno; struct snd_card *card; struct snd_ad1889 *chip; /* (1) */ if (devno >= SNDRV_CARDS) return -ENODEV; if (!enable[devno]) { devno++; return -ENOENT; } /* (2) */ card = snd_card_new(index[devno], id[devno], THIS_MODULE, 0); /* XXX REVISIT: we can probably allocate chip in this call */ if (card == NULL) return -ENOMEM; strcpy(card->driver, "AD1889"); strcpy(card->shortname, "Analog Devices AD1889"); /* (3) */ err = snd_ad1889_create(card, pci, &chip); if (err < 0) goto free_and_ret; /* (4) */ sprintf(card->longname, "%s at 0x%lx irq %i", card->shortname, chip->bar, chip->irq); /* (5) */ /* register AC97 mixer */ err = snd_ad1889_ac97_init(chip, ac97_quirk[devno]); if (err < 0) goto free_and_ret; err = snd_ad1889_pcm_init(chip, 0, NULL); if (err < 0) goto free_and_ret; /* register proc interface */ snd_ad1889_proc_init(chip); /* (6) */ err = snd_card_register(card); if (err < 0) goto free_and_ret; /* (7) */ pci_set_drvdata(pci, card); devno++; return 0;free_and_ret: snd_card_free(card); return err;}static void __devexitsnd_ad1889_remove(struct pci_dev *pci){ snd_card_free(pci_get_drvdata(pci)); pci_set_drvdata(pci, NULL);}static struct pci_device_id snd_ad1889_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_ANALOG_DEVICES, PCI_DEVICE_ID_AD1889JS) }, { 0, },};MODULE_DEVICE_TABLE(pci, snd_ad1889_ids);static struct pci_driver ad1889_pci = { .name = "AD1889 Audio", .id_table = snd_ad1889_ids, .probe = snd_ad1889_probe, .remove = __devexit_p(snd_ad1889_remove),};static int __initalsa_ad1889_init(void){ return pci_register_driver(&ad1889_pci);}static void __exitalsa_ad1889_fini(void){ pci_unregister_driver(&ad1889_pci);}module_init(alsa_ad1889_init);module_exit(alsa_ad1889_fini);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -