⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 aaci.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	int ret;	/*	 * Add rule describing channel dependency.	 */	ret = snd_pcm_hw_rule_add(substream->runtime, 0,				  SNDRV_PCM_HW_PARAM_CHANNELS,				  aaci_rule_channels, aaci,				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);	if (ret)		return ret;	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {		ret = __aaci_pcm_open(aaci, substream, &aaci->playback);	} else {		ret = __aaci_pcm_open(aaci, substream, &aaci->capture);	}	return ret;}static int aaci_pcm_playback_hw_params(struct snd_pcm_substream *substream,				       struct snd_pcm_hw_params *params){	struct aaci *aaci = substream->private_data;	struct aaci_runtime *aacirun = substream->runtime->private_data;	unsigned int channels = params_channels(params);	int ret;	WARN_ON(channels >= ARRAY_SIZE(channels_to_txmask) ||		!channels_to_txmask[channels]);	ret = aaci_pcm_hw_params(substream, aacirun, params);	/*	 * Enable FIFO, compact mode, 16 bits per sample.	 * FIXME: double rate slots?	 */	if (ret >= 0) {		aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;		aacirun->cr |= channels_to_txmask[channels];		aacirun->fifosz	= aaci->fifosize * 4;		if (aacirun->cr & CR_COMPACT)			aacirun->fifosz >>= 1;	}	return ret;}static void aaci_pcm_playback_stop(struct aaci_runtime *aacirun){	u32 ie;	ie = readl(aacirun->base + AACI_IE);	ie &= ~(IE_URIE|IE_TXIE);	writel(ie, aacirun->base + AACI_IE);	aacirun->cr &= ~CR_EN;	aaci_chan_wait_ready(aacirun);	writel(aacirun->cr, aacirun->base + AACI_TXCR);}static void aaci_pcm_playback_start(struct aaci_runtime *aacirun){	u32 ie;	aaci_chan_wait_ready(aacirun);	aacirun->cr |= CR_EN;	ie = readl(aacirun->base + AACI_IE);	ie |= IE_URIE | IE_TXIE;	writel(ie, aacirun->base + AACI_IE);	writel(aacirun->cr, aacirun->base + AACI_TXCR);}static int aaci_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd){	struct aaci *aaci = substream->private_data;	struct aaci_runtime *aacirun = substream->runtime->private_data;	unsigned long flags;	int ret = 0;	spin_lock_irqsave(&aaci->lock, flags);	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:		aaci_pcm_playback_start(aacirun);		break;	case SNDRV_PCM_TRIGGER_RESUME:		aaci_pcm_playback_start(aacirun);		break;	case SNDRV_PCM_TRIGGER_STOP:		aaci_pcm_playback_stop(aacirun);		break;	case SNDRV_PCM_TRIGGER_SUSPEND:		aaci_pcm_playback_stop(aacirun);		break;	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:		break;	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:		break;	default:		ret = -EINVAL;	}	spin_unlock_irqrestore(&aaci->lock, flags);	return ret;}static struct snd_pcm_ops aaci_playback_ops = {	.open		= aaci_pcm_open,	.close		= aaci_pcm_close,	.ioctl		= snd_pcm_lib_ioctl,	.hw_params	= aaci_pcm_playback_hw_params,	.hw_free	= aaci_pcm_hw_free,	.prepare	= aaci_pcm_prepare,	.trigger	= aaci_pcm_playback_trigger,	.pointer	= aaci_pcm_pointer,	.mmap		= aaci_pcm_mmap,};static int aaci_pcm_capture_hw_params(struct snd_pcm_substream *substream,				      struct snd_pcm_hw_params *params){	struct aaci *aaci = substream->private_data;	struct aaci_runtime *aacirun = substream->runtime->private_data;	int ret;	ret = aaci_pcm_hw_params(substream, aacirun, params);	if (ret >= 0) {		aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;		/* Line in record: slot 3 and 4 */		aacirun->cr |= CR_SL3 | CR_SL4;		aacirun->fifosz = aaci->fifosize * 4;		if (aacirun->cr & CR_COMPACT)			aacirun->fifosz >>= 1;	}	return ret;}static void aaci_pcm_capture_stop(struct aaci_runtime *aacirun){	u32 ie;	aaci_chan_wait_ready(aacirun);	ie = readl(aacirun->base + AACI_IE);	ie &= ~(IE_ORIE | IE_RXIE);	writel(ie, aacirun->base+AACI_IE);	aacirun->cr &= ~CR_EN;	writel(aacirun->cr, aacirun->base + AACI_RXCR);}static void aaci_pcm_capture_start(struct aaci_runtime *aacirun){	u32 ie;	aaci_chan_wait_ready(aacirun);#ifdef DEBUG	/* RX Timeout value: bits 28:17 in RXCR */	aacirun->cr |= 0xf << 17;#endif	aacirun->cr |= CR_EN;	writel(aacirun->cr, aacirun->base + AACI_RXCR);	ie = readl(aacirun->base + AACI_IE);	ie |= IE_ORIE |IE_RXIE; // overrun and rx interrupt -- half full	writel(ie, aacirun->base + AACI_IE);}static int aaci_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd){	struct aaci *aaci = substream->private_data;	struct aaci_runtime *aacirun = substream->runtime->private_data;	unsigned long flags;	int ret = 0;	spin_lock_irqsave(&aaci->lock, flags);	switch (cmd) {	case SNDRV_PCM_TRIGGER_START:		aaci_pcm_capture_start(aacirun);		break;	case SNDRV_PCM_TRIGGER_RESUME:		aaci_pcm_capture_start(aacirun);		break;	case SNDRV_PCM_TRIGGER_STOP:		aaci_pcm_capture_stop(aacirun);		break;	case SNDRV_PCM_TRIGGER_SUSPEND:		aaci_pcm_capture_stop(aacirun);		break;	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:		break;	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:		break;	default:		ret = -EINVAL;	}	spin_unlock_irqrestore(&aaci->lock, flags);	return ret;}static int aaci_pcm_capture_prepare(struct snd_pcm_substream *substream){	struct snd_pcm_runtime *runtime = substream->runtime;	struct aaci *aaci = substream->private_data;	aaci_pcm_prepare(substream);	/* allow changing of sample rate */	aaci_ac97_write(aaci->ac97, AC97_EXTENDED_STATUS, 0x0001); /* VRA */	aaci_ac97_write(aaci->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);	aaci_ac97_write(aaci->ac97, AC97_PCM_MIC_ADC_RATE, runtime->rate);	/* Record select: Mic: 0, Aux: 3, Line: 4 */	aaci_ac97_write(aaci->ac97, AC97_REC_SEL, 0x0404);	return 0;}static struct snd_pcm_ops aaci_capture_ops = {	.open		= aaci_pcm_open,	.close		= aaci_pcm_close,	.ioctl		= snd_pcm_lib_ioctl,	.hw_params	= aaci_pcm_capture_hw_params,	.hw_free	= aaci_pcm_hw_free,	.prepare	= aaci_pcm_capture_prepare,	.trigger	= aaci_pcm_capture_trigger,	.pointer	= aaci_pcm_pointer,	.mmap		= aaci_pcm_mmap,};/* * Power Management. */#ifdef CONFIG_PMstatic int aaci_do_suspend(struct snd_card *card, unsigned int state){	struct aaci *aaci = card->private_data;	snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);	snd_pcm_suspend_all(aaci->pcm);	return 0;}static int aaci_do_resume(struct snd_card *card, unsigned int state){	snd_power_change_state(card, SNDRV_CTL_POWER_D0);	return 0;}static int aaci_suspend(struct amba_device *dev, pm_message_t state){	struct snd_card *card = amba_get_drvdata(dev);	return card ? aaci_do_suspend(card) : 0;}static int aaci_resume(struct amba_device *dev){	struct snd_card *card = amba_get_drvdata(dev);	return card ? aaci_do_resume(card) : 0;}#else#define aaci_do_suspend		NULL#define aaci_do_resume		NULL#define aaci_suspend		NULL#define aaci_resume		NULL#endifstatic struct ac97_pcm ac97_defs[] __devinitdata = {	[0] = {	/* Front PCM */		.exclusive = 1,		.r = {			[0] = {				.slots	= (1 << AC97_SLOT_PCM_LEFT) |					  (1 << AC97_SLOT_PCM_RIGHT) |					  (1 << AC97_SLOT_PCM_CENTER) |					  (1 << AC97_SLOT_PCM_SLEFT) |					  (1 << AC97_SLOT_PCM_SRIGHT) |					  (1 << AC97_SLOT_LFE),			},		},	},	[1] = {	/* PCM in */		.stream = 1,		.exclusive = 1,		.r = {			[0] = {				.slots	= (1 << AC97_SLOT_PCM_LEFT) |					  (1 << AC97_SLOT_PCM_RIGHT),			},		},	},	[2] = {	/* Mic in */		.stream = 1,		.exclusive = 1,		.r = {			[0] = {				.slots	= (1 << AC97_SLOT_MIC),			},		},	}};static struct snd_ac97_bus_ops aaci_bus_ops = {	.write	= aaci_ac97_write,	.read	= aaci_ac97_read,};static int __devinit aaci_probe_ac97(struct aaci *aaci){	struct snd_ac97_template ac97_template;	struct snd_ac97_bus *ac97_bus;	struct snd_ac97 *ac97;	int ret;	/*	 * Assert AACIRESET for 2us	 */	writel(0, aaci->base + AACI_RESET);	udelay(2);	writel(RESET_NRST, aaci->base + AACI_RESET);	/*	 * Give the AC'97 codec more than enough time	 * to wake up. (42us = ~2 frames at 48kHz.)	 */	udelay(42);	ret = snd_ac97_bus(aaci->card, 0, &aaci_bus_ops, aaci, &ac97_bus);	if (ret)		goto out;	ac97_bus->clock = 48000;	aaci->ac97_bus = ac97_bus;	memset(&ac97_template, 0, sizeof(struct snd_ac97_template));	ac97_template.private_data = aaci;	ac97_template.num = 0;	ac97_template.scaps = AC97_SCAP_SKIP_MODEM;	ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97);	if (ret)		goto out;	aaci->ac97 = ac97;	/*	 * Disable AC97 PC Beep input on audio codecs.	 */	if (ac97_is_audio(ac97))		snd_ac97_write_cache(ac97, AC97_PC_BEEP, 0x801e);	ret = snd_ac97_pcm_assign(ac97_bus, ARRAY_SIZE(ac97_defs), ac97_defs);	if (ret)		goto out;	aaci->playback.pcm = &ac97_bus->pcms[0];	aaci->capture.pcm  = &ac97_bus->pcms[1]; out:	return ret;}static void aaci_free_card(struct snd_card *card){	struct aaci *aaci = card->private_data;	if (aaci->base)		iounmap(aaci->base);}static struct aaci * __devinit aaci_init_card(struct amba_device *dev){	struct aaci *aaci;	struct snd_card *card;	card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,			    THIS_MODULE, sizeof(struct aaci));	if (card == NULL)		return ERR_PTR(-ENOMEM);	card->private_free = aaci_free_card;	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));	strlcpy(card->shortname, "ARM AC'97 Interface", sizeof(card->shortname));	snprintf(card->longname, sizeof(card->longname),		 "%s at 0x%016llx, irq %d",		 card->shortname, (unsigned long long)dev->res.start,		 dev->irq[0]);	aaci = card->private_data;	mutex_init(&aaci->ac97_sem);	spin_lock_init(&aaci->lock);	aaci->card = card;	aaci->dev = dev;	/* Set MAINCR to allow slot 1 and 2 data IO */	aaci->maincr = MAINCR_IE | MAINCR_SL1RXEN | MAINCR_SL1TXEN |		       MAINCR_SL2RXEN | MAINCR_SL2TXEN;	return aaci;}static int __devinit aaci_init_pcm(struct aaci *aaci){	struct snd_pcm *pcm;	int ret;	ret = snd_pcm_new(aaci->card, "AACI AC'97", 0, 1, 1, &pcm);	if (ret == 0) {		aaci->pcm = pcm;		pcm->private_data = aaci;		pcm->info_flags = 0;		strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops);		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaci_capture_ops);	}	return ret;}static unsigned int __devinit aaci_size_fifo(struct aaci *aaci){	struct aaci_runtime *aacirun = &aaci->playback;	int i;	writel(CR_FEN | CR_SZ16 | CR_EN, aacirun->base + AACI_TXCR);	for (i = 0; !(readl(aacirun->base + AACI_SR) & SR_TXFF) && i < 4096; i++)		writel(0, aacirun->fifo);	writel(0, aacirun->base + AACI_TXCR);	/*	 * Re-initialise the AACI after the FIFO depth test, to	 * ensure that the FIFOs are empty.  Unfortunately, merely	 * disabling the channel doesn't clear the FIFO.	 */	writel(aaci->maincr & ~MAINCR_IE, aaci->base + AACI_MAINCR);	writel(aaci->maincr, aaci->base + AACI_MAINCR);	/*	 * If we hit 4096, we failed.  Go back to the specified	 * fifo depth.	 */	if (i == 4096)		i = 8;	return i;}static int __devinit aaci_probe(struct amba_device *dev, void *id){	struct aaci *aaci;	int ret, i;	ret = amba_request_regions(dev, NULL);	if (ret)		return ret;	aaci = aaci_init_card(dev);	if (IS_ERR(aaci)) {		ret = PTR_ERR(aaci);		goto out;	}	aaci->base = ioremap(dev->res.start, SZ_4K);	if (!aaci->base) {		ret = -ENOMEM;		goto out;	}	/*	 * Playback uses AACI channel 0	 */	aaci->playback.base = aaci->base + AACI_CSCH1;	aaci->playback.fifo = aaci->base + AACI_DR1;	/*	 * Capture uses AACI channel 0	 */	aaci->capture.base = aaci->base + AACI_CSCH1;	aaci->capture.fifo = aaci->base + AACI_DR1;	for (i = 0; i < 4; i++) {		void __iomem *base = aaci->base + i * 0x14;		writel(0, base + AACI_IE);		writel(0, base + AACI_TXCR);		writel(0, base + AACI_RXCR);	}	writel(0x1fff, aaci->base + AACI_INTCLR);	writel(aaci->maincr, aaci->base + AACI_MAINCR);	ret = aaci_probe_ac97(aaci);	if (ret)		goto out;	/*	 * Size the FIFOs (must be multiple of 16).	 */	aaci->fifosize = aaci_size_fifo(aaci);	if (aaci->fifosize & 15) {		printk(KERN_WARNING "AACI: fifosize = %d not supported\n",		       aaci->fifosize);		ret = -ENODEV;		goto out;	}	ret = aaci_init_pcm(aaci);	if (ret)		goto out;	snd_card_set_dev(aaci->card, &dev->dev);	ret = snd_card_register(aaci->card);	if (ret == 0) {		dev_info(&dev->dev, "%s, fifo %d\n", aaci->card->longname,			 aaci->fifosize);		amba_set_drvdata(dev, aaci->card);		return ret;	} out:	if (aaci)		snd_card_free(aaci->card);	amba_release_regions(dev);	return ret;}static int __devexit aaci_remove(struct amba_device *dev){	struct snd_card *card = amba_get_drvdata(dev);	amba_set_drvdata(dev, NULL);	if (card) {		struct aaci *aaci = card->private_data;		writel(0, aaci->base + AACI_MAINCR);		snd_card_free(card);		amba_release_regions(dev);	}	return 0;}static struct amba_id aaci_ids[] = {	{		.id	= 0x00041041,		.mask	= 0x000fffff,	},	{ 0, 0 },};static struct amba_driver aaci_driver = {	.drv		= {		.name	= DRIVER_NAME,	},	.probe		= aaci_probe,	.remove		= __devexit_p(aaci_remove),	.suspend	= aaci_suspend,	.resume		= aaci_resume,	.id_table	= aaci_ids,};static int __init aaci_init(void){	return amba_driver_register(&aaci_driver);}static void __exit aaci_exit(void){	amba_driver_unregister(&aaci_driver);}module_init(aaci_init);module_exit(aaci_exit);MODULE_LICENSE("GPL");MODULE_DESCRIPTION("ARM PrimeCell PL041 Advanced Audio CODEC Interface driver");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -