📄 via82cxxx_audio.c
字号:
static int __init via_ac97_init (struct via_info *card){ int rc; u16 tmp16; DPRINTK ("ENTER\n"); assert (card != NULL); memset (&card->ac97, 0, sizeof (card->ac97)); card->ac97.private_data = card; card->ac97.codec_read = via_ac97_read_reg; card->ac97.codec_write = via_ac97_write_reg; card->ac97.codec_wait = via_ac97_codec_wait; card->ac97.dev_mixer = register_sound_mixer (&via_mixer_fops, -1); if (card->ac97.dev_mixer < 0) { printk (KERN_ERR PFX "unable to register AC97 mixer, aborting\n"); DPRINTK ("EXIT, returning -EIO\n"); return -EIO; } rc = via_ac97_reset (card); if (rc) { printk (KERN_ERR PFX "unable to reset AC97 codec, aborting\n"); goto err_out; } if (ac97_probe_codec (&card->ac97) == 0) { printk (KERN_ERR PFX "unable to probe AC97 codec, aborting\n"); rc = -EIO; goto err_out; } /* enable variable rate */ tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); /* * If we cannot enable VRA, we have a locked-rate codec. * We try again to enable VRA before assuming so, however. */ tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); if ((tmp16 & 1) == 0) { via_ac97_write_reg (&card->ac97, AC97_EXTENDED_STATUS, tmp16 | 1); tmp16 = via_ac97_read_reg (&card->ac97, AC97_EXTENDED_STATUS); if ((tmp16 & 1) == 0) { card->locked_rate = 1; printk (KERN_WARNING PFX "Codec rate locked at 48Khz\n"); } } DPRINTK ("EXIT, returning 0\n"); return 0;err_out: unregister_sound_mixer (card->ac97.dev_mixer); DPRINTK ("EXIT, returning %d\n", rc); return rc;}static void via_ac97_cleanup (struct via_info *card){ DPRINTK ("ENTER\n"); assert (card != NULL); assert (card->ac97.dev_mixer >= 0); unregister_sound_mixer (card->ac97.dev_mixer); DPRINTK ("EXIT\n");}/**************************************************************** * * Interrupt-related code * *//** * via_intr_channel - handle an interrupt for a single channel * @chan: handle interrupt for this channel * * This is the "meat" of the interrupt handler, * containing the actions taken each time an interrupt * occurs. All communication and coordination with * userspace takes place here. * * Locking: inside card->lock */static void via_intr_channel (struct via_channel *chan){ u8 status; int n; /* check pertinent bits of status register for action bits */ status = inb (chan->iobase) & (VIA_SGD_FLAG | VIA_SGD_EOL | VIA_SGD_STOPPED); if (!status) return; /* acknowledge any flagged bits ASAP */ outb (status, chan->iobase); if (!chan->sgtable) /* XXX: temporary solution */ return; /* grab current h/w ptr value */ n = atomic_read (&chan->hw_ptr); /* sanity check: make sure our h/w ptr doesn't have a weird value */ assert (n >= 0); assert (n < chan->frag_number); /* reset SGD data structure in memory to reflect a full buffer, * and advance the h/w ptr, wrapping around to zero if needed */ if (n == (chan->frag_number - 1)) { chan->sgtable[n].count = cpu_to_le32(chan->frag_size | VIA_EOL); atomic_set (&chan->hw_ptr, 0); } else { chan->sgtable[n].count = cpu_to_le32(chan->frag_size | VIA_FLAG); atomic_inc (&chan->hw_ptr); } /* accounting crap for SNDCTL_DSP_GETxPTR */ chan->n_irqs++; chan->bytes += chan->frag_size; if (chan->bytes < 0) /* handle overflow of 31-bit value */ chan->bytes = chan->frag_size; /* wake up anyone listening to see when interrupts occur */ if (waitqueue_active (&chan->wait)) wake_up_all (&chan->wait); DPRINTK ("%s intr, status=0x%02X, hwptr=0x%lX, chan->hw_ptr=%d\n", chan->name, status, (long) inl (chan->iobase + 0x04), atomic_read (&chan->hw_ptr)); /* all following checks only occur when not in mmap(2) mode */ if (chan->is_mapped) return; /* If we are recording, then n_frags represents the number * of fragments waiting to be handled by userspace. * If we are playback, then n_frags represents the number * of fragments remaining to be filled by userspace. * We increment here. If we reach max number of fragments, * this indicates an underrun/overrun. For this case under OSS, * we stop the record/playback process. */ if (atomic_read (&chan->n_frags) < chan->frag_number) atomic_inc (&chan->n_frags); assert (atomic_read (&chan->n_frags) <= chan->frag_number); if (atomic_read (&chan->n_frags) == chan->frag_number) { chan->is_active = 0; via_chan_stop (chan->iobase); } DPRINTK ("%s intr, channel n_frags == %d\n", chan->name, atomic_read (&chan->n_frags));}static void via_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct via_info *card = dev_id; u32 status32; /* to minimize interrupt sharing costs, we use the SGD status * shadow register to check the status of all inputs and * outputs with a single 32-bit bus read. If no interrupt * conditions are flagged, we exit immediately */ status32 = inl (card->baseaddr + VIA_BASE0_SGD_STATUS_SHADOW); if (!(status32 & VIA_INTR_MASK)) {#ifdef CONFIG_MIDI_VIA82CXXX if (card->midi_devc) uart401intr(irq, card->midi_devc, regs);#endif return; } DPRINTK ("intr, status32 == 0x%08X\n", status32); /* synchronize interrupt handling under SMP. this spinlock * goes away completely on UP */ spin_lock (&card->lock); if (status32 & VIA_INTR_OUT) via_intr_channel (&card->ch_out); if (status32 & VIA_INTR_IN) via_intr_channel (&card->ch_in); if (status32 & VIA_INTR_FM) via_intr_channel (&card->ch_fm); spin_unlock (&card->lock);}/** * via_interrupt_init - Initialize interrupt handling * @card: Private info for specified board * * Obtain and reserve IRQ for using in handling audio events. * Also, disable any IRQ-generating resources, to make sure * we don't get interrupts before we want them. */static int via_interrupt_init (struct via_info *card){ u8 tmp8; DPRINTK ("ENTER\n"); assert (card != NULL); assert (card->pdev != NULL); /* check for sane IRQ number. can this ever happen? */ if (card->pdev->irq < 2) { printk (KERN_ERR PFX "insane IRQ %d, aborting\n", card->pdev->irq); DPRINTK ("EXIT, returning -EIO\n"); return -EIO; } /* make sure FM irq is not routed to us */ pci_read_config_byte (card->pdev, VIA_FM_NMI_CTRL, &tmp8); if ((tmp8 & VIA_CR48_FM_TRAP_TO_NMI) == 0) { tmp8 |= VIA_CR48_FM_TRAP_TO_NMI; pci_write_config_byte (card->pdev, VIA_FM_NMI_CTRL, tmp8); } if (request_irq (card->pdev->irq, via_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) { printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n", card->pdev->irq); DPRINTK ("EXIT, returning -EBUSY\n"); return -EBUSY; } DPRINTK ("EXIT, returning 0\n"); return 0;}/**************************************************************** * * OSS DSP device * */static struct file_operations via_dsp_fops = { owner: THIS_MODULE, open: via_dsp_open, release: via_dsp_release, read: via_dsp_read, write: via_dsp_write, poll: via_dsp_poll, llseek: no_llseek, ioctl: via_dsp_ioctl, mmap: via_dsp_mmap,};static int __init via_dsp_init (struct via_info *card){ u8 tmp8; DPRINTK ("ENTER\n"); assert (card != NULL); /* turn off legacy features, if not already */ pci_read_config_byte (card->pdev, VIA_FUNC_ENABLE, &tmp8); if (tmp8 & (VIA_CR42_SB_ENABLE | VIA_CR42_FM_ENABLE)) { tmp8 &= ~(VIA_CR42_SB_ENABLE | VIA_CR42_FM_ENABLE); pci_write_config_byte (card->pdev, VIA_FUNC_ENABLE, tmp8); } via_stop_everything (card); card->dev_dsp = register_sound_dsp (&via_dsp_fops, -1); if (card->dev_dsp < 0) { DPRINTK ("EXIT, returning -ENODEV\n"); return -ENODEV; } DPRINTK ("EXIT, returning 0\n"); return 0;}static void via_dsp_cleanup (struct via_info *card){ DPRINTK ("ENTER\n"); assert (card != NULL); assert (card->dev_dsp >= 0); via_stop_everything (card); unregister_sound_dsp (card->dev_dsp); DPRINTK ("EXIT\n");}static struct page * via_mm_nopage (struct vm_area_struct * vma, unsigned long address, int write_access){ struct via_info *card = vma->vm_private_data; struct via_channel *chan = &card->ch_out; struct page *dmapage; unsigned long pgoff; int rd, wr; DPRINTK ("ENTER, start %lXh, ofs %lXh, pgoff %ld, addr %lXh, wr %d\n", vma->vm_start, address - vma->vm_start, (address - vma->vm_start) >> PAGE_SHIFT, address, write_access); if (address > vma->vm_end) { DPRINTK ("EXIT, returning NOPAGE_SIGBUS\n"); return NOPAGE_SIGBUS; /* Disallow mremap */ } if (!card) { DPRINTK ("EXIT, returning NOPAGE_OOM\n"); return NOPAGE_OOM; /* Nothing allocated */ } pgoff = vma->vm_pgoff + ((address - vma->vm_start) >> PAGE_SHIFT); rd = card->ch_in.is_mapped; wr = card->ch_out.is_mapped;#ifndef VIA_NDEBUG { unsigned long max_bufs = chan->frag_number; if (rd && wr) max_bufs *= 2; /* via_dsp_mmap() should ensure this */ assert (pgoff < max_bufs); }#endif /* if full-duplex (read+write) and we have two sets of bufs, * then the playback buffers come first, sez soundcard.c */ if (pgoff >= chan->page_number) { pgoff -= chan->page_number; chan = &card->ch_in; } else if (!wr) chan = &card->ch_in; assert ((((unsigned long)chan->pgtbl[pgoff].cpuaddr) % PAGE_SIZE) == 0); dmapage = virt_to_page (chan->pgtbl[pgoff].cpuaddr); DPRINTK ("EXIT, returning page %p for cpuaddr %lXh\n", dmapage, (unsigned long) chan->pgtbl[pgoff].cpuaddr); get_page (dmapage); return dmapage;}#ifndef VM_RESERVEDstatic int via_mm_swapout (struct page *page, struct file *filp){ return 0;}#endif /* VM_RESERVED */struct vm_operations_struct via_mm_ops = { nopage: via_mm_nopage,#ifndef VM_RESERVED swapout: via_mm_swapout,#endif};static int via_dsp_mmap(struct file *file, struct vm_area_struct *vma){ struct via_info *card; int nonblock = (file->f_flags & O_NONBLOCK); int rc = -EINVAL, rd=0, wr=0; unsigned long max_size, size, start, offset; assert (file != NULL); assert (vma != NULL); card = file->private_data; assert (card != NULL); DPRINTK ("ENTER, start %lXh, size %ld, pgoff %ld\n", vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_pgoff); max_size = 0; if (vma->vm_flags & VM_READ) { rd = 1; via_chan_set_buffering(card, &card->ch_in, -1); via_chan_buffer_init (card, &card->ch_in); max_size += card->ch_in.page_number << PAGE_SHIFT; } if (vma->vm_flags & VM_WRITE) { wr = 1; via_chan_set_buffering(card, &card->ch_out, -1); via_chan_buffer_init (card, &card->ch_out); max_size += card->ch_out.page_number << PAGE_SHIFT; } start = vma->vm_start; offset = (vma->vm_pgoff << PAGE_SHIFT); size = vma->vm_end - vma->vm_start; /* some basic size/offset sanity checks */ if (size > max_size) goto out; if (offset > max_size - size) goto out; rc = via_syscall_down (card, nonblock); if (rc) goto out; vma->vm_ops = &via_mm_ops; vma->vm_private_data = card;#ifdef VM_RESERVED vma->vm_flags |= VM_RESERVED;#endif if (rd) card->ch_in.is_mapped = 1; if (wr) card->ch_out.is_mapped = 1; up (&card->syscall_sem); rc = 0;out: DPRINTK ("EXIT, returning %d\n", rc); return rc;}static ssize_t via_dsp_do_read (struct via_info *card, char *userbuf, size_t count, int nonblock){ DECLARE_WAITQUEUE(wait, current); const char *orig_userbuf = userbuf; struct via_channel *chan = &card->ch_in; size_t size; int n, tmp; ssize_t ret = 0; /* if SGD has not yet been started, start it */ via_chan_maybe_start (chan);handle_one_block: /* just to be a nice neighbor */ /* Thomas Sailer: * But also to ourselves, release semaphore if we do so */ if (current->need_resched) { up(&card->syscall_sem); schedule (); ret = via_syscall_down (card, nonblock); if (ret) goto out; } /* grab current channel software pointer. In the case of * recording, this is pointing to the next buffer that * will receive data from the audio hardware. */ n = chan->sw_ptr; /* n_frags represents the number of fragments waiting * to be copied to userland. sleep until at least * one buffer has been read from the audio hardware. */ add_wait_queue(&chan->wait, &wait); for (;;) { __set_current_state(TASK_INTERRUPTIBLE); tmp = atomic_read (&chan->n_frags); assert (tmp >= 0); assert (tmp <= chan->frag_number); if (tmp) break; if (nonblock || !chan->is_active) { ret = -EAGAIN; break; } up(&card->syscall_sem); DPRINTK ("Sleeping on block %d\n", n); schedule(); ret = via_syscall_down (card, nonblock); if (ret) break; if (signal_pending (current)) { ret = -ERESTARTSYS; break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -