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

📄 x900-wm8976.c

📁 这个代码是基于JADE X90+ wm8976 的 ALSA标准接口的驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  Driver for PhilipsWM8976 on JADE-x900 soundcard *  Copyright (C) 2002 Tomas Kasparek <tomas.kasparek@seznam.cz> * *   This program is free software; you can redistribute it and/or modify *   it under the terms of the GNU General Public License. *  * History: * *  * 2008-02-19   yolanda initial release  *                              *//* $Id: x900-wm8976.c 2 2008-02-19 03:58:36Z root $ *//***************************************************************************************************** To understand what Alsa Drivers should be doing look at "Writing an Alsa Driver" by Takashi Iwai* available in the Alsa doc section on the website		* * A few notes to make things clearer. The UDA1341 is hooked up to Serial port 4 on the SA1100.* We are using  SSP mode to talk to the UDA1341. The UDA1341 bit & wordselect clocks are generated* by this UART. Unfortunately, the clock only runs if the transmit buffer has something in it.* So, if we are just recording, we feed the transmit DMA stream a bunch of 0x0000 so that the* transmit buffer is full and the clock keeps going. The zeroes come from FLUSH_BASE_PHYS which* is a mem loc that always decodes to 0's w/ no off chip access.** Some alsa terminology:*	frame => num_channels * sample_size  e.g stereo 16 bit is 2 * 16 = 32 bytes*	period => the least number of bytes that will generate an interrupt e.g. we have a 1024 byte*             buffer and 4 periods in the runtime structure this means we'll get an int every 256*             bytes or 4 times per buffer.*             A number of the sizes are in frames rather than bytes, use frames_to_bytes and*             bytes_to_frames to convert.  The easiest way to tell the units is to look at the*             type i.e. runtime-> buffer_size is in frames and its type is snd_pcm_uframes_t*             *	Notes about the pointer fxn:*	The pointer fxn needs to return the offset into the dma buffer in frames.*	Interrupts must be blocked before calling the dma_get_pos fxn to avoid race with interrupts.**	Notes about pause/resume*	Implementing this would be complicated so it's skipped.  The problem case is:*	A full duplex connection is going, then play is paused. At this point you need to start xmitting*	0's to keep the record active which means you cant just freeze the dma and resume it later you'd*	need to	save off the dma info, and restore it properly on a resume.  Yeach!**	Notes about transfer methods:*	The async write calls fail.  I probably need to implement something else to support them?* ***************************************************************************************************/#include <linux/config.h>#include <sound/driver.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/errno.h>#include <linux/ioctl.h>#include <linux/delay.h>#include <linux/slab.h>#ifdef CONFIG_PM#include <linux/pm.h>#endif#include <asm/hardware.h>#include <asm/arch/platform.h>#include <asm/mach-types.h>#include <asm/dma.h>#include <asm/io.h>#include <sound/core.h>#include <sound/pcm.h>#include <sound/initval.h>#include <sound/wm8976.h>#undef DEBUG_MODE#undef DEBUG_FUNCTION_NAMES/* * FIXME: Is this enough as autodetection of 2.4.X-rmkY-hhZ kernels? * We use DMA stuff from 2.4.18-rmk3-hh24 here to be able to compile this * module for Familiar 0.6.1 *//* {{{ Type definitions */MODULE_AUTHOR("yolanda <zouzf@hotmail.com>");MODULE_LICENSE("GPL");MODULE_DESCRIPTION("x900 + WM8976driver for ALSA");MODULE_SUPPORTED_DEVICE("{{WM8976,JADE X900 WM8976}}");static char *id = NULL;	/* ID for this card */#define X900_WM8976_DRIVER	"x900_wm8976"module_param(id, charp, 0444);MODULE_PARM_DESC(id, "ID string for X900 + WM8976 soundcard.");#define IIS_BASE_HW 	0x2002a000#define PHY_SYS_BASE    0x20031000#define IIS_CR0      	((volatile int *)(IIS_BASE + 0x000))#define IIS_CR1      	((volatile int *)(IIS_BASE + 0x004))#define IIS_SDR        	((volatile int *)(IIS_BASE + 0x008))#define IIS_SSR       	((volatile int *)(IIS_BASE + 0x00C))#define IIS_CPSR        ((volatile int *)(IIS_BASE + 0x010))#define IIS_IMSC     	((volatile int *)(IIS_BASE + 0x014))#define IIS_RIS      	((volatile int *)(IIS_BASE + 0x018))#define IIS_MIS        	((volatile int *)(IIS_BASE + 0x01C))#define IIS_ICR       	((volatile int *)(IIS_BASE + 0x020))#define IIS_DMACR       ((volatile int *)(IIS_BASE + 0x024))u32 IIS_BASE;typedef struct  audio_stream{   	snd_pcm_substream_t * substream;	      void *dma_done;	spinlock_t dma_lock;		struct ver_dma_param pa;	int  dmach;	char *id;			/* identification string */	int stream_id;		/* numeric identification */		int active:1;		/* we are using this stream for transfer now */	int period;		/* current transfer period */	int periods;		/* current count of periods registerd in the DMA engine */	int tx_spin;		/* are we recoding - flag used to do DMA trans. for sync */	unsigned int old_offset;	}audio_stream_t;typedef struct snd_card_x900_wm8976 {	snd_card_t *card;		struct  wm8976_t *wm8976;	snd_pcm_t *pcm;	long samplerate;	audio_stream_t s[2];	/* playback & capture */} x900_wm8976_t;static struct snd_card_x900_wm8976 *x900_wm8976 = NULL;static unsigned int rates[] = {	8000,  12000, 16000,22000, 24000,	32000, 48000,};static snd_pcm_hw_constraint_list_t hw_constraints_rates = {	.count	= ARRAY_SIZE(rates),	.list	= rates,	.mask	= 0,};/* }}} *//* {{{ Clock and sample rate stuff */static void x900_wm8976_set_audio_clock(long val){		int  tmp_scr,tmp_cpsr;		 tmp_cpsr=(*IIS_CPSR);       	 tmp_scr =(*IIS_CR0)& 0x00ff;			switch (val) {		case 8000:				(*IIS_CR0) = tmp_scr | 0x0300;        		(*IIS_CPSR) = 0x00018;						break;			case 12000: 				(*IIS_CR0) = tmp_scr | 0x0300;          			(*IIS_CPSR) = 0x00010;							case 16000:					(*IIS_CR0) = tmp_scr | 0x0300;          			(*IIS_CPSR) = 0x00018;							case 24000:				(*IIS_CR0) = tmp_scr | 0x0300;        			(*IIS_CPSR) = 0x0008;			case 32000:				(*IIS_CR0) = tmp_scr | 0x0500;          			(*IIS_CPSR) = 0x0004;							break;							case 48000:					(*IIS_CR0) = tmp_scr | 0x0300;	   			(*IIS_CPSR) = 0x00004;				break;	}}static void x900_wm8976_set_samplerate(x900_wm8976_t *x900_wm8976, long rate){	unsigned char wm8976_for_samplerate[]={0x0e,0x00};		/* wait for any frame to complete */	udelay(125);		if (rate >= 48000)				rate = 48000;	else if (rate >= 44100)				rate = 44100;	else if (rate >= 32000)				rate = 32000;	else if (rate >= 24000)				rate = 24000;		else if (rate >= 16000)				rate = 16000;		else if (rate >= 12000)				rate = 12000;	else				rate = 8000;	/* Set the external clock generator */	x900_wm8976_set_audio_clock(rate);	/* Select the clock divisor */	switch (rate) {	case 8000:			wm8976_for_samplerate[1]=0x0a;				break;	case 12000:		wm8976_for_samplerate[1]=0x08;	case 16000:		wm8976_for_samplerate[1]=0x5;	case 24000:		wm8976_for_samplerate[1]=0x04;	case 32000:		wm8976_for_samplerate[1]=0x02;   	case 48000:				wm8976_for_samplerate[1]= 0x00;		break;	}	/* FMT setting should be moved away when other FMTs are added (FIXME) */	jade_command(x900_wm8976->wm8976,wm8976_for_samplerate);		x900_wm8976->samplerate = rate;}/* }}} *//* {{{ HW init and shutdown */static void x900_wm8976_audio_init(x900_wm8976_t *x900_wm8976){			x900_wm8976->s[SNDRV_PCM_STREAM_PLAYBACK].id = "WM8976 out";	x900_wm8976->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id = SNDRV_PCM_STREAM_PLAYBACK;	x900_wm8976->s[SNDRV_PCM_STREAM_CAPTURE].id = "WM8976 in";	x900_wm8976->s[SNDRV_PCM_STREAM_CAPTURE].stream_id = SNDRV_PCM_STREAM_CAPTURE;	/* Initialize the wm8976 internal state */	jade_open();	 }static void x900_wm8976_audio_shutdown(x900_wm8976_t *x900_wm8976){	/* disable the audio power and all signals leading to the audio chip */		//audio_stop_dma(&x900_wm8976->s[SNDRV_PCM_STREAM_PLAYBACK]);	//audio_stop_dma(&x900_wm8976->s[SNDRV_PCM_STREAM_CAPTURE]);		jade_close(x900_wm8976->wm8976);}/* }}} *//* * these are the address and sizes used to fill the xmit buffer * so we can get a clock in record only mode */#define FORCE_CLOCK_ADDR		(dma_addr_t)FLUSH_BASE_PHYS#define FORCE_CLOCK_SIZE		4096 // was 2048#define DMA_BUF_SIZE	4096	#define DMA_LOOP_NUM	4static void audio_dma_free(audio_stream_t *s){	    ver_free_dma((s)->dmach);		    ver_end_dma(s->dmach);}/* * this stops the dma and clears the dma ptrs */static void audio_stop_dma(audio_stream_t *s){	unsigned long flags;	spin_lock_irqsave(&s->dma_lock, flags);		s->active = 0;	s->period = 0;	/* this stops the dma channel and clears the buffer ptrs */	  audio_dma_free(s);        	spin_unlock_irqrestore(&s->dma_lock, flags);}static int audio_dma_request(audio_stream_t *s, int play){	s->dmach=ver_request_dma(0, "iis_p", 2, 3);	if(s->dmach < 0){		debug("<1>i2s  dma error\n");		return -1;	}else{		printk("SOUND: i2s  dma chan: %d\n", s->dmach);	}	memset(&s->pa, 0, sizeof(s->pa));	if (play){		s->pa.SWidth = DMAC_TSIZE_WORD;		s->pa.DWidth = DMAC_TSIZE_HALFWORD;		s->pa.SBsize = 0x1;		s->pa.DBsize = 0x4;		s->pa.SIncr = 1;          	s->pa.DIncr = 0;	}else{		s->pa.SWidth = DMAC_TSIZE_HALFWORD;		s->pa.DWidth = DMAC_TSIZE_WORD;		s->pa.SBsize = 0x4;		s->pa.DBsize = 0x1;		s->pa.SIncr = 0;          	s->pa.DIncr = 1;	}	        s->pa.FlowCntrl = FLOW_MEM_PER_DMAC;	if (ver_set_dma(s->dmach, &s->pa) != 0) {		debug("<1>i2s write:set  play dma fail:\n");		return -1;	}else		printk("SOUND: set dma OK\n");		return 0;}static int audio_dma_start(audio_stream_t *chip, void (*callback)(void *),int stream_id){	snd_pcm_runtime_t *runtime=chip->substream->runtime;	unsigned int dma_size;			unsigned int offset;	int ret;//	chip->dma_done=NULL;	#if 0	DECLARE_COMPLETION(chip->dma_done);#endif	dma_size = frames_to_bytes(runtime, runtime->period_size);	offset = dma_size *chip->period;		chip->period++;		chip->period %= runtime->periods;		chip->periods++;				chip->pa.sg_len = 0;	chip->pa.tsize = dma_size;	if (stream_id)	{			chip->pa.sbuf =(void *)(0x2002a008);		chip->pa.dbuf = runtime->dma_addr + offset;		}	else{				chip->pa.sbuf =runtime->dma_addr + offset;		chip->pa.dbuf = (void *)(0x2002a008);		}		chip->pa.trans_done = callback;		chip->pa.done_data = chip;	if (ver_start_dma(chip->dmach, &chip->pa) != 0) {		printk("<1>sound:start dma fail:\n");		return -1;	}		return 0;	}static void audio_dma_play_done(void *data){	audio_stream_t *s = data;	snd_pcm_substream_t * substream = s->substream;		//snd_pcm_runtime_t *runtime=substream->runtime;	//unsigned int dma_size,offset;			spin_lock(&s->dma_lock);	if (!s->tx_spin && s->periods > 0)		s->periods--;			snd_pcm_period_elapsed(s->substream);		audio_dma_start(s,audio_dma_play_done,0);			spin_unlock(&s->dma_lock);	

⌨️ 快捷键说明

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