?? ymfpci.c
字號:
} return -ENOTTY;}/* * open(2) * We use upper part of the minor to distinguish between soundcards. * Channels are opened with a clone open. */static int ymf_open(struct inode *inode, struct file *file){ struct list_head *list; ymfpci_t *unit = NULL; int minor; struct ymf_state *state; int err; minor = MINOR(inode->i_rdev); if ((minor & 0x0F) == 3) { /* /dev/dspN */ ; } else { return -ENXIO; } unit = NULL; /* gcc warns */ list_for_each(list, &ymf_devs) { unit = list_entry(list, ymfpci_t, ymf_devs); if (((unit->dev_audio ^ minor) & ~0x0F) == 0) break; } if (list == &ymf_devs) return -ENODEV; down(&unit->open_sem); if ((state = ymf_state_alloc(unit)) == NULL) { up(&unit->open_sem); return -ENOMEM; } list_add_tail(&state->chain, &unit->states); file->private_data = state; /* * ymf_read and ymf_write that we borrowed from cs46xx * allocate buffers with prog_dmabuf(). We call prog_dmabuf * here so that in case of DMA memory exhaustion open * fails rather than write. * * XXX prog_dmabuf allocates voice. Should allocate explicitly, above. */ if (file->f_mode & FMODE_WRITE) { if (!state->wpcm.dmabuf.ready) { if ((err = prog_dmabuf(state, 0)) != 0) { goto out_nodma; } } } if (file->f_mode & FMODE_READ) { if (!state->rpcm.dmabuf.ready) { if ((err = prog_dmabuf(state, 1)) != 0) { goto out_nodma; } } }#if 0 /* test if interrupts work */ ymfpci_writew(unit, YDSXGR_TIMERCOUNT, 0xfffe); /* ~ 680ms */ ymfpci_writeb(unit, YDSXGR_TIMERCTRL, (YDSXGR_TIMERCTRL_TEN|YDSXGR_TIMERCTRL_TIEN));#endif up(&unit->open_sem); return 0;out_nodma: /* * XXX Broken custom: "goto out_xxx" in other place is * a nestable exception, but here it is not nestable due to semaphore. * XXX Doubtful technique of self-describing objects.... */ dealloc_dmabuf(&state->wpcm.dmabuf); dealloc_dmabuf(&state->rpcm.dmabuf); ymf_pcm_free_substream(&state->wpcm); ymf_pcm_free_substream(&state->rpcm); list_del(&state->chain); kfree(state); up(&unit->open_sem); return err;}static int ymf_release(struct inode *inode, struct file *file){ struct ymf_state *state = (struct ymf_state *)file->private_data; ymfpci_t *unit = state->unit;#if 0 /* test if interrupts work */ ymfpci_writeb(unit, YDSXGR_TIMERCTRL, 0);#endif down(&unit->open_sem); /* * XXX Solve the case of O_NONBLOCK close - don't deallocate here. * Deallocate when unloading the driver and we can wait. */ ymf_wait_dac(state); ymf_stop_adc(state); /* fortunately, it's immediate */ dealloc_dmabuf(&state->wpcm.dmabuf); dealloc_dmabuf(&state->rpcm.dmabuf); ymf_pcm_free_substream(&state->wpcm); ymf_pcm_free_substream(&state->rpcm); list_del(&state->chain); file->private_data = NULL; /* Can you tell I programmed Solaris */ kfree(state); up(&unit->open_sem); return 0;}/* * Mixer operations are based on cs46xx. */static int ymf_open_mixdev(struct inode *inode, struct file *file){ int minor = MINOR(inode->i_rdev); struct list_head *list; ymfpci_t *unit; int i; list_for_each(list, &ymf_devs) { unit = list_entry(list, ymfpci_t, ymf_devs); for (i = 0; i < NR_AC97; i++) { if (unit->ac97_codec[i] != NULL && unit->ac97_codec[i]->dev_mixer == minor) { goto match; } } } return -ENODEV; match: file->private_data = unit->ac97_codec[i]; return 0;}static int ymf_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct ac97_codec *codec = (struct ac97_codec *)file->private_data; return codec->mixer_ioctl(codec, cmd, arg);}static int ymf_release_mixdev(struct inode *inode, struct file *file){ return 0;}static /*const*/ struct file_operations ymf_fops = { owner: THIS_MODULE, llseek: no_llseek, read: ymf_read, write: ymf_write, poll: ymf_poll, ioctl: ymf_ioctl, mmap: ymf_mmap, open: ymf_open, release: ymf_release,};static /*const*/ struct file_operations ymf_mixer_fops = { owner: THIS_MODULE, llseek: no_llseek, ioctl: ymf_ioctl_mixdev, open: ymf_open_mixdev, release: ymf_release_mixdev,};/* */static int ymf_suspend(struct pci_dev *pcidev, u32 unused){ struct ymf_unit *unit = pci_get_drvdata(pcidev); unsigned long flags; struct ymf_dmabuf *dmabuf; struct list_head *p; struct ymf_state *state; struct ac97_codec *codec; int i; spin_lock_irqsave(&unit->reg_lock, flags); unit->suspended = 1; /* * XXX Talk to Kai to remove ac97_save_state before it's too late! * Other drivers call ac97_reset, which does not have * a save counterpart. Current ac97_save_state is empty. */ for (i = 0; i < NR_AC97; i++) { if ((codec = unit->ac97_codec[i]) != NULL) ac97_save_state(codec); } list_for_each(p, &unit->states) { state = list_entry(p, struct ymf_state, chain); dmabuf = &state->wpcm.dmabuf; dmabuf->hwptr = dmabuf->swptr = 0; dmabuf->total_bytes = 0; dmabuf->count = 0; dmabuf = &state->rpcm.dmabuf; dmabuf->hwptr = dmabuf->swptr = 0; dmabuf->total_bytes = 0; dmabuf->count = 0; } ymfpci_writel(unit, YDSXGR_NATIVEDACOUTVOL, 0); ymfpci_disable_dsp(unit); spin_unlock_irqrestore(&unit->reg_lock, flags); return 0;}static int ymf_resume(struct pci_dev *pcidev){ struct ymf_unit *unit = pci_get_drvdata(pcidev); unsigned long flags; struct list_head *p; struct ymf_state *state; struct ac97_codec *codec; int i; ymfpci_aclink_reset(unit->pci); ymfpci_codec_ready(unit, 0, 1); /* prints diag if not ready. */#ifdef CONFIG_SOUND_YMFPCI_LEGACY /* XXX At this time the legacy registers are probably deprogrammed. */#endif ymfpci_download_image(unit); ymf_memload(unit); spin_lock_irqsave(&unit->reg_lock, flags); if (unit->start_count) { ymfpci_writel(unit, YDSXGR_MODE, 3); unit->active_bank = ymfpci_readl(unit, YDSXGR_CTRLSELECT) & 1; } for (i = 0; i < NR_AC97; i++) { if ((codec = unit->ac97_codec[i]) != NULL) ac97_restore_state(codec); } unit->suspended = 0; list_for_each(p, &unit->states) { state = list_entry(p, struct ymf_state, chain); wake_up(&state->wpcm.dmabuf.wait); wake_up(&state->rpcm.dmabuf.wait); } spin_unlock_irqrestore(&unit->reg_lock, flags); return 0;}/* * initialization routines */#ifdef CONFIG_SOUND_YMFPCI_LEGACYstatic int ymfpci_setup_legacy(ymfpci_t *unit, struct pci_dev *pcidev){ int v; int mpuio = -1, oplio = -1; switch (unit->iomidi) { case 0x330: mpuio = 0; break; case 0x300: mpuio = 1; break; case 0x332: mpuio = 2; break; case 0x334: mpuio = 3; break; default: ; } switch (unit->iosynth) { case 0x388: oplio = 0; break; case 0x398: oplio = 1; break; case 0x3a0: oplio = 2; break; case 0x3a8: oplio = 3; break; default: ; } if (mpuio >= 0 || oplio >= 0) { /* 0x0020: 1 - 10 bits of I/O address decoded, 0 - 16 bits. */ v = 0x001e; pci_write_config_word(pcidev, PCIR_LEGCTRL, v); switch (pcidev->device) { case PCI_DEVICE_ID_YAMAHA_724: case PCI_DEVICE_ID_YAMAHA_740: case PCI_DEVICE_ID_YAMAHA_724F: case PCI_DEVICE_ID_YAMAHA_740C: v = 0x8800; if (mpuio >= 0) { v |= mpuio<<4; } if (oplio >= 0) { v |= oplio; } pci_write_config_word(pcidev, PCIR_ELEGCTRL, v); break; case PCI_DEVICE_ID_YAMAHA_744: case PCI_DEVICE_ID_YAMAHA_754: v = 0x8800; pci_write_config_word(pcidev, PCIR_ELEGCTRL, v); if (oplio >= 0) { pci_write_config_word(pcidev, PCIR_OPLADR, unit->iosynth); } if (mpuio >= 0) { pci_write_config_word(pcidev, PCIR_MPUADR, unit->iomidi); } break; default: printk(KERN_ERR "ymfpci: Unknown device ID: 0x%x\n", pcidev->device); return -EINVAL; } } return 0;}#endif /* CONFIG_SOUND_YMFPCI_LEGACY */static void ymfpci_aclink_reset(struct pci_dev * pci){ u8 cmd; /* * In the 744, 754 only 0x01 exists, 0x02 is undefined. * It does not seem to hurt to trip both regardless of revision. */ pci_read_config_byte(pci, PCIR_DSXGCTRL, &cmd); pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd & 0xfc); pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd | 0x03); pci_write_config_byte(pci, PCIR_DSXGCTRL, cmd & 0xfc); pci_write_config_word(pci, PCIR_DSXPWRCTRL1, 0); pci_write_config_word(pci, PCIR_DSXPWRCTRL2, 0);}static void ymfpci_enable_dsp(ymfpci_t *codec){ ymfpci_writel(codec, YDSXGR_CONFIG, 0x00000001);}static void ymfpci_disable_dsp(ymfpci_t *codec){ u32 val; int timeout = 1000; val = ymfpci_readl(codec, YDSXGR_CONFIG); if (val) ymfpci_writel(codec, YDSXGR_CONFIG, 0x00000000); while (timeout-- > 0) { val = ymfpci_readl(codec, YDSXGR_STATUS); if ((val & 0x00000002) == 0) break; }}#include "ymfpci_image.h"static void ymfpci_download_image(ymfpci_t *codec){ int i, ver_1e; u16 ctrl; ymfpci_writel(codec, YDSXGR_NATIVEDACOUTVOL, 0x00000000); ymfpci_disable_dsp(codec); ymfpci_writel(codec, YDSXGR_MODE, 0x00010000); ymfpci_writel(codec, YDSXGR_MODE, 0x00000000); ymfpci_writel(codec, YDSXGR_MAPOFREC, 0x00000000); ymfpci_writel(codec, YDSXGR_MAPOFEFFECT, 0x00000000); ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, 0x00000000); ymfpci_writel(codec, YDSXGR_RECCTRLBASE, 0x00000000); ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, 0x00000000); ctrl = ymfpci_readw(codec, YDSXGR_GLOBALCTRL); ymfpci_writew(codec, YDSXGR_GLOBALCTRL, ctrl & ~0x0007); /* setup DSP instruction code */ for (i = 0; i < YDSXG_DSPLENGTH / 4; i++) ymfpci_writel(codec, YDSXGR_DSPINSTRAM + (i << 2), DspInst[i]); switch (codec->pci->device) { case PCI_DEVICE_ID_YAMAHA_724F: case PCI_DEVICE_ID_YAMAHA_740C: case PCI_DEVICE_ID_YAMAHA_744: case PCI_DEVICE_ID_YAMAHA_754: ver_1e = 1; break; default: ver_1e = 0; } if (ver_1e) { /* setup control instruction code */ for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++) ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + (i << 2), CntrlInst1E[i]); } else { for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++) ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + (i << 2), CntrlInst[i]); } ymfpci_enable_dsp(codec); /* 0.02s sounds not too bad, we may do schedule_timeout() later. */ mdelay(20); /* seems we need some delay after downloading image.. */}static int ymfpci_memalloc(ymfpci_t *codec){ long size, playback_ctrl_size; int voice, bank; u8 *ptr; playback_ctrl_size = 4 + 4 * YDSXG_PLAYBACK_VOICES; codec->bank_size_playback = ymfpci_readl(codec, YDSXGR_PLAYCTRLSIZE) << 2; codec->bank_size_capture = ymfpci_readl(codec, YDSXGR_RECCTRLSIZE) << 2; codec->bank_size_effect = ymfpci_readl(codec, YDSXGR_EFFCTRLSIZE) << 2; codec->work_size = YDSXG_DEFAULT_WORK_SIZE; size = ((playback_ctrl_size + 0x00ff) & ~0x
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -