📄 ad1889.c
字号:
buffer += cnt; ret += cnt; /* we have something to play - go play it! */ ad1889_trigger_playback(dev); }err2: remove_wait_queue(&state->dmabuf.wait, &wait);err1: up(&state->sem); return ret;}static unsigned int ad1889_poll(struct file *file, struct poll_table_struct *wait){ unsigned int mask = 0;#if 0 ad1889_dev_t *dev = (ad1889_dev_t *)file->private_data; ad1889_state_t *state = NULL; struct dmabuf *dmabuf; unsigned long flags; if (!(file->f_mode & (FMODE_READ | FMODE_WRITE))) return -EINVAL; if (file->f_mode & FMODE_WRITE) { state = &dev->state[AD_WAV_STATE]; if (!state) return 0; dmabuf = &state->dmabuf; poll_wait(file, &dmabuf->wait, wait); } if (file->f_mode & FMODE_READ) { state = &dev->state[AD_ADC_STATE]; if (!state) return 0; dmabuf = &state->dmabuf; poll_wait(file, &dmabuf->wait, wait); } spin_lock_irqsave(&dev->lock, flags); ad1889_update_ptr(dev, 0); if (file->f_mode & FMODE_WRITE) { state = &dev->state[WAV_STATE]; dmabuf = &state->dmabuf; if (dmabuf->mapped) { if (dmabuf->count >= (int)dmabuf->fragsize) mask |= POLLOUT | POLLWRNORM; } else { if ((int)dmabuf->dmasize >= dmabuf->count + (int)dmabuf->fragsize) mask |= POLLOUT | POLLWRNORM; } } if (file ->f_mode & FMODE_READ) { state = &dev->state[AD_ADC_STATE]; dmabuf = &state->dmabuf; if (dmabuf->count >= (int)dmabuf->fragsize) mask |= POLLIN | POLLRDNORM; } spin_unlock_irqrestore(&dev->lock, flags);#endif return mask;}static int ad1889_mmap(struct file *file, struct vm_area_struct *vma){ return 0;}static int ad1889_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ int val = 0; ad1889_dev_t *dev = (ad1889_dev_t *)file->private_data; struct dmabuf *dmabuf; audio_buf_info abinfo; int __user *p = (int __user *)arg; DBG("ad1889_ioctl cmd 0x%x arg %lu\n", cmd, arg); switch (cmd) { case OSS_GETVERSION: return put_user(SOUND_VERSION, p); case SNDCTL_DSP_RESET: break; case SNDCTL_DSP_SYNC: break; case SNDCTL_DSP_SPEED: /* set sampling rate */ if (get_user(val, p)) return -EFAULT; if (val > 5400 && val < 48000) { if (file->f_mode & FMODE_WRITE) AD1889_WRITEW(ad1889_dev, AD_DSWAS, val); if (file->f_mode & FMODE_READ) AD1889_WRITEW(ad1889_dev, AD_DSRES, val); } return 0; case SNDCTL_DSP_STEREO: /* undocumented? */ if (get_user(val, p)) return -EFAULT; if (file->f_mode & FMODE_READ) { val = AD1889_READW(ad1889_dev, AD_DSWSMC); if (val) { val |= 0x0200; /* set WAST */ } else { val &= ~0x0200; /* clear WAST */ } AD1889_WRITEW(ad1889_dev, AD_DSWSMC, val); } if (file->f_mode & FMODE_WRITE) { val = AD1889_READW(ad1889_dev, AD_DSRAMC); if (val) { val |= 0x0002; /* set ADST */ } else { val &= ~0x0002; /* clear ADST */ } AD1889_WRITEW(ad1889_dev, AD_DSRAMC, val); } return 0; case SNDCTL_DSP_GETBLKSIZE: return put_user(DMA_SIZE, p); case SNDCTL_DSP_GETFMTS: return put_user(AFMT_S16_LE|AFMT_U8, p); case SNDCTL_DSP_SETFMT: if (get_user(val, p)) return -EFAULT; if (val == 0) { if (file->f_mode & FMODE_READ) ad1889_set_adc_fmt(dev, val); if (file->f_mode & FMODE_WRITE) ad1889_set_wav_fmt(dev, val); } else { val = AFMT_S16_LE | AFMT_U8; } return put_user(val, p); case SNDCTL_DSP_CHANNELS: break; case SNDCTL_DSP_POST: /* send all data to device */ break; case SNDCTL_DSP_SUBDIVIDE: break; case SNDCTL_DSP_SETFRAGMENT: /* not supported; uses fixed fragment sizes */ return put_user(DMA_SIZE, p); case SNDCTL_DSP_GETOSPACE: case SNDCTL_DSP_GETISPACE: /* space left in dma buffers */ if (cmd == SNDCTL_DSP_GETOSPACE) dmabuf = &dev->state[AD_WAV_STATE].dmabuf; else dmabuf = &dev->state[AD_ADC_STATE].dmabuf; abinfo.fragments = 1; abinfo.fragstotal = 1; abinfo.fragsize = DMA_SIZE; abinfo.bytes = DMA_SIZE; return copy_to_user(p, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; case SNDCTL_DSP_NONBLOCK: file->f_flags |= O_NONBLOCK; return 0; case SNDCTL_DSP_GETCAPS: return put_user(0, p); case SNDCTL_DSP_GETTRIGGER: case SNDCTL_DSP_SETTRIGGER: break; case SNDCTL_DSP_GETIPTR: case SNDCTL_DSP_GETOPTR: break; case SNDCTL_DSP_SETDUPLEX: break; case SNDCTL_DSP_GETODELAY: break; case SOUND_PCM_READ_RATE: return put_user(AD1889_READW(ad1889_dev, AD_DSWAS), p); case SOUND_PCM_READ_CHANNELS: case SOUND_PCM_READ_BITS: break; case SNDCTL_DSP_MAPINBUF: case SNDCTL_DSP_MAPOUTBUF: case SNDCTL_DSP_SETSYNCRO: case SOUND_PCM_WRITE_FILTER: case SOUND_PCM_READ_FILTER: break; default: break; } return -ENOTTY;}static int ad1889_open(struct inode *inode, struct file *file){ /* check minor; only support /dev/dsp atm */ if (iminor(inode) != 3) return -ENXIO; file->private_data = ad1889_dev; ad1889_set_wav_rate(ad1889_dev, 48000); ad1889_set_wav_fmt(ad1889_dev, AFMT_S16_LE); AD1889_WRITEW(ad1889_dev, AD_DSWADA, 0x0404); /* attenuation */ return nonseekable_open(inode, file);}static int ad1889_release(struct inode *inode, struct file *file){ /* if we have state free it here */ return 0;}static struct file_operations ad1889_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = ad1889_read, .write = ad1889_write, .poll = ad1889_poll, .ioctl = ad1889_ioctl, .mmap = ad1889_mmap, .open = ad1889_open, .release = ad1889_release,};/************************* /dev/mixer interfaces ************************ */static int ad1889_mixer_open(struct inode *inode, struct file *file){ if (ad1889_dev->ac97_codec->dev_mixer != iminor(inode)) return -ENODEV; file->private_data = ad1889_dev->ac97_codec; return 0;}static int ad1889_mixer_release(struct inode *inode, struct file *file){ return 0;}static int ad1889_mixer_ioctl(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 struct file_operations ad1889_mixer_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .ioctl = ad1889_mixer_ioctl, .open = ad1889_mixer_open, .release = ad1889_mixer_release,};/************************* AC97 interfaces ****************************** */static void ad1889_codec_write(struct ac97_codec *ac97, u8 reg, u16 val){ ad1889_dev_t *dev = ac97->private_data; //DBG("Writing 0x%x to 0x%lx\n", val, dev->regbase + 0x100 + reg); AD1889_WRITEW(dev, 0x100 + reg, val);}static u16 ad1889_codec_read(struct ac97_codec *ac97, u8 reg){ ad1889_dev_t *dev = ac97->private_data; //DBG("Reading from 0x%lx\n", dev->regbase + 0x100 + reg); return AD1889_READW(dev, 0x100 + reg);} static int ad1889_ac97_init(ad1889_dev_t *dev, int id){ struct ac97_codec *ac97; u16 eid; if ((ac97 = ac97_alloc_codec()) == NULL) return -ENOMEM; ac97->private_data = dev; ac97->id = id; ac97->codec_read = ad1889_codec_read; ac97->codec_write = ad1889_codec_write; if (ac97_probe_codec(ac97) == 0) { printk(DEVNAME ": ac97_probe_codec failed\n"); goto out_free; } eid = ad1889_codec_read(ac97, AC97_EXTENDED_ID); if (eid == 0xffff) { printk(KERN_WARNING DEVNAME ": no codec attached?\n"); goto out_free; } dev->ac97_features = eid; if ((ac97->dev_mixer = register_sound_mixer(&ad1889_mixer_fops, -1)) < 0) { printk(KERN_ERR DEVNAME ": cannot register mixer\n"); goto out_free; } dev->ac97_codec = ac97; return 0;out_free: ac97_release_codec(ac97); return -ENODEV;}static int ad1889_aclink_reset(struct pci_dev * pcidev){ u16 stat; int retry = 200; ad1889_dev_t *dev = pci_get_drvdata(pcidev); AD1889_WRITEW(dev, AD_DSCCS, 0x8000); /* turn on clock */ AD1889_READW(dev, AD_DSCCS); WAIT_10MS(); stat = AD1889_READW(dev, AD_ACIC); stat |= 0x0002; /* Reset Disable */ AD1889_WRITEW(dev, AD_ACIC, stat); (void) AD1889_READW(dev, AD_ACIC); /* flush posted write */ udelay(10); stat = AD1889_READW(dev, AD_ACIC); stat |= 0x0001; /* Interface Enable */ AD1889_WRITEW(dev, AD_ACIC, stat); do { if (AD1889_READW(dev, AD_ACIC) & 0x8000) /* Ready */ break; WAIT_10MS(); retry--; } while (retry > 0); if (!retry) { printk(KERN_ERR "ad1889_aclink_reset: codec is not ready [0x%x]\n", AD1889_READW(dev, AD_ACIC)); return -EBUSY; } /* TODO reset AC97 codec */ /* TODO set wave/adc pci ctrl status */ stat = AD1889_READW(dev, AD_ACIC); stat |= 0x0004; /* Audio Stream Output Enable */ AD1889_WRITEW(dev, AD_ACIC, stat); return 0;}/************************* PCI interfaces ****************************** *//* PCI device table */static struct pci_device_id ad1889_id_tbl[] = { { PCI_VENDOR_ID_ANALOG_DEVICES, PCI_DEVICE_ID_AD1889JS, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)DEVNAME }, { },};MODULE_DEVICE_TABLE(pci, ad1889_id_tbl);static irqreturn_t ad1889_interrupt(int irq, void *dev_id, struct pt_regs *regs){ u32 stat; ad1889_dev_t *dev = (ad1889_dev_t *)dev_id; stat = AD1889_READL(dev, AD_DMADISR); /* clear ISR */ AD1889_WRITEL(dev, AD_DMADISR, stat); if (stat & 0x8) { /* WAVI */ DBG("WAV interrupt\n"); dev->stats.wav_intrs++; if (dev->state[AD_WAV_STATE].dmabuf.ready) { ad1889_stop_wav(&dev->state[AD_WAV_STATE]); /* clean up */ ad1889_start_wav(&dev->state[AD_WAV_STATE]); /* start new */ } } if ((stat & 0x2) && dev->state[AD_ADC_STATE].dmabuf.ready) { /* ADCI */ DBG("ADC interrupt\n"); dev->stats.adc_intrs++; } if(stat) return IRQ_HANDLED; return IRQ_NONE;}static void ad1889_initcfg(ad1889_dev_t *dev){ u16 tmp16; u32 tmp32; /* make sure the interrupt bits are setup the way we want */ tmp32 = AD1889_READL(dev, AD_DMAWAVCTRL); tmp32 &= ~0xff; /* flat dma, no sg, mask out the intr bits */ tmp32 |= 0x6; /* intr on count, loop */ AD1889_WRITEL(dev, AD_DMAWAVCTRL, tmp32); /* unmute... */ tmp16 = AD1889_READW(dev, AD_DSWADA); tmp16 &= ~0x8080; AD1889_WRITEW(dev, AD_DSWADA, tmp16);}static int __devinit ad1889_probe(struct pci_dev *pcidev, const struct pci_device_id *ent){ int err; ad1889_dev_t *dev; unsigned long bar; struct proc_dir_entry *proc_root = NULL; if ((err = pci_enable_device(pcidev)) != 0) { printk(KERN_ERR DEVNAME ": pci_enable_device failed\n"); return err; } pci_set_master(pcidev); if ((dev = ad1889_alloc_dev(pcidev)) == NULL) { printk(KERN_ERR DEVNAME ": cannot allocate memory for device\n"); return -ENOMEM; } pci_set_drvdata(pcidev, dev); bar = pci_resource_start(pcidev, 0); if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_MEM)) { printk(KERN_ERR DEVNAME ": memory region not assigned\n"); goto err_free_mem; } if (request_irq(pcidev->irq, ad1889_interrupt, SA_SHIRQ, DEVNAME, dev) != 0) { printk(KERN_ERR DEVNAME ": unable to request interrupt\n"); goto err_free_mem; } request_mem_region(bar, AD_DSIOMEMSIZE, DEVNAME); dev->regbase = (unsigned long)ioremap_nocache(bar, AD_DSIOMEMSIZE); printk(KERN_INFO DEVNAME ": %s at 0x%lx IRQ %d\n", (char *)ent->driver_data, dev->regbase, pcidev->irq); if (ad1889_aclink_reset(pcidev) != 0) goto err_free_mem; /* register /dev/dsp */ if ((dev->dev_audio = register_sound_dsp(&ad1889_fops, -1)) < 0) { printk(KERN_ERR DEVNAME ": cannot register /dev/dsp\n"); goto err_free_irq; } if ((err = ad1889_ac97_init(dev, 0)) != 0) goto err_free_dsp; if (((proc_root = proc_mkdir("driver/ad1889", NULL)) == NULL) || create_proc_read_entry("ac97", S_IFREG|S_IRUGO, proc_root, ac97_read_proc, dev->ac97_codec) == NULL || create_proc_read_entry("info", S_IFREG|S_IRUGO, proc_root, ad1889_read_proc, dev) == NULL) goto err_free_dsp; ad1889_initcfg(dev); //DBG(DEVNAME ": Driver initialization done!\n"); ad1889_dev = dev; return 0;err_free_dsp: unregister_sound_dsp(dev->dev_audio);err_free_irq: free_irq(pcidev->irq, dev);err_free_mem: ad1889_free_dev(dev); pci_set_drvdata(pcidev, NULL); return -ENODEV;}static void __devexit ad1889_remove(struct pci_dev *pcidev){ ad1889_dev_t *dev = pci_get_drvdata(pcidev); if (dev == NULL) return; unregister_sound_mixer(dev->ac97_codec->dev_mixer); unregister_sound_dsp(dev->dev_audio); free_irq(pcidev->irq, dev); release_mem_region(dev->regbase, AD_DSIOMEMSIZE); /* any hw programming needed? */ ad1889_free_dev(dev);}MODULE_AUTHOR("Randolph Chung");MODULE_DESCRIPTION("Analog Devices AD1889 PCI Audio");MODULE_LICENSE("GPL");static struct pci_driver ad1889_driver = { .name = DEVNAME, .id_table = ad1889_id_tbl, .probe = ad1889_probe, .remove = __devexit_p(ad1889_remove),};static int __init ad1889_init_module(void){ return pci_module_init(&ad1889_driver);}static void ad1889_exit_module(void){ pci_unregister_driver(&ad1889_driver); return;}module_init(ad1889_init_module);module_exit(ad1889_exit_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -