📄 emu8000.c
字号:
/* * Copyright (c) by Jaroslav Kysela <perex@suse.cz> * and (c) 1999 Steve Ratcliffe <steve@parabola.demon.co.uk> * Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de> * * Routines for control of EMU8000 chip * * 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/wait.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/ioport.h>#include <linux/delay.h>#include <sound/core.h>#include <sound/emu8000.h>#include <sound/emu8000_reg.h>#include <asm/io.h>#include <asm/uaccess.h>#include <linux/init.h>#include <sound/control.h>#include <sound/initval.h>/* * emu8000 register controls *//* * The following routines read and write registers on the emu8000. They * should always be called via the EMU8000*READ/WRITE macros and never * directly. The macros handle the port number and command word. *//* Write a word */void snd_emu8000_poke(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val){ unsigned long flags; spin_lock_irqsave(&emu->reg_lock, flags); if (reg != emu->last_reg) { outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ emu->last_reg = reg; } outw((unsigned short)val, port); /* Send data */ spin_unlock_irqrestore(&emu->reg_lock, flags);}/* Read a word */unsigned short snd_emu8000_peek(emu8000_t *emu, unsigned int port, unsigned int reg){ unsigned short res; unsigned long flags; spin_lock_irqsave(&emu->reg_lock, flags); if (reg != emu->last_reg) { outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ emu->last_reg = reg; } res = inw(port); /* Read data */ spin_unlock_irqrestore(&emu->reg_lock, flags); return res;}/* Write a double word */void snd_emu8000_poke_dw(emu8000_t *emu, unsigned int port, unsigned int reg, unsigned int val){ unsigned long flags; spin_lock_irqsave(&emu->reg_lock, flags); if (reg != emu->last_reg) { outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ emu->last_reg = reg; } outw((unsigned short)val, port); /* Send low word of data */ outw((unsigned short)(val>>16), port+2); /* Send high word of data */ spin_unlock_irqrestore(&emu->reg_lock, flags);}/* Read a double word */unsigned int snd_emu8000_peek_dw(emu8000_t *emu, unsigned int port, unsigned int reg){ unsigned short low; unsigned int res; unsigned long flags; spin_lock_irqsave(&emu->reg_lock, flags); if (reg != emu->last_reg) { outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ emu->last_reg = reg; } low = inw(port); /* Read low word of data */ res = low + (inw(port+2) << 16); spin_unlock_irqrestore(&emu->reg_lock, flags); return res;}/* * Set up / close a channel to be used for DMA. *//*exported*/ voidsnd_emu8000_dma_chan(emu8000_t *emu, int ch, int mode){ unsigned right_bit = (mode & EMU8000_RAM_RIGHT) ? 0x01000000 : 0; mode &= EMU8000_RAM_MODE_MASK; if (mode == EMU8000_RAM_CLOSE) { EMU8000_CCCA_WRITE(emu, ch, 0); EMU8000_DCYSUSV_WRITE(emu, ch, 0x807F); return; } EMU8000_DCYSUSV_WRITE(emu, ch, 0x80); EMU8000_VTFT_WRITE(emu, ch, 0); EMU8000_CVCF_WRITE(emu, ch, 0); EMU8000_PTRX_WRITE(emu, ch, 0x40000000); EMU8000_CPF_WRITE(emu, ch, 0x40000000); EMU8000_PSST_WRITE(emu, ch, 0); EMU8000_CSL_WRITE(emu, ch, 0); if (mode == EMU8000_RAM_WRITE) /* DMA write */ EMU8000_CCCA_WRITE(emu, ch, 0x06000000 | right_bit); else /* DMA read */ EMU8000_CCCA_WRITE(emu, ch, 0x04000000 | right_bit);}/* */static void __initsnd_emu8000_read_wait(emu8000_t *emu){ while ((EMU8000_SMALR_READ(emu) & 0x80000000) != 0) { schedule_timeout_interruptible(1); if (signal_pending(current)) break; }}/* */static void __initsnd_emu8000_write_wait(emu8000_t *emu){ while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) { schedule_timeout_interruptible(1); if (signal_pending(current)) break; }}/* * detect a card at the given port */static int __initsnd_emu8000_detect(emu8000_t *emu){ /* Initialise */ EMU8000_HWCF1_WRITE(emu, 0x0059); EMU8000_HWCF2_WRITE(emu, 0x0020); EMU8000_HWCF3_WRITE(emu, 0x0000); /* Check for a recognisable emu8000 */ /* if ((EMU8000_U1_READ(emu) & 0x000f) != 0x000c) return -ENODEV; */ if ((EMU8000_HWCF1_READ(emu) & 0x007e) != 0x0058) return -ENODEV; if ((EMU8000_HWCF2_READ(emu) & 0x0003) != 0x0003) return -ENODEV; snd_printdd("EMU8000 [0x%lx]: Synth chip found\n", emu->port1); return 0;}/* * intiailize audio channels */static void __initinit_audio(emu8000_t *emu){ int ch; /* turn off envelope engines */ for (ch = 0; ch < EMU8000_CHANNELS; ch++) EMU8000_DCYSUSV_WRITE(emu, ch, 0x80); /* reset all other parameters to zero */ for (ch = 0; ch < EMU8000_CHANNELS; ch++) { EMU8000_ENVVOL_WRITE(emu, ch, 0); EMU8000_ENVVAL_WRITE(emu, ch, 0); EMU8000_DCYSUS_WRITE(emu, ch, 0); EMU8000_ATKHLDV_WRITE(emu, ch, 0); EMU8000_LFO1VAL_WRITE(emu, ch, 0); EMU8000_ATKHLD_WRITE(emu, ch, 0); EMU8000_LFO2VAL_WRITE(emu, ch, 0); EMU8000_IP_WRITE(emu, ch, 0); EMU8000_IFATN_WRITE(emu, ch, 0); EMU8000_PEFE_WRITE(emu, ch, 0); EMU8000_FMMOD_WRITE(emu, ch, 0); EMU8000_TREMFRQ_WRITE(emu, ch, 0); EMU8000_FM2FRQ2_WRITE(emu, ch, 0); EMU8000_PTRX_WRITE(emu, ch, 0); EMU8000_VTFT_WRITE(emu, ch, 0); EMU8000_PSST_WRITE(emu, ch, 0); EMU8000_CSL_WRITE(emu, ch, 0); EMU8000_CCCA_WRITE(emu, ch, 0); } for (ch = 0; ch < EMU8000_CHANNELS; ch++) { EMU8000_CPF_WRITE(emu, ch, 0); EMU8000_CVCF_WRITE(emu, ch, 0); }}/* * initialize DMA address */static void __initinit_dma(emu8000_t *emu){ EMU8000_SMALR_WRITE(emu, 0); EMU8000_SMARR_WRITE(emu, 0); EMU8000_SMALW_WRITE(emu, 0); EMU8000_SMARW_WRITE(emu, 0);}/* * initialization arrays; from ADIP */static unsigned short init1[128] /*__devinitdata*/ = { 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330, 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730, 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30, 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30, 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330, 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730, 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30, 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30, 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330, 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730, 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30, 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30, 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330, 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730, 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30, 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30,};static unsigned short init2[128] /*__devinitdata*/ = { 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330, 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730, 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30, 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30, 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330, 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730, 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30, 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30, 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330, 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730, 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30, 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30, 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330, 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730, 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30, 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30,};static unsigned short init3[128] /*__devinitdata*/ = { 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254, 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234, 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224, 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254, 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264, 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294, 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3, 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287, 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7, 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386, 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55, 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308, 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F, 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319, 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570,};static unsigned short init4[128] /*__devinitdata*/ = { 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254, 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234, 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224, 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254, 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264, 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294, 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3, 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287, 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7, 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386, 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55, 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308, 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F, 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319, 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570,};/* send an initialization array * Taken from the oss driver, not obvious from the doc how this * is meant to work */static void __initsend_array(emu8000_t *emu, unsigned short *data, int size){ int i; unsigned short *p; p = data; for (i = 0; i < size; i++, p++) EMU8000_INIT1_WRITE(emu, i, *p); for (i = 0; i < size; i++, p++) EMU8000_INIT2_WRITE(emu, i, *p); for (i = 0; i < size; i++, p++) EMU8000_INIT3_WRITE(emu, i, *p); for (i = 0; i < size; i++, p++) EMU8000_INIT4_WRITE(emu, i, *p);}/* * Send initialization arrays to start up, this just follows the * initialisation sequence in the adip. */static void __initinit_arrays(emu8000_t *emu){ send_array(emu, init1, ARRAY_SIZE(init1)/4); msleep((1024 * 1000) / 44100); /* wait for 1024 clocks */ send_array(emu, init2, ARRAY_SIZE(init2)/4); send_array(emu, init3, ARRAY_SIZE(init3)/4); EMU8000_HWCF4_WRITE(emu, 0); EMU8000_HWCF5_WRITE(emu, 0x83); EMU8000_HWCF6_WRITE(emu, 0x8000); send_array(emu, init4, ARRAY_SIZE(init4)/4);}#define UNIQUE_ID1 0xa5b9#define UNIQUE_ID2 0x9d53/* * Size the onboard memory. * This is written so as not to need arbitary delays after the write. It * seems that the only way to do this is to use the one channel and keep * reallocating between read and write. */static void __initsize_dram(emu8000_t *emu){ int i, size; if (emu->dram_checked) return; size = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -