📄 sonicvibes.c
字号:
/*****************************************************************************//* * sonicvibes.c -- S3 Sonic Vibes 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. * * Special thanks to David C. Niemi * * * 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 * * The card has both an FM and a Wavetable synth, but I have to figure * out first how to drive them... * * Revision history * 06.05.1998 0.1 Initial release * 10.05.1998 0.2 Fixed many bugs, esp. ADC rate calculation * First stab at a simple midi interface (no bells&whistles) * 13.05.1998 0.3 Fix stupid cut&paste error: set_adc_rate was called instead of * set_dac_rate in the FMODE_WRITE case in sv_open * Fix hwptr out of bounds (now mpg123 works) * 14.05.1998 0.4 Don't allow excessive interrupt rates * 08.06.1998 0.5 First release using Alan Cox' soundcore instead of miscdevice * 03.08.1998 0.6 Do not include modversions.h * Now mixer behaviour can basically be selected between * "OSS documented" and "OSS actual" behaviour * 31.08.1998 0.7 Fix realplayer problems - dac.count issues * 10.12.1998 0.8 Fix drain_dac trying to wait on not yet initialized DMA * 16.12.1998 0.9 Fix a few f_file & FMODE_ bugs * 06.01.1999 0.10 remove the silly SA_INTERRUPT flag. * hopefully killed the egcs section type conflict * 12.03.1999 0.11 cinfo.blocks should be reset after GETxPTR ioctl. * reported by Johan Maes <joma@telindus.be> * 22.03.1999 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK * read/write cannot be executed * 05.04.1999 0.13 added code to sv_read and sv_write which should detect * lockups of the sound chip and revive it. This is basically * an ugly hack, but at least applications using this driver * won't hang forever. I don't know why these lockups happen, * it might well be the motherboard chipset (an early 486 PCI * board with ALI chipset), since every busmastering 100MB * ethernet card I've tried (Realtek 8139 and Macronix tulip clone) * exhibit similar behaviour (they work for a couple of packets * and then lock up and can be revived by ifconfig down/up). * 07.04.1999 0.14 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> * Note: dmaio hack might still be wrong on archs other than i386 * 15.06.1999 0.15 Fix bad allocation bug. * Thanks to Deti Fliegl <fliegl@in.tum.de> * 28.06.1999 0.16 Add pci_set_master * 03.08.1999 0.17 adapt to Linus' new __setup/__initcall * added kernel command line options "sonicvibes=reverb" and "sonicvibesdmaio=dmaioaddr" * 12.08.1999 0.18 module_init/__setup fixes * 24.08.1999 0.19 get rid of the dmaio kludge, replace with allocate_resource * 31.08.1999 0.20 add spin_lock_init * use new resource allocation to allocate DDMA IO space * replaced current->state = x with set_current_state(x) * 03.09.1999 0.21 change read semantics for MIDI to match * OSS more closely; remove possible wakeup race * 28.10.1999 0.22 More waitqueue races fixed * 01.12.1999 0.23 New argument to allocate_resource * 07.12.1999 0.24 More allocate_resource semantics change * 08.01.2000 0.25 Prevent some ioctl's from returning bad count values on underrun/overrun; * Tim Janik's BSE (Bedevilled Sound Engine) found this * use Martin Mares' pci_assign_resource * 07.02.2000 0.26 Use pci_alloc_consistent and pci_register_driver * 21.11.2000 0.27 Initialize dma buffers in poll, otherwise poll may return a bogus mask * 12.12.2000 0.28 More dma buffer initializations, patch from * Tjeerd Mulder <tjeerd.mulder@fujitsu-siemens.com> * 31.01.2001 0.29 Register/Unregister gameport * Fix SETTRIGGER non OSS API conformity * 18.05.2001 0.30 PCI probing and error values cleaned up by 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 <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_S3#define PCI_VENDOR_ID_S3 0x5333#endif#ifndef PCI_DEVICE_ID_S3_SONICVIBES#define PCI_DEVICE_ID_S3_SONICVIBES 0xca00#endif#define SV_MAGIC ((PCI_VENDOR_ID_S3<<16)|PCI_DEVICE_ID_S3_SONICVIBES)#define SV_EXTENT_SB 0x10#define SV_EXTENT_ENH 0x10#define SV_EXTENT_SYNTH 0x4#define SV_EXTENT_MIDI 0x4#define SV_EXTENT_GAME 0x8#define SV_EXTENT_DMA 0x10/* * we are not a bridge and thus use a resource for DDMA that is used for bridges but * left empty for normal devices */#define RESOURCE_SB 0#define RESOURCE_ENH 1#define RESOURCE_SYNTH 2#define RESOURCE_MIDI 3#define RESOURCE_GAME 4#define RESOURCE_DDMA 7#define SV_MIDI_DATA 0#define SV_MIDI_COMMAND 1#define SV_MIDI_STATUS 1#define SV_DMA_ADDR0 0#define SV_DMA_ADDR1 1#define SV_DMA_ADDR2 2#define SV_DMA_ADDR3 3#define SV_DMA_COUNT0 4#define SV_DMA_COUNT1 5#define SV_DMA_COUNT2 6#define SV_DMA_MODE 0xb#define SV_DMA_RESET 0xd#define SV_DMA_MASK 0xf/* * DONT reset the DMA controllers unless you understand * the reset semantics. Assuming reset semantics as in * the 8237 does not work. */#define DMA_MODE_AUTOINIT 0x10#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */#define SV_CODEC_CONTROL 0#define SV_CODEC_INTMASK 1#define SV_CODEC_STATUS 2#define SV_CODEC_IADDR 4#define SV_CODEC_IDATA 5#define SV_CCTRL_RESET 0x80#define SV_CCTRL_INTADRIVE 0x20#define SV_CCTRL_WAVETABLE 0x08#define SV_CCTRL_REVERB 0x04#define SV_CCTRL_ENHANCED 0x01#define SV_CINTMASK_DMAA 0x01#define SV_CINTMASK_DMAC 0x04#define SV_CINTMASK_SPECIAL 0x08#define SV_CINTMASK_UPDOWN 0x40#define SV_CINTMASK_MIDI 0x80#define SV_CSTAT_DMAA 0x01#define SV_CSTAT_DMAC 0x04#define SV_CSTAT_SPECIAL 0x08#define SV_CSTAT_UPDOWN 0x40#define SV_CSTAT_MIDI 0x80#define SV_CIADDR_TRD 0x80#define SV_CIADDR_MCE 0x40/* codec indirect registers */#define SV_CIMIX_ADCINL 0x00#define SV_CIMIX_ADCINR 0x01#define SV_CIMIX_AUX1INL 0x02#define SV_CIMIX_AUX1INR 0x03#define SV_CIMIX_CDINL 0x04#define SV_CIMIX_CDINR 0x05#define SV_CIMIX_LINEINL 0x06#define SV_CIMIX_LINEINR 0x07#define SV_CIMIX_MICIN 0x08#define SV_CIMIX_SYNTHINL 0x0A#define SV_CIMIX_SYNTHINR 0x0B#define SV_CIMIX_AUX2INL 0x0C#define SV_CIMIX_AUX2INR 0x0D#define SV_CIMIX_ANALOGINL 0x0E#define SV_CIMIX_ANALOGINR 0x0F#define SV_CIMIX_PCMINL 0x10#define SV_CIMIX_PCMINR 0x11#define SV_CIGAMECONTROL 0x09#define SV_CIDATAFMT 0x12#define SV_CIENABLE 0x13#define SV_CIUPDOWN 0x14#define SV_CIREVISION 0x15#define SV_CIADCOUTPUT 0x16#define SV_CIDMAABASECOUNT1 0x18#define SV_CIDMAABASECOUNT0 0x19#define SV_CIDMACBASECOUNT1 0x1c#define SV_CIDMACBASECOUNT0 0x1d#define SV_CIPCMSR0 0x1e#define SV_CIPCMSR1 0x1f#define SV_CISYNTHSR0 0x20#define SV_CISYNTHSR1 0x21#define SV_CIADCCLKSOURCE 0x22#define SV_CIADCALTSR 0x23#define SV_CIADCPLLM 0x24#define SV_CIADCPLLN 0x25#define SV_CISYNTHPLLM 0x26#define SV_CISYNTHPLLN 0x27#define SV_CIUARTCONTROL 0x2a#define SV_CIDRIVECONTROL 0x2b#define SV_CISRSSPACE 0x2c#define SV_CISRSCENTER 0x2d#define SV_CIWAVETABLESRC 0x2e#define SV_CIANALOGPWRDOWN 0x30#define SV_CIDIGITALPWRDOWN 0x31#define SV_CIMIX_ADCSRC_CD 0x20#define SV_CIMIX_ADCSRC_DAC 0x40#define SV_CIMIX_ADCSRC_AUX2 0x60#define SV_CIMIX_ADCSRC_LINE 0x80#define SV_CIMIX_ADCSRC_AUX1 0xa0#define SV_CIMIX_ADCSRC_MIC 0xc0#define SV_CIMIX_ADCSRC_MIXOUT 0xe0#define SV_CIMIX_ADCSRC_MASK 0xe0#define SV_CFMT_STEREO 0x01#define SV_CFMT_16BIT 0x02#define SV_CFMT_MASK 0x03#define SV_CFMT_ASHIFT 0 #define SV_CFMT_CSHIFT 4static const unsigned sample_size[] = { 1, 2, 2, 4 };static const unsigned sample_shift[] = { 0, 1, 1, 2 };#define SV_CENABLE_PPE 0x4#define SV_CENABLE_RE 0x2#define SV_CENABLE_PE 0x1/* MIDI buffer sizes */#define MIDIINBUF 256#define MIDIOUTBUF 256#define FMODE_MIDI_SHIFT 2#define FMODE_MIDI_READ (FMODE_READ << FMODE_MIDI_SHIFT)#define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)#define FMODE_DMFM 0x10/* --------------------------------------------------------------------- */struct sv_state { /* magic */ unsigned int magic; /* list of sonicvibes devices */ struct list_head devs; /* 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 iosb, ioenh, iosynth, iomidi; /* long for SPARC */ unsigned int iodmaa, iodmac, irq; /* mixer stuff */ struct { unsigned int modcnt;#ifndef OSS_DOCUMENTED_MIXER_SEMANTICS unsigned short vol[13];#endif /* OSS_DOCUMENTED_MIXER_SEMANTICS */ } mix; /* wave stuff */ unsigned int rateadc, ratedac; unsigned char fmt, enable; 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 LIST_HEAD(devs);static unsigned long wavetable_mem = 0;/* --------------------------------------------------------------------- */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;}/* * hweightN: returns the hamming weight (i.e. the number * of bits set) of a N-bit word */#ifdef hweight32#undef hweight32#endifstatic inline unsigned int hweight32(unsigned int w){ unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); res = (res & 0x33333333) + ((res >> 2) & 0x33333333); res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);}/* --------------------------------------------------------------------- *//* * Why use byte IO? Nobody knows, but S3 does it also in their Windows driver. */#undef DMABYTEIOstatic void set_dmaa(struct sv_state *s, unsigned int addr, unsigned int count){#ifdef DMABYTEIO unsigned io = s->iodmaa, u; count--; for (u = 4; u > 0; u--, addr >>= 8, io++) outb(addr & 0xff, io); for (u = 3; u > 0; u--, count >>= 8, io++) outb(count & 0xff, io);#else /* DMABYTEIO */ count--; outl(addr, s->iodmaa + SV_DMA_ADDR0); outl(count, s->iodmaa + SV_DMA_COUNT0);#endif /* DMABYTEIO */ outb(0x18, s->iodmaa + SV_DMA_MODE);}static void set_dmac(struct sv_state *s, unsigned int addr, unsigned int count){#ifdef DMABYTEIO unsigned io = s->iodmac, u; count >>= 1; count--; for (u = 4; u > 0; u--, addr >>= 8, io++)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -