📄 cmpci.c
字号:
/*****************************************************************************//* * cmpci.c -- C-Media PCI audio driver. * * Copyright (C) 1999 ChenLi Tien (cltien@home.com) * * Based on the PCI drivers by Thomas Sailer (sailer@ife.ee.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, Jan Pfeifer * * * 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.98 0.1 Initial release * 10.05.98 0.2 Fixed many bugs, esp. ADC rate calculation * First stab at a simple midi interface (no bells&whistles) * 13.05.98 0.3 Fix stupid cut&paste error: set_adc_rate was called instead of * set_dac_rate in the FMODE_WRITE case in cm_open * Fix hwptr out of bounds (now mpg123 works) * 14.05.98 0.4 Don't allow excessive interrupt rates * 08.06.98 0.5 First release using Alan Cox' soundcore instead of miscdevice * 03.08.98 0.6 Do not include modversions.h * Now mixer behaviour can basically be selected between * "OSS documented" and "OSS actual" behaviour * 31.08.98 0.7 Fix realplayer problems - dac.count issues * 10.12.98 0.8 Fix drain_dac trying to wait on not yet initialized DMA * 16.12.98 0.9 Fix a few f_file & FMODE_ bugs * 06.01.99 0.10 remove the silly SA_INTERRUPT flag. * hopefully killed the egcs section type conflict * 12.03.99 0.11 cinfo.blocks should be reset after GETxPTR ioctl. * reported by Johan Maes <joma@telindus.be> * 22.03.99 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK * read/write cannot be executed * 20 09 99 0.13 merged the generic changes in sonicvibes since this * diverged. * 18.08.99 1.5 Only deallocate DMA buffer when unloading. * 02.09.99 1.6 Enable SPDIF LOOP * Change the mixer read back * 21.09.99 2.33 Use RCS version as driver version. * Add support for modem, S/PDIF loop and 4 channels. * (8738 only) * Fix bug cause x11amp cannot play. * $Log: cmpci.c,v $ * Revision 2.41 1999/10/27 02:00:05 cltien * Now the fragsize for modem is activated by parameter. * * Revision 2.40 1999/10/26 23:38:26 cltien * Remove debugging message in cm_write which may cause module counter not 0. * * Revision 2.39 1999/10/26 21:52:50 cltien * I forgor too adjust mic recording volume, as it should be moved to 5MUTEMONO. * Change the DYNAMIC macro to FIXEDDMA, which means static DMA buffer. * * Revision 2.38 1999/10/08 21:59:03 cltien * Set FLINKON and reset FLINKOFF for modem. * * Revision 2.37 1999/09/28 02:57:04 cltien * Add set_bus_master() to make sure bus master enabled. * * Revision 2.36 1999/09/22 14:15:03 cltien * Use open_sem to avoid multiple access to open_mode. * Use wakeup in IntrClose to activate process in waiting queue. * * Revision 2.35 1999/09/22 13:20:53 cltien * Use open_mode to check if DAC in used. Also more check in IntrWrite and IntrClose. Now the modem can access DAC safely. * * Revision 2.34 1999/09/22 03:29:57 cltien * Use module count to decide which one to access the dac. * * */ /*****************************************************************************/ #include <linux/config.h>#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/malloc.h>#include <linux/soundcard.h>#include <linux/pci.h>#include <linux/wrapper.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 <asm/uaccess.h>#include <asm/hardirq.h>#include "dm.h"/* --------------------------------------------------------------------- */#undef OSS_DOCUMENTED_MIXER_SEMANTICS/* --------------------------------------------------------------------- */#ifndef PCI_VENDOR_ID_CMEDIA#define PCI_VENDOR_ID_CMEDIA 0x13F6#endif#ifndef PCI_DEVICE_ID_CMEDIA_CM8338A#define PCI_DEVICE_ID_CMEDIA_CM8338A 0x0100#endif#ifndef PCI_DEVICE_ID_CMEDIA_CM8338B#define PCI_DEVICE_ID_CMEDIA_CM8338B 0x0101#endif#ifndef PCI_DEVICE_ID_CMEDIA_CM8738#define PCI_DEVICE_ID_CMEDIA_CM8738 0x0111#endif#define CM_MAGIC ((PCI_VENDOR_ID_CMEDIA<<16)|PCI_DEVICE_ID_CMEDIA_CM8338A)/* * CM8338 registers definition */#define CODEC_CMI_FUNCTRL0 (0x00)#define CODEC_CMI_FUNCTRL1 (0x04)#define CODEC_CMI_CHFORMAT (0x08)#define CODEC_CMI_INT_HLDCLR (0x0C)#define CODEC_CMI_INT_STATUS (0x10)#define CODEC_CMI_LEGACY_CTRL (0x14)#define CODEC_CMI_MISC_CTRL (0x18)#define CODEC_CMI_TDMA_POS (0x1C)#define CODEC_CMI_MIXER (0x20)#define CODEC_SB16_DATA (0x22)#define CODEC_SB16_ADDR (0x23)#define CODEC_CMI_MIXER1 (0x24)#define CODEC_CMI_MIXER2 (0x25)#define CODEC_CMI_AUX_VOL (0x26)#define CODEC_CMI_MISC (0x27)#define CODEC_CMI_AC97 (0x28)#define CODEC_CMI_CH0_FRAME1 (0x80)#define CODEC_CMI_CH0_FRAME2 (0x84)#define CODEC_CMI_CH1_FRAME1 (0x88)#define CODEC_CMI_CH1_FRAME2 (0x8C)#define CODEC_CMI_EXT_REG (0xF0)#define UCHAR unsigned char/*** Mixer registers for SB16*/#define DSP_MIX_DATARESETIDX ((UCHAR)(0x00))#define DSP_MIX_MASTERVOLIDX_L ((UCHAR)(0x30))#define DSP_MIX_MASTERVOLIDX_R ((UCHAR)(0x31))#define DSP_MIX_VOICEVOLIDX_L ((UCHAR)(0x32))#define DSP_MIX_VOICEVOLIDX_R ((UCHAR)(0x33))#define DSP_MIX_FMVOLIDX_L ((UCHAR)(0x34))#define DSP_MIX_FMVOLIDX_R ((UCHAR)(0x35))#define DSP_MIX_CDVOLIDX_L ((UCHAR)(0x36))#define DSP_MIX_CDVOLIDX_R ((UCHAR)(0x37))#define DSP_MIX_LINEVOLIDX_L ((UCHAR)(0x38))#define DSP_MIX_LINEVOLIDX_R ((UCHAR)(0x39))#define DSP_MIX_MICVOLIDX ((UCHAR)(0x3A))#define DSP_MIX_SPKRVOLIDX ((UCHAR)(0x3B))#define DSP_MIX_OUTMIXIDX ((UCHAR)(0x3C))#define DSP_MIX_ADCMIXIDX_L ((UCHAR)(0x3D))#define DSP_MIX_ADCMIXIDX_R ((UCHAR)(0x3E))#define DSP_MIX_INGAINIDX_L ((UCHAR)(0x3F))#define DSP_MIX_INGAINIDX_R ((UCHAR)(0x40))#define DSP_MIX_OUTGAINIDX_L ((UCHAR)(0x41))#define DSP_MIX_OUTGAINIDX_R ((UCHAR)(0x42))#define DSP_MIX_AGCIDX ((UCHAR)(0x43))#define DSP_MIX_TREBLEIDX_L ((UCHAR)(0x44))#define DSP_MIX_TREBLEIDX_R ((UCHAR)(0x45))#define DSP_MIX_BASSIDX_L ((UCHAR)(0x46))#define DSP_MIX_BASSIDX_R ((UCHAR)(0x47))#define CM_CH0_RESET 0x04#define CM_CH1_RESET 0x08#define CM_EXTENT_CODEC 0x100#define CM_EXTENT_MIDI 0x2#define CM_EXTENT_SYNTH 0x4#define CM_INT_CH0 1#define CM_INT_CH1 2#define CM_CFMT_STEREO 0x01#define CM_CFMT_16BIT 0x02#define CM_CFMT_MASK 0x03#define CM_CFMT_DACSHIFT 0 #define CM_CFMT_ADCSHIFT 2static const unsigned sample_size[] = { 1, 2, 2, 4 };static const unsigned sample_shift[] = { 0, 1, 1, 2 };#define CM_CENABLE_RE 0x2#define CM_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 cm_state { /* magic */ unsigned int magic; /* we keep cm cards in a linked list */ struct cm_state *next; /* soundcore stuff */ int dev_audio; int dev_mixer; int dev_midi; int dev_dmfm; /* hardware resources */ unsigned int iosb, iobase, iosynth, iomidi, iogame, 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; 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; unsigned dmasamples; /* OSS stuff */ unsigned mapped:1; unsigned ready:1; unsigned endcleared: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;};/* --------------------------------------------------------------------- */static struct cm_state *devs = NULL;static unsigned long wavetable_mem = 0;/* --------------------------------------------------------------------- */extern __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#endifextern __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);}/* --------------------------------------------------------------------- */static void set_dmadac(struct cm_state *s, unsigned int addr, unsigned int count){ count--; outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1); outw(count, s->iobase + CODEC_CMI_CH0_FRAME2); outb(inb(s->iobase + CODEC_CMI_FUNCTRL0) & ~1, s->iobase + CODEC_CMI_FUNCTRL0);// outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 1, s->iobase + CODEC_CMI_FUNCTRL0 + 2);}static void set_dmaadc(struct cm_state *s, unsigned int addr, unsigned int count){ count--; outl(addr, s->iobase + CODEC_CMI_CH1_FRAME1); outw(count, s->iobase + CODEC_CMI_CH1_FRAME2); outb(inb(s->iobase + CODEC_CMI_FUNCTRL0) | 2, s->iobase + CODEC_CMI_FUNCTRL0);// outb(inb(s->iobase + CODEC_CMI_FUNCTRL0 + 2) | 2, s->iobase + CODEC_CMI_FUNCTRL0 + 2);}extern __inline__ unsigned get_dmadac(struct cm_state *s){ unsigned int curr_addr; if (!s->dma_dac.dmasize || !(s->enable & CM_CENABLE_PE)) return 0; curr_addr = inl(s->iobase + CODEC_CMI_CH0_FRAME1); curr_addr -= virt_to_bus(s->dma_dac.rawbuf); curr_addr = s->dma_dac.dmasize - curr_addr; curr_addr &= ~(sample_size[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]-1); return curr_addr;}extern __inline__ unsigned get_dmaadc(struct cm_state *s){ unsigned int curr_addr; if (!s->dma_adc.dmasize || !(s->enable & CM_CENABLE_RE)) return 0; curr_addr = inl(s->iobase + CODEC_CMI_CH1_FRAME1); curr_addr -= virt_to_bus(s->dma_adc.rawbuf); curr_addr = s->dma_adc.dmasize - curr_addr; curr_addr &= ~(sample_size[(s->fmt >> CM_CFMT_ADCSHIFT) & CM_CFMT_MASK]-1); return curr_addr;}static void wrmixer(struct cm_state *s, unsigned char idx, unsigned char data){ outb(idx, s->iobase + CODEC_SB16_ADDR); udelay(10); outb(data, s->iobase + CODEC_SB16_DATA); udelay(10);}static unsigned char rdmixer(struct cm_state *s, unsigned char idx){ unsigned char v; outb(idx, s->iobase + CODEC_SB16_ADDR); udelay(10); v = inb(s->iobase + CODEC_SB16_DATA); udelay(10); return v;}static void set_fmt(struct cm_state *s, unsigned char mask, unsigned char data){ unsigned long flags; spin_lock_irqsave(&s->lock, flags); if (mask) { s->fmt = inb(s->iobase + CODEC_CMI_CHFORMAT); udelay(10); } s->fmt = (s->fmt & mask) | data; outb(s->fmt, s->iobase + CODEC_CMI_CHFORMAT); spin_unlock_irqrestore(&s->lock, flags); udelay(10);}static void frobindir(struct cm_state *s, unsigned char idx, unsigned char mask, unsigned char data){ outb(idx, s->iobase + CODEC_SB16_ADDR); udelay(10); outb((inb(s->iobase + CODEC_SB16_DATA) & mask) | data, s->iobase + CODEC_SB16_DATA); udelay(10);}static struct { unsigned rate; unsigned lower; unsigned upper;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -