au1x00.c
来自「linux 内核源代码」· C语言 代码 · 共 694 行 · 第 1/2 页
C
694 行
struct audio_stream *stream = substream->private_data; au1000_release_dma_link(stream); return snd_pcm_lib_free_pages(substream);}static intsnd_au1000_playback_prepare(struct snd_pcm_substream *substream){ struct snd_au1000 *au1000 = substream->pcm->private_data; struct snd_pcm_runtime *runtime = substream->runtime; if (runtime->channels == 1) au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_4); else au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4); snd_ac97_set_rate(au1000->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); return 0;}static intsnd_au1000_capture_prepare(struct snd_pcm_substream *substream){ struct snd_au1000 *au1000 = substream->pcm->private_data; struct snd_pcm_runtime *runtime = substream->runtime; if (runtime->channels == 1) au1000_set_ac97_recv_slots(au1000, AC97_SLOT_4); else au1000_set_ac97_recv_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4); snd_ac97_set_rate(au1000->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); return 0;}static intsnd_au1000_trigger(struct snd_pcm_substream *substream, int cmd){ struct audio_stream *stream = substream->private_data; int err = 0; spin_lock(&stream->dma_lock); switch (cmd) { case SNDRV_PCM_TRIGGER_START: au1000_dma_start(stream); break; case SNDRV_PCM_TRIGGER_STOP: au1000_dma_stop(stream); break; default: err = -EINVAL; break; } spin_unlock(&stream->dma_lock); return err;}static snd_pcm_uframes_tsnd_au1000_pointer(struct snd_pcm_substream *substream){ struct audio_stream *stream = substream->private_data; struct snd_pcm_runtime *runtime = substream->runtime; long location; spin_lock(&stream->dma_lock); location = get_dma_residue(stream->dma); spin_unlock(&stream->dma_lock); location = stream->buffer->relative_end - location; if (location == -1) location = 0; return bytes_to_frames(runtime,location);}static struct snd_pcm_ops snd_card_au1000_playback_ops = { .open = snd_au1000_playback_open, .close = snd_au1000_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_au1000_hw_params, .hw_free = snd_au1000_hw_free, .prepare = snd_au1000_playback_prepare, .trigger = snd_au1000_trigger, .pointer = snd_au1000_pointer,};static struct snd_pcm_ops snd_card_au1000_capture_ops = { .open = snd_au1000_capture_open, .close = snd_au1000_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_au1000_hw_params, .hw_free = snd_au1000_hw_free, .prepare = snd_au1000_capture_prepare, .trigger = snd_au1000_trigger, .pointer = snd_au1000_pointer,};static int __devinitsnd_au1000_pcm_new(struct snd_au1000 *au1000){ struct snd_pcm *pcm; int err; unsigned long flags; if ((err = snd_pcm_new(au1000->card, "AU1000 AC97 PCM", 0, 1, 1, &pcm)) < 0) return err; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_KERNEL), 128*1024, 128*1024); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_au1000_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_au1000_capture_ops); pcm->private_data = au1000; pcm->info_flags = 0; strcpy(pcm->name, "Au1000 AC97 PCM"); spin_lock_init(&au1000->stream[PLAYBACK]->dma_lock); spin_lock_init(&au1000->stream[CAPTURE]->dma_lock); flags = claim_dma_lock(); if ((au1000->stream[PLAYBACK]->dma = request_au1000_dma(DMA_ID_AC97C_TX, "AC97 TX", au1000_dma_interrupt, IRQF_DISABLED, au1000->stream[PLAYBACK])) < 0) { release_dma_lock(flags); return -EBUSY; } if ((au1000->stream[CAPTURE]->dma = request_au1000_dma(DMA_ID_AC97C_RX, "AC97 RX", au1000_dma_interrupt, IRQF_DISABLED, au1000->stream[CAPTURE])) < 0){ release_dma_lock(flags); return -EBUSY; } /* enable DMA coherency in read/write DMA channels */ set_dma_mode(au1000->stream[PLAYBACK]->dma, get_dma_mode(au1000->stream[PLAYBACK]->dma) & ~DMA_NC); set_dma_mode(au1000->stream[CAPTURE]->dma, get_dma_mode(au1000->stream[CAPTURE]->dma) & ~DMA_NC); release_dma_lock(flags); au1000->pcm = pcm; return 0;}/*-------------------------- AC97 CODEC Control ------------------------------*/static unsigned shortsnd_au1000_ac97_read(struct snd_ac97 *ac97, unsigned short reg){ struct snd_au1000 *au1000 = ac97->private_data; u32 volatile cmd; u16 volatile data; int i; spin_lock(&au1000->ac97_lock);/* would rather use the interrupt than this polling but it works and I can'tget the interrupt driven case to work efficiently */ for (i = 0; i < 0x5000; i++) if (!(au1000->ac97_ioport->status & AC97C_CP)) break; if (i == 0x5000) printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n"); cmd = (u32) reg & AC97C_INDEX_MASK; cmd |= AC97C_READ; au1000->ac97_ioport->cmd = cmd; /* now wait for the data */ for (i = 0; i < 0x5000; i++) if (!(au1000->ac97_ioport->status & AC97C_CP)) break; if (i == 0x5000) { printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n"); return 0; } data = au1000->ac97_ioport->cmd & 0xffff; spin_unlock(&au1000->ac97_lock); return data;}static voidsnd_au1000_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val){ struct snd_au1000 *au1000 = ac97->private_data; u32 cmd; int i; spin_lock(&au1000->ac97_lock);/* would rather use the interrupt than this polling but it works and I can'tget the interrupt driven case to work efficiently */ for (i = 0; i < 0x5000; i++) if (!(au1000->ac97_ioport->status & AC97C_CP)) break; if (i == 0x5000) printk(KERN_ERR "au1000 AC97: AC97 command write timeout\n"); cmd = (u32) reg & AC97C_INDEX_MASK; cmd &= ~AC97C_READ; cmd |= ((u32) val << AC97C_WD_BIT); au1000->ac97_ioport->cmd = cmd; spin_unlock(&au1000->ac97_lock);}static int __devinitsnd_au1000_ac97_new(struct snd_au1000 *au1000){ int err; struct snd_ac97_bus *pbus; struct snd_ac97_template ac97; static struct snd_ac97_bus_ops ops = { .write = snd_au1000_ac97_write, .read = snd_au1000_ac97_read, }; if ((au1000->ac97_res_port = request_mem_region(CPHYSADDR(AC97C_CONFIG), 0x100000, "Au1x00 AC97")) == NULL) { snd_printk(KERN_ERR "ALSA AC97: can't grap AC97 port\n"); return -EBUSY; } au1000->ac97_ioport = (struct au1000_ac97_reg *) KSEG1ADDR(au1000->ac97_res_port->start); spin_lock_init(&au1000->ac97_lock); /* configure pins for AC'97 TODO: move to board_setup.c */ au_writel(au_readl(SYS_PINFUNC) & ~0x02, SYS_PINFUNC); /* Initialise Au1000's AC'97 Control Block */ au1000->ac97_ioport->cntrl = AC97C_RS | AC97C_CE; udelay(10); au1000->ac97_ioport->cntrl = AC97C_CE; udelay(10); /* Initialise External CODEC -- cold reset */ au1000->ac97_ioport->config = AC97C_RESET; udelay(10); au1000->ac97_ioport->config = 0x0; mdelay(5); /* Initialise AC97 middle-layer */ if ((err = snd_ac97_bus(au1000->card, 0, &ops, au1000, &pbus)) < 0) return err; memset(&ac97, 0, sizeof(ac97)); ac97.private_data = au1000; if ((err = snd_ac97_mixer(pbus, &ac97, &au1000->ac97)) < 0) return err; return 0;}/*------------------------------ Setup / Destroy ----------------------------*/voidsnd_au1000_free(struct snd_card *card){ struct snd_au1000 *au1000 = card->private_data; if (au1000->ac97_res_port) { /* put internal AC97 block into reset */ au1000->ac97_ioport->cntrl = AC97C_RS; au1000->ac97_ioport = NULL; release_and_free_resource(au1000->ac97_res_port); } if (au1000->stream[PLAYBACK]) { if (au1000->stream[PLAYBACK]->dma >= 0) free_au1000_dma(au1000->stream[PLAYBACK]->dma); kfree(au1000->stream[PLAYBACK]); } if (au1000->stream[CAPTURE]) { if (au1000->stream[CAPTURE]->dma >= 0) free_au1000_dma(au1000->stream[CAPTURE]->dma); kfree(au1000->stream[CAPTURE]); }}static struct snd_card *au1000_card;static int __initau1000_init(void){ int err; struct snd_card *card; struct snd_au1000 *au1000; card = snd_card_new(-1, "AC97", THIS_MODULE, sizeof(struct snd_au1000)); if (card == NULL) return -ENOMEM; card->private_free = snd_au1000_free; au1000 = card->private_data; au1000->card = card; au1000->stream[PLAYBACK] = kmalloc(sizeof(struct audio_stream), GFP_KERNEL); au1000->stream[CAPTURE ] = kmalloc(sizeof(struct audio_stream), GFP_KERNEL); /* so that snd_au1000_free will work as intended */ au1000->ac97_res_port = NULL; if (au1000->stream[PLAYBACK]) au1000->stream[PLAYBACK]->dma = -1; if (au1000->stream[CAPTURE ]) au1000->stream[CAPTURE ]->dma = -1; if (au1000->stream[PLAYBACK] == NULL || au1000->stream[CAPTURE ] == NULL) { snd_card_free(card); return -ENOMEM; } if ((err = snd_au1000_ac97_new(au1000)) < 0 ) { snd_card_free(card); return err; } if ((err = snd_au1000_pcm_new(au1000)) < 0) { snd_card_free(card); return err; } strcpy(card->driver, "Au1000-AC97"); strcpy(card->shortname, "AMD Au1000-AC97"); sprintf(card->longname, "AMD Au1000--AC97 ALSA Driver"); if ((err = snd_card_register(card)) < 0) { snd_card_free(card); return err; } printk( KERN_INFO "ALSA AC97: Driver Initialized\n" ); au1000_card = card; return 0;}static void __exit au1000_exit(void){ snd_card_free(au1000_card);}module_init(au1000_init);module_exit(au1000_exit);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?