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 + -
显示快捷键?