📄 ali5455.c
字号:
/* * ALI ali5455 and friends ICH driver for Linux * LEI HU <Lei_Hu@ali.com.tw> * * Built from: * drivers/sound/i810_audio * * The ALi 5455 is similar but not quite identical to the Intel ICH * series of controllers. Its easier to keep the driver separated from * the i810 driver. * * 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. * * * ALi 5455 theory of operation * * The chipset provides three DMA channels that talk to an AC97 * CODEC (AC97 is a digital/analog mixer standard). At its simplest * you get 48Khz audio with basic volume and mixer controls. At the * best you get rate adaption in the codec. We set the card up so * that we never take completion interrupts but instead keep the card * chasing its tail around a ring buffer. This is needed for mmap * mode audio and happens to work rather well for non-mmap modes too. * * The board has one output channel for PCM audio (supported) and * a stereo line in and mono microphone input. Again these are normally * locked to 48Khz only. Right now recording is not finished. * * There is no midi support, no synth support. Use timidity. To get * esd working you need to use esd -r 48000 as it won't probe 48KHz * by default. mpg123 can't handle 48Khz only audio so use xmms. * * If you need to force a specific rate set the clocking= option * */#include <linux/module.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 <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/ac97_codec.h>#include <linux/interrupt.h>#include <asm/uaccess.h>#ifndef PCI_DEVICE_ID_ALI_5455#define PCI_DEVICE_ID_ALI_5455 0x5455#endif#ifndef PCI_VENDOR_ID_ALI#define PCI_VENDOR_ID_ALI 0x10b9#endifstatic int strict_clocking = 0;static unsigned int clocking = 0;static unsigned int codec_pcmout_share_spdif_locked = 0;static unsigned int codec_independent_spdif_locked = 0;static unsigned int controller_pcmout_share_spdif_locked = 0;static unsigned int controller_independent_spdif_locked = 0;static unsigned int globel = 0;#define ADC_RUNNING 1#define DAC_RUNNING 2#define CODEC_SPDIFOUT_RUNNING 8#define CONTROLLER_SPDIFOUT_RUNNING 4#define SPDIF_ENABLE_OUTPUT 4 /* bits 0,1 are PCM */#define ALI5455_FMT_16BIT 1#define ALI5455_FMT_STEREO 2#define ALI5455_FMT_MASK 3#define SPDIF_ON 0x0004#define SURR_ON 0x0010#define CENTER_LFE_ON 0x0020#define VOL_MUTED 0x8000#define ALI_SPDIF_OUT_CH_STATUS 0xbf/* the 810's array of pointers to data buffers */struct sg_item {#define BUSADDR_MASK 0xFFFFFFFE u32 busaddr;#define CON_IOC 0x80000000 /* interrupt on completion */#define CON_BUFPAD 0x40000000 /* pad underrun with last sample, else 0 */#define CON_BUFLEN_MASK 0x0000ffff /* buffer length in samples */ u32 control;};/* an instance of the ali channel */#define SG_LEN 32struct ali_channel { /* these sg guys should probably be allocated separately as nocache. Must be 8 byte aligned */ struct sg_item sg[SG_LEN]; /* 32*8 */ u32 offset; /* 4 */ u32 port; /* 4 */ u32 used; u32 num;};/* * we have 3 separate dma engines. pcm in, pcm out, and mic. * each dma engine has controlling registers. These goofy * names are from the datasheet, but make it easy to write * code while leafing through it. */#define ENUM_ENGINE(PRE,DIG) \enum { \ PRE##_BDBAR = 0x##DIG##0, /* Buffer Descriptor list Base Address */ \ PRE##_CIV = 0x##DIG##4, /* Current Index Value */ \ PRE##_LVI = 0x##DIG##5, /* Last Valid Index */ \ PRE##_SR = 0x##DIG##6, /* Status Register */ \ PRE##_PICB = 0x##DIG##8, /* Position In Current Buffer */ \ PRE##_CR = 0x##DIG##b /* Control Register */ \}ENUM_ENGINE(OFF, 0); /* Offsets */ENUM_ENGINE(PI, 4); /* PCM In */ENUM_ENGINE(PO, 5); /* PCM Out */ENUM_ENGINE(MC, 6); /* Mic In */ENUM_ENGINE(CODECSPDIFOUT, 7); /* CODEC SPDIF OUT */ENUM_ENGINE(CONTROLLERSPDIFIN, A); /* CONTROLLER SPDIF In */ENUM_ENGINE(CONTROLLERSPDIFOUT, B); /* CONTROLLER SPDIF OUT */enum { ALI_SCR = 0x00, /* System Control Register */ ALI_SSR = 0x04, /* System Status Register */ ALI_DMACR = 0x08, /* DMA Control Register */ ALI_FIFOCR1 = 0x0c, /* FIFO Control Register 1 */ ALI_INTERFACECR = 0x10, /* Interface Control Register */ ALI_INTERRUPTCR = 0x14, /* Interrupt control Register */ ALI_INTERRUPTSR = 0x18, /* Interrupt Status Register */ ALI_FIFOCR2 = 0x1c, /* FIFO Control Register 2 */ ALI_CPR = 0x20, /* Command Port Register */ ALI_SPR = 0x24, /* Status Port Register */ ALI_FIFOCR3 = 0x2c, /* FIFO Control Register 3 */ ALI_TTSR = 0x30, /* Transmit Tag Slot Register */ ALI_RTSR = 0x34, /* Receive Tag Slot Register */ ALI_CSPSR = 0x38, /* Command/Status Port Status Register */ ALI_CAS = 0x3c, /* Codec Write Semaphore Register */ ALI_SPDIFCSR = 0xf8, /* spdif channel status register */ ALI_SPDIFICS = 0xfc /* spdif interface control/status */};// x-status register(x:pcm in ,pcm out, mic in,)/* interrupts for a dma engine */#define DMA_INT_FIFO (1<<4) /* fifo under/over flow */#define DMA_INT_COMPLETE (1<<3) /* buffer read/write complete and ioc set */#define DMA_INT_LVI (1<<2) /* last valid done */#define DMA_INT_CELV (1<<1) /* last valid is current */#define DMA_INT_DCH (1) /* DMA Controller Halted (happens on LVI interrupts) */ //not eqult intel#define DMA_INT_MASK (DMA_INT_FIFO|DMA_INT_COMPLETE|DMA_INT_LVI)/* interrupts for the whole chip */// by interrupt status register finish#define INT_SPDIFOUT (1<<23) /* controller spdif out INTERRUPT */#define INT_SPDIFIN (1<<22)#define INT_CODECSPDIFOUT (1<<19)#define INT_MICIN (1<<18)#define INT_PCMOUT (1<<17)#define INT_PCMIN (1<<16)#define INT_CPRAIS (1<<7)#define INT_SPRAIS (1<<5)#define INT_GPIO (1<<1)#define INT_MASK (INT_SPDIFOUT|INT_CODECSPDIFOUT|INT_MICIN|INT_PCMOUT|INT_PCMIN)#define DRIVER_VERSION "0.02ac"/* magic numbers to protect our data structures */#define ALI5455_CARD_MAGIC 0x5072696E /* "Prin" */#define ALI5455_STATE_MAGIC 0x63657373 /* "cess" */#define ALI5455_DMA_MASK 0xffffffff /* DMA buffer mask for pci_alloc_consist */#define NR_HW_CH 5 //I think 5 channel/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */#define NR_AC97 2/* Please note that an 8bit mono stream is not valid on this card, you must have a 16bit *//* stream at a minimum for this card to be happy */static const unsigned sample_size[] = { 1, 2, 2, 4 };/* Samples are 16bit values, so we are shifting to a word, not to a byte, hence shift *//* values are one less than might be expected */static const unsigned sample_shift[] = { -1, 0, 0, 1 };#define ALI5455static char *card_names[] = { "ALI 5455"};static struct pci_device_id ali_pci_tbl[] = { {PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ALI5455}, {0,}};MODULE_DEVICE_TABLE(pci, ali_pci_tbl);#ifdef CONFIG_PM#define PM_SUSPENDED(card) (card->pm_suspended)#else#define PM_SUSPENDED(card) (0)#endif/* "software" or virtual channel, an instance of opened /dev/dsp */struct ali_state { unsigned int magic; struct ali_card *card; /* Card info */ /* single open lock mechanism, only used for recording */ struct semaphore open_sem; wait_queue_head_t open_wait; /* file mode */ mode_t open_mode; /* virtual channel number */ int virt;#ifdef CONFIG_PM unsigned int pm_saved_dac_rate, pm_saved_adc_rate;#endif struct dmabuf { /* wave sample stuff */ unsigned int rate; unsigned char fmt, enable, trigger; /* hardware channel */ struct ali_channel *read_channel; struct ali_channel *write_channel; struct ali_channel *codec_spdifout_channel; struct ali_channel *controller_spdifout_channel; /* OSS buffer management stuff */ void *rawbuf; dma_addr_t dma_handle; unsigned buforder; unsigned numfrag; unsigned fragshift; /* our buffer acts like a circular ring */ unsigned hwptr; /* where dma last started, updated by update_ptr */ unsigned swptr; /* where driver last clear/filled, updated by read/write */ int count; /* bytes to be consumed or been generated by dma machine */ unsigned total_bytes; /* total bytes dmaed by hardware */ unsigned error; /* number of over/underruns */ wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ /* redundant, but makes calculations easier */ /* what the hardware uses */ unsigned dmasize; unsigned fragsize; unsigned fragsamples; /* what we tell the user to expect */ unsigned userfrags; unsigned userfragsize; /* OSS stuff */ unsigned mapped:1; unsigned ready:1; unsigned update_flag; unsigned ossfragsize; unsigned ossmaxfrags; unsigned subdivision; } dmabuf;};struct ali_card { struct ali_channel channel[5]; unsigned int magic; /* We keep ali5455 cards in a linked list */ struct ali_card *next; /* The ali has a certain amount of cross channel interaction so we use a single per card lock */ spinlock_t lock; spinlock_t ac97_lock; /* PCI device stuff */ struct pci_dev *pci_dev; u16 pci_id;#ifdef CONFIG_PM u16 pm_suspended; u32 pm_save_state[64 / sizeof(u32)]; int pm_saved_mixer_settings[SOUND_MIXER_NRDEVICES][NR_AC97];#endif /* soundcore stuff */ int dev_audio; /* structures for abstraction of hardware facilities, codecs, banks and channels */ struct ac97_codec *ac97_codec[NR_AC97]; struct ali_state *states[NR_HW_CH]; u16 ac97_features; u16 ac97_status; u16 channels; /* hardware resources */ unsigned long iobase; u32 irq; /* Function support */ struct ali_channel *(*alloc_pcm_channel) (struct ali_card *); struct ali_channel *(*alloc_rec_pcm_channel) (struct ali_card *); struct ali_channel *(*alloc_rec_mic_channel) (struct ali_card *); struct ali_channel *(*alloc_codec_spdifout_channel) (struct ali_card *); struct ali_channel *(*alloc_controller_spdifout_channel) (struct ali_card *); void (*free_pcm_channel) (struct ali_card *, int chan); /* We have a *very* long init time possibly, so use this to block */ /* attempts to open our devices before we are ready (stops oops'es) */ int initializing;};static struct ali_card *devs = NULL;static int ali_open_mixdev(struct inode *inode, struct file *file);static int ali_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);static u16 ali_ac97_get(struct ac97_codec *dev, u8 reg);static void ali_ac97_set(struct ac97_codec *dev, u8 reg, u16 data);static struct ali_channel *ali_alloc_pcm_channel(struct ali_card *card){ if (card->channel[1].used == 1) return NULL; card->channel[1].used = 1; return &card->channel[1];}static struct ali_channel *ali_alloc_rec_pcm_channel(struct ali_card *card){ if (card->channel[0].used == 1) return NULL; card->channel[0].used = 1; return &card->channel[0];}static struct ali_channel *ali_alloc_rec_mic_channel(struct ali_card *card){ if (card->channel[2].used == 1) return NULL; card->channel[2].used = 1; return &card->channel[2];}static struct ali_channel *ali_alloc_codec_spdifout_channel(struct ali_card *card){ if (card->channel[3].used == 1) return NULL; card->channel[3].used = 1; return &card->channel[3];}static struct ali_channel *ali_alloc_controller_spdifout_channel(struct ali_card *card){ if (card->channel[4].used == 1) return NULL; card->channel[4].used = 1; return &card->channel[4];}static void ali_free_pcm_channel(struct ali_card *card, int channel){ card->channel[channel].used = 0;}//add support codec spdif out static int ali_valid_spdif_rate(struct ac97_codec *codec, int rate){ unsigned long id = 0L; id = (ali_ac97_get(codec, AC97_VENDOR_ID1) << 16); id |= ali_ac97_get(codec, AC97_VENDOR_ID2) & 0xffff; switch (id) { case 0x41445361: /* AD1886 */ if (rate == 48000) { return 1; } break; case 0x414c4720: /* ALC650 */ if (rate == 48000) { return 1; } break; default: /* all other codecs, until we know otherwiae */ if (rate == 48000 || rate == 44100 || rate == 32000) { return 1; } break; } return (0);}/* ali_set_spdif_output * * Configure the S/PDIF output transmitter. When we turn on * S/PDIF, we turn off the analog output. This may not be * the right thing to do. * * Assumptions: * The DSP sample rate must already be set to a supported * S/PDIF rate (32kHz, 44.1kHz, or 48kHz) or we abort. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -