162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 462306a36Sopenharmony_ci * and (c) 1999 Steve Ratcliffe <steve@parabola.demon.co.uk> 562306a36Sopenharmony_ci * Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Routines for control of EMU8000 chip 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/wait.h> 1162306a36Sopenharmony_ci#include <linux/sched/signal.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/ioport.h> 1462306a36Sopenharmony_ci#include <linux/export.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <sound/core.h> 1862306a36Sopenharmony_ci#include <sound/emu8000.h> 1962306a36Sopenharmony_ci#include <sound/emu8000_reg.h> 2062306a36Sopenharmony_ci#include <linux/uaccess.h> 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci#include <sound/control.h> 2362306a36Sopenharmony_ci#include <sound/initval.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * emu8000 register controls 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* 3062306a36Sopenharmony_ci * The following routines read and write registers on the emu8000. They 3162306a36Sopenharmony_ci * should always be called via the EMU8000*READ/WRITE macros and never 3262306a36Sopenharmony_ci * directly. The macros handle the port number and command word. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_ci/* Write a word */ 3562306a36Sopenharmony_civoid snd_emu8000_poke(struct snd_emu8000 *emu, unsigned int port, unsigned int reg, unsigned int val) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci unsigned long flags; 3862306a36Sopenharmony_ci spin_lock_irqsave(&emu->reg_lock, flags); 3962306a36Sopenharmony_ci if (reg != emu->last_reg) { 4062306a36Sopenharmony_ci outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ 4162306a36Sopenharmony_ci emu->last_reg = reg; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci outw((unsigned short)val, port); /* Send data */ 4462306a36Sopenharmony_ci spin_unlock_irqrestore(&emu->reg_lock, flags); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Read a word */ 4862306a36Sopenharmony_ciunsigned short snd_emu8000_peek(struct snd_emu8000 *emu, unsigned int port, unsigned int reg) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci unsigned short res; 5162306a36Sopenharmony_ci unsigned long flags; 5262306a36Sopenharmony_ci spin_lock_irqsave(&emu->reg_lock, flags); 5362306a36Sopenharmony_ci if (reg != emu->last_reg) { 5462306a36Sopenharmony_ci outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ 5562306a36Sopenharmony_ci emu->last_reg = reg; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci res = inw(port); /* Read data */ 5862306a36Sopenharmony_ci spin_unlock_irqrestore(&emu->reg_lock, flags); 5962306a36Sopenharmony_ci return res; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* Write a double word */ 6362306a36Sopenharmony_civoid snd_emu8000_poke_dw(struct snd_emu8000 *emu, unsigned int port, unsigned int reg, unsigned int val) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci unsigned long flags; 6662306a36Sopenharmony_ci spin_lock_irqsave(&emu->reg_lock, flags); 6762306a36Sopenharmony_ci if (reg != emu->last_reg) { 6862306a36Sopenharmony_ci outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ 6962306a36Sopenharmony_ci emu->last_reg = reg; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci outw((unsigned short)val, port); /* Send low word of data */ 7262306a36Sopenharmony_ci outw((unsigned short)(val>>16), port+2); /* Send high word of data */ 7362306a36Sopenharmony_ci spin_unlock_irqrestore(&emu->reg_lock, flags); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* Read a double word */ 7762306a36Sopenharmony_ciunsigned int snd_emu8000_peek_dw(struct snd_emu8000 *emu, unsigned int port, unsigned int reg) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci unsigned short low; 8062306a36Sopenharmony_ci unsigned int res; 8162306a36Sopenharmony_ci unsigned long flags; 8262306a36Sopenharmony_ci spin_lock_irqsave(&emu->reg_lock, flags); 8362306a36Sopenharmony_ci if (reg != emu->last_reg) { 8462306a36Sopenharmony_ci outw((unsigned short)reg, EMU8000_PTR(emu)); /* Set register */ 8562306a36Sopenharmony_ci emu->last_reg = reg; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci low = inw(port); /* Read low word of data */ 8862306a36Sopenharmony_ci res = low + (inw(port+2) << 16); 8962306a36Sopenharmony_ci spin_unlock_irqrestore(&emu->reg_lock, flags); 9062306a36Sopenharmony_ci return res; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* 9462306a36Sopenharmony_ci * Set up / close a channel to be used for DMA. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_ci/*exported*/ void 9762306a36Sopenharmony_cisnd_emu8000_dma_chan(struct snd_emu8000 *emu, int ch, int mode) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci unsigned right_bit = (mode & EMU8000_RAM_RIGHT) ? 0x01000000 : 0; 10062306a36Sopenharmony_ci mode &= EMU8000_RAM_MODE_MASK; 10162306a36Sopenharmony_ci if (mode == EMU8000_RAM_CLOSE) { 10262306a36Sopenharmony_ci EMU8000_CCCA_WRITE(emu, ch, 0); 10362306a36Sopenharmony_ci EMU8000_DCYSUSV_WRITE(emu, ch, 0x807F); 10462306a36Sopenharmony_ci return; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci EMU8000_DCYSUSV_WRITE(emu, ch, 0x80); 10762306a36Sopenharmony_ci EMU8000_VTFT_WRITE(emu, ch, 0); 10862306a36Sopenharmony_ci EMU8000_CVCF_WRITE(emu, ch, 0); 10962306a36Sopenharmony_ci EMU8000_PTRX_WRITE(emu, ch, 0x40000000); 11062306a36Sopenharmony_ci EMU8000_CPF_WRITE(emu, ch, 0x40000000); 11162306a36Sopenharmony_ci EMU8000_PSST_WRITE(emu, ch, 0); 11262306a36Sopenharmony_ci EMU8000_CSL_WRITE(emu, ch, 0); 11362306a36Sopenharmony_ci if (mode == EMU8000_RAM_WRITE) /* DMA write */ 11462306a36Sopenharmony_ci EMU8000_CCCA_WRITE(emu, ch, 0x06000000 | right_bit); 11562306a36Sopenharmony_ci else /* DMA read */ 11662306a36Sopenharmony_ci EMU8000_CCCA_WRITE(emu, ch, 0x04000000 | right_bit); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/* 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_cistatic void 12262306a36Sopenharmony_cisnd_emu8000_read_wait(struct snd_emu8000 *emu) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci while ((EMU8000_SMALR_READ(emu) & 0x80000000) != 0) { 12562306a36Sopenharmony_ci schedule_timeout_interruptible(1); 12662306a36Sopenharmony_ci if (signal_pending(current)) 12762306a36Sopenharmony_ci break; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_cistatic void 13462306a36Sopenharmony_cisnd_emu8000_write_wait(struct snd_emu8000 *emu) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) { 13762306a36Sopenharmony_ci schedule_timeout_interruptible(1); 13862306a36Sopenharmony_ci if (signal_pending(current)) 13962306a36Sopenharmony_ci break; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* 14462306a36Sopenharmony_ci * detect a card at the given port 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_cistatic int 14762306a36Sopenharmony_cisnd_emu8000_detect(struct snd_emu8000 *emu) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci /* Initialise */ 15062306a36Sopenharmony_ci EMU8000_HWCF1_WRITE(emu, 0x0059); 15162306a36Sopenharmony_ci EMU8000_HWCF2_WRITE(emu, 0x0020); 15262306a36Sopenharmony_ci EMU8000_HWCF3_WRITE(emu, 0x0000); 15362306a36Sopenharmony_ci /* Check for a recognisable emu8000 */ 15462306a36Sopenharmony_ci /* 15562306a36Sopenharmony_ci if ((EMU8000_U1_READ(emu) & 0x000f) != 0x000c) 15662306a36Sopenharmony_ci return -ENODEV; 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci if ((EMU8000_HWCF1_READ(emu) & 0x007e) != 0x0058) 15962306a36Sopenharmony_ci return -ENODEV; 16062306a36Sopenharmony_ci if ((EMU8000_HWCF2_READ(emu) & 0x0003) != 0x0003) 16162306a36Sopenharmony_ci return -ENODEV; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci snd_printdd("EMU8000 [0x%lx]: Synth chip found\n", 16462306a36Sopenharmony_ci emu->port1); 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* 17062306a36Sopenharmony_ci * intiailize audio channels 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_cistatic void 17362306a36Sopenharmony_ciinit_audio(struct snd_emu8000 *emu) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci int ch; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* turn off envelope engines */ 17862306a36Sopenharmony_ci for (ch = 0; ch < EMU8000_CHANNELS; ch++) 17962306a36Sopenharmony_ci EMU8000_DCYSUSV_WRITE(emu, ch, 0x80); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* reset all other parameters to zero */ 18262306a36Sopenharmony_ci for (ch = 0; ch < EMU8000_CHANNELS; ch++) { 18362306a36Sopenharmony_ci EMU8000_ENVVOL_WRITE(emu, ch, 0); 18462306a36Sopenharmony_ci EMU8000_ENVVAL_WRITE(emu, ch, 0); 18562306a36Sopenharmony_ci EMU8000_DCYSUS_WRITE(emu, ch, 0); 18662306a36Sopenharmony_ci EMU8000_ATKHLDV_WRITE(emu, ch, 0); 18762306a36Sopenharmony_ci EMU8000_LFO1VAL_WRITE(emu, ch, 0); 18862306a36Sopenharmony_ci EMU8000_ATKHLD_WRITE(emu, ch, 0); 18962306a36Sopenharmony_ci EMU8000_LFO2VAL_WRITE(emu, ch, 0); 19062306a36Sopenharmony_ci EMU8000_IP_WRITE(emu, ch, 0); 19162306a36Sopenharmony_ci EMU8000_IFATN_WRITE(emu, ch, 0); 19262306a36Sopenharmony_ci EMU8000_PEFE_WRITE(emu, ch, 0); 19362306a36Sopenharmony_ci EMU8000_FMMOD_WRITE(emu, ch, 0); 19462306a36Sopenharmony_ci EMU8000_TREMFRQ_WRITE(emu, ch, 0); 19562306a36Sopenharmony_ci EMU8000_FM2FRQ2_WRITE(emu, ch, 0); 19662306a36Sopenharmony_ci EMU8000_PTRX_WRITE(emu, ch, 0); 19762306a36Sopenharmony_ci EMU8000_VTFT_WRITE(emu, ch, 0); 19862306a36Sopenharmony_ci EMU8000_PSST_WRITE(emu, ch, 0); 19962306a36Sopenharmony_ci EMU8000_CSL_WRITE(emu, ch, 0); 20062306a36Sopenharmony_ci EMU8000_CCCA_WRITE(emu, ch, 0); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci for (ch = 0; ch < EMU8000_CHANNELS; ch++) { 20462306a36Sopenharmony_ci EMU8000_CPF_WRITE(emu, ch, 0); 20562306a36Sopenharmony_ci EMU8000_CVCF_WRITE(emu, ch, 0); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/* 21162306a36Sopenharmony_ci * initialize DMA address 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_cistatic void 21462306a36Sopenharmony_ciinit_dma(struct snd_emu8000 *emu) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci EMU8000_SMALR_WRITE(emu, 0); 21762306a36Sopenharmony_ci EMU8000_SMARR_WRITE(emu, 0); 21862306a36Sopenharmony_ci EMU8000_SMALW_WRITE(emu, 0); 21962306a36Sopenharmony_ci EMU8000_SMARW_WRITE(emu, 0); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci/* 22362306a36Sopenharmony_ci * initialization arrays; from ADIP 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_cistatic const unsigned short init1[128] = { 22662306a36Sopenharmony_ci 0x03ff, 0x0030, 0x07ff, 0x0130, 0x0bff, 0x0230, 0x0fff, 0x0330, 22762306a36Sopenharmony_ci 0x13ff, 0x0430, 0x17ff, 0x0530, 0x1bff, 0x0630, 0x1fff, 0x0730, 22862306a36Sopenharmony_ci 0x23ff, 0x0830, 0x27ff, 0x0930, 0x2bff, 0x0a30, 0x2fff, 0x0b30, 22962306a36Sopenharmony_ci 0x33ff, 0x0c30, 0x37ff, 0x0d30, 0x3bff, 0x0e30, 0x3fff, 0x0f30, 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci 0x43ff, 0x0030, 0x47ff, 0x0130, 0x4bff, 0x0230, 0x4fff, 0x0330, 23262306a36Sopenharmony_ci 0x53ff, 0x0430, 0x57ff, 0x0530, 0x5bff, 0x0630, 0x5fff, 0x0730, 23362306a36Sopenharmony_ci 0x63ff, 0x0830, 0x67ff, 0x0930, 0x6bff, 0x0a30, 0x6fff, 0x0b30, 23462306a36Sopenharmony_ci 0x73ff, 0x0c30, 0x77ff, 0x0d30, 0x7bff, 0x0e30, 0x7fff, 0x0f30, 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci 0x83ff, 0x0030, 0x87ff, 0x0130, 0x8bff, 0x0230, 0x8fff, 0x0330, 23762306a36Sopenharmony_ci 0x93ff, 0x0430, 0x97ff, 0x0530, 0x9bff, 0x0630, 0x9fff, 0x0730, 23862306a36Sopenharmony_ci 0xa3ff, 0x0830, 0xa7ff, 0x0930, 0xabff, 0x0a30, 0xafff, 0x0b30, 23962306a36Sopenharmony_ci 0xb3ff, 0x0c30, 0xb7ff, 0x0d30, 0xbbff, 0x0e30, 0xbfff, 0x0f30, 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci 0xc3ff, 0x0030, 0xc7ff, 0x0130, 0xcbff, 0x0230, 0xcfff, 0x0330, 24262306a36Sopenharmony_ci 0xd3ff, 0x0430, 0xd7ff, 0x0530, 0xdbff, 0x0630, 0xdfff, 0x0730, 24362306a36Sopenharmony_ci 0xe3ff, 0x0830, 0xe7ff, 0x0930, 0xebff, 0x0a30, 0xefff, 0x0b30, 24462306a36Sopenharmony_ci 0xf3ff, 0x0c30, 0xf7ff, 0x0d30, 0xfbff, 0x0e30, 0xffff, 0x0f30, 24562306a36Sopenharmony_ci}; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic const unsigned short init2[128] = { 24862306a36Sopenharmony_ci 0x03ff, 0x8030, 0x07ff, 0x8130, 0x0bff, 0x8230, 0x0fff, 0x8330, 24962306a36Sopenharmony_ci 0x13ff, 0x8430, 0x17ff, 0x8530, 0x1bff, 0x8630, 0x1fff, 0x8730, 25062306a36Sopenharmony_ci 0x23ff, 0x8830, 0x27ff, 0x8930, 0x2bff, 0x8a30, 0x2fff, 0x8b30, 25162306a36Sopenharmony_ci 0x33ff, 0x8c30, 0x37ff, 0x8d30, 0x3bff, 0x8e30, 0x3fff, 0x8f30, 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci 0x43ff, 0x8030, 0x47ff, 0x8130, 0x4bff, 0x8230, 0x4fff, 0x8330, 25462306a36Sopenharmony_ci 0x53ff, 0x8430, 0x57ff, 0x8530, 0x5bff, 0x8630, 0x5fff, 0x8730, 25562306a36Sopenharmony_ci 0x63ff, 0x8830, 0x67ff, 0x8930, 0x6bff, 0x8a30, 0x6fff, 0x8b30, 25662306a36Sopenharmony_ci 0x73ff, 0x8c30, 0x77ff, 0x8d30, 0x7bff, 0x8e30, 0x7fff, 0x8f30, 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci 0x83ff, 0x8030, 0x87ff, 0x8130, 0x8bff, 0x8230, 0x8fff, 0x8330, 25962306a36Sopenharmony_ci 0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730, 26062306a36Sopenharmony_ci 0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30, 26162306a36Sopenharmony_ci 0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30, 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci 0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330, 26462306a36Sopenharmony_ci 0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730, 26562306a36Sopenharmony_ci 0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30, 26662306a36Sopenharmony_ci 0xf3ff, 0x8c30, 0xf7ff, 0x8d30, 0xfbff, 0x8e30, 0xffff, 0x8f30, 26762306a36Sopenharmony_ci}; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic const unsigned short init3[128] = { 27062306a36Sopenharmony_ci 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, 27162306a36Sopenharmony_ci 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x8F7C, 0x167E, 0xF254, 27262306a36Sopenharmony_ci 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x8BAA, 0x1B6D, 0xF234, 27362306a36Sopenharmony_ci 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x86E7, 0x229E, 0xF224, 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x87F6, 0x2C28, 0xF254, 27662306a36Sopenharmony_ci 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x8F02, 0x1341, 0xF264, 27762306a36Sopenharmony_ci 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x8FA9, 0x3EB5, 0xF294, 27862306a36Sopenharmony_ci 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0xC4C3, 0x3EBB, 0xC5C3, 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x8671, 0x14FD, 0x8287, 28162306a36Sopenharmony_ci 0x3EBC, 0xE610, 0x3EC8, 0x8C7B, 0x031A, 0x87E6, 0x3EC8, 0x86F7, 28262306a36Sopenharmony_ci 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x821F, 0x3ECA, 0x8386, 28362306a36Sopenharmony_ci 0x3EC1, 0x8C03, 0x3EC9, 0x831E, 0x3ECA, 0x8C4C, 0x3EBF, 0x8C55, 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x8EAD, 0x3EC8, 0xD308, 28662306a36Sopenharmony_ci 0x3EC2, 0x8F7E, 0x3ECB, 0x8219, 0x3ECB, 0xD26E, 0x3EC5, 0x831F, 28762306a36Sopenharmony_ci 0x3EC6, 0xC308, 0x3EC3, 0xB2FF, 0x3EC9, 0x8265, 0x3EC9, 0x8319, 28862306a36Sopenharmony_ci 0x1342, 0xD36E, 0x3EC7, 0xB3FF, 0x0000, 0x8365, 0x1420, 0x9570, 28962306a36Sopenharmony_ci}; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic const unsigned short init4[128] = { 29262306a36Sopenharmony_ci 0x0C10, 0x8470, 0x14FE, 0xB488, 0x167F, 0xA470, 0x18E7, 0x84B5, 29362306a36Sopenharmony_ci 0x1B6E, 0x842A, 0x1F1D, 0x852A, 0x0DA3, 0x0F7C, 0x167E, 0x7254, 29462306a36Sopenharmony_ci 0x0000, 0x842A, 0x0001, 0x852A, 0x18E6, 0x0BAA, 0x1B6D, 0x7234, 29562306a36Sopenharmony_ci 0x229F, 0x8429, 0x2746, 0x8529, 0x1F1C, 0x06E7, 0x229E, 0x7224, 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci 0x0DA4, 0x8429, 0x2C29, 0x8529, 0x2745, 0x07F6, 0x2C28, 0x7254, 29862306a36Sopenharmony_ci 0x383B, 0x8428, 0x320F, 0x8528, 0x320E, 0x0F02, 0x1341, 0x7264, 29962306a36Sopenharmony_ci 0x3EB6, 0x8428, 0x3EB9, 0x8528, 0x383A, 0x0FA9, 0x3EB5, 0x7294, 30062306a36Sopenharmony_ci 0x3EB7, 0x8474, 0x3EBA, 0x8575, 0x3EB8, 0x44C3, 0x3EBB, 0x45C3, 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci 0x0000, 0xA404, 0x0001, 0xA504, 0x141F, 0x0671, 0x14FD, 0x0287, 30362306a36Sopenharmony_ci 0x3EBC, 0xE610, 0x3EC8, 0x0C7B, 0x031A, 0x07E6, 0x3EC8, 0x86F7, 30462306a36Sopenharmony_ci 0x3EC0, 0x821E, 0x3EBE, 0xD208, 0x3EBD, 0x021F, 0x3ECA, 0x0386, 30562306a36Sopenharmony_ci 0x3EC1, 0x0C03, 0x3EC9, 0x031E, 0x3ECA, 0x8C4C, 0x3EBF, 0x0C55, 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci 0x3EC9, 0xC208, 0x3EC4, 0xBC84, 0x3EC8, 0x0EAD, 0x3EC8, 0xD308, 30862306a36Sopenharmony_ci 0x3EC2, 0x8F7E, 0x3ECB, 0x0219, 0x3ECB, 0xD26E, 0x3EC5, 0x031F, 30962306a36Sopenharmony_ci 0x3EC6, 0xC308, 0x3EC3, 0x32FF, 0x3EC9, 0x0265, 0x3EC9, 0x8319, 31062306a36Sopenharmony_ci 0x1342, 0xD36E, 0x3EC7, 0x33FF, 0x0000, 0x8365, 0x1420, 0x9570, 31162306a36Sopenharmony_ci}; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci/* send an initialization array 31462306a36Sopenharmony_ci * Taken from the oss driver, not obvious from the doc how this 31562306a36Sopenharmony_ci * is meant to work 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_cistatic void 31862306a36Sopenharmony_cisend_array(struct snd_emu8000 *emu, const unsigned short *data, int size) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci int i; 32162306a36Sopenharmony_ci const unsigned short *p; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci p = data; 32462306a36Sopenharmony_ci for (i = 0; i < size; i++, p++) 32562306a36Sopenharmony_ci EMU8000_INIT1_WRITE(emu, i, *p); 32662306a36Sopenharmony_ci for (i = 0; i < size; i++, p++) 32762306a36Sopenharmony_ci EMU8000_INIT2_WRITE(emu, i, *p); 32862306a36Sopenharmony_ci for (i = 0; i < size; i++, p++) 32962306a36Sopenharmony_ci EMU8000_INIT3_WRITE(emu, i, *p); 33062306a36Sopenharmony_ci for (i = 0; i < size; i++, p++) 33162306a36Sopenharmony_ci EMU8000_INIT4_WRITE(emu, i, *p); 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci/* 33662306a36Sopenharmony_ci * Send initialization arrays to start up, this just follows the 33762306a36Sopenharmony_ci * initialisation sequence in the adip. 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_cistatic void 34062306a36Sopenharmony_ciinit_arrays(struct snd_emu8000 *emu) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci send_array(emu, init1, ARRAY_SIZE(init1)/4); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci msleep((1024 * 1000) / 44100); /* wait for 1024 clocks */ 34562306a36Sopenharmony_ci send_array(emu, init2, ARRAY_SIZE(init2)/4); 34662306a36Sopenharmony_ci send_array(emu, init3, ARRAY_SIZE(init3)/4); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci EMU8000_HWCF4_WRITE(emu, 0); 34962306a36Sopenharmony_ci EMU8000_HWCF5_WRITE(emu, 0x83); 35062306a36Sopenharmony_ci EMU8000_HWCF6_WRITE(emu, 0x8000); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci send_array(emu, init4, ARRAY_SIZE(init4)/4); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci#define UNIQUE_ID1 0xa5b9 35762306a36Sopenharmony_ci#define UNIQUE_ID2 0x9d53 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci/* 36062306a36Sopenharmony_ci * Size the onboard memory. 36162306a36Sopenharmony_ci * This is written so as not to need arbitrary delays after the write. It 36262306a36Sopenharmony_ci * seems that the only way to do this is to use the one channel and keep 36362306a36Sopenharmony_ci * reallocating between read and write. 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_cistatic void 36662306a36Sopenharmony_cisize_dram(struct snd_emu8000 *emu) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci int i, size; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci if (emu->dram_checked) 37162306a36Sopenharmony_ci return; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci size = 0; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* write out a magic number */ 37662306a36Sopenharmony_ci snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE); 37762306a36Sopenharmony_ci snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_READ); 37862306a36Sopenharmony_ci EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET); 37962306a36Sopenharmony_ci EMU8000_SMLD_WRITE(emu, UNIQUE_ID1); 38062306a36Sopenharmony_ci snd_emu8000_init_fm(emu); /* This must really be here and not 2 lines back even */ 38162306a36Sopenharmony_ci snd_emu8000_write_wait(emu); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* 38462306a36Sopenharmony_ci * Detect first 512 KiB. If a write succeeds at the beginning of a 38562306a36Sopenharmony_ci * 512 KiB page we assume that the whole page is there. 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_ci EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET); 38862306a36Sopenharmony_ci EMU8000_SMLD_READ(emu); /* discard stale data */ 38962306a36Sopenharmony_ci if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1) 39062306a36Sopenharmony_ci goto skip_detect; /* No RAM */ 39162306a36Sopenharmony_ci snd_emu8000_read_wait(emu); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci for (size = 512 * 1024; size < EMU8000_MAX_DRAM; size += 512 * 1024) { 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* Write a unique data on the test address. 39662306a36Sopenharmony_ci * if the address is out of range, the data is written on 39762306a36Sopenharmony_ci * 0x200000(=EMU8000_DRAM_OFFSET). Then the id word is 39862306a36Sopenharmony_ci * changed by this data. 39962306a36Sopenharmony_ci */ 40062306a36Sopenharmony_ci /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);*/ 40162306a36Sopenharmony_ci EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1)); 40262306a36Sopenharmony_ci EMU8000_SMLD_WRITE(emu, UNIQUE_ID2); 40362306a36Sopenharmony_ci snd_emu8000_write_wait(emu); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* 40662306a36Sopenharmony_ci * read the data on the just written DRAM address 40762306a36Sopenharmony_ci * if not the same then we have reached the end of ram. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_READ);*/ 41062306a36Sopenharmony_ci EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1)); 41162306a36Sopenharmony_ci /*snd_emu8000_read_wait(emu);*/ 41262306a36Sopenharmony_ci EMU8000_SMLD_READ(emu); /* discard stale data */ 41362306a36Sopenharmony_ci if (EMU8000_SMLD_READ(emu) != UNIQUE_ID2) 41462306a36Sopenharmony_ci break; /* no memory at this address */ 41562306a36Sopenharmony_ci snd_emu8000_read_wait(emu); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* 41862306a36Sopenharmony_ci * If it is the same it could be that the address just 41962306a36Sopenharmony_ci * wraps back to the beginning; so check to see if the 42062306a36Sopenharmony_ci * initial value has been overwritten. 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_ci EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET); 42362306a36Sopenharmony_ci EMU8000_SMLD_READ(emu); /* discard stale data */ 42462306a36Sopenharmony_ci if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1) 42562306a36Sopenharmony_ci break; /* we must have wrapped around */ 42662306a36Sopenharmony_ci snd_emu8000_read_wait(emu); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* Otherwise, it's valid memory. */ 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ciskip_detect: 43262306a36Sopenharmony_ci /* wait until FULL bit in SMAxW register is false */ 43362306a36Sopenharmony_ci for (i = 0; i < 10000; i++) { 43462306a36Sopenharmony_ci if ((EMU8000_SMALW_READ(emu) & 0x80000000) == 0) 43562306a36Sopenharmony_ci break; 43662306a36Sopenharmony_ci schedule_timeout_interruptible(1); 43762306a36Sopenharmony_ci if (signal_pending(current)) 43862306a36Sopenharmony_ci break; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_CLOSE); 44162306a36Sopenharmony_ci snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_CLOSE); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci pr_info("EMU8000 [0x%lx]: %d KiB on-board DRAM detected\n", 44462306a36Sopenharmony_ci emu->port1, size/1024); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci emu->mem_size = size; 44762306a36Sopenharmony_ci emu->dram_checked = 1; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci/* 45262306a36Sopenharmony_ci * Initiailise the FM section. You have to do this to use sample RAM 45362306a36Sopenharmony_ci * and therefore lose 2 voices. 45462306a36Sopenharmony_ci */ 45562306a36Sopenharmony_ci/*exported*/ void 45662306a36Sopenharmony_cisnd_emu8000_init_fm(struct snd_emu8000 *emu) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci unsigned long flags; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* Initialize the last two channels for DRAM refresh and producing 46162306a36Sopenharmony_ci the reverb and chorus effects for Yamaha OPL-3 synthesizer */ 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* 31: FM left channel, 0xffffe0-0xffffe8 */ 46462306a36Sopenharmony_ci EMU8000_DCYSUSV_WRITE(emu, 30, 0x80); 46562306a36Sopenharmony_ci EMU8000_PSST_WRITE(emu, 30, 0xFFFFFFE0); /* full left */ 46662306a36Sopenharmony_ci EMU8000_CSL_WRITE(emu, 30, 0x00FFFFE8 | (emu->fm_chorus_depth << 24)); 46762306a36Sopenharmony_ci EMU8000_PTRX_WRITE(emu, 30, (emu->fm_reverb_depth << 8)); 46862306a36Sopenharmony_ci EMU8000_CPF_WRITE(emu, 30, 0); 46962306a36Sopenharmony_ci EMU8000_CCCA_WRITE(emu, 30, 0x00FFFFE3); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* 32: FM right channel, 0xfffff0-0xfffff8 */ 47262306a36Sopenharmony_ci EMU8000_DCYSUSV_WRITE(emu, 31, 0x80); 47362306a36Sopenharmony_ci EMU8000_PSST_WRITE(emu, 31, 0x00FFFFF0); /* full right */ 47462306a36Sopenharmony_ci EMU8000_CSL_WRITE(emu, 31, 0x00FFFFF8 | (emu->fm_chorus_depth << 24)); 47562306a36Sopenharmony_ci EMU8000_PTRX_WRITE(emu, 31, (emu->fm_reverb_depth << 8)); 47662306a36Sopenharmony_ci EMU8000_CPF_WRITE(emu, 31, 0x8000); 47762306a36Sopenharmony_ci EMU8000_CCCA_WRITE(emu, 31, 0x00FFFFF3); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci spin_lock_irqsave(&emu->reg_lock, flags); 48262306a36Sopenharmony_ci while (!(inw(EMU8000_PTR(emu)) & 0x1000)) 48362306a36Sopenharmony_ci ; 48462306a36Sopenharmony_ci while ((inw(EMU8000_PTR(emu)) & 0x1000)) 48562306a36Sopenharmony_ci ; 48662306a36Sopenharmony_ci spin_unlock_irqrestore(&emu->reg_lock, flags); 48762306a36Sopenharmony_ci snd_emu8000_poke((emu), EMU8000_DATA0(emu), EMU8000_CMD(1, (30)), 0x4828); 48862306a36Sopenharmony_ci /* this is really odd part.. */ 48962306a36Sopenharmony_ci outb(0x3C, EMU8000_PTR(emu)); 49062306a36Sopenharmony_ci outb(0, EMU8000_DATA1(emu)); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* skew volume & cutoff */ 49362306a36Sopenharmony_ci EMU8000_VTFT_WRITE(emu, 30, 0x8000FFFF); 49462306a36Sopenharmony_ci EMU8000_VTFT_WRITE(emu, 31, 0x8000FFFF); 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/* 49962306a36Sopenharmony_ci * The main initialization routine. 50062306a36Sopenharmony_ci */ 50162306a36Sopenharmony_cistatic void 50262306a36Sopenharmony_cisnd_emu8000_init_hw(struct snd_emu8000 *emu) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci int i; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci emu->last_reg = 0xffff; /* reset the last register index */ 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* initialize hardware configuration */ 50962306a36Sopenharmony_ci EMU8000_HWCF1_WRITE(emu, 0x0059); 51062306a36Sopenharmony_ci EMU8000_HWCF2_WRITE(emu, 0x0020); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* disable audio; this seems to reduce a clicking noise a bit.. */ 51362306a36Sopenharmony_ci EMU8000_HWCF3_WRITE(emu, 0); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* initialize audio channels */ 51662306a36Sopenharmony_ci init_audio(emu); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* initialize DMA */ 51962306a36Sopenharmony_ci init_dma(emu); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* initialize init arrays */ 52262306a36Sopenharmony_ci init_arrays(emu); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* 52562306a36Sopenharmony_ci * Initialize the FM section of the AWE32, this is needed 52662306a36Sopenharmony_ci * for DRAM refresh as well 52762306a36Sopenharmony_ci */ 52862306a36Sopenharmony_ci snd_emu8000_init_fm(emu); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* terminate all voices */ 53162306a36Sopenharmony_ci for (i = 0; i < EMU8000_DRAM_VOICES; i++) 53262306a36Sopenharmony_ci EMU8000_DCYSUSV_WRITE(emu, 0, 0x807F); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* check DRAM memory size */ 53562306a36Sopenharmony_ci size_dram(emu); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* enable audio */ 53862306a36Sopenharmony_ci EMU8000_HWCF3_WRITE(emu, 0x4); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* set equzlier, chorus and reverb modes */ 54162306a36Sopenharmony_ci snd_emu8000_update_equalizer(emu); 54262306a36Sopenharmony_ci snd_emu8000_update_chorus_mode(emu); 54362306a36Sopenharmony_ci snd_emu8000_update_reverb_mode(emu); 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci/*---------------------------------------------------------------- 54862306a36Sopenharmony_ci * Bass/Treble Equalizer 54962306a36Sopenharmony_ci *----------------------------------------------------------------*/ 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic const unsigned short bass_parm[12][3] = { 55262306a36Sopenharmony_ci {0xD26A, 0xD36A, 0x0000}, /* -12 dB */ 55362306a36Sopenharmony_ci {0xD25B, 0xD35B, 0x0000}, /* -8 */ 55462306a36Sopenharmony_ci {0xD24C, 0xD34C, 0x0000}, /* -6 */ 55562306a36Sopenharmony_ci {0xD23D, 0xD33D, 0x0000}, /* -4 */ 55662306a36Sopenharmony_ci {0xD21F, 0xD31F, 0x0000}, /* -2 */ 55762306a36Sopenharmony_ci {0xC208, 0xC308, 0x0001}, /* 0 (HW default) */ 55862306a36Sopenharmony_ci {0xC219, 0xC319, 0x0001}, /* +2 */ 55962306a36Sopenharmony_ci {0xC22A, 0xC32A, 0x0001}, /* +4 */ 56062306a36Sopenharmony_ci {0xC24C, 0xC34C, 0x0001}, /* +6 */ 56162306a36Sopenharmony_ci {0xC26E, 0xC36E, 0x0001}, /* +8 */ 56262306a36Sopenharmony_ci {0xC248, 0xC384, 0x0002}, /* +10 */ 56362306a36Sopenharmony_ci {0xC26A, 0xC36A, 0x0002}, /* +12 dB */ 56462306a36Sopenharmony_ci}; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic const unsigned short treble_parm[12][9] = { 56762306a36Sopenharmony_ci {0x821E, 0xC26A, 0x031E, 0xC36A, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, /* -12 dB */ 56862306a36Sopenharmony_ci {0x821E, 0xC25B, 0x031E, 0xC35B, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, 56962306a36Sopenharmony_ci {0x821E, 0xC24C, 0x031E, 0xC34C, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, 57062306a36Sopenharmony_ci {0x821E, 0xC23D, 0x031E, 0xC33D, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, 57162306a36Sopenharmony_ci {0x821E, 0xC21F, 0x031E, 0xC31F, 0x021E, 0xD208, 0x831E, 0xD308, 0x0001}, 57262306a36Sopenharmony_ci {0x821E, 0xD208, 0x031E, 0xD308, 0x021E, 0xD208, 0x831E, 0xD308, 0x0002}, 57362306a36Sopenharmony_ci {0x821E, 0xD208, 0x031E, 0xD308, 0x021D, 0xD219, 0x831D, 0xD319, 0x0002}, 57462306a36Sopenharmony_ci {0x821E, 0xD208, 0x031E, 0xD308, 0x021C, 0xD22A, 0x831C, 0xD32A, 0x0002}, 57562306a36Sopenharmony_ci {0x821E, 0xD208, 0x031E, 0xD308, 0x021A, 0xD24C, 0x831A, 0xD34C, 0x0002}, 57662306a36Sopenharmony_ci {0x821E, 0xD208, 0x031E, 0xD308, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, /* +8 (HW default) */ 57762306a36Sopenharmony_ci {0x821D, 0xD219, 0x031D, 0xD319, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002}, 57862306a36Sopenharmony_ci {0x821C, 0xD22A, 0x031C, 0xD32A, 0x0219, 0xD26E, 0x8319, 0xD36E, 0x0002} /* +12 dB */ 57962306a36Sopenharmony_ci}; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci/* 58362306a36Sopenharmony_ci * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB] 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ci/*exported*/ void 58662306a36Sopenharmony_cisnd_emu8000_update_equalizer(struct snd_emu8000 *emu) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci unsigned short w; 58962306a36Sopenharmony_ci int bass = emu->bass_level; 59062306a36Sopenharmony_ci int treble = emu->treble_level; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci if (bass < 0 || bass > 11 || treble < 0 || treble > 11) 59362306a36Sopenharmony_ci return; 59462306a36Sopenharmony_ci EMU8000_INIT4_WRITE(emu, 0x01, bass_parm[bass][0]); 59562306a36Sopenharmony_ci EMU8000_INIT4_WRITE(emu, 0x11, bass_parm[bass][1]); 59662306a36Sopenharmony_ci EMU8000_INIT3_WRITE(emu, 0x11, treble_parm[treble][0]); 59762306a36Sopenharmony_ci EMU8000_INIT3_WRITE(emu, 0x13, treble_parm[treble][1]); 59862306a36Sopenharmony_ci EMU8000_INIT3_WRITE(emu, 0x1b, treble_parm[treble][2]); 59962306a36Sopenharmony_ci EMU8000_INIT4_WRITE(emu, 0x07, treble_parm[treble][3]); 60062306a36Sopenharmony_ci EMU8000_INIT4_WRITE(emu, 0x0b, treble_parm[treble][4]); 60162306a36Sopenharmony_ci EMU8000_INIT4_WRITE(emu, 0x0d, treble_parm[treble][5]); 60262306a36Sopenharmony_ci EMU8000_INIT4_WRITE(emu, 0x17, treble_parm[treble][6]); 60362306a36Sopenharmony_ci EMU8000_INIT4_WRITE(emu, 0x19, treble_parm[treble][7]); 60462306a36Sopenharmony_ci w = bass_parm[bass][2] + treble_parm[treble][8]; 60562306a36Sopenharmony_ci EMU8000_INIT4_WRITE(emu, 0x15, (unsigned short)(w + 0x0262)); 60662306a36Sopenharmony_ci EMU8000_INIT4_WRITE(emu, 0x1d, (unsigned short)(w + 0x8362)); 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci/*---------------------------------------------------------------- 61162306a36Sopenharmony_ci * Chorus mode control 61262306a36Sopenharmony_ci *----------------------------------------------------------------*/ 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci/* 61562306a36Sopenharmony_ci * chorus mode parameters 61662306a36Sopenharmony_ci */ 61762306a36Sopenharmony_ci#define SNDRV_EMU8000_CHORUS_1 0 61862306a36Sopenharmony_ci#define SNDRV_EMU8000_CHORUS_2 1 61962306a36Sopenharmony_ci#define SNDRV_EMU8000_CHORUS_3 2 62062306a36Sopenharmony_ci#define SNDRV_EMU8000_CHORUS_4 3 62162306a36Sopenharmony_ci#define SNDRV_EMU8000_CHORUS_FEEDBACK 4 62262306a36Sopenharmony_ci#define SNDRV_EMU8000_CHORUS_FLANGER 5 62362306a36Sopenharmony_ci#define SNDRV_EMU8000_CHORUS_SHORTDELAY 6 62462306a36Sopenharmony_ci#define SNDRV_EMU8000_CHORUS_SHORTDELAY2 7 62562306a36Sopenharmony_ci#define SNDRV_EMU8000_CHORUS_PREDEFINED 8 62662306a36Sopenharmony_ci/* user can define chorus modes up to 32 */ 62762306a36Sopenharmony_ci#define SNDRV_EMU8000_CHORUS_NUMBERS 32 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistruct soundfont_chorus_fx { 63062306a36Sopenharmony_ci unsigned short feedback; /* feedback level (0xE600-0xE6FF) */ 63162306a36Sopenharmony_ci unsigned short delay_offset; /* delay (0-0x0DA3) [1/44100 sec] */ 63262306a36Sopenharmony_ci unsigned short lfo_depth; /* LFO depth (0xBC00-0xBCFF) */ 63362306a36Sopenharmony_ci unsigned int delay; /* right delay (0-0xFFFFFFFF) [1/256/44100 sec] */ 63462306a36Sopenharmony_ci unsigned int lfo_freq; /* LFO freq LFO freq (0-0xFFFFFFFF) */ 63562306a36Sopenharmony_ci}; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */ 63862306a36Sopenharmony_cistatic char chorus_defined[SNDRV_EMU8000_CHORUS_NUMBERS]; 63962306a36Sopenharmony_cistatic struct soundfont_chorus_fx chorus_parm[SNDRV_EMU8000_CHORUS_NUMBERS] = { 64062306a36Sopenharmony_ci {0xE600, 0x03F6, 0xBC2C ,0x00000000, 0x0000006D}, /* chorus 1 */ 64162306a36Sopenharmony_ci {0xE608, 0x031A, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 2 */ 64262306a36Sopenharmony_ci {0xE610, 0x031A, 0xBC84, 0x00000000, 0x00000083}, /* chorus 3 */ 64362306a36Sopenharmony_ci {0xE620, 0x0269, 0xBC6E, 0x00000000, 0x0000017C}, /* chorus 4 */ 64462306a36Sopenharmony_ci {0xE680, 0x04D3, 0xBCA6, 0x00000000, 0x0000005B}, /* feedback */ 64562306a36Sopenharmony_ci {0xE6E0, 0x044E, 0xBC37, 0x00000000, 0x00000026}, /* flanger */ 64662306a36Sopenharmony_ci {0xE600, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay */ 64762306a36Sopenharmony_ci {0xE6C0, 0x0B06, 0xBC00, 0x0006E000, 0x00000083}, /* short delay + feedback */ 64862306a36Sopenharmony_ci}; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/*exported*/ int 65162306a36Sopenharmony_cisnd_emu8000_load_chorus_fx(struct snd_emu8000 *emu, int mode, const void __user *buf, long len) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct soundfont_chorus_fx rec; 65462306a36Sopenharmony_ci if (mode < SNDRV_EMU8000_CHORUS_PREDEFINED || mode >= SNDRV_EMU8000_CHORUS_NUMBERS) { 65562306a36Sopenharmony_ci snd_printk(KERN_WARNING "invalid chorus mode %d for uploading\n", mode); 65662306a36Sopenharmony_ci return -EINVAL; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci if (len < (long)sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec))) 65962306a36Sopenharmony_ci return -EFAULT; 66062306a36Sopenharmony_ci chorus_parm[mode] = rec; 66162306a36Sopenharmony_ci chorus_defined[mode] = 1; 66262306a36Sopenharmony_ci return 0; 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci/*exported*/ void 66662306a36Sopenharmony_cisnd_emu8000_update_chorus_mode(struct snd_emu8000 *emu) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci int effect = emu->chorus_mode; 66962306a36Sopenharmony_ci if (effect < 0 || effect >= SNDRV_EMU8000_CHORUS_NUMBERS || 67062306a36Sopenharmony_ci (effect >= SNDRV_EMU8000_CHORUS_PREDEFINED && !chorus_defined[effect])) 67162306a36Sopenharmony_ci return; 67262306a36Sopenharmony_ci EMU8000_INIT3_WRITE(emu, 0x09, chorus_parm[effect].feedback); 67362306a36Sopenharmony_ci EMU8000_INIT3_WRITE(emu, 0x0c, chorus_parm[effect].delay_offset); 67462306a36Sopenharmony_ci EMU8000_INIT4_WRITE(emu, 0x03, chorus_parm[effect].lfo_depth); 67562306a36Sopenharmony_ci EMU8000_HWCF4_WRITE(emu, chorus_parm[effect].delay); 67662306a36Sopenharmony_ci EMU8000_HWCF5_WRITE(emu, chorus_parm[effect].lfo_freq); 67762306a36Sopenharmony_ci EMU8000_HWCF6_WRITE(emu, 0x8000); 67862306a36Sopenharmony_ci EMU8000_HWCF7_WRITE(emu, 0x0000); 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci/*---------------------------------------------------------------- 68262306a36Sopenharmony_ci * Reverb mode control 68362306a36Sopenharmony_ci *----------------------------------------------------------------*/ 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci/* 68662306a36Sopenharmony_ci * reverb mode parameters 68762306a36Sopenharmony_ci */ 68862306a36Sopenharmony_ci#define SNDRV_EMU8000_REVERB_ROOM1 0 68962306a36Sopenharmony_ci#define SNDRV_EMU8000_REVERB_ROOM2 1 69062306a36Sopenharmony_ci#define SNDRV_EMU8000_REVERB_ROOM3 2 69162306a36Sopenharmony_ci#define SNDRV_EMU8000_REVERB_HALL1 3 69262306a36Sopenharmony_ci#define SNDRV_EMU8000_REVERB_HALL2 4 69362306a36Sopenharmony_ci#define SNDRV_EMU8000_REVERB_PLATE 5 69462306a36Sopenharmony_ci#define SNDRV_EMU8000_REVERB_DELAY 6 69562306a36Sopenharmony_ci#define SNDRV_EMU8000_REVERB_PANNINGDELAY 7 69662306a36Sopenharmony_ci#define SNDRV_EMU8000_REVERB_PREDEFINED 8 69762306a36Sopenharmony_ci/* user can define reverb modes up to 32 */ 69862306a36Sopenharmony_ci#define SNDRV_EMU8000_REVERB_NUMBERS 32 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cistruct soundfont_reverb_fx { 70162306a36Sopenharmony_ci unsigned short parms[28]; 70262306a36Sopenharmony_ci}; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci/* reverb mode settings; write the following 28 data of 16 bit length 70562306a36Sopenharmony_ci * on the corresponding ports in the reverb_cmds array 70662306a36Sopenharmony_ci */ 70762306a36Sopenharmony_cistatic char reverb_defined[SNDRV_EMU8000_CHORUS_NUMBERS]; 70862306a36Sopenharmony_cistatic struct soundfont_reverb_fx reverb_parm[SNDRV_EMU8000_REVERB_NUMBERS] = { 70962306a36Sopenharmony_ci{{ /* room 1 */ 71062306a36Sopenharmony_ci 0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4, 71162306a36Sopenharmony_ci 0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516, 71262306a36Sopenharmony_ci 0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, 71362306a36Sopenharmony_ci 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, 71462306a36Sopenharmony_ci}}, 71562306a36Sopenharmony_ci{{ /* room 2 */ 71662306a36Sopenharmony_ci 0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284, 71762306a36Sopenharmony_ci 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, 71862306a36Sopenharmony_ci 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, 71962306a36Sopenharmony_ci 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, 72062306a36Sopenharmony_ci}}, 72162306a36Sopenharmony_ci{{ /* room 3 */ 72262306a36Sopenharmony_ci 0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284, 72362306a36Sopenharmony_ci 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516, 72462306a36Sopenharmony_ci 0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B, 72562306a36Sopenharmony_ci 0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A, 72662306a36Sopenharmony_ci}}, 72762306a36Sopenharmony_ci{{ /* hall 1 */ 72862306a36Sopenharmony_ci 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284, 72962306a36Sopenharmony_ci 0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548, 73062306a36Sopenharmony_ci 0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A, 73162306a36Sopenharmony_ci 0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529, 73262306a36Sopenharmony_ci}}, 73362306a36Sopenharmony_ci{{ /* hall 2 */ 73462306a36Sopenharmony_ci 0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254, 73562306a36Sopenharmony_ci 0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3, 73662306a36Sopenharmony_ci 0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, 73762306a36Sopenharmony_ci 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, 73862306a36Sopenharmony_ci}}, 73962306a36Sopenharmony_ci{{ /* plate */ 74062306a36Sopenharmony_ci 0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234, 74162306a36Sopenharmony_ci 0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548, 74262306a36Sopenharmony_ci 0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429, 74362306a36Sopenharmony_ci 0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528, 74462306a36Sopenharmony_ci}}, 74562306a36Sopenharmony_ci{{ /* delay */ 74662306a36Sopenharmony_ci 0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204, 74762306a36Sopenharmony_ci 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, 74862306a36Sopenharmony_ci 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 74962306a36Sopenharmony_ci 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 75062306a36Sopenharmony_ci}}, 75162306a36Sopenharmony_ci{{ /* panning delay */ 75262306a36Sopenharmony_ci 0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204, 75362306a36Sopenharmony_ci 0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500, 75462306a36Sopenharmony_ci 0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 75562306a36Sopenharmony_ci 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 75662306a36Sopenharmony_ci}}, 75762306a36Sopenharmony_ci}; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cienum { DATA1, DATA2 }; 76062306a36Sopenharmony_ci#define AWE_INIT1(c) EMU8000_CMD(2,c), DATA1 76162306a36Sopenharmony_ci#define AWE_INIT2(c) EMU8000_CMD(2,c), DATA2 76262306a36Sopenharmony_ci#define AWE_INIT3(c) EMU8000_CMD(3,c), DATA1 76362306a36Sopenharmony_ci#define AWE_INIT4(c) EMU8000_CMD(3,c), DATA2 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_cistatic struct reverb_cmd_pair { 76662306a36Sopenharmony_ci unsigned short cmd, port; 76762306a36Sopenharmony_ci} reverb_cmds[28] = { 76862306a36Sopenharmony_ci {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)}, 76962306a36Sopenharmony_ci {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)}, 77062306a36Sopenharmony_ci {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)}, 77162306a36Sopenharmony_ci {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)}, 77262306a36Sopenharmony_ci {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)}, 77362306a36Sopenharmony_ci {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)}, 77462306a36Sopenharmony_ci {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)}, 77562306a36Sopenharmony_ci}; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci/*exported*/ int 77862306a36Sopenharmony_cisnd_emu8000_load_reverb_fx(struct snd_emu8000 *emu, int mode, const void __user *buf, long len) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci struct soundfont_reverb_fx rec; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci if (mode < SNDRV_EMU8000_REVERB_PREDEFINED || mode >= SNDRV_EMU8000_REVERB_NUMBERS) { 78362306a36Sopenharmony_ci snd_printk(KERN_WARNING "invalid reverb mode %d for uploading\n", mode); 78462306a36Sopenharmony_ci return -EINVAL; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci if (len < (long)sizeof(rec) || copy_from_user(&rec, buf, sizeof(rec))) 78762306a36Sopenharmony_ci return -EFAULT; 78862306a36Sopenharmony_ci reverb_parm[mode] = rec; 78962306a36Sopenharmony_ci reverb_defined[mode] = 1; 79062306a36Sopenharmony_ci return 0; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci/*exported*/ void 79462306a36Sopenharmony_cisnd_emu8000_update_reverb_mode(struct snd_emu8000 *emu) 79562306a36Sopenharmony_ci{ 79662306a36Sopenharmony_ci int effect = emu->reverb_mode; 79762306a36Sopenharmony_ci int i; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (effect < 0 || effect >= SNDRV_EMU8000_REVERB_NUMBERS || 80062306a36Sopenharmony_ci (effect >= SNDRV_EMU8000_REVERB_PREDEFINED && !reverb_defined[effect])) 80162306a36Sopenharmony_ci return; 80262306a36Sopenharmony_ci for (i = 0; i < 28; i++) { 80362306a36Sopenharmony_ci int port; 80462306a36Sopenharmony_ci if (reverb_cmds[i].port == DATA1) 80562306a36Sopenharmony_ci port = EMU8000_DATA1(emu); 80662306a36Sopenharmony_ci else 80762306a36Sopenharmony_ci port = EMU8000_DATA2(emu); 80862306a36Sopenharmony_ci snd_emu8000_poke(emu, port, reverb_cmds[i].cmd, reverb_parm[effect].parms[i]); 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci/*---------------------------------------------------------------- 81462306a36Sopenharmony_ci * mixer interface 81562306a36Sopenharmony_ci *----------------------------------------------------------------*/ 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci/* 81862306a36Sopenharmony_ci * bass/treble 81962306a36Sopenharmony_ci */ 82062306a36Sopenharmony_cistatic int mixer_bass_treble_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 82362306a36Sopenharmony_ci uinfo->count = 1; 82462306a36Sopenharmony_ci uinfo->value.integer.min = 0; 82562306a36Sopenharmony_ci uinfo->value.integer.max = 11; 82662306a36Sopenharmony_ci return 0; 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic int mixer_bass_treble_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->treble_level : emu->bass_level; 83462306a36Sopenharmony_ci return 0; 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic int mixer_bass_treble_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol); 84062306a36Sopenharmony_ci unsigned long flags; 84162306a36Sopenharmony_ci int change; 84262306a36Sopenharmony_ci unsigned short val1; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci val1 = ucontrol->value.integer.value[0] % 12; 84562306a36Sopenharmony_ci spin_lock_irqsave(&emu->control_lock, flags); 84662306a36Sopenharmony_ci if (kcontrol->private_value) { 84762306a36Sopenharmony_ci change = val1 != emu->treble_level; 84862306a36Sopenharmony_ci emu->treble_level = val1; 84962306a36Sopenharmony_ci } else { 85062306a36Sopenharmony_ci change = val1 != emu->bass_level; 85162306a36Sopenharmony_ci emu->bass_level = val1; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci spin_unlock_irqrestore(&emu->control_lock, flags); 85462306a36Sopenharmony_ci snd_emu8000_update_equalizer(emu); 85562306a36Sopenharmony_ci return change; 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic const struct snd_kcontrol_new mixer_bass_control = 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 86162306a36Sopenharmony_ci .name = "Synth Tone Control - Bass", 86262306a36Sopenharmony_ci .info = mixer_bass_treble_info, 86362306a36Sopenharmony_ci .get = mixer_bass_treble_get, 86462306a36Sopenharmony_ci .put = mixer_bass_treble_put, 86562306a36Sopenharmony_ci .private_value = 0, 86662306a36Sopenharmony_ci}; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_cistatic const struct snd_kcontrol_new mixer_treble_control = 86962306a36Sopenharmony_ci{ 87062306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 87162306a36Sopenharmony_ci .name = "Synth Tone Control - Treble", 87262306a36Sopenharmony_ci .info = mixer_bass_treble_info, 87362306a36Sopenharmony_ci .get = mixer_bass_treble_get, 87462306a36Sopenharmony_ci .put = mixer_bass_treble_put, 87562306a36Sopenharmony_ci .private_value = 1, 87662306a36Sopenharmony_ci}; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci/* 87962306a36Sopenharmony_ci * chorus/reverb mode 88062306a36Sopenharmony_ci */ 88162306a36Sopenharmony_cistatic int mixer_chorus_reverb_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 88262306a36Sopenharmony_ci{ 88362306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 88462306a36Sopenharmony_ci uinfo->count = 1; 88562306a36Sopenharmony_ci uinfo->value.integer.min = 0; 88662306a36Sopenharmony_ci uinfo->value.integer.max = kcontrol->private_value ? (SNDRV_EMU8000_CHORUS_NUMBERS-1) : (SNDRV_EMU8000_REVERB_NUMBERS-1); 88762306a36Sopenharmony_ci return 0; 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_cistatic int mixer_chorus_reverb_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->chorus_mode : emu->reverb_mode; 89562306a36Sopenharmony_ci return 0; 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic int mixer_chorus_reverb_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 89962306a36Sopenharmony_ci{ 90062306a36Sopenharmony_ci struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol); 90162306a36Sopenharmony_ci unsigned long flags; 90262306a36Sopenharmony_ci int change; 90362306a36Sopenharmony_ci unsigned short val1; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci spin_lock_irqsave(&emu->control_lock, flags); 90662306a36Sopenharmony_ci if (kcontrol->private_value) { 90762306a36Sopenharmony_ci val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_CHORUS_NUMBERS; 90862306a36Sopenharmony_ci change = val1 != emu->chorus_mode; 90962306a36Sopenharmony_ci emu->chorus_mode = val1; 91062306a36Sopenharmony_ci } else { 91162306a36Sopenharmony_ci val1 = ucontrol->value.integer.value[0] % SNDRV_EMU8000_REVERB_NUMBERS; 91262306a36Sopenharmony_ci change = val1 != emu->reverb_mode; 91362306a36Sopenharmony_ci emu->reverb_mode = val1; 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci spin_unlock_irqrestore(&emu->control_lock, flags); 91662306a36Sopenharmony_ci if (change) { 91762306a36Sopenharmony_ci if (kcontrol->private_value) 91862306a36Sopenharmony_ci snd_emu8000_update_chorus_mode(emu); 91962306a36Sopenharmony_ci else 92062306a36Sopenharmony_ci snd_emu8000_update_reverb_mode(emu); 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci return change; 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic const struct snd_kcontrol_new mixer_chorus_mode_control = 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 92862306a36Sopenharmony_ci .name = "Chorus Mode", 92962306a36Sopenharmony_ci .info = mixer_chorus_reverb_info, 93062306a36Sopenharmony_ci .get = mixer_chorus_reverb_get, 93162306a36Sopenharmony_ci .put = mixer_chorus_reverb_put, 93262306a36Sopenharmony_ci .private_value = 1, 93362306a36Sopenharmony_ci}; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_cistatic const struct snd_kcontrol_new mixer_reverb_mode_control = 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 93862306a36Sopenharmony_ci .name = "Reverb Mode", 93962306a36Sopenharmony_ci .info = mixer_chorus_reverb_info, 94062306a36Sopenharmony_ci .get = mixer_chorus_reverb_get, 94162306a36Sopenharmony_ci .put = mixer_chorus_reverb_put, 94262306a36Sopenharmony_ci .private_value = 0, 94362306a36Sopenharmony_ci}; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci/* 94662306a36Sopenharmony_ci * FM OPL3 chorus/reverb depth 94762306a36Sopenharmony_ci */ 94862306a36Sopenharmony_cistatic int mixer_fm_depth_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 94962306a36Sopenharmony_ci{ 95062306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 95162306a36Sopenharmony_ci uinfo->count = 1; 95262306a36Sopenharmony_ci uinfo->value.integer.min = 0; 95362306a36Sopenharmony_ci uinfo->value.integer.max = 255; 95462306a36Sopenharmony_ci return 0; 95562306a36Sopenharmony_ci} 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cistatic int mixer_fm_depth_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 95862306a36Sopenharmony_ci{ 95962306a36Sopenharmony_ci struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci ucontrol->value.integer.value[0] = kcontrol->private_value ? emu->fm_chorus_depth : emu->fm_reverb_depth; 96262306a36Sopenharmony_ci return 0; 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic int mixer_fm_depth_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci struct snd_emu8000 *emu = snd_kcontrol_chip(kcontrol); 96862306a36Sopenharmony_ci unsigned long flags; 96962306a36Sopenharmony_ci int change; 97062306a36Sopenharmony_ci unsigned short val1; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci val1 = ucontrol->value.integer.value[0] % 256; 97362306a36Sopenharmony_ci spin_lock_irqsave(&emu->control_lock, flags); 97462306a36Sopenharmony_ci if (kcontrol->private_value) { 97562306a36Sopenharmony_ci change = val1 != emu->fm_chorus_depth; 97662306a36Sopenharmony_ci emu->fm_chorus_depth = val1; 97762306a36Sopenharmony_ci } else { 97862306a36Sopenharmony_ci change = val1 != emu->fm_reverb_depth; 97962306a36Sopenharmony_ci emu->fm_reverb_depth = val1; 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci spin_unlock_irqrestore(&emu->control_lock, flags); 98262306a36Sopenharmony_ci if (change) 98362306a36Sopenharmony_ci snd_emu8000_init_fm(emu); 98462306a36Sopenharmony_ci return change; 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic const struct snd_kcontrol_new mixer_fm_chorus_depth_control = 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 99062306a36Sopenharmony_ci .name = "FM Chorus Depth", 99162306a36Sopenharmony_ci .info = mixer_fm_depth_info, 99262306a36Sopenharmony_ci .get = mixer_fm_depth_get, 99362306a36Sopenharmony_ci .put = mixer_fm_depth_put, 99462306a36Sopenharmony_ci .private_value = 1, 99562306a36Sopenharmony_ci}; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_cistatic const struct snd_kcontrol_new mixer_fm_reverb_depth_control = 99862306a36Sopenharmony_ci{ 99962306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 100062306a36Sopenharmony_ci .name = "FM Reverb Depth", 100162306a36Sopenharmony_ci .info = mixer_fm_depth_info, 100262306a36Sopenharmony_ci .get = mixer_fm_depth_get, 100362306a36Sopenharmony_ci .put = mixer_fm_depth_put, 100462306a36Sopenharmony_ci .private_value = 0, 100562306a36Sopenharmony_ci}; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_cistatic const struct snd_kcontrol_new *mixer_defs[EMU8000_NUM_CONTROLS] = { 100962306a36Sopenharmony_ci &mixer_bass_control, 101062306a36Sopenharmony_ci &mixer_treble_control, 101162306a36Sopenharmony_ci &mixer_chorus_mode_control, 101262306a36Sopenharmony_ci &mixer_reverb_mode_control, 101362306a36Sopenharmony_ci &mixer_fm_chorus_depth_control, 101462306a36Sopenharmony_ci &mixer_fm_reverb_depth_control, 101562306a36Sopenharmony_ci}; 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci/* 101862306a36Sopenharmony_ci * create and attach mixer elements for WaveTable treble/bass controls 101962306a36Sopenharmony_ci */ 102062306a36Sopenharmony_cistatic int 102162306a36Sopenharmony_cisnd_emu8000_create_mixer(struct snd_card *card, struct snd_emu8000 *emu) 102262306a36Sopenharmony_ci{ 102362306a36Sopenharmony_ci struct snd_kcontrol *kctl; 102462306a36Sopenharmony_ci int i, err = 0; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci if (snd_BUG_ON(!emu || !card)) 102762306a36Sopenharmony_ci return -EINVAL; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci spin_lock_init(&emu->control_lock); 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci memset(emu->controls, 0, sizeof(emu->controls)); 103262306a36Sopenharmony_ci for (i = 0; i < EMU8000_NUM_CONTROLS; i++) { 103362306a36Sopenharmony_ci kctl = snd_ctl_new1(mixer_defs[i], emu); 103462306a36Sopenharmony_ci err = snd_ctl_add(card, kctl); 103562306a36Sopenharmony_ci if (err < 0) 103662306a36Sopenharmony_ci goto __error; 103762306a36Sopenharmony_ci emu->controls[i] = kctl; 103862306a36Sopenharmony_ci } 103962306a36Sopenharmony_ci return 0; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci__error: 104262306a36Sopenharmony_ci for (i = 0; i < EMU8000_NUM_CONTROLS; i++) { 104362306a36Sopenharmony_ci if (emu->controls[i]) 104462306a36Sopenharmony_ci snd_ctl_remove(card, emu->controls[i]); 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci return err; 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci/* 105062306a36Sopenharmony_ci * initialize and register emu8000 synth device. 105162306a36Sopenharmony_ci */ 105262306a36Sopenharmony_ciint 105362306a36Sopenharmony_cisnd_emu8000_new(struct snd_card *card, int index, long port, int seq_ports, 105462306a36Sopenharmony_ci struct snd_seq_device **awe_ret) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci struct snd_seq_device *awe; 105762306a36Sopenharmony_ci struct snd_emu8000 *hw; 105862306a36Sopenharmony_ci int err; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci if (awe_ret) 106162306a36Sopenharmony_ci *awe_ret = NULL; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci if (seq_ports <= 0) 106462306a36Sopenharmony_ci return 0; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci hw = devm_kzalloc(card->dev, sizeof(*hw), GFP_KERNEL); 106762306a36Sopenharmony_ci if (hw == NULL) 106862306a36Sopenharmony_ci return -ENOMEM; 106962306a36Sopenharmony_ci spin_lock_init(&hw->reg_lock); 107062306a36Sopenharmony_ci hw->index = index; 107162306a36Sopenharmony_ci hw->port1 = port; 107262306a36Sopenharmony_ci hw->port2 = port + 0x400; 107362306a36Sopenharmony_ci hw->port3 = port + 0x800; 107462306a36Sopenharmony_ci if (!devm_request_region(card->dev, hw->port1, 4, "Emu8000-1") || 107562306a36Sopenharmony_ci !devm_request_region(card->dev, hw->port2, 4, "Emu8000-2") || 107662306a36Sopenharmony_ci !devm_request_region(card->dev, hw->port3, 4, "Emu8000-3")) { 107762306a36Sopenharmony_ci snd_printk(KERN_ERR "sbawe: can't grab ports 0x%lx, 0x%lx, 0x%lx\n", hw->port1, hw->port2, hw->port3); 107862306a36Sopenharmony_ci return -EBUSY; 107962306a36Sopenharmony_ci } 108062306a36Sopenharmony_ci hw->mem_size = 0; 108162306a36Sopenharmony_ci hw->card = card; 108262306a36Sopenharmony_ci hw->seq_ports = seq_ports; 108362306a36Sopenharmony_ci hw->bass_level = 5; 108462306a36Sopenharmony_ci hw->treble_level = 9; 108562306a36Sopenharmony_ci hw->chorus_mode = 2; 108662306a36Sopenharmony_ci hw->reverb_mode = 4; 108762306a36Sopenharmony_ci hw->fm_chorus_depth = 0; 108862306a36Sopenharmony_ci hw->fm_reverb_depth = 0; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (snd_emu8000_detect(hw) < 0) 109162306a36Sopenharmony_ci return -ENODEV; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci snd_emu8000_init_hw(hw); 109462306a36Sopenharmony_ci err = snd_emu8000_create_mixer(card, hw); 109562306a36Sopenharmony_ci if (err < 0) 109662306a36Sopenharmony_ci return err; 109762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SEQUENCER) 109862306a36Sopenharmony_ci if (snd_seq_device_new(card, index, SNDRV_SEQ_DEV_ID_EMU8000, 109962306a36Sopenharmony_ci sizeof(struct snd_emu8000*), &awe) >= 0) { 110062306a36Sopenharmony_ci strcpy(awe->name, "EMU-8000"); 110162306a36Sopenharmony_ci *(struct snd_emu8000 **)SNDRV_SEQ_DEVICE_ARGPTR(awe) = hw; 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci#else 110462306a36Sopenharmony_ci awe = NULL; 110562306a36Sopenharmony_ci#endif 110662306a36Sopenharmony_ci if (awe_ret) 110762306a36Sopenharmony_ci *awe_ret = awe; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci return 0; 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci/* 111462306a36Sopenharmony_ci * exported stuff 111562306a36Sopenharmony_ci */ 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_poke); 111862306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_peek); 111962306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_poke_dw); 112062306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_peek_dw); 112162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_dma_chan); 112262306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_init_fm); 112362306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_load_chorus_fx); 112462306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_load_reverb_fx); 112562306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_update_chorus_mode); 112662306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_update_reverb_mode); 112762306a36Sopenharmony_ciEXPORT_SYMBOL(snd_emu8000_update_equalizer); 1128