欢迎来到虫虫下载站 | 资源下载 资源专辑 关于我们
虫虫下载站

aica.c

linux 内核源代码
C
第 1 页 / 共 2 页
字号:
/** This code is licenced under * the General Public Licence* version 2** Copyright Adrian McMenamin 2005, 2006, 2007* <adrian@mcmen.demon.co.uk>* Requires firmware (BSD licenced) available from:* http://linuxdc.cvs.sourceforge.net/linuxdc/linux-sh-dc/sound/oss/aica/firmware/* or the maintainer** This program is free software; you can redistribute it and/or modify* it under the terms of version 2 of the GNU General Public License as published by* the Free Software Foundation.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the* GNU General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, write to the Free Software* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA**/#include <linux/init.h>#include <linux/jiffies.h>#include <linux/slab.h>#include <linux/time.h>#include <linux/wait.h>#include <linux/moduleparam.h>#include <linux/platform_device.h>#include <linux/firmware.h>#include <linux/timer.h>#include <linux/delay.h>#include <linux/workqueue.h>#include <sound/driver.h>#include <sound/core.h>#include <sound/control.h>#include <sound/pcm.h>#include <sound/initval.h>#include <sound/info.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/dreamcast/sysasic.h>#include "aica.h"MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");MODULE_DESCRIPTION("Dreamcast AICA sound (pcm) driver");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{{Yamaha/SEGA, AICA}}");/* module parameters */#define CARD_NAME "AICA"static int index = -1;static char *id;static int enable = 1;module_param(index, int, 0444);MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");module_param(id, charp, 0444);MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");module_param(enable, bool, 0644);MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");/* Use workqueue */static struct workqueue_struct *aica_queue;/* Simple platform device */static struct platform_device *pd;static struct resource aica_memory_space[2] = {	{	 .name = "AICA ARM CONTROL",	 .start = ARM_RESET_REGISTER,	 .flags = IORESOURCE_MEM,	 .end = ARM_RESET_REGISTER + 3,	 },	{	 .name = "AICA Sound RAM",	 .start = SPU_MEMORY_BASE,	 .flags = IORESOURCE_MEM,	 .end = SPU_MEMORY_BASE + 0x200000 - 1,	 },};/* SPU specific functions *//* spu_write_wait - wait for G2-SH FIFO to clear */static void spu_write_wait(void){	int time_count;	time_count = 0;	while (1) {		if (!(readl(G2_FIFO) & 0x11))			break;		/* To ensure hardware failure doesn't wedge kernel */		time_count++;		if (time_count > 0x10000) {			snd_printk			    ("WARNING: G2 FIFO appears to be blocked.\n");			break;		}	}}/* spu_memset - write to memory in SPU address space */static void spu_memset(u32 toi, u32 what, int length){	int i;	unsigned long flags;	snd_assert(length % 4 == 0, return);	for (i = 0; i < length; i++) {		if (!(i % 8))			spu_write_wait();		local_irq_save(flags);		writel(what, toi + SPU_MEMORY_BASE);		local_irq_restore(flags);		toi++;	}}/* spu_memload - write to SPU address space */static void spu_memload(u32 toi, void *from, int length){	unsigned long flags;	u32 *froml = from;	u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi);	int i;	u32 val;	length = DIV_ROUND_UP(length, 4);	spu_write_wait();	for (i = 0; i < length; i++) {		if (!(i % 8))			spu_write_wait();		val = *froml;		local_irq_save(flags);		writel(val, to);		local_irq_restore(flags);		froml++;		to++;	}}/* spu_disable - set spu registers to stop sound output */static void spu_disable(void){	int i;	unsigned long flags;	u32 regval;	spu_write_wait();	regval = readl(ARM_RESET_REGISTER);	regval |= 1;	spu_write_wait();	local_irq_save(flags);	writel(regval, ARM_RESET_REGISTER);	local_irq_restore(flags);	for (i = 0; i < 64; i++) {		spu_write_wait();		regval = readl(SPU_REGISTER_BASE + (i * 0x80));		regval = (regval & ~0x4000) | 0x8000;		spu_write_wait();		local_irq_save(flags);		writel(regval, SPU_REGISTER_BASE + (i * 0x80));		local_irq_restore(flags);	}}/* spu_enable - set spu registers to enable sound output */static void spu_enable(void){	unsigned long flags;	u32 regval = readl(ARM_RESET_REGISTER);	regval &= ~1;	spu_write_wait();	local_irq_save(flags);	writel(regval, ARM_RESET_REGISTER);	local_irq_restore(flags);}/*  * Halt the sound processor, clear the memory, * load some default ARM7 code, and then restart ARM7*/static void spu_reset(void){	unsigned long flags;	spu_disable();	spu_memset(0, 0, 0x200000 / 4);	/* Put ARM7 in endless loop */	local_irq_save(flags);	ctrl_outl(0xea000002, SPU_MEMORY_BASE);	local_irq_restore(flags);	spu_enable();}/* aica_chn_start - write to spu to start playback */static void aica_chn_start(void){	unsigned long flags;	spu_write_wait();	local_irq_save(flags);	writel(AICA_CMD_KICK | AICA_CMD_START, (u32 *) AICA_CONTROL_POINT);	local_irq_restore(flags);}/* aica_chn_halt - write to spu to halt playback */static void aica_chn_halt(void){	unsigned long flags;	spu_write_wait();	local_irq_save(flags);	writel(AICA_CMD_KICK | AICA_CMD_STOP, (u32 *) AICA_CONTROL_POINT);	local_irq_restore(flags);}/* ALSA code below */static struct snd_pcm_hardware snd_pcm_aica_playback_hw = {	.info = (SNDRV_PCM_INFO_NONINTERLEAVED),	.formats =	    (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |	     SNDRV_PCM_FMTBIT_IMA_ADPCM),	.rates = SNDRV_PCM_RATE_8000_48000,	.rate_min = 8000,	.rate_max = 48000,	.channels_min = 1,	.channels_max = 2,	.buffer_bytes_max = AICA_BUFFER_SIZE,	.period_bytes_min = AICA_PERIOD_SIZE,	.period_bytes_max = AICA_PERIOD_SIZE,	.periods_min = AICA_PERIOD_NUMBER,	.periods_max = AICA_PERIOD_NUMBER,};static int aica_dma_transfer(int channels, int buffer_size,			     struct snd_pcm_substream *substream){	int q, err, period_offset;	struct snd_card_aica *dreamcastcard;	struct snd_pcm_runtime *runtime;	unsigned long flags;	dreamcastcard = substream->pcm->private_data;	period_offset = dreamcastcard->clicks;	period_offset %= (AICA_PERIOD_NUMBER / channels);	runtime = substream->runtime;	for (q = 0; q < channels; q++) {		local_irq_save(flags);		err = dma_xfer(AICA_DMA_CHANNEL,			       (unsigned long) (runtime->dma_area +						(AICA_BUFFER_SIZE * q) /						channels +						AICA_PERIOD_SIZE *						period_offset),			       AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET +			       AICA_PERIOD_SIZE * period_offset,			       buffer_size / channels, AICA_DMA_MODE);		if (unlikely(err < 0)) {			local_irq_restore(flags);			break;		}		dma_wait_for_completion(AICA_DMA_CHANNEL);		local_irq_restore(flags);	}	return err;}static void startup_aica(struct snd_card_aica *dreamcastcard){	spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,		    dreamcastcard->channel, sizeof(struct aica_channel));	aica_chn_start();}static void run_spu_dma(struct work_struct *work){	int buffer_size;	struct snd_pcm_runtime *runtime;	struct snd_card_aica *dreamcastcard;	dreamcastcard =	    container_of(work, struct snd_card_aica, spu_dma_work);	runtime = dreamcastcard->substream->runtime;	if (unlikely(dreamcastcard->dma_check == 0)) {		buffer_size =		    frames_to_bytes(runtime, runtime->buffer_size);		if (runtime->channels > 1)			dreamcastcard->channel->flags |= 0x01;		aica_dma_transfer(runtime->channels, buffer_size,				  dreamcastcard->substream);		startup_aica(dreamcastcard);		dreamcastcard->clicks =		    buffer_size / (AICA_PERIOD_SIZE * runtime->channels);		return;	} else {		aica_dma_transfer(runtime->channels,				  AICA_PERIOD_SIZE * runtime->channels,				  dreamcastcard->substream);		snd_pcm_period_elapsed(dreamcastcard->substream);		dreamcastcard->clicks++;		if (unlikely(dreamcastcard->clicks >= AICA_PERIOD_NUMBER))			dreamcastcard->clicks %= AICA_PERIOD_NUMBER;		mod_timer(&dreamcastcard->timer, jiffies + 1);	}}static void aica_period_elapsed(unsigned long timer_var){	/*timer function - so cannot sleep */	int play_period;	struct snd_pcm_runtime *runtime;	struct snd_pcm_substream *substream;	struct snd_card_aica *dreamcastcard;	substream = (struct snd_pcm_substream *) timer_var;	runtime = substream->runtime;	dreamcastcard = substream->pcm->private_data;	/* Have we played out an additional period? */	play_period =	    frames_to_bytes(runtime,			    readl			    (AICA_CONTROL_CHANNEL_SAMPLE_NUMBER)) /	    AICA_PERIOD_SIZE;	if (play_period == dreamcastcard->current_period) {		/* reschedule the timer */		mod_timer(&(dreamcastcard->timer), jiffies + 1);		return;	}	if (runtime->channels > 1)		dreamcastcard->current_period = play_period;	if (unlikely(dreamcastcard->dma_check == 0))		dreamcastcard->dma_check = 1;	queue_work(aica_queue, &(dreamcastcard->spu_dma_work));}static void spu_begin_dma(struct snd_pcm_substream *substream){	struct snd_card_aica *dreamcastcard;	struct snd_pcm_runtime *runtime;	runtime = substream->runtime;	dreamcastcard = substream->pcm->private_data;	/*get the queue to do the work */	queue_work(aica_queue, &(dreamcastcard->spu_dma_work));	/* Timer may already be running */	if (unlikely(dreamcastcard->timer.data)) {		mod_timer(&dreamcastcard->timer, jiffies + 4);		return;	}

⌨️ 快捷键说明

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