📄 cmpci.c
字号:
/*****************************************************************************//* * cmpci.c -- C-Media PCI audio driver. * * Copyright (C) 1999 ChenLi Tien (cltien@cmedia.com.tw) * C-media support (support@cmedia.com.tw) * * Based on the PCI drivers by Thomas Sailer (sailer@ife.ee.ethz.ch) * * For update, visit: * http://members.home.net/puresoft/cmedia.html * http://www.cmedia.com.tw * * 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 * 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. * * Fixes: * Arnaldo Carvalho de Melo <acme@conectiva.com.br> * 18/05/2001 - .bss nitpicks, fix a bug in set_dac_channels where it * was calling prog_dmabuf with s->lock held, call missing * unlock_kernel in cm_midi_release * 08/10/2001 - use set_current_state in some more places * * Carlos Eduardo Gorges <carlos@techlinux.com.br> * Fri May 25 2001 * - SMP support ( spin[un]lock* revision ) * - speaker mixer support * Mon Aug 13 2001 * - optimizations and cleanups * *//*****************************************************************************/ #include <linux/version.h>#include <linux/config.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/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 <linux/bitops.h>#include "dm.h"/* --------------------------------------------------------------------- */#undef OSS_DOCUMENTED_MIXER_SEMANTICS#undef DMABYTEIO/* --------------------------------------------------------------------- */#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)/* Mixer registers for SB16 ******************/#define DSP_MIX_DATARESETIDX ((unsigned char)(0x00))#define DSP_MIX_MASTERVOLIDX_L ((unsigned char)(0x30))#define DSP_MIX_MASTERVOLIDX_R ((unsigned char)(0x31))#define DSP_MIX_VOICEVOLIDX_L ((unsigned char)(0x32))#define DSP_MIX_VOICEVOLIDX_R ((unsigned char)(0x33))#define DSP_MIX_FMVOLIDX_L ((unsigned char)(0x34))#define DSP_MIX_FMVOLIDX_R ((unsigned char)(0x35))#define DSP_MIX_CDVOLIDX_L ((unsigned char)(0x36))#define DSP_MIX_CDVOLIDX_R ((unsigned char)(0x37))#define DSP_MIX_LINEVOLIDX_L ((unsigned char)(0x38))#define DSP_MIX_LINEVOLIDX_R ((unsigned char)(0x39))#define DSP_MIX_MICVOLIDX ((unsigned char)(0x3A))#define DSP_MIX_SPKRVOLIDX ((unsigned char)(0x3B))#define DSP_MIX_OUTMIXIDX ((unsigned char)(0x3C))#define DSP_MIX_ADCMIXIDX_L ((unsigned char)(0x3D))#define DSP_MIX_ADCMIXIDX_R ((unsigned char)(0x3E))#define DSP_MIX_INGAINIDX_L ((unsigned char)(0x3F))#define DSP_MIX_INGAINIDX_R ((unsigned char)(0x40))#define DSP_MIX_OUTGAINIDX_L ((unsigned char)(0x41))#define DSP_MIX_OUTGAINIDX_R ((unsigned char)(0x42))#define DSP_MIX_AGCIDX ((unsigned char)(0x43))#define DSP_MIX_TREBLEIDX_L ((unsigned char)(0x44))#define DSP_MIX_TREBLEIDX_R ((unsigned char)(0x45))#define DSP_MIX_BASSIDX_L ((unsigned char)(0x46))#define DSP_MIX_BASSIDX_R ((unsigned char)(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 2#define CM_CFMT_ADCSHIFT 0static const unsigned sample_shift[] = { 0, 1, 1, 2 };#define CM_ENABLE_CH1 0x2#define CM_ENABLE_CH0 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#define SND_DEV_DSP16 5 #define NR_DEVICE 3 /* maximum number of devices *//*********************************************/struct cm_state { unsigned int magic; /* magic */ struct cm_state *next; /* we keep cm cards in a linked list */ int dev_audio; /* soundcore stuff */ int dev_mixer; int dev_midi; int dev_dmfm; unsigned int iosb, iobase, iosynth, iomidi, iogame, irq; /* hardware resources */ unsigned short deviceid; /* pci_id */ struct { /* mixer stuff */ unsigned int modcnt; unsigned short vol[13]; } mix; unsigned int rateadc, ratedac; /* wave stuff */ 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 rawphys; unsigned buforder; unsigned numfrag; unsigned fragshift; unsigned hwptr, swptr; unsigned total_bytes; int count; unsigned error; /* over/underrun */ wait_queue_head_t wait; unsigned fragsize; /* redundant, but makes calculations easier */ unsigned dmasize; unsigned fragsamples; unsigned dmasamples; unsigned mapped:1; /* OSS stuff */ unsigned ready:1; unsigned endcleared:1; unsigned ossfragshift; int ossmaxfrags; unsigned subdivision; } dma_dac, dma_adc; struct { /* midi stuff */ 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; int chip_version; int max_channels; int curr_channels; int speakers; /* number of speakers */ int capability; /* HW capability, various for chip versions */ int status; /* HW or SW state */ int spdif_counter; /* spdif frame counter */};/* flags used for capability */#define CAN_AC3_HW 0x00000001 /* 037 or later */#define CAN_AC3_SW 0x00000002 /* 033 or later */#define CAN_AC3 (CAN_AC3_HW | CAN_AC3_SW)#define CAN_DUAL_DAC 0x00000004 /* 033 or later */#define CAN_MULTI_CH_HW 0x00000008 /* 039 or later */#define CAN_MULTI_CH (CAN_MULTI_CH_HW | CAN_DUAL_DAC)#define CAN_LINE_AS_REAR 0x00000010 /* 033 or later */#define CAN_LINE_AS_BASS 0x00000020 /* 039 or later */#define CAN_MIC_AS_BASS 0x00000040 /* 039 or later *//* flags used for status */#define DO_AC3_HW 0x00000001#define DO_AC3_SW 0x00000002#define DO_AC3 (DO_AC3_HW | DO_AC3_SW)#define DO_DUAL_DAC 0x00000004#define DO_MULTI_CH_HW 0x00000008#define DO_MULTI_CH (DO_MULTI_CH_HW | DO_DUAL_DAC)#define DO_LINE_AS_REAR 0x00000010 /* 033 or later */#define DO_LINE_AS_BASS 0x00000020 /* 039 or later */#define DO_MIC_AS_BASS 0x00000040 /* 039 or later */#define DO_SPDIF_OUT 0x00000100#define DO_SPDIF_IN 0x00000200#define DO_SPDIF_LOOP 0x00000400static struct cm_state *devs;static unsigned long wavetable_mem;/* --------------------------------------------------------------------- */static inline unsigned ld2(unsigned int x){ unsigned exp=16,l=5,r=0; static const unsigned num[]={0x2,0x4,0x10,0x100,0x10000}; /* num: 2, 4, 16, 256, 65536 */ /* exp: 1, 2, 4, 8, 16 */ while(l--) { if( x >= num[l] ) { if(num[l]>2) x >>= exp; r+=exp; } exp>>=1; } return r;}/* --------------------------------------------------------------------- */static void maskb(unsigned int addr, unsigned int mask, unsigned int value){ outb((inb(addr) & mask) | value, addr);}static void maskw(unsigned int addr, unsigned int mask, unsigned int value){ outw((inw(addr) & mask) | value, addr);}static void maskl(unsigned int addr, unsigned int mask, unsigned int value){ outl((inl(addr) & mask) | value, addr);}static void set_dmadac1(struct cm_state *s, unsigned int addr, unsigned int count){ if (addr) outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1); outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2); maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~1, 0);}static void set_dmaadc(struct cm_state *s, unsigned int addr, unsigned int count){ outl(addr, s->iobase + CODEC_CMI_CH0_FRAME1); outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2); maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~0, 1);}static void set_dmadac(struct cm_state *s, unsigned int addr, unsigned int count){ outl(addr, s->iobase + CODEC_CMI_CH1_FRAME1); outw(count - 1, s->iobase + CODEC_CMI_CH1_FRAME2); maskb(s->iobase + CODEC_CMI_FUNCTRL0, ~2, 0); if (s->status & DO_DUAL_DAC) set_dmadac1(s, 0, count);}static void set_countadc(struct cm_state *s, unsigned count){ outw(count - 1, s->iobase + CODEC_CMI_CH0_FRAME2 + 2);}static void set_countdac(struct cm_state *s, unsigned count){ outw(count - 1, s->iobase + CODEC_CMI_CH1_FRAME2 + 2); if (s->status & DO_DUAL_DAC) set_countadc(s, count);}static inline unsigned get_dmadac(struct cm_state *s){ unsigned int curr_addr; curr_addr = inw(s->iobase + CODEC_CMI_CH1_FRAME2) + 1; curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_DACSHIFT) & CM_CFMT_MASK]; curr_addr = s->dma_dac.dmasize - curr_addr; return curr_addr;}static inline unsigned get_dmaadc(struct cm_state *s){ unsigned int curr_addr; curr_addr = inw(s->iobase + CODEC_CMI_CH0_FRAME2) + 1; curr_addr <<= sample_shift[(s->fmt >> CM_CFMT_ADCSHIFT) & CM_CFMT_MASK]; curr_addr = s->dma_adc.dmasize - curr_addr; 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; unsigned long flags; spin_lock_irqsave(&s->lock, flags); outb(idx, s->iobase + CODEC_SB16_ADDR); udelay(10); v = inb(s->iobase + CODEC_SB16_DATA); udelay(10); spin_unlock_irqrestore(&s->lock, flags); return v;}static void set_fmt_unlocked(struct cm_state *s, unsigned char mask, unsigned char data){ 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); udelay(10);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -