📄 nec_vrc5477.c
字号:
int ret=0; for (list = devs.next; ; list = list->next) { if (list == &devs) return -ENODEV; s = list_entry(list, struct vrc5477_ac97_state, devs); if (!((s->dev_audio ^ minor) & ~0xf)) break; } file->private_data = s; /* wait for device to become free */ down(&s->open_sem); while (s->open_mode & file->f_mode) { if (file->f_flags & O_NONBLOCK) { up(&s->open_sem); return -EBUSY; } add_wait_queue(&s->open_wait, &wait); __set_current_state(TASK_INTERRUPTIBLE); up(&s->open_sem); schedule(); remove_wait_queue(&s->open_wait, &wait); set_current_state(TASK_RUNNING); if (signal_pending(current)) return -ERESTARTSYS; down(&s->open_sem); } spin_lock_irqsave(&s->lock, flags); if (file->f_mode & FMODE_READ) { /* set default settings */ set_adc_rate(s, 48000); s->adcChannels = 2; ret = prog_dmabuf_adc(s); if (ret) goto bailout; } if (file->f_mode & FMODE_WRITE) { /* set default settings */ set_dac_rate(s, 48000); s->dacChannels = 2; ret = prog_dmabuf_dac(s); if (ret) goto bailout; } s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); bailout: spin_unlock_irqrestore(&s->lock, flags); up(&s->open_sem); return ret;}static int vrc5477_ac97_release(struct inode *inode, struct file *file){ struct vrc5477_ac97_state *s = (struct vrc5477_ac97_state *)file->private_data; lock_kernel(); if (file->f_mode & FMODE_WRITE) drain_dac(s, file->f_flags & O_NONBLOCK); down(&s->open_sem); if (file->f_mode & FMODE_WRITE) { stop_dac(s); dealloc_dmabuf(s, &s->dma_dac); } if (file->f_mode & FMODE_READ) { stop_adc(s); dealloc_dmabuf(s, &s->dma_adc); } s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); up(&s->open_sem); wake_up(&s->open_wait); unlock_kernel(); return 0;}static /*const*/ struct file_operations vrc5477_ac97_audio_fops = { owner: THIS_MODULE, llseek: vrc5477_ac97_llseek, read: vrc5477_ac97_read, write: vrc5477_ac97_write, poll: vrc5477_ac97_poll, ioctl: vrc5477_ac97_ioctl, // mmap: vrc5477_ac97_mmap, open: vrc5477_ac97_open, release: vrc5477_ac97_release,};/* --------------------------------------------------------------------- *//* --------------------------------------------------------------------- *//* * for debugging purposes, we'll create a proc device that dumps the * CODEC chipstate */#ifdef CONFIG_LL_DEBUGstruct { const char *regname; unsigned regaddr;} vrc5477_ac97_regs[] = { {"VRC5477_INT_STATUS", VRC5477_INT_STATUS}, {"VRC5477_CODEC_WR", VRC5477_CODEC_WR}, {"VRC5477_CODEC_RD", VRC5477_CODEC_RD}, {"VRC5477_CTRL", VRC5477_CTRL}, {"VRC5477_ACLINK_CTRL", VRC5477_ACLINK_CTRL}, {"VRC5477_INT_MASK", VRC5477_INT_MASK}, {"VRC5477_DAC1_CTRL", VRC5477_DAC1_CTRL}, {"VRC5477_DAC1L", VRC5477_DAC1L}, {"VRC5477_DAC1_BADDR", VRC5477_DAC1_BADDR}, {"VRC5477_DAC2_CTRL", VRC5477_DAC2_CTRL}, {"VRC5477_DAC2L", VRC5477_DAC2L}, {"VRC5477_DAC2_BADDR", VRC5477_DAC2_BADDR}, {"VRC5477_DAC3_CTRL", VRC5477_DAC3_CTRL}, {"VRC5477_DAC3L", VRC5477_DAC3L}, {"VRC5477_DAC3_BADDR", VRC5477_DAC3_BADDR}, {"VRC5477_ADC1_CTRL", VRC5477_ADC1_CTRL}, {"VRC5477_ADC1L", VRC5477_ADC1L}, {"VRC5477_ADC1_BADDR", VRC5477_ADC1_BADDR}, {"VRC5477_ADC2_CTRL", VRC5477_ADC2_CTRL}, {"VRC5477_ADC2L", VRC5477_ADC2L}, {"VRC5477_ADC2_BADDR", VRC5477_ADC2_BADDR}, {"VRC5477_ADC3_CTRL", VRC5477_ADC3_CTRL}, {"VRC5477_ADC3L", VRC5477_ADC3L}, {"VRC5477_ADC3_BADDR", VRC5477_ADC3_BADDR}, {NULL, 0x0}};static int proc_vrc5477_ac97_dump (char *buf, char **start, off_t fpos, int length, int *eof, void *data){ struct vrc5477_ac97_state *s; int cnt, len = 0; if (list_empty(&devs)) return 0; s = list_entry(devs.next, struct vrc5477_ac97_state, devs); /* print out header */ len += sprintf(buf + len, "\n\t\tVrc5477 Audio Debug\n\n"); // print out digital controller state len += sprintf (buf + len, "NEC Vrc5477 Audio Controller registers\n"); len += sprintf (buf + len, "---------------------------------\n"); for (cnt=0; vrc5477_ac97_regs[cnt].regname != NULL; cnt++) { len+= sprintf (buf + len, "%-20s = %08x\n", vrc5477_ac97_regs[cnt].regname, inl(s->io + vrc5477_ac97_regs[cnt].regaddr)); } /* print out driver state */ len += sprintf (buf + len, "NEC Vrc5477 Audio driver states\n"); len += sprintf (buf + len, "---------------------------------\n"); len += sprintf (buf + len, "dacChannels = %d\n", s->dacChannels); len += sprintf (buf + len, "adcChannels = %d\n", s->adcChannels); len += sprintf (buf + len, "dacRate = %d\n", s->dacRate); len += sprintf (buf + len, "adcRate = %d\n", s->adcRate); len += sprintf (buf + len, "dma_dac is %s ready\n", s->dma_dac.ready? "" : "not"); if (s->dma_dac.ready) { len += sprintf (buf + len, "dma_dac is %s stopped.\n", s->dma_dac.stopped? "" : "not"); len += sprintf (buf + len, "dma_dac.fragSize = %x\n", s->dma_dac.fragSize); len += sprintf (buf + len, "dma_dac.fragShift = %x\n", s->dma_dac.fragShift); len += sprintf (buf + len, "dma_dac.numFrag = %x\n", s->dma_dac.numFrag); len += sprintf (buf + len, "dma_dac.fragTotalSize = %x\n", s->dma_dac.fragTotalSize); len += sprintf (buf + len, "dma_dac.nextIn = %x\n", s->dma_dac.nextIn); len += sprintf (buf + len, "dma_dac.nextOut = %x\n", s->dma_dac.nextOut); len += sprintf (buf + len, "dma_dac.count = %x\n", s->dma_dac.count); } len += sprintf (buf + len, "dma_adc is %s ready\n", s->dma_adc.ready? "" : "not"); if (s->dma_adc.ready) { len += sprintf (buf + len, "dma_adc is %s stopped.\n", s->dma_adc.stopped? "" : "not"); len += sprintf (buf + len, "dma_adc.fragSize = %x\n", s->dma_adc.fragSize); len += sprintf (buf + len, "dma_adc.fragShift = %x\n", s->dma_adc.fragShift); len += sprintf (buf + len, "dma_adc.numFrag = %x\n", s->dma_adc.numFrag); len += sprintf (buf + len, "dma_adc.fragTotalSize = %x\n", s->dma_adc.fragTotalSize); len += sprintf (buf + len, "dma_adc.nextIn = %x\n", s->dma_adc.nextIn); len += sprintf (buf + len, "dma_adc.nextOut = %x\n", s->dma_adc.nextOut); len += sprintf (buf + len, "dma_adc.count = %x\n", s->dma_adc.count); } /* print out CODEC state */ len += sprintf (buf + len, "\nAC97 CODEC registers\n"); len += sprintf (buf + len, "----------------------\n"); for (cnt=0; cnt <= 0x7e; cnt = cnt +2) len+= sprintf (buf + len, "reg %02x = %04x\n", cnt, rdcodec(&s->codec, cnt)); if (fpos >=len){ *start = buf; *eof =1; return 0; } *start = buf + fpos; if ((len -= fpos) > length) return length; *eof =1; return len;}#endif /* CONFIG_LL_DEBUG *//* --------------------------------------------------------------------- *//* maximum number of devices; only used for command line params */#define NR_DEVICE 5static unsigned int devindex = 0;MODULE_AUTHOR("Monta Vista Software, jsun@mvista.com or jsun@junsun.net");MODULE_DESCRIPTION("NEC Vrc5477 audio (AC97) Driver");MODULE_LICENSE("GPL");/* --------------------------------------------------------------------- */extern void jsun_scan_pci_bus(void);extern void vrc5477_show_pci_regs(void);extern void vrc5477_show_pdar_regs(void);/* -------------------------------------------------------- */#define AC97_BASE 0xbb000000#define myinl(x) *(volatile u32*)(AC97_BASE + (x))#define myoutl(x,y) *(volatile u32*)(AC97_BASE + (y)) = (x)u16 myrdcodec(u8 addr){ u32 result; /* wait until we can access codec registers */ // while (inl(VRC5477_CODEC_WR) & 0x80000000); /* write the address and "read" command to codec */ addr = addr & 0x7f; myoutl((addr << 16) | VRC5477_CODEC_WR_RWC, VRC5477_CODEC_WR); /* get the return result */ udelay(100); /* workaround hardware bug */ // dump_memory(0xbb000000, 48); while ( ((result=myinl(VRC5477_CODEC_RD)) & 0xc0000000) != 0xc0000000); MIPS_ASSERT(addr == ((result >> 16) & 0x7f) ); return result & 0xffff;}void mywrcodec(u8 addr, u16 data){ /* wait until we can access codec registers */ while (myinl(VRC5477_CODEC_WR) & 0x80000000); /* write the address and value to codec */ myoutl((addr << 16) | data, VRC5477_CODEC_WR);}void jsun_ac97_test(struct vrc5477_ac97_state *s){ int i; /* reset codec */ /* wrcodec(&s->codec, 0, 0); while (inl(s->io + VRC5477_CODEC_WR) & 0x80000000); */ mywrcodec(0, 0); while (myinl(VRC5477_CODEC_WR) & 0x80000000); for (i=0; i< 0x40; i+=4) { MIPS_ASSERT(inl(s->io+i) == myinl(i)); } printk("codec registers : "); for (i=0; i<= 0x3a; i+=2) { if ( (i%0x10) == 0) { printk("\n%02x\t", i); } // printk("%04x\t", rdcodec(&s->codec, i)); printk("%04x\t", myrdcodec(i)); } printk("\n\n"); printk("codec registers : "); for (i=0; i<= 0x3a; i+=2) { if ( (i%0x10) == 0) { printk("\n%02x\t", i); } printk("%04x\t", rdcodec(&s->codec, i)); } printk("\n\n");}static int __devinit vrc5477_ac97_probe(struct pci_dev *pcidev, const struct pci_device_id *pciid){ struct vrc5477_ac97_state *s; char proc_str[80]; MIPS_DEBUG(printk("vrc5477_ac97_probe() invoked\n")); if (pcidev->irq == 0) return -1; if (!(s = kmalloc(sizeof(struct vrc5477_ac97_state), GFP_KERNEL))) { printk(KERN_ERR PFX "alloc of device struct failed\n"); return -1; } memset(s, 0, sizeof(struct vrc5477_ac97_state)); init_waitqueue_head(&s->dma_adc.wait); init_waitqueue_head(&s->dma_dac.wait); init_waitqueue_head(&s->open_wait); init_MUTEX(&s->open_sem); spin_lock_init(&s->lock); s->dev = pcidev; s->io = pci_resource_start(pcidev, 0); s->irq = pcidev->irq; s->codec.private_data = s; s->codec.id = 0; s->codec.codec_read = rdcodec; s->codec.codec_write = wrcodec; s->codec.codec_wait = waitcodec; /* setting some other default values such as * adcChannels, adcRate is done in open() so that * no persistent state across file opens. */ if (!request_region(s->io, pci_resource_len(pcidev,0), VRC5477_AC97_MODULE_NAME)) { printk(KERN_ERR PFX "io ports %#lx->%#lx in use\n", s->io, s->io + pci_resource_len(pcidev,0)-1); goto err_region; } if (request_irq(s->irq, vrc5477_ac97_interrupt, SA_INTERRUPT, VRC5477_AC97_MODULE_NAME, s)) { printk(KERN_ERR PFX "irq %u in use\n", s->irq); goto err_irq; } printk(KERN_INFO PFX "IO at %#lx, IRQ %d\n", s->io, s->irq); /* register devices */ if ((s->dev_audio = register_sound_dsp(&vrc5477_ac97_audio_fops, -1)) < 0) goto err_dev1; if ((s->codec.dev_mixer = register_sound_mixer(&vrc5477_ac97_mixer_fops, -1)) < 0) goto err_dev2;#ifdef CONFIG_LL_DEBUG /* intialize the debug proc device */ s->ps = create_proc_read_entry(VRC5477_AC97_MODULE_NAME, 0, NULL, proc_vrc5477_ac97_dump, NULL);#endif /* CONFIG_LL_DEBUG */ /* enable pci io and bus mastering */ if (pci_enable_device(pcidev)) goto err_dev3; pci_set_master(pcidev);/*jsun_scan_pci_bus();vrc5477_show_pci_regs();vrc5477_show_pdar_regs();*/ /* cold reset the AC97 */ outl(VRC5477_ACLINK_CTRL_RST_ON | VRC5477_ACLINK_CTRL_RST_TIME, s->io + VRC5477_ACLINK_CTRL); while (inl(s->io + VRC5477_ACLINK_CTRL) & VRC5477_ACLINK_CTRL_RST_ON);/*jsun_ac97_test(s);*/ /* codec init */ if (!ac97_probe_codec(&s->codec)) goto err_dev3;#ifdef CONFIG_LL_DEBUG sprintf(proc_str, "driver/%s/%d/ac97", VRC5477_AC97_MODULE_NAME, s->codec.id); s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL, ac97_read_proc, &s->codec); /* TODO : why this proc file does not show up? */#endif /* let us get the default volumne louder */ wrcodec(&s->codec, 0x2, 0); wrcodec(&s->codec, 0x18, 0x0707); /* mute line in loopback to line out */ wrcodec(&s->codec, 0x10, 0x8000); /* by default we select line in the input */ wrcodec(&s->codec, 0x1a, 0x0404); /* pick middle value for record gain */ // wrcodec(&s->codec, 0x1c, 0x0707); wrcodec(&s->codec, 0x1c, 0x0f0f); wrcodec(&s->codec, 0x1e, 0x07); /* enable the master interrupt but disable all others */ outl(VRC5477_INT_MASK_NMASK, s->io + VRC5477_INT_MASK); /* store it in the driver field */ pci_set_drvdata(pcidev, s); pcidev->dma_mask = 0xffffffff; /* put it into driver list */ list_add_tail(&s->devs, &devs); /* increment devindex */ if (devindex < NR_DEVICE-1) devindex++; return 0; err_dev3: unregister_sound_mixer(s->codec.dev_mixer); err_dev2: unregister_sound_dsp(s->dev_audio); err_dev1: printk(KERN_ERR PFX "cannot register misc device\n"); free_irq(s->irq, s); err_irq: release_region(s->io, pci_resource_len(pcidev,0)); err_region: kfree(s); return -1;}static void __devinit vrc5477_ac97_remove(struct pci_dev *dev){ struct vrc5477_ac97_state *s = pci_get_drvdata(dev); if (!s) return; list_del(&s->devs);#ifdef CONFIG_LL_DEBUG if (s->ps) remove_proc_entry(VRC5477_AC97_MODULE_NAME, NULL);#endif /* CONFIG_LL_DEBUG */ synchronize_irq(); free_irq(s->irq, s); release_region(s->io, pci_resource_len(dev,0)); unregister_sound_dsp(s->dev_audio); unregister_sound_mixer(s->codec.dev_mixer); kfree(s); pci_set_drvdata(dev, NULL);}#define PCI_VENDOR_ID_NEC 0x1033#define PCI_DEVICE_ID_NEC_VRC5477_AC97 0x00A6static struct pci_device_id id_table[] __devinitdata = { { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_VRC5477_AC97, PCI_ANY_ID, PCI_ANY_ID, 0, 0 }, { 0, }};MODULE_DEVICE_TABLE(pci, id_table);static struct pci_driver vrc5477_ac97_driver = { name: VRC5477_AC97_MODULE_NAME, id_table: id_table, probe: vrc5477_ac97_probe, remove: vrc5477_ac97_remove};static int __init init_vrc5477_ac97(void){ if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; printk("Vrc5477 AC97 driver: version v0.1 time " __TIME__ " " __DATE__ " by Jun Sun\n"); return pci_module_init(&vrc5477_ac97_driver);}static void __exit cleanup_vrc5477_ac97(void){ printk(KERN_INFO PFX "unloading\n"); pci_unregister_driver(&vrc5477_ac97_driver);}module_init(init_vrc5477_ac97);module_exit(cleanup_vrc5477_ac97);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -