📄 emu10k1x.c
字号:
/* * Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com> * Driver EMU10K1X chips * * Parts of this code were adapted from audigyls.c driver which is * Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk> * * BUGS: * -- * * TODO: * * Chips (SB0200 model): * - EMU10K1X-DBQ * - STAC 9708T * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <sound/driver.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/moduleparam.h>#include <sound/core.h>#include <sound/initval.h>#include <sound/pcm.h>#include <sound/ac97_codec.h>#include <sound/info.h>#include <sound/rawmidi.h>MODULE_AUTHOR("Francisco Moraes <fmoraes@nc.rr.com>");MODULE_DESCRIPTION("EMU10K1X");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE("{{Dell Creative Labs,SB Live!}");// module parameters (see "Module Parameters")static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;module_param_array(index, int, NULL, 0444);MODULE_PARM_DESC(index, "Index value for the EMU10K1X soundcard.");module_param_array(id, charp, NULL, 0444);MODULE_PARM_DESC(id, "ID string for the EMU10K1X soundcard.");module_param_array(enable, bool, NULL, 0444);MODULE_PARM_DESC(enable, "Enable the EMU10K1X soundcard.");// some definitions were borrowed from emu10k1 driver as they seem to be the same/************************************************************************************************//* PCI function 0 registers, address = <val> + PCIBASE0 *//************************************************************************************************/#define PTR 0x00 /* Indexed register set pointer register */ /* NOTE: The CHANNELNUM and ADDRESS words can */ /* be modified independently of each other. */#define DATA 0x04 /* Indexed register set data register */#define IPR 0x08 /* Global interrupt pending register */ /* Clear pending interrupts by writing a 1 to */ /* the relevant bits and zero to the other bits */#define IPR_MIDITRANSBUFEMPTY 0x00000001 /* MIDI UART transmit buffer empty */#define IPR_MIDIRECVBUFEMPTY 0x00000002 /* MIDI UART receive buffer empty */#define IPR_CH_0_LOOP 0x00000800 /* Channel 0 loop */#define IPR_CH_0_HALF_LOOP 0x00000100 /* Channel 0 half loop */#define IPR_CAP_0_LOOP 0x00080000 /* Channel capture loop */#define IPR_CAP_0_HALF_LOOP 0x00010000 /* Channel capture half loop */#define INTE 0x0c /* Interrupt enable register */#define INTE_MIDITXENABLE 0x00000001 /* Enable MIDI transmit-buffer-empty interrupts */#define INTE_MIDIRXENABLE 0x00000002 /* Enable MIDI receive-buffer-empty interrupts */#define INTE_CH_0_LOOP 0x00000800 /* Channel 0 loop */#define INTE_CH_0_HALF_LOOP 0x00000100 /* Channel 0 half loop */#define INTE_CAP_0_LOOP 0x00080000 /* Channel capture loop */#define INTE_CAP_0_HALF_LOOP 0x00010000 /* Channel capture half loop */#define HCFG 0x14 /* Hardware config register */#define HCFG_LOCKSOUNDCACHE 0x00000008 /* 1 = Cancel bustmaster accesses to soundcache */ /* NOTE: This should generally never be used. */#define HCFG_AUDIOENABLE 0x00000001 /* 0 = CODECs transmit zero-valued samples */ /* Should be set to 1 when the EMU10K1 is */ /* completely initialized. */#define GPIO 0x18 /* Defaults: 00001080-Analog, 00001000-SPDIF. */#define AC97DATA 0x1c /* AC97 register set data register (16 bit) */#define AC97ADDRESS 0x1e /* AC97 register set address register (8 bit) *//********************************************************************************************************//* Emu10k1x pointer-offset register set, accessed through the PTR and DATA registers *//********************************************************************************************************/#define PLAYBACK_LIST_ADDR 0x00 /* Base DMA address of a list of pointers to each period/size */ /* One list entry: 4 bytes for DMA address, * 4 bytes for period_size << 16. * One list entry is 8 bytes long. * One list entry for each period in the buffer. */#define PLAYBACK_LIST_SIZE 0x01 /* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000 */#define PLAYBACK_LIST_PTR 0x02 /* Pointer to the current period being played */#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA addresss */#define PLAYBACK_PERIOD_SIZE 0x05 /* Playback period size */#define PLAYBACK_POINTER 0x06 /* Playback period pointer. Sample currently in DAC */#define PLAYBACK_UNKNOWN1 0x07#define PLAYBACK_UNKNOWN2 0x08/* Only one capture channel supported */#define CAPTURE_DMA_ADDR 0x10 /* Capture DMA address */#define CAPTURE_BUFFER_SIZE 0x11 /* Capture buffer size */#define CAPTURE_POINTER 0x12 /* Capture buffer pointer. Sample currently in ADC */#define CAPTURE_UNKNOWN 0x13/* From 0x20 - 0x3f, last samples played on each channel */#define TRIGGER_CHANNEL 0x40 /* Trigger channel playback */#define TRIGGER_CHANNEL_0 0x00000001 /* Trigger channel 0 */#define TRIGGER_CHANNEL_1 0x00000002 /* Trigger channel 1 */#define TRIGGER_CHANNEL_2 0x00000004 /* Trigger channel 2 */#define TRIGGER_CAPTURE 0x00000100 /* Trigger capture channel */#define ROUTING 0x41 /* Setup sound routing ? */#define ROUTING_FRONT_LEFT 0x00000001#define ROUTING_FRONT_RIGHT 0x00000002#define ROUTING_REAR_LEFT 0x00000004#define ROUTING_REAR_RIGHT 0x00000008#define ROUTING_CENTER_LFE 0x00010000#define SPCS0 0x42 /* SPDIF output Channel Status 0 register */#define SPCS1 0x43 /* SPDIF output Channel Status 1 register */#define SPCS2 0x44 /* SPDIF output Channel Status 2 register */#define SPCS_CLKACCYMASK 0x30000000 /* Clock accuracy */#define SPCS_CLKACCY_1000PPM 0x00000000 /* 1000 parts per million */#define SPCS_CLKACCY_50PPM 0x10000000 /* 50 parts per million */#define SPCS_CLKACCY_VARIABLE 0x20000000 /* Variable accuracy */#define SPCS_SAMPLERATEMASK 0x0f000000 /* Sample rate */#define SPCS_SAMPLERATE_44 0x00000000 /* 44.1kHz sample rate */#define SPCS_SAMPLERATE_48 0x02000000 /* 48kHz sample rate */#define SPCS_SAMPLERATE_32 0x03000000 /* 32kHz sample rate */#define SPCS_CHANNELNUMMASK 0x00f00000 /* Channel number */#define SPCS_CHANNELNUM_UNSPEC 0x00000000 /* Unspecified channel number */#define SPCS_CHANNELNUM_LEFT 0x00100000 /* Left channel */#define SPCS_CHANNELNUM_RIGHT 0x00200000 /* Right channel */#define SPCS_SOURCENUMMASK 0x000f0000 /* Source number */#define SPCS_SOURCENUM_UNSPEC 0x00000000 /* Unspecified source number */#define SPCS_GENERATIONSTATUS 0x00008000 /* Originality flag (see IEC-958 spec) */#define SPCS_CATEGORYCODEMASK 0x00007f00 /* Category code (see IEC-958 spec) */#define SPCS_MODEMASK 0x000000c0 /* Mode (see IEC-958 spec) */#define SPCS_EMPHASISMASK 0x00000038 /* Emphasis */#define SPCS_EMPHASIS_NONE 0x00000000 /* No emphasis */#define SPCS_EMPHASIS_50_15 0x00000008 /* 50/15 usec 2 channel */#define SPCS_COPYRIGHT 0x00000004 /* Copyright asserted flag -- do not modify */#define SPCS_NOTAUDIODATA 0x00000002 /* 0 = Digital audio, 1 = not audio */#define SPCS_PROFESSIONAL 0x00000001 /* 0 = Consumer (IEC-958), 1 = pro (AES3-1992) */#define SPDIF_SELECT 0x45 /* Enables SPDIF or Analogue outputs 0-Analogue, 0x700-SPDIF *//* This is the MPU port on the card */#define MUDATA 0x47#define MUCMD 0x48#define MUSTAT MUCMD/* From 0x50 - 0x5f, last samples captured *//** * The hardware has 3 channels for playback and 1 for capture. * - channel 0 is the front channel * - channel 1 is the rear channel * - channel 2 is the center/lfe chanel * Volume is controlled by the AC97 for the front and rear channels by * the PCM Playback Volume, Sigmatel Surround Playback Volume and * Surround Playback Volume. The Sigmatel 4-Speaker Stereo switch affects * the front/rear channel mixing in the REAR OUT jack. When using the * 4-Speaker Stereo, both front and rear channels will be mixed in the * REAR OUT. * The center/lfe channel has no volume control and cannot be muted during * playback. */typedef struct snd_emu10k1x_voice emu10k1x_voice_t;typedef struct snd_emu10k1x emu10k1x_t;typedef struct snd_emu10k1x_pcm emu10k1x_pcm_t;struct snd_emu10k1x_voice { emu10k1x_t *emu; int number; int use; emu10k1x_pcm_t *epcm;};struct snd_emu10k1x_pcm { emu10k1x_t *emu; snd_pcm_substream_t *substream; emu10k1x_voice_t *voice; unsigned short running;};typedef struct { struct snd_emu10k1x *emu; snd_rawmidi_t *rmidi; snd_rawmidi_substream_t *substream_input; snd_rawmidi_substream_t *substream_output; unsigned int midi_mode; spinlock_t input_lock; spinlock_t output_lock; spinlock_t open_lock; int tx_enable, rx_enable; int port; int ipr_tx, ipr_rx; void (*interrupt)(emu10k1x_t *emu, unsigned int status);} emu10k1x_midi_t;// definition of the chip-specific recordstruct snd_emu10k1x { snd_card_t *card; struct pci_dev *pci; unsigned long port; struct resource *res_port; int irq; unsigned int revision; /* chip revision */ unsigned int serial; /* serial number */ unsigned short model; /* subsystem id */ spinlock_t emu_lock; spinlock_t voice_lock; ac97_t *ac97; snd_pcm_t *pcm; emu10k1x_voice_t voices[3]; emu10k1x_voice_t capture_voice; u32 spdif_bits[3]; // SPDIF out setup struct snd_dma_buffer dma_buffer; emu10k1x_midi_t midi;};/* hardware definition */static snd_pcm_hardware_t snd_emu10k1x_playback_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_48000, .rate_min = 48000, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = (32*1024), .period_bytes_min = 64, .period_bytes_max = (16*1024), .periods_min = 2, .periods_max = 8, .fifo_size = 0,};static snd_pcm_hardware_t snd_emu10k1x_capture_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_48000, .rate_min = 48000, .rate_max = 48000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = (32*1024), .period_bytes_min = 64, .period_bytes_max = (16*1024), .periods_min = 2, .periods_max = 2, .fifo_size = 0,};static unsigned int snd_emu10k1x_ptr_read(emu10k1x_t * emu, unsigned int reg, unsigned int chn){ unsigned long flags; unsigned int regptr, val; regptr = (reg << 16) | chn; spin_lock_irqsave(&emu->emu_lock, flags); outl(regptr, emu->port + PTR); val = inl(emu->port + DATA); spin_unlock_irqrestore(&emu->emu_lock, flags); return val;}static void snd_emu10k1x_ptr_write(emu10k1x_t *emu, unsigned int reg, unsigned int chn, unsigned int data){ unsigned int regptr; unsigned long flags; regptr = (reg << 16) | chn; spin_lock_irqsave(&emu->emu_lock, flags); outl(regptr, emu->port + PTR); outl(data, emu->port + DATA); spin_unlock_irqrestore(&emu->emu_lock, flags);}static void snd_emu10k1x_intr_enable(emu10k1x_t *emu, unsigned int intrenb){ unsigned long flags; unsigned int enable; spin_lock_irqsave(&emu->emu_lock, flags); enable = inl(emu->port + INTE) | intrenb; outl(enable, emu->port + INTE); spin_unlock_irqrestore(&emu->emu_lock, flags);}static void snd_emu10k1x_intr_disable(emu10k1x_t *emu, unsigned int intrenb){ unsigned long flags; unsigned int enable; spin_lock_irqsave(&emu->emu_lock, flags); enable = inl(emu->port + INTE) & ~intrenb; outl(enable, emu->port + INTE); spin_unlock_irqrestore(&emu->emu_lock, flags);}static void snd_emu10k1x_gpio_write(emu10k1x_t *emu, unsigned int value){ unsigned long flags; spin_lock_irqsave(&emu->emu_lock, flags); outl(value, emu->port + GPIO); spin_unlock_irqrestore(&emu->emu_lock, flags);}static void snd_emu10k1x_pcm_free_substream(snd_pcm_runtime_t *runtime){ kfree(runtime->private_data);}static void snd_emu10k1x_pcm_interrupt(emu10k1x_t *emu, emu10k1x_voice_t *voice){ emu10k1x_pcm_t *epcm; if ((epcm = voice->epcm) == NULL) return; if (epcm->substream == NULL) return;#if 0 snd_printk(KERN_INFO "IRQ: position = 0x%x, period = 0x%x, size = 0x%x\n", epcm->substream->ops->pointer(epcm->substream), snd_pcm_lib_period_bytes(epcm->substream), snd_pcm_lib_buffer_bytes(epcm->substream));#endif snd_pcm_period_elapsed(epcm->substream);}/* open callback */static int snd_emu10k1x_playback_open(snd_pcm_substream_t *substream){ emu10k1x_t *chip = snd_pcm_substream_chip(substream); emu10k1x_pcm_t *epcm; snd_pcm_runtime_t *runtime = substream->runtime; int err; if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) { return err; } if ((err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64)) < 0) return err; epcm = kzalloc(sizeof(*epcm), GFP_KERNEL); if (epcm == NULL) return -ENOMEM; epcm->emu = chip; epcm->substream = substream; runtime->private_data = epcm; runtime->private_free = snd_emu10k1x_pcm_free_substream; runtime->hw = snd_emu10k1x_playback_hw; return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -