📄 ymfpci.c
字号:
spin_unlock_irqrestore(&state->card->lock, flags); return put_user(val, (int *)arg);#endif case SOUND_PCM_READ_RATE: return put_user(state->format.rate, (int *)arg); case SOUND_PCM_READ_CHANNELS: return put_user(state->format.voices, (int *)arg); case SOUND_PCM_READ_BITS: return put_user(AFMT_S16_LE, (int *)arg); case SNDCTL_DSP_MAPINBUF: case SNDCTL_DSP_MAPOUTBUF: case SNDCTL_DSP_SETSYNCRO: case SOUND_PCM_WRITE_FILTER: case SOUND_PCM_READ_FILTER: return -ENOTTY; default: /* * Some programs mix up audio devices and ioctls * or perhaps they expect "universal" ioctls, * for instance we get SNDCTL_TMR_CONTINUE here. * XXX Is there sound_generic_ioctl() around? */ } return -ENOTTY;}static int ymf_open(struct inode *inode, struct file *file){ struct list_head *list; ymfpci_t *unit; int minor; struct ymf_state *state; int nvirt; int err; /* * This is how we do it currently: only one channel is used * in every board, so that we could use several boards in one machine. * We waste 63 out of 64 playback slots, but so what. * OSS model is constructed for devices with single playback channel. */ minor = MINOR(inode->i_rdev); if ((minor & 0x0F) == 3) { /* /dev/dspN */ ; } else { return -ENXIO; } nvirt = 0; /* Such is the partitioning of minor */ for (list = ymf_devs.next; list != &ymf_devs; list = list->next) { 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 (unit->states[nvirt] != NULL) { up(&unit->open_sem); return -EBUSY; } if ((err = ymf_state_alloc(unit, nvirt)) != 0) { up(&unit->open_sem); return err; } state = unit->states[nvirt]; file->private_data = state; /* * XXX This ymf_playback_prepare is totally unneeded here. * The question is if we want to allow write to fail if * prog_dmabuf fails... Say, no memory in DMA zone? */ if ((err = ymf_playback_prepare(unit, state)) != 0) { /* XXX This recovery is ugly as hell. */ ymf_pcm_free_substream(&state->ypcm); unit->states[state->virt] = NULL; kfree(state); up(&unit->open_sem); return err; }#if 0 /* test if interrupts work */ ymfpci_writew(codec, YDSXGR_TIMERCOUNT, 0xfffe); /* ~ 680ms */ ymfpci_writeb(codec, YDSXGR_TIMERCTRL, (YDSXGR_TIMERCTRL_TEN|YDSXGR_TIMERCTRL_TIEN));#endif up(&unit->open_sem); /* XXX Is it correct to have MOD_INC_USE_COUNT outside of sem.? */ MOD_INC_USE_COUNT; return 0;}static int ymf_release(struct inode *inode, struct file *file){ struct ymf_state *state = (struct ymf_state *)file->private_data; ymfpci_t *codec = state->unit;#if 0 /* test if interrupts work */ ymfpci_writeb(codec, YDSXGR_TIMERCTRL, 0);#endif if (state != codec->states[state->virt]) { printk(KERN_ERR "ymfpci%d.%d: state mismatch\n", state->unit->dev_audio, state->virt); return -EIO; } down(&codec->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); dealloc_dmabuf(state); ymf_pcm_free_substream(&state->ypcm); codec->states[state->virt] = NULL; kfree(state); up(&codec->open_sem); MOD_DEC_USE_COUNT; return 0;}/* * Mixer operations are based on cs46xx. */static int ymf_open_mixdev(struct inode *inode, struct file *file){ int i; int minor = MINOR(inode->i_rdev); struct list_head *list; ymfpci_t *unit; for (list = ymf_devs.next; list != &ymf_devs; list = list->next) { 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]; MOD_INC_USE_COUNT; 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){ MOD_DEC_USE_COUNT; return 0;}static /*const*/ struct file_operations ymf_fops = { llseek: ymf_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 = { llseek: ymf_llseek, ioctl: ymf_ioctl_mixdev, open: ymf_open_mixdev, release: ymf_release_mixdev,};/* * initialization routines */static void ymfpci_aclink_reset(struct pci_dev * pci){ u8 cmd; pci_read_config_byte(pci, PCIR_DSXGCTRL, &cmd); if (cmd & 0x03) { 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); }}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; i++) ymfpci_writel(codec, YDSXGR_DSPINSTRAM + i, DspInst[i >> 2]); 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; i++) ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + i, CntrlInst1E[i >> 2]); } else { for (i = 0; i < YDSXG_CTRLLENGTH; i++) ymfpci_writel(codec, YDSXGR_CTRLINSTRAM + i, CntrlInst[i >> 2]); } ymfpci_enable_dsp(codec);}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) & ~0x00ff) + ((codec->bank_size_playback * 2 * YDSXG_PLAYBACK_VOICES + 0xff) & ~0xff) + ((codec->bank_size_capture * 2 * YDSXG_CAPTURE_VOICES + 0xff) & ~0xff) + ((codec->bank_size_effect * 2 * YDSXG_EFFECT_VOICES + 0xff) & ~0xff) + codec->work_size; ptr = (u8 *)kmalloc(size + 0x00ff, GFP_KERNEL); if (ptr == NULL) return -ENOMEM; codec->work_ptr = ptr; ptr += 0x00ff; (long)ptr &= ~0x00ff; codec->bank_base_playback = ptr; codec->ctrl_playback = (u32 *)ptr; codec->ctrl_playback[0] = YDSXG_PLAYBACK_VOICES; ptr += (playback_ctrl_size + 0x00ff) & ~0x00ff; for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) { for (bank = 0; bank < 2; bank++) { codec->bank_playback[voice][bank] = (ymfpci_playback_bank_t *)ptr; ptr += codec->bank_size_playback; } codec->voices[voice].number = voice; codec->voices[voice].bank = codec->bank_playback[voice][0]; } ptr += (codec->bank_size_playback + 0x00ff) & ~0x00ff; codec->bank_base_capture = ptr; for (voice = 0; voice < YDSXG_CAPTURE_VOICES; voice++) for (bank = 0; bank < 2; bank++) { codec->bank_capture[voice][bank] = (ymfpci_capture_bank_t *)ptr; ptr += codec->bank_size_capture; } ptr += (codec->bank_size_capture + 0x00ff) & ~0x00ff; codec->bank_base_effect = ptr; for (voice = 0; voice < YDSXG_EFFECT_VOICES; voice++) for (bank = 0; bank < 2; bank++) { codec->bank_effect[voice][bank] = (ymfpci_effect_bank_t *)ptr; ptr += codec->bank_size_effect; } ptr += (codec->bank_size_effect + 0x00ff) & ~0x00ff; codec->work_base = ptr; ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, virt_to_bus(codec->bank_base_playback)); ymfpci_writel(codec, YDSXGR_RECCTRLBASE, virt_to_bus(codec->bank_base_capture)); ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, virt_to_bus(codec->bank_base_effect)); ymfpci_writel(codec, YDSXGR_WORKBASE, virt_to_bus(codec->work_base)); ymfpci_writel(codec, YDSXGR_WORKSIZE, codec->work_size >> 2); /* S/PDIF output initialization */ ymfpci_writew(codec, YDSXGR_SPDIFOUTCTRL, 0); ymfpci_writew(codec, YDSXGR_SPDIFOUTSTATUS, SND_PCM_AES0_CON_EMPHASIS_NONE | (SND_PCM_AES1_CON_ORIGINAL << 8) | (SND_PCM_AES1_CON_PCM_CODER << 8)); /* S/PDIF input initialization */ ymfpci_writew(codec, YDSXGR_SPDIFINCTRL, 0); /* move this volume setup to mixer */ ymfpci_writel(codec, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff); ymfpci_writel(codec, YDSXGR_BUF441OUTVOL, 0x3fff3fff); ymfpci_writel(codec, YDSXGR_NATIVEADCINVOL, 0x3fff3fff); ymfpci_writel(codec, YDSXGR_NATIVEDACINVOL, 0x3fff3fff); return 0;}static void ymfpci_memfree(ymfpci_t *codec){ ymfpci_writel(codec, YDSXGR_PLAYCTRLBASE, 0); ymfpci_writel(codec, YDSXGR_RECCTRLBASE, 0); ymfpci_writel(codec, YDSXGR_EFFCTRLBASE, 0); ymfpci_writel(codec, YDSXGR_WORKBASE, 0); ymfpci_writel(codec, YDSXGR_WORKSIZE, 0); kfree(codec->work_ptr);}static int ymf_ac97_init(ymfpci_t *card, int num_ac97){ struct ac97_codec *codec; u16 eid; if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) return -ENOMEM; memset(codec, 0, sizeof(struct ac97_codec)); /* initialize some basic codec information, other fields will be filled in ac97_probe_codec */ codec->private_data = card; codec->id = num_ac97; codec->codec_read = ymfpci_codec_read; codec->codec_write = ymfpci_codec_write; if (ac97_probe_codec(codec) == 0) { printk("ymfpci: ac97_probe_codec failed\n"); goto out_kfree; } eid = ymfpci_codec_read(codec, AC97_EXTENDED_ID); if (eid==0xFFFFFF) { printk(KERN_WARNING "ymfpci: no codec attached ?\n"); goto out_kfree; } card->ac97_features = eid; if ((codec->dev_mixer = register_sound_mixer(&ymf_mixer_fops, -1)) < 0) { printk(KERN_ERR "ymfpci: couldn't register mixer!\n"); goto out_kfree; } card->ac97_codec[num_ac97] = codec; return 0; out_kfree: kfree(codec); return -ENODEV;}static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_device_id *ent){ u16 ctrl; ymfpci_t *codec; int err; if (pci_enable_device(pcidev) < 0) { printk(KERN_ERR "ymfpci: pci_enable_device failed\n"); return -ENODEV; } if ((codec = kmalloc(sizeof(ymfpci_t), GFP_KERNEL)) == NULL) { printk(KERN_ERR "ymfpci: no core\n"); return -ENOMEM; } memset(codec, 0, sizeof(*codec)); spin_lock_init(&codec->reg_lock); spin_lock_init(&codec->voice_lock); init_MUTEX(&codec->open_sem); codec->pci = pcidev; pci_read_config_byte(pcidev, PCI_REVISION_ID, &codec->rev); codec->reg_area_virt = ioremap(pci_resource_start(pcidev, 0), 0x8000); printk(KERN_INFO "ymfpci: %s at 0x%lx IRQ %d\n", (char *)ent->driver_data, pci_resource_start(pcidev, 0), pcidev->irq); ymfpci_aclink_reset(pcidev); if (ymfpci_codec_ready(codec, 0, 1) < 0) goto out_unmap; ymfpci_download_image(codec); udelay(100); /* seems we need some delay after downloading image.. */ if (ymfpci_memalloc(codec) < 0) goto out_disable_dsp; /* ymfpci_proc_init(card, codec); */ if (request_irq(pcidev->irq, ymf_interrupt, SA_SHIRQ, "ymfpci", codec) != 0) { printk(KERN_ERR "ymfpci%d: unable to request IRQ %d\n", codec->dev_audio, pcidev->irq); goto out
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -