📄 emu10k1_main.c
字号:
/* * Copyright (c) by Jaroslav Kysela <perex@perex.cz> * Creative Labs, Inc. * Routines for control of EMU10K1 chips * * Copyright (c) by James Courtier-Dutton <James@superbug.co.uk> * Added support for Audigy 2 Value. * Added EMU 1010 support. * General bug fixes and enhancements. * * * BUGS: * -- * * TODO: * -- * * 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 <linux/sched.h>#include <linux/kthread.h>#include <sound/driver.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/mutex.h>#include <sound/core.h>#include <sound/emu10k1.h>#include <linux/firmware.h>#include "p16v.h"#include "tina2.h"#include "p17v.h"#define HANA_FILENAME "emu/hana.fw"#define DOCK_FILENAME "emu/audio_dock.fw"#define EMU1010B_FILENAME "emu/emu1010b.fw"#define MICRO_DOCK_FILENAME "emu/micro_dock.fw"#define EMU1010_NOTEBOOK_FILENAME "emu/emu1010_notebook.fw"MODULE_FIRMWARE(HANA_FILENAME);MODULE_FIRMWARE(DOCK_FILENAME);MODULE_FIRMWARE(EMU1010B_FILENAME);MODULE_FIRMWARE(MICRO_DOCK_FILENAME);MODULE_FIRMWARE(EMU1010_NOTEBOOK_FILENAME);/************************************************************************* * EMU10K1 init / done *************************************************************************/void snd_emu10k1_voice_init(struct snd_emu10k1 * emu, int ch){ snd_emu10k1_ptr_write(emu, DCYSUSV, ch, 0); snd_emu10k1_ptr_write(emu, IP, ch, 0); snd_emu10k1_ptr_write(emu, VTFT, ch, 0xffff); snd_emu10k1_ptr_write(emu, CVCF, ch, 0xffff); snd_emu10k1_ptr_write(emu, PTRX, ch, 0); snd_emu10k1_ptr_write(emu, CPF, ch, 0); snd_emu10k1_ptr_write(emu, CCR, ch, 0); snd_emu10k1_ptr_write(emu, PSST, ch, 0); snd_emu10k1_ptr_write(emu, DSL, ch, 0x10); snd_emu10k1_ptr_write(emu, CCCA, ch, 0); snd_emu10k1_ptr_write(emu, Z1, ch, 0); snd_emu10k1_ptr_write(emu, Z2, ch, 0); snd_emu10k1_ptr_write(emu, FXRT, ch, 0x32100000); snd_emu10k1_ptr_write(emu, ATKHLDM, ch, 0); snd_emu10k1_ptr_write(emu, DCYSUSM, ch, 0); snd_emu10k1_ptr_write(emu, IFATN, ch, 0xffff); snd_emu10k1_ptr_write(emu, PEFE, ch, 0); snd_emu10k1_ptr_write(emu, FMMOD, ch, 0); snd_emu10k1_ptr_write(emu, TREMFRQ, ch, 24); /* 1 Hz */ snd_emu10k1_ptr_write(emu, FM2FRQ2, ch, 24); /* 1 Hz */ snd_emu10k1_ptr_write(emu, TEMPENV, ch, 0); /*** these are last so OFF prevents writing ***/ snd_emu10k1_ptr_write(emu, LFOVAL2, ch, 0); snd_emu10k1_ptr_write(emu, LFOVAL1, ch, 0); snd_emu10k1_ptr_write(emu, ATKHLDV, ch, 0); snd_emu10k1_ptr_write(emu, ENVVOL, ch, 0); snd_emu10k1_ptr_write(emu, ENVVAL, ch, 0); /* Audigy extra stuffs */ if (emu->audigy) { snd_emu10k1_ptr_write(emu, 0x4c, ch, 0); /* ?? */ snd_emu10k1_ptr_write(emu, 0x4d, ch, 0); /* ?? */ snd_emu10k1_ptr_write(emu, 0x4e, ch, 0); /* ?? */ snd_emu10k1_ptr_write(emu, 0x4f, ch, 0); /* ?? */ snd_emu10k1_ptr_write(emu, A_FXRT1, ch, 0x03020100); snd_emu10k1_ptr_write(emu, A_FXRT2, ch, 0x3f3f3f3f); snd_emu10k1_ptr_write(emu, A_SENDAMOUNTS, ch, 0); }}static unsigned int spi_dac_init[] = { 0x00ff, 0x02ff, 0x0400, 0x0520, 0x0600, 0x08ff, 0x0aff, 0x0cff, 0x0eff, 0x10ff, 0x1200, 0x1400, 0x1480, 0x1800, 0x1aff, 0x1cff, 0x1e00, 0x0530, 0x0602, 0x0622, 0x1400,};static unsigned int i2c_adc_init[][2] = { { 0x17, 0x00 }, /* Reset */ { 0x07, 0x00 }, /* Timeout */ { 0x0b, 0x22 }, /* Interface control */ { 0x0c, 0x22 }, /* Master mode control */ { 0x0d, 0x08 }, /* Powerdown control */ { 0x0e, 0xcf }, /* Attenuation Left 0x01 = -103dB, 0xff = 24dB */ { 0x0f, 0xcf }, /* Attenuation Right 0.5dB steps */ { 0x10, 0x7b }, /* ALC Control 1 */ { 0x11, 0x00 }, /* ALC Control 2 */ { 0x12, 0x32 }, /* ALC Control 3 */ { 0x13, 0x00 }, /* Noise gate control */ { 0x14, 0xa6 }, /* Limiter control */ { 0x15, ADC_MUX_2 }, /* ADC Mixer control. Mic for Audigy 2 ZS Notebook */}; static int snd_emu10k1_init(struct snd_emu10k1 *emu, int enable_ir, int resume){ unsigned int silent_page; int ch; u32 tmp; /* disable audio and lock cache */ outl(HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, emu->port + HCFG); /* reset recording buffers */ snd_emu10k1_ptr_write(emu, MICBS, 0, ADCBS_BUFSIZE_NONE); snd_emu10k1_ptr_write(emu, MICBA, 0, 0); snd_emu10k1_ptr_write(emu, FXBS, 0, ADCBS_BUFSIZE_NONE); snd_emu10k1_ptr_write(emu, FXBA, 0, 0); snd_emu10k1_ptr_write(emu, ADCBS, 0, ADCBS_BUFSIZE_NONE); snd_emu10k1_ptr_write(emu, ADCBA, 0, 0); /* disable channel interrupt */ outl(0, emu->port + INTE); snd_emu10k1_ptr_write(emu, CLIEL, 0, 0); snd_emu10k1_ptr_write(emu, CLIEH, 0, 0); snd_emu10k1_ptr_write(emu, SOLEL, 0, 0); snd_emu10k1_ptr_write(emu, SOLEH, 0, 0); if (emu->audigy){ /* set SPDIF bypass mode */ snd_emu10k1_ptr_write(emu, SPBYPASS, 0, SPBYPASS_FORMAT); /* enable rear left + rear right AC97 slots */ snd_emu10k1_ptr_write(emu, AC97SLOT, 0, AC97SLOT_REAR_RIGHT | AC97SLOT_REAR_LEFT); } /* init envelope engine */ for (ch = 0; ch < NUM_G; ch++) snd_emu10k1_voice_init(emu, ch); snd_emu10k1_ptr_write(emu, SPCS0, 0, emu->spdif_bits[0]); snd_emu10k1_ptr_write(emu, SPCS1, 0, emu->spdif_bits[1]); snd_emu10k1_ptr_write(emu, SPCS2, 0, emu->spdif_bits[2]); if (emu->card_capabilities->ca0151_chip) { /* audigy2 */ /* Hacks for Alice3 to work independent of haP16V driver */ //Setup SRCMulti_I2S SamplingRate tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0); tmp &= 0xfffff1ff; tmp |= (0x2<<9); snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp); /* Setup SRCSel (Enable Spdif,I2S SRCMulti) */ snd_emu10k1_ptr20_write(emu, SRCSel, 0, 0x14); /* Setup SRCMulti Input Audio Enable */ /* Use 0xFFFFFFFF to enable P16V sounds. */ snd_emu10k1_ptr20_write(emu, SRCMULTI_ENABLE, 0, 0xFFFFFFFF); /* Enabled Phased (8-channel) P16V playback */ outl(0x0201, emu->port + HCFG2); /* Set playback routing. */ snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, 0x78e4); } if (emu->card_capabilities->ca0108_chip) { /* audigy2 Value */ /* Hacks for Alice3 to work independent of haP16V driver */ snd_printk(KERN_INFO "Audigy2 value: Special config.\n"); //Setup SRCMulti_I2S SamplingRate tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, 0); tmp &= 0xfffff1ff; tmp |= (0x2<<9); snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, 0, tmp); /* Setup SRCSel (Enable Spdif,I2S SRCMulti) */ outl(0x600000, emu->port + 0x20); outl(0x14, emu->port + 0x24); /* Setup SRCMulti Input Audio Enable */ outl(0x7b0000, emu->port + 0x20); outl(0xFF000000, emu->port + 0x24); /* Setup SPDIF Out Audio Enable */ /* The Audigy 2 Value has a separate SPDIF out, * so no need for a mixer switch */ outl(0x7a0000, emu->port + 0x20); outl(0xFF000000, emu->port + 0x24); tmp = inl(emu->port + A_IOCFG) & ~0x8; /* Clear bit 3 */ outl(tmp, emu->port + A_IOCFG); } if (emu->card_capabilities->spi_dac) { /* Audigy 2 ZS Notebook with DAC Wolfson WM8768/WM8568 */ int size, n; size = ARRAY_SIZE(spi_dac_init); for (n = 0; n < size; n++) snd_emu10k1_spi_write(emu, spi_dac_init[n]); snd_emu10k1_ptr20_write(emu, 0x60, 0, 0x10); /* Enable GPIOs * GPIO0: Unknown * GPIO1: Speakers-enabled. * GPIO2: Unknown * GPIO3: Unknown * GPIO4: IEC958 Output on. * GPIO5: Unknown * GPIO6: Unknown * GPIO7: Unknown */ outl(0x76, emu->port + A_IOCFG); /* Windows uses 0x3f76 */ } if (emu->card_capabilities->i2c_adc) { /* Audigy 2 ZS Notebook with ADC Wolfson WM8775 */ int size, n; snd_emu10k1_ptr20_write(emu, P17V_I2S_SRC_SEL, 0, 0x2020205f); tmp = inl(emu->port + A_IOCFG); outl(tmp | 0x4, emu->port + A_IOCFG); /* Set bit 2 for mic input */ tmp = inl(emu->port + A_IOCFG); size = ARRAY_SIZE(i2c_adc_init); for (n = 0; n < size; n++) snd_emu10k1_i2c_write(emu, i2c_adc_init[n][0], i2c_adc_init[n][1]); for (n=0; n < 4; n++) { emu->i2c_capture_volume[n][0]= 0xcf; emu->i2c_capture_volume[n][1]= 0xcf; } } snd_emu10k1_ptr_write(emu, PTB, 0, emu->ptb_pages.addr); snd_emu10k1_ptr_write(emu, TCB, 0, 0); /* taken from original driver */ snd_emu10k1_ptr_write(emu, TCBS, 0, 4); /* taken from original driver */ silent_page = (emu->silent_page.addr << 1) | MAP_PTI_MASK; for (ch = 0; ch < NUM_G; ch++) { snd_emu10k1_ptr_write(emu, MAPA, ch, silent_page); snd_emu10k1_ptr_write(emu, MAPB, ch, silent_page); } if (emu->card_capabilities->emu1010) { outl(HCFG_AUTOMUTE_ASYNC | HCFG_EMU32_SLAVE | HCFG_AUDIOENABLE, emu->port + HCFG); /* * Hokay, setup HCFG * Mute Disable Audio = 0 * Lock Tank Memory = 1 * Lock Sound Memory = 0 * Auto Mute = 1 */ } else if (emu->audigy) { if (emu->revision == 4) /* audigy2 */ outl(HCFG_AUDIOENABLE | HCFG_AC3ENABLE_CDSPDIF | HCFG_AC3ENABLE_GPSPDIF | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); else outl(HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); /* FIXME: Remove all these emu->model and replace it with a card recognition parameter, * e.g. card_capabilities->joystick */ } else if (emu->model == 0x20 || emu->model == 0xc400 || (emu->model == 0x21 && emu->revision < 6)) outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE, emu->port + HCFG); else // With on-chip joystick outl(HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, emu->port + HCFG); if (enable_ir) { /* enable IR for SB Live */ if (emu->card_capabilities->emu1010) { ; /* Disable all access to A_IOCFG for the emu1010 */ } else if (emu->card_capabilities->i2c_adc) { ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ } else if (emu->audigy) { unsigned int reg = inl(emu->port + A_IOCFG); outl(reg | A_IOCFG_GPOUT2, emu->port + A_IOCFG); udelay(500); outl(reg | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, emu->port + A_IOCFG); udelay(100); outl(reg, emu->port + A_IOCFG); } else { unsigned int reg = inl(emu->port + HCFG); outl(reg | HCFG_GPOUT2, emu->port + HCFG); udelay(500); outl(reg | HCFG_GPOUT1 | HCFG_GPOUT2, emu->port + HCFG); udelay(100); outl(reg, emu->port + HCFG); } } if (emu->card_capabilities->emu1010) { ; /* Disable all access to A_IOCFG for the emu1010 */ } else if (emu->card_capabilities->i2c_adc) { ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ } else if (emu->audigy) { /* enable analog output */ unsigned int reg = inl(emu->port + A_IOCFG); outl(reg | A_IOCFG_GPOUT0, emu->port + A_IOCFG); } return 0;}static void snd_emu10k1_audio_enable(struct snd_emu10k1 *emu){ /* * Enable the audio bit */ outl(inl(emu->port + HCFG) | HCFG_AUDIOENABLE, emu->port + HCFG); /* Enable analog/digital outs on audigy */ if (emu->card_capabilities->emu1010) { ; /* Disable all access to A_IOCFG for the emu1010 */ } else if (emu->card_capabilities->i2c_adc) { ; /* Disable A_IOCFG for Audigy 2 ZS Notebook */ } else if (emu->audigy) { outl(inl(emu->port + A_IOCFG) & ~0x44, emu->port + A_IOCFG); if (emu->card_capabilities->ca0151_chip) { /* audigy2 */ /* Unmute Analog now. Set GPO6 to 1 for Apollo. * This has to be done after init ALice3 I2SOut beyond 48KHz. * So, sequence is important. */ outl(inl(emu->port + A_IOCFG) | 0x0040, emu->port + A_IOCFG); } else if (emu->card_capabilities->ca0108_chip) { /* audigy2 value */ /* Unmute Analog now. */ outl(inl(emu->port + A_IOCFG) | 0x0060, emu->port + A_IOCFG); } else { /* Disable routing from AC97 line out to Front speakers */ outl(inl(emu->port + A_IOCFG) | 0x0080, emu->port + A_IOCFG); } } #if 0 { unsigned int tmp; /* FIXME: the following routine disables LiveDrive-II !! */ // TOSLink detection emu->tos_link = 0; tmp = inl(emu->port + HCFG); if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) { outl(tmp|0x800, emu->port + HCFG); udelay(50); if (tmp != (inl(emu->port + HCFG) & ~0x800)) { emu->tos_link = 1; outl(tmp, emu->port + HCFG); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -