📄 nm256.c
字号:
* if it timed out. */static intsnd_nm256_ac97_ready(nm256_t *chip){ int timeout = 10; u32 testaddr; u16 testb; testaddr = chip->mixer_status_offset; testb = chip->mixer_status_mask; /* * Loop around waiting for the mixer to become ready. */ while (timeout-- > 0) { if ((snd_nm256_readw(chip, testaddr) & testb) == 0) return 1; udelay(100); } return 0;}/* */static unsigned shortsnd_nm256_ac97_read(ac97_t *ac97, unsigned short reg){ nm256_t *chip = ac97->private_data; int res; if (reg >= 128) return 0; if (! snd_nm256_ac97_ready(chip)) return 0; res = snd_nm256_readw(chip, chip->mixer_base + reg); /* Magic delay. Bleah yucky. */ msleep(1); return res;}/* */static voidsnd_nm256_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val){ nm256_t *chip = ac97->private_data; int tries = 2; u32 base; base = chip->mixer_base; snd_nm256_ac97_ready(chip); /* Wait for the write to take, too. */ while (tries-- > 0) { snd_nm256_writew(chip, base + reg, val); msleep(1); /* a little delay here seems better.. */ if (snd_nm256_ac97_ready(chip)) return; } snd_printd("nm256: ac97 codec not ready..\n");}/* initialize the ac97 into a known state */static voidsnd_nm256_ac97_reset(ac97_t *ac97){ nm256_t *chip = ac97->private_data; /* Reset the mixer. 'Tis magic! */ snd_nm256_writeb(chip, 0x6c0, 1); if (! chip->reset_workaround) { /* Dell latitude LS will lock up by this */ snd_nm256_writeb(chip, 0x6cc, 0x87); } if (! chip->reset_workaround_2) { /* Dell latitude CSx will lock up by this */ snd_nm256_writeb(chip, 0x6cc, 0x80); snd_nm256_writeb(chip, 0x6cc, 0x0); }}/* create an ac97 mixer interface */static int __devinitsnd_nm256_mixer(nm256_t *chip){ ac97_bus_t *pbus; ac97_template_t ac97; int i, err; static ac97_bus_ops_t ops = { .reset = snd_nm256_ac97_reset, .write = snd_nm256_ac97_write, .read = snd_nm256_ac97_read, }; /* looks like nm256 hangs up when unexpected registers are touched... */ static int mixer_regs[] = { AC97_MASTER, AC97_HEADPHONE, AC97_MASTER_MONO, AC97_PC_BEEP, AC97_PHONE, AC97_MIC, AC97_LINE, AC97_CD, AC97_VIDEO, AC97_AUX, AC97_PCM, AC97_REC_SEL, AC97_REC_GAIN, AC97_GENERAL_PURPOSE, AC97_3D_CONTROL, /*AC97_EXTENDED_ID,*/ AC97_VENDOR_ID1, AC97_VENDOR_ID2, -1 }; if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &pbus)) < 0) return err; memset(&ac97, 0, sizeof(ac97)); ac97.scaps = AC97_SCAP_AUDIO; /* we support audio! */ ac97.limited_regs = 1; for (i = 0; mixer_regs[i] >= 0; i++) set_bit(mixer_regs[i], ac97.reg_accessed); ac97.private_data = chip; pbus->no_vra = 1; err = snd_ac97_mixer(pbus, &ac97, &chip->ac97); if (err < 0) return err; if (! (chip->ac97->id & (0xf0000000))) { /* looks like an invalid id */ sprintf(chip->card->mixername, "%s AC97", chip->card->driver); } return 0;}/* * See if the signature left by the NM256 BIOS is intact; if so, we use * the associated address as the end of our audio buffer in the video * RAM. */static int __devinitsnd_nm256_peek_for_sig(nm256_t *chip){ /* The signature is located 1K below the end of video RAM. */ void __iomem *temp; /* Default buffer end is 5120 bytes below the top of RAM. */ unsigned long pointer_found = chip->buffer_end - 0x1400; u32 sig; temp = ioremap_nocache(chip->buffer_addr + chip->buffer_end - 0x400, 16); if (temp == NULL) { snd_printk(KERN_ERR "Unable to scan for card signature in video RAM\n"); return -EBUSY; } sig = readl(temp); if ((sig & NM_SIG_MASK) == NM_SIGNATURE) { u32 pointer = readl(temp + 4); /* * If it's obviously invalid, don't use it */ if (pointer == 0xffffffff || pointer < chip->buffer_size || pointer > chip->buffer_end) { snd_printk(KERN_ERR "invalid signature found: 0x%x\n", pointer); iounmap(temp); return -ENODEV; } else { pointer_found = pointer; printk(KERN_INFO "nm256: found card signature in video RAM: 0x%x\n", pointer); } } iounmap(temp); chip->buffer_end = pointer_found; return 0;}#ifdef CONFIG_PM/* * APM event handler, so the card is properly reinitialized after a power * event. */static int nm256_suspend(snd_card_t *card, pm_message_t state){ nm256_t *chip = card->pm_private_data; snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); chip->coeffs_current = 0; pci_disable_device(chip->pci); return 0;}static int nm256_resume(snd_card_t *card){ nm256_t *chip = card->pm_private_data; int i; /* Perform a full reset on the hardware */ pci_enable_device(chip->pci); snd_nm256_init_chip(chip); /* restore ac97 */ snd_ac97_resume(chip->ac97); for (i = 0; i < 2; i++) { nm256_stream_t *s = &chip->streams[i]; if (s->substream && s->suspended) { spin_lock_irq(&chip->reg_lock); snd_nm256_set_format(chip, s, s->substream); spin_unlock_irq(&chip->reg_lock); } } return 0;}#endif /* CONFIG_PM */static int snd_nm256_free(nm256_t *chip){ if (chip->streams[SNDRV_PCM_STREAM_PLAYBACK].running) snd_nm256_playback_stop(chip); if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running) snd_nm256_capture_stop(chip); if (chip->irq >= 0) synchronize_irq(chip->irq); if (chip->cport) iounmap(chip->cport); if (chip->buffer) iounmap(chip->buffer); release_and_free_resource(chip->res_cport); release_and_free_resource(chip->res_buffer); if (chip->irq >= 0) free_irq(chip->irq, (void*)chip); pci_disable_device(chip->pci); kfree(chip); return 0;}static int snd_nm256_dev_free(snd_device_t *device){ nm256_t *chip = device->device_data; return snd_nm256_free(chip);}static int __devinitsnd_nm256_create(snd_card_t *card, struct pci_dev *pci, int play_bufsize, int capt_bufsize, int force_load, u32 buffertop, int usecache, nm256_t **chip_ret){ nm256_t *chip; int err, pval; static snd_device_ops_t ops = { .dev_free = snd_nm256_dev_free, }; u32 addr; *chip_ret = NULL; if ((err = pci_enable_device(pci)) < 0) return err; chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (chip == NULL) { pci_disable_device(pci); return -ENOMEM; } chip->card = card; chip->pci = pci; chip->use_cache = usecache; spin_lock_init(&chip->reg_lock); chip->irq = -1; init_MUTEX(&chip->irq_mutex); chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize = play_bufsize; chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize = capt_bufsize; /* * The NM256 has two memory ports. The first port is nothing * more than a chunk of video RAM, which is used as the I/O ring * buffer. The second port has the actual juicy stuff (like the * mixer and the playback engine control registers). */ chip->buffer_addr = pci_resource_start(pci, 0); chip->cport_addr = pci_resource_start(pci, 1); /* Init the memory port info. */ /* remap control port (#2) */ chip->res_cport = request_mem_region(chip->cport_addr, NM_PORT2_SIZE, card->driver); if (chip->res_cport == NULL) { snd_printk(KERN_ERR "memory region 0x%lx (size 0x%x) busy\n", chip->cport_addr, NM_PORT2_SIZE); err = -EBUSY; goto __error; } chip->cport = ioremap_nocache(chip->cport_addr, NM_PORT2_SIZE); if (chip->cport == NULL) { snd_printk(KERN_ERR "unable to map control port %lx\n", chip->cport_addr); err = -ENOMEM; goto __error; } if (!strcmp(card->driver, "NM256AV")) { /* Ok, try to see if this is a non-AC97 version of the hardware. */ pval = snd_nm256_readw(chip, NM_MIXER_PRESENCE); if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) { if (! force_load) { printk(KERN_ERR "nm256: no ac97 is found!\n"); printk(KERN_ERR " force the driver to load by passing in the module parameter\n"); printk(KERN_ERR " force_ac97=1\n"); printk(KERN_ERR " or try sb16 or cs423x drivers instead.\n"); err = -ENXIO; goto __error; } } chip->buffer_end = 2560 * 1024; chip->interrupt = snd_nm256_interrupt; chip->mixer_status_offset = NM_MIXER_STATUS_OFFSET; chip->mixer_status_mask = NM_MIXER_READY_MASK; } else { /* Not sure if there is any relevant detect for the ZX or not. */ if (snd_nm256_readb(chip, 0xa0b) != 0) chip->buffer_end = 6144 * 1024; else chip->buffer_end = 4096 * 1024; chip->interrupt = snd_nm256_interrupt_zx; chip->mixer_status_offset = NM2_MIXER_STATUS_OFFSET; chip->mixer_status_mask = NM2_MIXER_READY_MASK; } chip->buffer_size = chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize + chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize; if (chip->use_cache) chip->buffer_size += NM_TOTAL_COEFF_COUNT * 4; else chip->buffer_size += NM_MAX_PLAYBACK_COEF_SIZE + NM_MAX_RECORD_COEF_SIZE; if (buffertop >= chip->buffer_size && buffertop < chip->buffer_end) chip->buffer_end = buffertop; else { /* get buffer end pointer from signature */ if ((err = snd_nm256_peek_for_sig(chip)) < 0) goto __error; } chip->buffer_start = chip->buffer_end - chip->buffer_size; chip->buffer_addr += chip->buffer_start; printk(KERN_INFO "nm256: Mapping port 1 from 0x%x - 0x%x\n", chip->buffer_start, chip->buffer_end); chip->res_buffer = request_mem_region(chip->buffer_addr, chip->buffer_size, card->driver); if (chip->res_buffer == NULL) { snd_printk(KERN_ERR "nm256: buffer 0x%lx (size 0x%x) busy\n", chip->buffer_addr, chip->buffer_size); err = -EBUSY; goto __error; } chip->buffer = ioremap_nocache(chip->buffer_addr, chip->buffer_size); if (chip->buffer == NULL) { err = -ENOMEM; snd_printk(KERN_ERR "unable to map ring buffer at %lx\n", chip->buffer_addr); goto __error; } /* set offsets */ addr = chip->buffer_start; chip->streams[SNDRV_PCM_STREAM_PLAYBACK].buf = addr; addr += chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize; chip->streams[SNDRV_PCM_STREAM_CAPTURE].buf = addr; addr += chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize; if (chip->use_cache) { chip->all_coeff_buf = addr; } else { chip->coeff_buf[SNDRV_PCM_STREAM_PLAYBACK] = addr; addr += NM_MAX_PLAYBACK_COEF_SIZE; chip->coeff_buf[SNDRV_PCM_STREAM_CAPTURE] = addr; } /* Fixed setting. */ chip->mixer_base = NM_MIXER_OFFSET; chip->coeffs_current = 0; snd_nm256_init_chip(chip); // pci_set_master(pci); /* needed? */ snd_card_set_pm_callback(card, nm256_suspend, nm256_resume, chip); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) goto __error; snd_card_set_dev(card, &pci->dev); *chip_ret = chip; return 0;__error: snd_nm256_free(chip); return err;}struct nm256_quirk { unsigned short vendor; unsigned short device; int type;};enum { NM_BLACKLISTED, NM_RESET_WORKAROUND, NM_RESET_WORKAROUND_2 };static struct nm256_quirk nm256_quirks[] __devinitdata = { /* HP omnibook 4150 has cs4232 codec internally */ { .vendor = 0x103c, .device = 0x0007, .type = NM_BLACKLISTED }, /* Sony PCG-F305 */ { .vendor = 0x104d, .device = 0x8041, .type = NM_RESET_WORKAROUND }, /* Dell Latitude LS */ { .vendor = 0x1028, .device = 0x0080, .type = NM_RESET_WORKAROUND }, /* Dell Latitude CSx */ { .vendor = 0x1028, .device = 0x0091, .type = NM_RESET_WORKAROUND_2 }, { } /* terminator */};static int __devinit snd_nm256_probe(struct pci_dev *pci, const struct pci_device_id *pci_id){ snd_card_t *card; nm256_t *chip; int err; unsigned int xbuffer_top; struct nm256_quirk *q; u16 subsystem_vendor, subsystem_device; pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor); pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device); for (q = nm256_quirks; q->vendor; q++) { if (q->vendor == subsystem_vendor && q->device == subsystem_device) { switch (q->type) { case NM_BLACKLISTED: printk(KERN_INFO "nm256: The device is blacklisted. Loading stopped\n"); return -ENODEV; case NM_RESET_WORKAROUND_2: reset_workaround_2 = 1; /* Fall-through */ case NM_RESET_WORKAROUND: reset_workaround = 1; break; } } } card = snd_card_new(index, id, THIS_MODULE, 0); if (card == NULL) return -ENOMEM; switch (pci->device) { case PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO: strcpy(card->driver, "NM256AV"); break; case PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO: strcpy(card->driver, "NM256ZX"); break; case PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO: strcpy(card->driver, "NM256XL+"); break; default: snd_printk(KERN_ERR "invalid device id 0x%x\n", pci->device); snd_card_free(card); return -EINVAL; } if (vaio_hack) xbuffer_top = 0x25a800; /* this avoids conflicts with XFree86 server */ else xbuffer_top = buffer_top; if (playback_bufsize < 4) playback_bufsize = 4; if (playback_bufsize > 128) playback_bufsize = 128; if (capture_bufsize < 4) capture_bufsize = 4; if (capture_bufsize > 128) capture_bufsize = 128; if ((err = snd_nm256_create(card, pci, playback_bufsize * 1024, /* in bytes */ capture_bufsize * 1024, /* in bytes */ force_ac97, xbuffer_top, use_cache, &chip)) < 0) { snd_card_free(card); return err; } if (reset_workaround) { snd_printdd(KERN_INFO "nm256: reset_workaround activated\n"); chip->reset_workaround = 1; } if (reset_workaround_2) { snd_printdd(KERN_INFO "nm256: reset_workaround_2 activated\n"); chip->reset_workaround_2 = 1; } if ((err = snd_nm256_pcm(chip, 0)) < 0 || (err = snd_nm256_mixer(chip)) < 0) { snd_card_free(card); return err; } sprintf(card->shortname, "NeoMagic %s", card->driver); sprintf(card->longname, "%s at 0x%lx & 0x%lx, irq %d", card->shortname, chip->buffer_addr, chip->cport_addr, chip->irq); if ((err = snd_card_register(card)) < 0) { snd_card_free(card); return err; } pci_set_drvdata(pci, card); return 0;}static void __devexit snd_nm256_remove(struct pci_dev *pci){ snd_card_free(pci_get_drvdata(pci)); pci_set_drvdata(pci, NULL);}static struct pci_driver driver = { .name = "NeoMagic 256", .id_table = snd_nm256_ids, .probe = snd_nm256_probe, .remove = __devexit_p(snd_nm256_remove), SND_PCI_PM_CALLBACKS};static int __init alsa_card_nm256_init(void){ return pci_register_driver(&driver);}static void __exit alsa_card_nm256_exit(void){ pci_unregister_driver(&driver);}module_init(alsa_card_nm256_init)module_exit(alsa_card_nm256_exit)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -