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

📄 esssolo1.c

📁 iis s3c2410-uda1341语音系统的 开发
💻 C
📖 第 1 页 / 共 5 页
字号:
/****************************************************************************//* *      esssolo1.c  --  ESS Technology Solo1 (ES1946) audio driver. * *      Copyright (C) 1998-2001  Thomas Sailer (t.sailer@alumni.ethz.ch) * *      This program is free software; you can redistribute it and/or modify *      it under the terms of the GNU General Public License as published by *      the Free Software Foundation; either version 2 of the License, or *      (at your option) any later version. * *      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., 675 Mass Ave, Cambridge, MA 02139, USA. * * Module command line parameters: *   none so far * *  Supported devices: *  /dev/dsp    standard /dev/dsp device, (mostly) OSS compatible *  /dev/mixer  standard /dev/mixer device, (mostly) OSS compatible *  /dev/midi   simple MIDI UART interface, no ioctl * *  Revision history *    10.11.1998   0.1   Initial release (without any hardware) *    22.03.1999   0.2   cinfo.blocks should be reset after GETxPTR ioctl. *                       reported by Johan Maes <joma@telindus.be> *                       return EAGAIN instead of EBUSY when O_NONBLOCK *                       read/write cannot be executed *    07.04.1999   0.3   implemented the following ioctl's: SOUND_PCM_READ_RATE,  *                       SOUND_PCM_READ_CHANNELS, SOUND_PCM_READ_BITS;  *                       Alpha fixes reported by Peter Jones <pjones@redhat.com> *    15.06.1999   0.4   Fix bad allocation bug. *                       Thanks to Deti Fliegl <fliegl@in.tum.de> *    28.06.1999   0.5   Add pci_set_master *    12.08.1999   0.6   Fix MIDI UART crashing the driver *                       Changed mixer semantics from OSS documented *                       behaviour to OSS "code behaviour". *                       Recording might actually work now. *                       The real DDMA controller address register is at PCI config *                       0x60, while the register at 0x18 is used as a placeholder *                       register for BIOS address allocation. This register *                       is supposed to be copied into 0x60, according *                       to the Solo1 datasheet. When I do that, I can access *                       the DDMA registers except the mask bit, which *                       is stuck at 1. When I copy the contents of 0x18 +0x10 *                       to the DDMA base register, everything seems to work. *                       The fun part is that the Windows Solo1 driver doesn't *                       seem to do these tricks. *                       Bugs remaining: plops and clicks when starting/stopping playback *    31.08.1999   0.7   add spin_lock_init *                       replaced current->state = x with set_current_state(x) *    03.09.1999   0.8   change read semantics for MIDI to match *                       OSS more closely; remove possible wakeup race *    07.10.1999   0.9   Fix initialization; complain if sequencer writes time out *                       Revised resource grabbing for the FM synthesizer *    28.10.1999   0.10  More waitqueue races fixed *    09.12.1999   0.11  Work around stupid Alpha port issue (virt_to_bus(kmalloc(GFP_DMA)) > 16M) *                       Disabling recording on Alpha *    12.01.2000   0.12  Prevent some ioctl's from returning bad count values on underrun/overrun; *                       Tim Janik's BSE (Bedevilled Sound Engine) found this *                       Integrated (aka redid 8-)) APM support patch by Zach Brown *    07.02.2000   0.13  Use pci_alloc_consistent and pci_register_driver *    19.02.2000   0.14  Use pci_dma_supported to determine if recording should be disabled *    13.03.2000   0.15  Reintroduce initialization of a couple of PCI config space registers *    21.11.2000   0.16  Initialize dma buffers in poll, otherwise poll may return a bogus mask *    12.12.2000   0.17  More dma buffer initializations, patch from *                       Tjeerd Mulder <tjeerd.mulder@fujitsu-siemens.com> *    31.01.2001   0.18  Register/Unregister gameport, original patch from *                       Nathaniel Daw <daw@cs.cmu.edu> *                       Fix SETTRIGGER non OSS API conformity *    10.03.2001         provide abs function, prevent picking up a bogus kernel macro *                       for abs. Bug report by Andrew Morton <andrewm@uow.edu.au> *    15.05.2001         pci_enable_device moved, return values in probe cleaned *                       up. Marcus Meissner <mm@caldera.de> *    22.05.2001   0.19  more cleanups, changed PM to PCI 2.4 style, got rid *                       of global list of devices, using pci device data. *                       Marcus Meissner <mm@caldera.de> *//*****************************************************************************/      #include <linux/version.h>#include <linux/module.h>#include <linux/string.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/sound.h>#include <linux/slab.h>#include <linux/soundcard.h>#include <linux/pci.h>#include <linux/bitops.h>#include <asm/io.h>#include <asm/dma.h>#include <linux/init.h>#include <linux/poll.h>#include <linux/spinlock.h>#include <linux/smp_lock.h>#include <linux/wrapper.h>#include <asm/uaccess.h>#include <asm/hardirq.h>#include <linux/gameport.h>#include "dm.h"/* --------------------------------------------------------------------- */#undef OSS_DOCUMENTED_MIXER_SEMANTICS/* --------------------------------------------------------------------- */#ifndef PCI_VENDOR_ID_ESS#define PCI_VENDOR_ID_ESS         0x125d#endif#ifndef PCI_DEVICE_ID_ESS_SOLO1#define PCI_DEVICE_ID_ESS_SOLO1   0x1969#endif#define SOLO1_MAGIC  ((PCI_VENDOR_ID_ESS<<16)|PCI_DEVICE_ID_ESS_SOLO1)#define DDMABASE_OFFSET           0    /* chip bug workaround kludge */#define DDMABASE_EXTENT           16#define IOBASE_EXTENT             16#define SBBASE_EXTENT             16#define VCBASE_EXTENT             (DDMABASE_EXTENT+DDMABASE_OFFSET)#define MPUBASE_EXTENT            4#define GPBASE_EXTENT             4#define GAMEPORT_EXTENT		  4#define FMSYNTH_EXTENT            4/* MIDI buffer sizes */#define MIDIINBUF  256#define MIDIOUTBUF 256#define FMODE_MIDI_SHIFT 3#define FMODE_MIDI_READ  (FMODE_READ << FMODE_MIDI_SHIFT)#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)#define FMODE_DMFM 0x10static struct pci_driver solo1_driver;/* --------------------------------------------------------------------- */struct solo1_state {	/* magic */	unsigned int magic;	/* the corresponding pci_dev structure */	struct pci_dev *dev;	/* soundcore stuff */	int dev_audio;	int dev_mixer;	int dev_midi;	int dev_dmfm;	/* hardware resources */	unsigned long iobase, sbbase, vcbase, ddmabase, mpubase; /* long for SPARC */	unsigned int irq;	/* mixer registers */	struct {		unsigned short vol[10];		unsigned int recsrc;		unsigned int modcnt;		unsigned short micpreamp;	} mix;	/* wave stuff */	unsigned fmt;	unsigned channels;	unsigned rate;	unsigned char clkdiv;	unsigned ena;	spinlock_t lock;	struct semaphore open_sem;	mode_t open_mode;	wait_queue_head_t open_wait;	struct dmabuf {		void *rawbuf;		dma_addr_t dmaaddr;		unsigned buforder;		unsigned numfrag;		unsigned fragshift;		unsigned hwptr, swptr;		unsigned total_bytes;		int count;		unsigned error; /* over/underrun */		wait_queue_head_t wait;		/* redundant, but makes calculations easier */		unsigned fragsize;		unsigned dmasize;		unsigned fragsamples;		/* OSS stuff */		unsigned mapped:1;		unsigned ready:1;		unsigned endcleared:1;		unsigned enabled:1;		unsigned ossfragshift;		int ossmaxfrags;		unsigned subdivision;	} dma_dac, dma_adc;	/* midi stuff */	struct {		unsigned ird, iwr, icnt;		unsigned ord, owr, ocnt;		wait_queue_head_t iwait;		wait_queue_head_t owait;		struct timer_list timer;		unsigned char ibuf[MIDIINBUF];		unsigned char obuf[MIDIOUTBUF];	} midi;	struct gameport gameport;};/* --------------------------------------------------------------------- */static inline void write_seq(struct solo1_state *s, unsigned char data){        int i;	unsigned long flags;	/* the __cli stunt is to send the data within the command window */        for (i = 0; i < 0xffff; i++) {		__save_flags(flags);		__cli();                if (!(inb(s->sbbase+0xc) & 0x80)) {                        outb(data, s->sbbase+0xc);			__restore_flags(flags);                        return;                }		__restore_flags(flags);	}	printk(KERN_ERR "esssolo1: write_seq timeout\n");	outb(data, s->sbbase+0xc);}static inline int read_seq(struct solo1_state *s, unsigned char *data){        int i;        if (!data)                return 0;        for (i = 0; i < 0xffff; i++)                if (inb(s->sbbase+0xe) & 0x80) {                        *data = inb(s->sbbase+0xa);                        return 1;                }	printk(KERN_ERR "esssolo1: read_seq timeout\n");        return 0;}static int inline reset_ctrl(struct solo1_state *s){        int i;        outb(3, s->sbbase+6); /* clear sequencer and FIFO */        udelay(10);        outb(0, s->sbbase+6);        for (i = 0; i < 0xffff; i++)                if (inb(s->sbbase+0xe) & 0x80)                        if (inb(s->sbbase+0xa) == 0xaa) {				write_seq(s, 0xc6); /* enter enhanced mode */                                return 1;			}        return 0;}static void write_ctrl(struct solo1_state *s, unsigned char reg, unsigned char data){	write_seq(s, reg);	write_seq(s, data);}#if 0 /* unused */static unsigned char read_ctrl(struct solo1_state *s, unsigned char reg){        unsigned char r;	write_seq(s, 0xc0);	write_seq(s, reg);	read_seq(s, &r);	return r;}#endif /* unused */static void write_mixer(struct solo1_state *s, unsigned char reg, unsigned char data){	outb(reg, s->sbbase+4);	outb(data, s->sbbase+5);}static unsigned char read_mixer(struct solo1_state *s, unsigned char reg){	outb(reg, s->sbbase+4);	return inb(s->sbbase+5);}/* --------------------------------------------------------------------- */static inline unsigned ld2(unsigned int x){	unsigned r = 0;		if (x >= 0x10000) {		x >>= 16;		r += 16;	}	if (x >= 0x100) {		x >>= 8;		r += 8;	}	if (x >= 0x10) {		x >>= 4;		r += 4;	}	if (x >= 4) {		x >>= 2;		r += 2;	}	if (x >= 2)		r++;	return r;}/* --------------------------------------------------------------------- */static inline void stop_dac(struct solo1_state *s){	unsigned long flags;	spin_lock_irqsave(&s->lock, flags);	s->ena &= ~FMODE_WRITE;	write_mixer(s, 0x78, 0x10);	spin_unlock_irqrestore(&s->lock, flags);}static void start_dac(struct solo1_state *s){	unsigned long flags;	spin_lock_irqsave(&s->lock, flags);	if (!(s->ena & FMODE_WRITE) && (s->dma_dac.mapped || s->dma_dac.count > 0) && s->dma_dac.ready) {		s->ena |= FMODE_WRITE;		write_mixer(s, 0x78, 0x12);		udelay(10);		write_mixer(s, 0x78, 0x13);	}	spin_unlock_irqrestore(&s->lock, flags);}	static inline void stop_adc(struct solo1_state *s){	unsigned long flags;	spin_lock_irqsave(&s->lock, flags);	s->ena &= ~FMODE_READ;	write_ctrl(s, 0xb8, 0xe);	spin_unlock_irqrestore(&s->lock, flags);}static void start_adc(struct solo1_state *s){	unsigned long flags;	spin_lock_irqsave(&s->lock, flags);	if (!(s->ena & FMODE_READ) && (s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize))	    && s->dma_adc.ready) {		s->ena |= FMODE_READ;		write_ctrl(s, 0xb8, 0xf);#if 0		printk(KERN_DEBUG "solo1: DMAbuffer: 0x%08lx\n", (long)s->dma_adc.rawbuf);		printk(KERN_DEBUG "solo1: DMA: mask: 0x%02x cnt: 0x%04x addr: 0x%08x  stat: 0x%02x\n", 		       inb(s->ddmabase+0xf), inw(s->ddmabase+4), inl(s->ddmabase), inb(s->ddmabase+8));#endif                outb(0, s->ddmabase+0xd); /* master reset */		outb(1, s->ddmabase+0xf);  /* mask */		outb(0x54/*0x14*/, s->ddmabase+0xb);  /* DMA_MODE_READ | DMA_MODE_AUTOINIT */		outl(virt_to_bus(s->dma_adc.rawbuf), s->ddmabase);		outw(s->dma_adc.dmasize-1, s->ddmabase+4);		outb(0, s->ddmabase+0xf);	}	spin_unlock_irqrestore(&s->lock, flags);#if 0	printk(KERN_DEBUG "solo1: start DMA: reg B8: 0x%02x  SBstat: 0x%02x\n"	       KERN_DEBUG "solo1: DMA: stat: 0x%02x  cnt: 0x%04x  mask: 0x%02x\n", 	       read_ctrl(s, 0xb8), inb(s->sbbase+0xc), 	       inb(s->ddmabase+8), inw(s->ddmabase+4), inb(s->ddmabase+0xf));	printk(KERN_DEBUG "solo1: A1: 0x%02x  A2: 0x%02x  A4: 0x%02x  A5: 0x%02x  A8: 0x%02x\n"  	       KERN_DEBUG "solo1: B1: 0x%02x  B2: 0x%02x  B4: 0x%02x  B7: 0x%02x  B8: 0x%02x  B9: 0x%02x\n",	       read_ctrl(s, 0xa1), read_ctrl(s, 0xa2), read_ctrl(s, 0xa4), read_ctrl(s, 0xa5), read_ctrl(s, 0xa8), 	       read_ctrl(s, 0xb1), read_ctrl(s, 0xb2), read_ctrl(s, 0xb4), read_ctrl(s, 0xb7), read_ctrl(s, 0xb8), 	       read_ctrl(s, 0xb9));#endif}/* --------------------------------------------------------------------- */#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT)#define DMABUF_MINORDER 1static inline void dealloc_dmabuf(struct solo1_state *s, struct dmabuf *db){	struct page *page, *pend;	if (db->rawbuf) {		/* undo marking the pages as reserved */		pend = virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) - 1);		for (page = virt_to_page(db->rawbuf); page <= pend; page++)			mem_map_unreserve(page);		pci_free_consistent(s->dev, PAGE_SIZE << db->buforder, db->rawbuf, db->dmaaddr);	}	db->rawbuf = NULL;	db->mapped = db->ready = 0;}static int prog_dmabuf(struct solo1_state *s, struct dmabuf *db){	int order;	unsigned bytespersec;	unsigned bufs, sample_shift = 0;	struct page *page, *pend;	db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0;	if (!db->rawbuf) {		db->ready = db->mapped = 0;                for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--)			if ((db->rawbuf = pci_alloc_consistent(s->dev, PAGE_SIZE << order, &db->dmaaddr)))				break;		if (!db->rawbuf)

⌨️ 快捷键说明

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