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

📄 harmony.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *  Harmony chipset driver * *	This is a sound driver for ASP's and Lasi's Harmony sound chip *	and is unlikely to be used for anything other than on a HP PA-RISC. * *	Harmony is found in HP 712s, 715/new and many other GSC based machines. *	On older 715 machines you'll find the technically identical chip  *	called 'Vivace'. Both Harmony and Vivace are supported by this driver. * *  this ALSA driver is based on OSS driver by: *	Copyright 2000 (c) Linuxcare Canada, Alex deVries <alex@linuxcare.com> *	Copyright 2000-2002 (c) Helge Deller <deller@gmx.de> *	Copyright 2001 (c) Matthieu Delahaye <delahaym@esiee.fr> * * TODO: * - use generic DMA interface and ioremap()/iounmap() * - capture is still untested (and probaby non-working) * - spin locks * - implement non-consistent DMA pages * - implement gain meter * - module parameters * - correct cleaning sequence * - better error checking * - try to have a better quality. *    *//* * Harmony chipset 'modus operandi'. * - This chipset is found in some HP 32bit workstations, like 712, or B132 class. * most of controls are done through registers. Register are found at a fixed offset * from the hard physical adress, given in struct dev by register_parisc_driver. * * Playback and recording use 4kb pages (dma or not, depending on the machine). * * Most of PCM playback & capture is done through interrupt. When harmony needs * a new buffer to put recorded data or read played PCM, it sends an interrupt. * Bits 2 and 10 of DSTATUS register are '1' when harmony needs respectively * a new page for recording and playing.  * Interrupt are disabled/enabled by writing to bit 32 of DSTATUS.  * Adresses of next page to be played is put in PNXTADD register, next page * to be recorded is put in RNXTADD. There is 2 read-only registers, PCURADD and  * RCURADD that provides adress of current page. *  * Harmony has no way to control full duplex or half duplex mode. It means * that we always need to provide adresses of playback and capture data, even * when this is not needed. That's why we statically alloc one graveyard * buffer (to put recorded data in play-only mode) and a silence buffer. *  * Bitrate, number of channels and data format are controlled with * the CNTL register. * * Mixer work is done through one register (GAINCTL). Only input gain, * output attenuation and general attenuation control is provided. There is * also controls for enabling/disabling internal speaker and line * input. * * Buffers used by this driver are all DMA consistent. */#include <linux/delay.h>#include <sound/driver.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/time.h>#include <linux/wait.h>#include <linux/moduleparam.h>#include <sound/core.h>#include <sound/control.h>#include <sound/pcm.h>#include <sound/rawmidi.h>#include <sound/initval.h>#include <sound/info.h>#include <asm/hardware.h>#include <asm/io.h>#include <asm/parisc-device.h>MODULE_AUTHOR("Laurent Canet <canetl@esiee.fr>");MODULE_DESCRIPTION("ALSA Harmony sound driver");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{{ALSA,Harmony soundcard}}");#undef DEBUG#ifdef DEBUG# define DPRINTK printk #else# define DPRINTK(x,...)#endif#define PFX	"harmony: "#define MAX_PCM_DEVICES		1#define MAX_PCM_SUBSTREAMS	4#define MAX_MIDI_DEVICES	0#define HARMONY_BUF_SIZE	4096#define MAX_BUFS		10#define MAX_BUFFER_SIZE		(MAX_BUFS * HARMONY_BUF_SIZE)/* number of silence & graveyard buffers */#define GRAVEYARD_BUFS		3#define SILENCE_BUFS		3#define HARMONY_CNTL_C		0x80000000#define HARMONY_DSTATUS_PN	0x00000200#define HARMONY_DSTATUS_RN	0x00000002#define HARMONY_DSTATUS_IE	0x80000000#define HARMONY_DF_16BIT_LINEAR	0x00000000#define HARMONY_DF_8BIT_ULAW	0x00000001#define HARMONY_DF_8BIT_ALAW	0x00000002#define HARMONY_SS_MONO		0x00000000#define HARMONY_SS_STEREO	0x00000001/* * Channels Mask in mixer register * try some "reasonable" default gain values */#define HARMONY_GAIN_TOTAL_SILENCE 0x00F00FFF/* the following should be enough (mixer is  * very sensible on harmony) */#define HARMONY_GAIN_DEFAULT       0x0F2FF082/* useless since only one card is supported ATM */static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;static int boot_devs;module_param_array(index, int, boot_devs, 0444);MODULE_PARM_DESC(index, "Index value for Sun CS4231 soundcard.");module_param_array(id, charp, boot_devs, 0444);MODULE_PARM_DESC(id, "ID string for Sun CS4231 soundcard.");module_param_array(enable, bool, boot_devs, 0444);MODULE_PARM_DESC(enable, "Enable Sun CS4231 soundcard.");/* Register offset (from base hpa) */#define REG_ID		0x00#define REG_RESET	0x04#define REG_CNTL	0x08#define REG_GAINCTL	0x0C#define REG_PNXTADD	0x10#define REG_PCURADD	0x14#define REG_RNXTADD	0x18#define REG_RCURADD	0x1C#define REG_DSTATUS	0x20#define REG_OV		0x24#define REG_PIO		0x28#define REG_DIAG	0x3C/* * main harmony structure */typedef struct snd_card_harmony {	/* spinlocks (To be done) */	spinlock_t mixer_lock;	spinlock_t control_lock;	/* parameters */		int irq;	unsigned long hpa;	int id;	int rev;		u32 current_gain;	int data_format;		/* HARMONY_DF_xx_BIT_xxx */	int sample_rate;		/* HARMONY_SR_xx_KHZ */	int stereo_select;	/* HARMONY_SS_MONO or HARMONY_SS_STEREO */	int format_initialized;		unsigned long ply_buffer;	int ply_buf;	int ply_count;	int ply_size;	int ply_stopped;	int ply_total;		unsigned long cap_buffer;	int cap_buf;	int cap_count;	int cap_size;	int cap_stopped;	int cap_total;	struct parisc_device *pa_dev;	struct snd_dma_device dma_dev;	/* the graveyard buffer is used as recording buffer when playback, 	 * because harmony always want a buffer to put recorded data */	struct snd_dma_buffer graveyard_dma;	int graveyard_count;		/* same thing for silence buffer */	struct snd_dma_buffer silence_dma;	int silence_count;	/* alsa stuff */	snd_card_t *card;	snd_pcm_t *pcm;	snd_pcm_substream_t *playback_substream;	snd_pcm_substream_t *capture_substream;	snd_info_entry_t *proc_entry;} snd_card_harmony_t;static snd_card_t *snd_harmony_cards[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;/* wait to be out of control mode */static inline void snd_harmony_wait_cntl(snd_card_harmony_t *harmony){	int timeout = 5000;	while ( (gsc_readl(harmony->hpa+REG_CNTL) & HARMONY_CNTL_C) && --timeout)	{		/* Wait */ ;		}	if (timeout == 0) DPRINTK(KERN_DEBUG PFX "Error: wait cntl timeouted\n");}/* * sample rate routines  */static unsigned int snd_card_harmony_rates[] = {	5125, 6615, 8000, 9600,	11025, 16000, 18900, 22050,	27428, 32000, 33075, 37800,	44100, 48000};static snd_pcm_hw_constraint_list_t hw_constraint_rates = {	.count = ARRAY_SIZE(snd_card_harmony_rates),	.list = snd_card_harmony_rates,	.mask = 0,};#define HARMONY_SR_8KHZ		0x08#define HARMONY_SR_16KHZ	0x09#define HARMONY_SR_27KHZ	0x0A#define HARMONY_SR_32KHZ	0x0B#define HARMONY_SR_48KHZ	0x0E#define HARMONY_SR_9KHZ		0x0F#define HARMONY_SR_5KHZ		0x10#define HARMONY_SR_11KHZ	0x11#define HARMONY_SR_18KHZ	0x12#define HARMONY_SR_22KHZ	0x13#define HARMONY_SR_37KHZ	0x14#define HARMONY_SR_44KHZ	0x15#define HARMONY_SR_33KHZ	0x16#define HARMONY_SR_6KHZ		0x17/* bits corresponding to the entries of snd_card_harmony_rates */static unsigned int rate_bits[14] = {	HARMONY_SR_5KHZ, HARMONY_SR_6KHZ, HARMONY_SR_8KHZ,	HARMONY_SR_9KHZ, HARMONY_SR_11KHZ, HARMONY_SR_16KHZ,	HARMONY_SR_18KHZ, HARMONY_SR_22KHZ, HARMONY_SR_27KHZ,	HARMONY_SR_32KHZ, HARMONY_SR_33KHZ, HARMONY_SR_37KHZ,	HARMONY_SR_44KHZ, HARMONY_SR_48KHZ};/* snd_card_harmony_rate_bits * @rate:	index of current data rate in list * returns: harmony hex code for registers */static unsigned int snd_card_harmony_rate_bits(int rate){	unsigned int idx;		for (idx = 0; idx <= ARRAY_SIZE(snd_card_harmony_rates); idx++)		if (snd_card_harmony_rates[idx] == rate)			return rate_bits[idx];	return HARMONY_SR_44KHZ; /* fallback */}/* * update controls (data format, sample rate, number of channels) * according to value supplied in data structure */void snd_harmony_update_control(snd_card_harmony_t *harmony) {	u32 default_cntl;		/* Set CNTL */	default_cntl = (HARMONY_CNTL_C |  	/* The C bit */		(harmony->data_format << 6) |	/* Set the data format */		(harmony->stereo_select << 5) |	/* Stereo select */		(harmony->sample_rate));		/* Set sample rate */		/* initialize CNTL */ 	snd_harmony_wait_cntl(harmony);		gsc_writel(default_cntl, harmony->hpa+REG_CNTL);	}/* * interruption controls routines */static void snd_harmony_disable_interrupts(snd_card_harmony_t *chip) { 	snd_harmony_wait_cntl(chip);	gsc_writel(0, chip->hpa+REG_DSTATUS); }static void snd_harmony_enable_interrupts(snd_card_harmony_t *chip) { 	snd_harmony_wait_cntl(chip);	gsc_writel(HARMONY_DSTATUS_IE, chip->hpa+REG_DSTATUS); }/* * interruption routine: * The interrupt routine must provide adresse of next physical pages  * used by harmony */static int snd_card_harmony_interrupt(int irq, void *dev, struct pt_regs *regs){	snd_card_harmony_t *harmony = (snd_card_harmony_t *)dev;	u32 dstatus = 0;	unsigned long hpa = harmony->hpa;		/* Turn off interrupts */	snd_harmony_disable_interrupts(harmony);		/* wait for control to free */ 	snd_harmony_wait_cntl(harmony);		/* Read dstatus and pcuradd (the current address) */	dstatus = gsc_readl(hpa+REG_DSTATUS);		/* Check if this is a request to get the next play buffer */	if (dstatus & HARMONY_DSTATUS_PN) {		if (harmony->playback_substream) {			harmony->ply_buf += harmony->ply_count;			harmony->ply_buf %= harmony->ply_size;					gsc_writel(harmony->ply_buffer + harmony->ply_buf,					hpa+REG_PNXTADD);					snd_pcm_period_elapsed(harmony->playback_substream);			harmony->ply_total++;		} else {			gsc_writel(harmony->silence_dma.addr + 				   (HARMONY_BUF_SIZE*harmony->silence_count),				   hpa+REG_PNXTADD);			harmony->silence_count++;			harmony->silence_count %= SILENCE_BUFS;		}	}		/* Check if we're being asked to fill in a recording buffer */	if (dstatus & HARMONY_DSTATUS_RN) {		if (harmony->capture_substream) {			harmony->cap_buf += harmony->cap_count;			harmony->cap_buf %= harmony->cap_size;					gsc_writel(harmony->cap_buffer + harmony->cap_buf,					hpa+REG_RNXTADD);					snd_pcm_period_elapsed(harmony->capture_substream);			harmony->cap_total++;		} else {			/* graveyard buffer */			gsc_writel(harmony->graveyard_dma.addr +				   (HARMONY_BUF_SIZE*harmony->graveyard_count),				   hpa+REG_RNXTADD);			harmony->graveyard_count++;			harmony->graveyard_count %= GRAVEYARD_BUFS;		}	}	snd_harmony_enable_interrupts(harmony);

⌨️ 快捷键说明

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