diff options
Diffstat (limited to 'ANDROID_3.4.5/sound/pci/emu10k1/emu10k1_callback.c')
-rw-r--r-- | ANDROID_3.4.5/sound/pci/emu10k1/emu10k1_callback.c | 552 |
1 files changed, 0 insertions, 552 deletions
diff --git a/ANDROID_3.4.5/sound/pci/emu10k1/emu10k1_callback.c b/ANDROID_3.4.5/sound/pci/emu10k1/emu10k1_callback.c deleted file mode 100644 index a0afa505..00000000 --- a/ANDROID_3.4.5/sound/pci/emu10k1/emu10k1_callback.c +++ /dev/null @@ -1,552 +0,0 @@ -/* - * synth callback routines for Emu10k1 - * - * Copyright (C) 2000 Takashi Iwai <tiwai@suse.de> - * - * 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/export.h> -#include "emu10k1_synth_local.h" -#include <sound/asoundef.h> - -/* voice status */ -enum { - V_FREE=0, V_OFF, V_RELEASED, V_PLAYING, V_END -}; - -/* Keeps track of what we are finding */ -struct best_voice { - unsigned int time; - int voice; -}; - -/* - * prototypes - */ -static void lookup_voices(struct snd_emux *emux, struct snd_emu10k1 *hw, - struct best_voice *best, int active_only); -static struct snd_emux_voice *get_voice(struct snd_emux *emux, - struct snd_emux_port *port); -static int start_voice(struct snd_emux_voice *vp); -static void trigger_voice(struct snd_emux_voice *vp); -static void release_voice(struct snd_emux_voice *vp); -static void update_voice(struct snd_emux_voice *vp, int update); -static void terminate_voice(struct snd_emux_voice *vp); -static void free_voice(struct snd_emux_voice *vp); -static void set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp); -static void set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp); -static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp); - -/* - * Ensure a value is between two points - * macro evaluates its args more than once, so changed to upper-case. - */ -#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0) -#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0) - - -/* - * set up operators - */ -static struct snd_emux_operators emu10k1_ops = { - .owner = THIS_MODULE, - .get_voice = get_voice, - .prepare = start_voice, - .trigger = trigger_voice, - .release = release_voice, - .update = update_voice, - .terminate = terminate_voice, - .free_voice = free_voice, - .sample_new = snd_emu10k1_sample_new, - .sample_free = snd_emu10k1_sample_free, -}; - -void -snd_emu10k1_ops_setup(struct snd_emux *emux) -{ - emux->ops = emu10k1_ops; -} - - -/* - * get more voice for pcm - * - * terminate most inactive voice and give it as a pcm voice. - */ -int -snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw) -{ - struct snd_emux *emu; - struct snd_emux_voice *vp; - struct best_voice best[V_END]; - unsigned long flags; - int i; - - emu = hw->synth; - - spin_lock_irqsave(&emu->voice_lock, flags); - lookup_voices(emu, hw, best, 1); /* no OFF voices */ - for (i = 0; i < V_END; i++) { - if (best[i].voice >= 0) { - int ch; - vp = &emu->voices[best[i].voice]; - if ((ch = vp->ch) < 0) { - /* - printk(KERN_WARNING - "synth_get_voice: ch < 0 (%d) ??", i); - */ - continue; - } - vp->emu->num_voices--; - vp->ch = -1; - vp->state = SNDRV_EMUX_ST_OFF; - spin_unlock_irqrestore(&emu->voice_lock, flags); - return ch; - } - } - spin_unlock_irqrestore(&emu->voice_lock, flags); - - /* not found */ - return -ENOMEM; -} - - -/* - * turn off the voice (not terminated) - */ -static void -release_voice(struct snd_emux_voice *vp) -{ - int dcysusv; - struct snd_emu10k1 *hw; - - hw = vp->hw; - dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease; - snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv); - dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK; - snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, dcysusv); -} - - -/* - * terminate the voice - */ -static void -terminate_voice(struct snd_emux_voice *vp) -{ - struct snd_emu10k1 *hw; - - if (snd_BUG_ON(!vp)) - return; - hw = vp->hw; - snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK); - if (vp->block) { - struct snd_emu10k1_memblk *emem; - emem = (struct snd_emu10k1_memblk *)vp->block; - if (emem->map_locked > 0) - emem->map_locked--; - } -} - -/* - * release the voice to system - */ -static void -free_voice(struct snd_emux_voice *vp) -{ - struct snd_emu10k1 *hw; - - hw = vp->hw; - /* FIXME: emu10k1_synth is broken. */ - /* This can get called with hw == 0 */ - /* Problem apparent on plug, unplug then plug */ - /* on the Audigy 2 ZS Notebook. */ - if (hw && (vp->ch >= 0)) { - snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00); - snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK); - // snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0); - snd_emu10k1_ptr_write(hw, VTFT, vp->ch, 0xffff); - snd_emu10k1_ptr_write(hw, CVCF, vp->ch, 0xffff); - snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]); - vp->emu->num_voices--; - vp->ch = -1; - } -} - - -/* - * update registers - */ -static void -update_voice(struct snd_emux_voice *vp, int update) -{ - struct snd_emu10k1 *hw; - - hw = vp->hw; - if (update & SNDRV_EMUX_UPDATE_VOLUME) - snd_emu10k1_ptr_write(hw, IFATN_ATTENUATION, vp->ch, vp->avol); - if (update & SNDRV_EMUX_UPDATE_PITCH) - snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch); - if (update & SNDRV_EMUX_UPDATE_PAN) { - snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_A, vp->ch, vp->apan); - snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux); - } - if (update & SNDRV_EMUX_UPDATE_FMMOD) - set_fmmod(hw, vp); - if (update & SNDRV_EMUX_UPDATE_TREMFREQ) - snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq); - if (update & SNDRV_EMUX_UPDATE_FM2FRQ2) - set_fm2frq2(hw, vp); - if (update & SNDRV_EMUX_UPDATE_Q) - set_filterQ(hw, vp); -} - - -/* - * look up voice table - get the best voice in order of preference - */ -/* spinlock held! */ -static void -lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw, - struct best_voice *best, int active_only) -{ - struct snd_emux_voice *vp; - struct best_voice *bp; - int i; - - for (i = 0; i < V_END; i++) { - best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */; - best[i].voice = -1; - } - - /* - * Go through them all and get a best one to use. - * NOTE: could also look at volume and pick the quietest one. - */ - for (i = 0; i < emu->max_voices; i++) { - int state, val; - - vp = &emu->voices[i]; - state = vp->state; - if (state == SNDRV_EMUX_ST_OFF) { - if (vp->ch < 0) { - if (active_only) - continue; - bp = best + V_FREE; - } else - bp = best + V_OFF; - } - else if (state == SNDRV_EMUX_ST_RELEASED || - state == SNDRV_EMUX_ST_PENDING) { - bp = best + V_RELEASED; -#if 1 - val = snd_emu10k1_ptr_read(hw, CVCF_CURRENTVOL, vp->ch); - if (! val) - bp = best + V_OFF; -#endif - } - else if (state == SNDRV_EMUX_ST_STANDBY) - continue; - else if (state & SNDRV_EMUX_ST_ON) - bp = best + V_PLAYING; - else - continue; - - /* check if sample is finished playing (non-looping only) */ - if (bp != best + V_OFF && bp != best + V_FREE && - (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) { - val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch); - if (val >= vp->reg.loopstart) - bp = best + V_OFF; - } - - if (vp->time < bp->time) { - bp->time = vp->time; - bp->voice = i; - } - } -} - -/* - * get an empty voice - * - * emu->voice_lock is already held. - */ -static struct snd_emux_voice * -get_voice(struct snd_emux *emu, struct snd_emux_port *port) -{ - struct snd_emu10k1 *hw; - struct snd_emux_voice *vp; - struct best_voice best[V_END]; - int i; - - hw = emu->hw; - - lookup_voices(emu, hw, best, 0); - for (i = 0; i < V_END; i++) { - if (best[i].voice >= 0) { - vp = &emu->voices[best[i].voice]; - if (vp->ch < 0) { - /* allocate a voice */ - struct snd_emu10k1_voice *hwvoice; - if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, &hwvoice) < 0 || hwvoice == NULL) - continue; - vp->ch = hwvoice->number; - emu->num_voices++; - } - return vp; - } - } - - /* not found */ - return NULL; -} - -/* - * prepare envelopes and LFOs - */ -static int -start_voice(struct snd_emux_voice *vp) -{ - unsigned int temp; - int ch; - unsigned int addr, mapped_offset; - struct snd_midi_channel *chan; - struct snd_emu10k1 *hw; - struct snd_emu10k1_memblk *emem; - - hw = vp->hw; - ch = vp->ch; - if (snd_BUG_ON(ch < 0)) - return -EINVAL; - chan = vp->chan; - - emem = (struct snd_emu10k1_memblk *)vp->block; - if (emem == NULL) - return -EINVAL; - emem->map_locked++; - if (snd_emu10k1_memblk_map(hw, emem) < 0) { - /* printk(KERN_ERR "emu: cannot map!\n"); */ - return -ENOMEM; - } - mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1; - vp->reg.start += mapped_offset; - vp->reg.end += mapped_offset; - vp->reg.loopstart += mapped_offset; - vp->reg.loopend += mapped_offset; - - /* set channel routing */ - /* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */ - if (hw->audigy) { - temp = FXBUS_MIDI_LEFT | (FXBUS_MIDI_RIGHT << 8) | - (FXBUS_MIDI_REVERB << 16) | (FXBUS_MIDI_CHORUS << 24); - snd_emu10k1_ptr_write(hw, A_FXRT1, ch, temp); - } else { - temp = (FXBUS_MIDI_LEFT << 16) | (FXBUS_MIDI_RIGHT << 20) | - (FXBUS_MIDI_REVERB << 24) | (FXBUS_MIDI_CHORUS << 28); - snd_emu10k1_ptr_write(hw, FXRT, ch, temp); - } - - /* channel to be silent and idle */ - snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0x0000); - snd_emu10k1_ptr_write(hw, VTFT, ch, 0x0000FFFF); - snd_emu10k1_ptr_write(hw, CVCF, ch, 0x0000FFFF); - snd_emu10k1_ptr_write(hw, PTRX, ch, 0); - snd_emu10k1_ptr_write(hw, CPF, ch, 0); - - /* set pitch offset */ - snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch); - - /* set envelope parameters */ - snd_emu10k1_ptr_write(hw, ENVVAL, ch, vp->reg.parm.moddelay); - snd_emu10k1_ptr_write(hw, ATKHLDM, ch, vp->reg.parm.modatkhld); - snd_emu10k1_ptr_write(hw, DCYSUSM, ch, vp->reg.parm.moddcysus); - snd_emu10k1_ptr_write(hw, ENVVOL, ch, vp->reg.parm.voldelay); - snd_emu10k1_ptr_write(hw, ATKHLDV, ch, vp->reg.parm.volatkhld); - /* decay/sustain parameter for volume envelope is used - for triggerg the voice */ - - /* cutoff and volume */ - temp = (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol; - snd_emu10k1_ptr_write(hw, IFATN, vp->ch, temp); - - /* modulation envelope heights */ - snd_emu10k1_ptr_write(hw, PEFE, ch, vp->reg.parm.pefe); - - /* lfo1/2 delay */ - snd_emu10k1_ptr_write(hw, LFOVAL1, ch, vp->reg.parm.lfo1delay); - snd_emu10k1_ptr_write(hw, LFOVAL2, ch, vp->reg.parm.lfo2delay); - - /* lfo1 pitch & cutoff shift */ - set_fmmod(hw, vp); - /* lfo1 volume & freq */ - snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq); - /* lfo2 pitch & freq */ - set_fm2frq2(hw, vp); - - /* reverb and loop start (reverb 8bit, MSB) */ - temp = vp->reg.parm.reverb; - temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10; - LIMITMAX(temp, 255); - addr = vp->reg.loopstart; - snd_emu10k1_ptr_write(hw, PSST, vp->ch, (temp << 24) | addr); - - /* chorus & loop end (chorus 8bit, MSB) */ - addr = vp->reg.loopend; - temp = vp->reg.parm.chorus; - temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10; - LIMITMAX(temp, 255); - temp = (temp <<24) | addr; - snd_emu10k1_ptr_write(hw, DSL, ch, temp); - - /* clear filter delay memory */ - snd_emu10k1_ptr_write(hw, Z1, ch, 0); - snd_emu10k1_ptr_write(hw, Z2, ch, 0); - - /* invalidate maps */ - temp = (hw->silent_page.addr << 1) | MAP_PTI_MASK; - snd_emu10k1_ptr_write(hw, MAPA, ch, temp); - snd_emu10k1_ptr_write(hw, MAPB, ch, temp); -#if 0 - /* cache */ - { - unsigned int val, sample; - val = 32; - if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS) - sample = 0x80808080; - else { - sample = 0; - val *= 2; - } - - /* cache */ - snd_emu10k1_ptr_write(hw, CCR, ch, 0x1c << 16); - snd_emu10k1_ptr_write(hw, CDE, ch, sample); - snd_emu10k1_ptr_write(hw, CDF, ch, sample); - - /* invalidate maps */ - temp = ((unsigned int)hw->silent_page.addr << 1) | MAP_PTI_MASK; - snd_emu10k1_ptr_write(hw, MAPA, ch, temp); - snd_emu10k1_ptr_write(hw, MAPB, ch, temp); - - /* fill cache */ - val -= 4; - val <<= 25; - val |= 0x1c << 16; - snd_emu10k1_ptr_write(hw, CCR, ch, val); - } -#endif - - /* Q & current address (Q 4bit value, MSB) */ - addr = vp->reg.start; - temp = vp->reg.parm.filterQ; - temp = (temp<<28) | addr; - if (vp->apitch < 0xe400) - temp |= CCCA_INTERPROM_0; - else { - unsigned int shift = (vp->apitch - 0xe000) >> 10; - temp |= shift << 25; - } - if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS) - temp |= CCCA_8BITSELECT; - snd_emu10k1_ptr_write(hw, CCCA, ch, temp); - - /* reset volume */ - temp = (unsigned int)vp->vtarget << 16; - snd_emu10k1_ptr_write(hw, VTFT, ch, temp | vp->ftarget); - snd_emu10k1_ptr_write(hw, CVCF, ch, temp | 0xff00); - return 0; -} - -/* - * Start envelope - */ -static void -trigger_voice(struct snd_emux_voice *vp) -{ - unsigned int temp, ptarget; - struct snd_emu10k1 *hw; - struct snd_emu10k1_memblk *emem; - - hw = vp->hw; - - emem = (struct snd_emu10k1_memblk *)vp->block; - if (! emem || emem->mapped_page < 0) - return; /* not mapped */ - -#if 0 - ptarget = (unsigned int)vp->ptarget << 16; -#else - ptarget = IP_TO_CP(vp->apitch); -#endif - /* set pitch target and pan (volume) */ - temp = ptarget | (vp->apan << 8) | vp->aaux; - snd_emu10k1_ptr_write(hw, PTRX, vp->ch, temp); - - /* pitch target */ - snd_emu10k1_ptr_write(hw, CPF, vp->ch, ptarget); - - /* trigger voice */ - snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, vp->reg.parm.voldcysus|DCYSUSV_CHANNELENABLE_MASK); -} - -#define MOD_SENSE 18 - -/* set lfo1 modulation height and cutoff */ -static void -set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp) -{ - unsigned short fmmod; - short pitch; - unsigned char cutoff; - int modulation; - - pitch = (char)(vp->reg.parm.fmmod>>8); - cutoff = (vp->reg.parm.fmmod & 0xff); - modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; - pitch += (MOD_SENSE * modulation) / 1200; - LIMITVALUE(pitch, -128, 127); - fmmod = ((unsigned char)pitch<<8) | cutoff; - snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, fmmod); -} - -/* set lfo2 pitch & frequency */ -static void -set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp) -{ - unsigned short fm2frq2; - short pitch; - unsigned char freq; - int modulation; - - pitch = (char)(vp->reg.parm.fm2frq2>>8); - freq = vp->reg.parm.fm2frq2 & 0xff; - modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; - pitch += (MOD_SENSE * modulation) / 1200; - LIMITVALUE(pitch, -128, 127); - fm2frq2 = ((unsigned char)pitch<<8) | freq; - snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, fm2frq2); -} - -/* set filterQ */ -static void -set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp) -{ - unsigned int val; - val = snd_emu10k1_ptr_read(hw, CCCA, vp->ch) & ~CCCA_RESONANCE; - val |= (vp->reg.parm.filterQ << 28); - snd_emu10k1_ptr_write(hw, CCCA, vp->ch, val); -} |