📄 maestro3.c
字号:
/***************************************************************************** * * ESS Maestro3/Allegro driver for Linux 2.4.x * * 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. * * (c) Copyright 2000 Zach Brown <zab@zabbo.net> * * I need to thank many people for helping make this driver happen. * As always, Eric Brombaugh was a hacking machine and killed many bugs * that I was too dumb to notice. Howard Kim at ESS provided reference boards * and as much docs as he could. Todd and Mick at Dell tested snapshots on * an army of laptops. msw and deviant at Red Hat also humoured me by hanging * their laptops every few hours in the name of science. * * Shouts go out to Mike "DJ XPCom" Ang. * * History * v1.22 - Feb 28 2001 - Zach Brown <zab@zabbo.net> * allocate mem at insmod/setup, rather than open * limit pci dma addresses to 28bit, thanks guys. * v1.21 - Feb 04 2001 - Zach Brown <zab@zabbo.net> * fix up really dumb notifier -> suspend oops * v1.20 - Jan 30 2001 - Zach Brown <zab@zabbo.net> * get rid of pm callback and use pci_dev suspend/resume instead * m3_probe cleanups, including pm oops think-o * v1.10 - Jan 6 2001 - Zach Brown <zab@zabbo.net> * revert to lame remap_page_range mmap() just to make it work * record mmap fixed. * fix up incredibly broken open/release resource management * duh. fix record format setting. * add SMP locking and cleanup formatting here and there * v1.00 - Dec 16 2000 - Zach Brown <zab@zabbo.net> * port to sexy 2.4 interfaces * properly align instance allocations so recording works * clean up function namespace a little :/ * update PCI IDs based on mail from ESS * arbitrarily bump version number to show its 2.4 now, * 2.2 will stay 0., oss_audio port gets 2. * v0.03 - Nov 05 2000 - Zach Brown <zab@zabbo.net> * disable recording but allow dsp to be opened read * pull out most silly compat defines * v0.02 - Nov 04 2000 - Zach Brown <zab@zabbo.net> * changed clocking setup for m3, slowdown fixed. * codec reset is hopefully reliable now * rudimentary apm/power management makes suspend/resume work * v0.01 - Oct 31 2000 - Zach Brown <zab@zabbo.net> * first release * v0.00 - Sep 09 2000 - Zach Brown <zab@zabbo.net> * first pass derivation from maestro.c * * TODO * in/out allocated contiguously so fullduplex mmap will work? * no beep on init (mute) * resetup msrc data memory if freq changes? * * -- * * Allow me to ramble a bit about the m3 architecture. The core of the * chip is the 'assp', the custom ESS dsp that runs the show. It has * a small amount of code and data ram. ESS drops binary dsp code images * on our heads, but we don't get to see specs on the dsp. * * The constant piece of code on the dsp is the 'kernel'. It also has a * chunk of the dsp memory that is statically set aside for its control * info. This is the KDATA defines in maestro3.h. Part of its core * data is a list of code addresses that point to the pieces of DSP code * that it should walk through in its loop. These other pieces of code * do the real work. The kernel presumably jumps into each of them in turn. * These code images tend to have their own data area, and one can have * multiple data areas representing different states for each of the 'client * instance' code portions. There is generally a list in the kernel data * that points to the data instances for a given piece of code. * * We've only been given the binary image for the 'minisrc', mini sample * rate converter. This is rather annoying because it limits the work * we can do on the dsp, but it also greatly simplifies the job of managing * dsp data memory for the code and data for our playing streams :). We * statically allocate the minisrc code into a region we 'know' to be free * based on the map of the binary kernel image we're loading. We also * statically allocate the data areas for the maximum number of pcm streams * we can be dealing with. This max is set by the length of the static list * in the kernel data that records the number of minisrc data regions we * can have. Thats right, all software dsp mixing with static code list * limits. Rock. * * How sound goes in and out is still a relative mystery. It appears * that the dsp has the ability to get input and output through various * 'connections'. To do IO from or to a connection, you put the address * of the minisrc client area in the static kernel data lists for that * input or output. so for pcm -> dsp -> mixer, we put the minisrc data * instance in the DMA list and also in the list for the mixer. I guess * it Just Knows which is in/out, and we give some dma control info that * helps. There are all sorts of cool inputs/outputs that it seems we can't * use without dsp code images that know how to use them. * * So at init time we preload all the memory allocation stuff and set some * system wide parameters. When we really get a sound to play we build * up its minisrc header (stream parameters, buffer addresses, input/output * settings). Then we throw its header on the various lists. We also * tickle some KDATA settings that ask the assp to raise clock interrupts * and do some amount of software mixing before handing data to the ac97. * * Sorry for the vague details. Feel free to ask Eric or myself if you * happen to be trying to use this driver elsewhere. Please accept my * apologies for the quality of the OSS support code, its passed through * too many hands now and desperately wants to be rethought. *//*****************************************************************************/#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/ctype.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/vmalloc.h>#include <asm/io.h>#include <asm/dma.h>#include <linux/init.h>#include <linux/poll.h>#include <linux/reboot.h>#include <asm/uaccess.h>#include <asm/hardirq.h>#include <linux/spinlock.h>#include <linux/ac97_codec.h> /* * for crizappy mmap() */#include <linux/wrapper.h>#include "maestro3.h"#define M_DEBUG 1#define DRIVER_VERSION "1.22"#define M3_MODULE_NAME "maestro3"#define PFX M3_MODULE_NAME ": "#define M3_STATE_MAGIC 0x734d724d#define M3_CARD_MAGIC 0x646e6f50#define ESS_FMT_STEREO 0x01#define ESS_FMT_16BIT 0x02#define ESS_FMT_MASK 0x03#define ESS_DAC_SHIFT 0 #define ESS_ADC_SHIFT 4#define DAC_RUNNING 1#define ADC_RUNNING 2#define SND_DEV_DSP16 5 #ifdef M_DEBUGstatic int debug;#define DPMOD 1 /* per module load */#define DPSTR 2 /* per 'stream' */#define DPSYS 3 /* per syscall */#define DPCRAP 4 /* stuff the user shouldn't see unless they're really debuggin */#define DPINT 5 /* per interrupt, LOTS */#define DPRINTK(DP, args...) {if (debug >= (DP)) printk(KERN_DEBUG PFX args);}#else#define DPRINTK(x)#endifstruct m3_list { int curlen; u16 mem_addr; int max;};int external_amp = 1;struct m3_state { unsigned int magic; struct m3_card *card; unsigned char fmt, enable; int index; /* this locks around the oss state in the driver */ spinlock_t lock; struct semaphore open_sem; wait_queue_head_t open_wait; mode_t open_mode; int dev_audio; struct assp_instance { u16 code, data; } dac_inst, adc_inst; /* should be in dmabuf */ unsigned int rateadc, ratedac; struct dmabuf { void *rawbuf; 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 ossfragshift; int ossmaxfrags; unsigned subdivision; /* new in m3 */ int mixer_index, dma_index, msrc_index, adc1_index; int in_lists; /* 2.4.. */ dma_addr_t handle; } dma_dac, dma_adc;}; struct m3_card { unsigned int magic; struct m3_card *next; struct ac97_codec *ac97; spinlock_t ac97_lock; int card_type;#define NR_DSPS 1#define MAX_DSPS NR_DSPS struct m3_state channels[MAX_DSPS]; /* this locks around the physical registers on the card */ spinlock_t lock; /* hardware resources */ struct pci_dev *pcidev; u32 iobase; u32 irq; int dacs_active; int timer_users; struct m3_list msrc_list, mixer_list, adc1_list, dma_list; /* for storing reset state..*/ u8 reset_state; u16 *suspend_mem; int in_suspend; wait_queue_head_t suspend_queue;};/* * an arbitrary volume we set the internal * volume settings to so that the ac97 volume * range is a little less insane. 0x7fff is * max. */#define ARB_VOLUME ( 0x6800 )static const unsigned sample_shift[] = { 0, 1, 1, 2 };enum { ESS_ALLEGRO, ESS_MAESTRO3, /* * a maestro3 with 'hardware strapping', only * found inside ESS? */ ESS_MAESTRO3HW,};static char *card_names[] = { [ESS_ALLEGRO] = "Allegro", [ESS_MAESTRO3] = "Maestro3(i)", [ESS_MAESTRO3HW] = "Maestro3(i)hw"};#ifndef PCI_VENDOR_ESS#define PCI_VENDOR_ESS 0x125D#endif#define M3_DEVICE(DEV, TYPE) \{ \vendor: PCI_VENDOR_ESS, \device: DEV, \subvendor: PCI_ANY_ID, \subdevice: PCI_ANY_ID, \class: PCI_CLASS_MULTIMEDIA_AUDIO << 8, \class_mask: 0xffff << 8, \driver_data: TYPE, \}static struct pci_device_id m3_id_table[] __initdata = { M3_DEVICE(0x1988, ESS_ALLEGRO), M3_DEVICE(0x1998, ESS_MAESTRO3), M3_DEVICE(0x199a, ESS_MAESTRO3HW), {0,}};MODULE_DEVICE_TABLE (pci, m3_id_table);/* * reports seem to indicate that the m3 is limited * to 28bit bus addresses. aaaargggh... */#define M3_PCI_DMA_MASK 0x0fffffffstatic 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 struct m3_card *devs;/* * I'm not very good at laying out functions in a file :) */static int m3_notifier(struct notifier_block *nb, unsigned long event, void *buf);static int m3_suspend(struct pci_dev *pci_dev, u32 state);static void check_suspend(struct m3_card *card);struct notifier_block m3_reboot_nb = {m3_notifier, NULL, 0};static void m3_outw(struct m3_card *card, u16 value, unsigned long reg){ check_suspend(card); outw(value, card->iobase + reg);}static u16 m3_inw(struct m3_card *card, unsigned long reg){ check_suspend(card); return inw(card->iobase + reg);}static void m3_outb(struct m3_card *card, u8 value, unsigned long reg){ check_suspend(card); outb(value, card->iobase + reg);}static u8 m3_inb(struct m3_card *card, unsigned long reg){ check_suspend(card); return inb(card->iobase + reg);}/* * access 16bit words to the code or data regions of the dsp's memory. * index addresses 16bit words. */static u16 __m3_assp_read(struct m3_card *card, u16 region, u16 index){ m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); m3_outw(card, index, DSP_PORT_MEMORY_INDEX); return m3_inw(card, DSP_PORT_MEMORY_DATA);}static u16 m3_assp_read(struct m3_card *card, u16 region, u16 index){ unsigned long flags; u16 ret; spin_lock_irqsave(&(card->lock), flags); ret = __m3_assp_read(card, region, index); spin_unlock_irqrestore(&(card->lock), flags); return ret;}static void __m3_assp_write(struct m3_card *card, u16 region, u16 index, u16 data){ m3_outw(card, region & MEMTYPE_MASK, DSP_PORT_MEMORY_TYPE); m3_outw(card, index, DSP_PORT_MEMORY_INDEX); m3_outw(card, data, DSP_PORT_MEMORY_DATA);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -