Path: blob/master/sound/pci/emu10k1/emu10k1_callback.c
10817 views
/*1* synth callback routines for Emu10k12*3* Copyright (C) 2000 Takashi Iwai <[email protected]>4*5* This program is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License as published by7* the Free Software Foundation; either version 2 of the License, or8* (at your option) any later version.9*10* This program is distributed in the hope that it will be useful,11* but WITHOUT ANY WARRANTY; without even the implied warranty of12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13* GNU General Public License for more details.14*15* You should have received a copy of the GNU General Public License16* along with this program; if not, write to the Free Software17* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA18*/1920#include "emu10k1_synth_local.h"21#include <sound/asoundef.h>2223/* voice status */24enum {25V_FREE=0, V_OFF, V_RELEASED, V_PLAYING, V_END26};2728/* Keeps track of what we are finding */29struct best_voice {30unsigned int time;31int voice;32};3334/*35* prototypes36*/37static void lookup_voices(struct snd_emux *emux, struct snd_emu10k1 *hw,38struct best_voice *best, int active_only);39static struct snd_emux_voice *get_voice(struct snd_emux *emux,40struct snd_emux_port *port);41static int start_voice(struct snd_emux_voice *vp);42static void trigger_voice(struct snd_emux_voice *vp);43static void release_voice(struct snd_emux_voice *vp);44static void update_voice(struct snd_emux_voice *vp, int update);45static void terminate_voice(struct snd_emux_voice *vp);46static void free_voice(struct snd_emux_voice *vp);47static void set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);48static void set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);49static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);5051/*52* Ensure a value is between two points53* macro evaluates its args more than once, so changed to upper-case.54*/55#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)56#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)575859/*60* set up operators61*/62static struct snd_emux_operators emu10k1_ops = {63.owner = THIS_MODULE,64.get_voice = get_voice,65.prepare = start_voice,66.trigger = trigger_voice,67.release = release_voice,68.update = update_voice,69.terminate = terminate_voice,70.free_voice = free_voice,71.sample_new = snd_emu10k1_sample_new,72.sample_free = snd_emu10k1_sample_free,73};7475void76snd_emu10k1_ops_setup(struct snd_emux *emux)77{78emux->ops = emu10k1_ops;79}808182/*83* get more voice for pcm84*85* terminate most inactive voice and give it as a pcm voice.86*/87int88snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)89{90struct snd_emux *emu;91struct snd_emux_voice *vp;92struct best_voice best[V_END];93unsigned long flags;94int i;9596emu = hw->synth;9798spin_lock_irqsave(&emu->voice_lock, flags);99lookup_voices(emu, hw, best, 1); /* no OFF voices */100for (i = 0; i < V_END; i++) {101if (best[i].voice >= 0) {102int ch;103vp = &emu->voices[best[i].voice];104if ((ch = vp->ch) < 0) {105/*106printk(KERN_WARNING107"synth_get_voice: ch < 0 (%d) ??", i);108*/109continue;110}111vp->emu->num_voices--;112vp->ch = -1;113vp->state = SNDRV_EMUX_ST_OFF;114spin_unlock_irqrestore(&emu->voice_lock, flags);115return ch;116}117}118spin_unlock_irqrestore(&emu->voice_lock, flags);119120/* not found */121return -ENOMEM;122}123124125/*126* turn off the voice (not terminated)127*/128static void129release_voice(struct snd_emux_voice *vp)130{131int dcysusv;132struct snd_emu10k1 *hw;133134hw = vp->hw;135dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;136snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv);137dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK;138snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, dcysusv);139}140141142/*143* terminate the voice144*/145static void146terminate_voice(struct snd_emux_voice *vp)147{148struct snd_emu10k1 *hw;149150if (snd_BUG_ON(!vp))151return;152hw = vp->hw;153snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);154if (vp->block) {155struct snd_emu10k1_memblk *emem;156emem = (struct snd_emu10k1_memblk *)vp->block;157if (emem->map_locked > 0)158emem->map_locked--;159}160}161162/*163* release the voice to system164*/165static void166free_voice(struct snd_emux_voice *vp)167{168struct snd_emu10k1 *hw;169170hw = vp->hw;171/* FIXME: emu10k1_synth is broken. */172/* This can get called with hw == 0 */173/* Problem apparent on plug, unplug then plug */174/* on the Audigy 2 ZS Notebook. */175if (hw && (vp->ch >= 0)) {176snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00);177snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);178// snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0);179snd_emu10k1_ptr_write(hw, VTFT, vp->ch, 0xffff);180snd_emu10k1_ptr_write(hw, CVCF, vp->ch, 0xffff);181snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]);182vp->emu->num_voices--;183vp->ch = -1;184}185}186187188/*189* update registers190*/191static void192update_voice(struct snd_emux_voice *vp, int update)193{194struct snd_emu10k1 *hw;195196hw = vp->hw;197if (update & SNDRV_EMUX_UPDATE_VOLUME)198snd_emu10k1_ptr_write(hw, IFATN_ATTENUATION, vp->ch, vp->avol);199if (update & SNDRV_EMUX_UPDATE_PITCH)200snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);201if (update & SNDRV_EMUX_UPDATE_PAN) {202snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_A, vp->ch, vp->apan);203snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux);204}205if (update & SNDRV_EMUX_UPDATE_FMMOD)206set_fmmod(hw, vp);207if (update & SNDRV_EMUX_UPDATE_TREMFREQ)208snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);209if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)210set_fm2frq2(hw, vp);211if (update & SNDRV_EMUX_UPDATE_Q)212set_filterQ(hw, vp);213}214215216/*217* look up voice table - get the best voice in order of preference218*/219/* spinlock held! */220static void221lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw,222struct best_voice *best, int active_only)223{224struct snd_emux_voice *vp;225struct best_voice *bp;226int i;227228for (i = 0; i < V_END; i++) {229best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */;230best[i].voice = -1;231}232233/*234* Go through them all and get a best one to use.235* NOTE: could also look at volume and pick the quietest one.236*/237for (i = 0; i < emu->max_voices; i++) {238int state, val;239240vp = &emu->voices[i];241state = vp->state;242if (state == SNDRV_EMUX_ST_OFF) {243if (vp->ch < 0) {244if (active_only)245continue;246bp = best + V_FREE;247} else248bp = best + V_OFF;249}250else if (state == SNDRV_EMUX_ST_RELEASED ||251state == SNDRV_EMUX_ST_PENDING) {252bp = best + V_RELEASED;253#if 1254val = snd_emu10k1_ptr_read(hw, CVCF_CURRENTVOL, vp->ch);255if (! val)256bp = best + V_OFF;257#endif258}259else if (state == SNDRV_EMUX_ST_STANDBY)260continue;261else if (state & SNDRV_EMUX_ST_ON)262bp = best + V_PLAYING;263else264continue;265266/* check if sample is finished playing (non-looping only) */267if (bp != best + V_OFF && bp != best + V_FREE &&268(vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {269val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch);270if (val >= vp->reg.loopstart)271bp = best + V_OFF;272}273274if (vp->time < bp->time) {275bp->time = vp->time;276bp->voice = i;277}278}279}280281/*282* get an empty voice283*284* emu->voice_lock is already held.285*/286static struct snd_emux_voice *287get_voice(struct snd_emux *emu, struct snd_emux_port *port)288{289struct snd_emu10k1 *hw;290struct snd_emux_voice *vp;291struct best_voice best[V_END];292int i;293294hw = emu->hw;295296lookup_voices(emu, hw, best, 0);297for (i = 0; i < V_END; i++) {298if (best[i].voice >= 0) {299vp = &emu->voices[best[i].voice];300if (vp->ch < 0) {301/* allocate a voice */302struct snd_emu10k1_voice *hwvoice;303if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, &hwvoice) < 0 || hwvoice == NULL)304continue;305vp->ch = hwvoice->number;306emu->num_voices++;307}308return vp;309}310}311312/* not found */313return NULL;314}315316/*317* prepare envelopes and LFOs318*/319static int320start_voice(struct snd_emux_voice *vp)321{322unsigned int temp;323int ch;324unsigned int addr, mapped_offset;325struct snd_midi_channel *chan;326struct snd_emu10k1 *hw;327struct snd_emu10k1_memblk *emem;328329hw = vp->hw;330ch = vp->ch;331if (snd_BUG_ON(ch < 0))332return -EINVAL;333chan = vp->chan;334335emem = (struct snd_emu10k1_memblk *)vp->block;336if (emem == NULL)337return -EINVAL;338emem->map_locked++;339if (snd_emu10k1_memblk_map(hw, emem) < 0) {340/* printk(KERN_ERR "emu: cannot map!\n"); */341return -ENOMEM;342}343mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1;344vp->reg.start += mapped_offset;345vp->reg.end += mapped_offset;346vp->reg.loopstart += mapped_offset;347vp->reg.loopend += mapped_offset;348349/* set channel routing */350/* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */351if (hw->audigy) {352temp = FXBUS_MIDI_LEFT | (FXBUS_MIDI_RIGHT << 8) |353(FXBUS_MIDI_REVERB << 16) | (FXBUS_MIDI_CHORUS << 24);354snd_emu10k1_ptr_write(hw, A_FXRT1, ch, temp);355} else {356temp = (FXBUS_MIDI_LEFT << 16) | (FXBUS_MIDI_RIGHT << 20) |357(FXBUS_MIDI_REVERB << 24) | (FXBUS_MIDI_CHORUS << 28);358snd_emu10k1_ptr_write(hw, FXRT, ch, temp);359}360361/* channel to be silent and idle */362snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0x0000);363snd_emu10k1_ptr_write(hw, VTFT, ch, 0x0000FFFF);364snd_emu10k1_ptr_write(hw, CVCF, ch, 0x0000FFFF);365snd_emu10k1_ptr_write(hw, PTRX, ch, 0);366snd_emu10k1_ptr_write(hw, CPF, ch, 0);367368/* set pitch offset */369snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);370371/* set envelope parameters */372snd_emu10k1_ptr_write(hw, ENVVAL, ch, vp->reg.parm.moddelay);373snd_emu10k1_ptr_write(hw, ATKHLDM, ch, vp->reg.parm.modatkhld);374snd_emu10k1_ptr_write(hw, DCYSUSM, ch, vp->reg.parm.moddcysus);375snd_emu10k1_ptr_write(hw, ENVVOL, ch, vp->reg.parm.voldelay);376snd_emu10k1_ptr_write(hw, ATKHLDV, ch, vp->reg.parm.volatkhld);377/* decay/sustain parameter for volume envelope is used378for triggerg the voice */379380/* cutoff and volume */381temp = (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol;382snd_emu10k1_ptr_write(hw, IFATN, vp->ch, temp);383384/* modulation envelope heights */385snd_emu10k1_ptr_write(hw, PEFE, ch, vp->reg.parm.pefe);386387/* lfo1/2 delay */388snd_emu10k1_ptr_write(hw, LFOVAL1, ch, vp->reg.parm.lfo1delay);389snd_emu10k1_ptr_write(hw, LFOVAL2, ch, vp->reg.parm.lfo2delay);390391/* lfo1 pitch & cutoff shift */392set_fmmod(hw, vp);393/* lfo1 volume & freq */394snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);395/* lfo2 pitch & freq */396set_fm2frq2(hw, vp);397398/* reverb and loop start (reverb 8bit, MSB) */399temp = vp->reg.parm.reverb;400temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;401LIMITMAX(temp, 255);402addr = vp->reg.loopstart;403snd_emu10k1_ptr_write(hw, PSST, vp->ch, (temp << 24) | addr);404405/* chorus & loop end (chorus 8bit, MSB) */406addr = vp->reg.loopend;407temp = vp->reg.parm.chorus;408temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;409LIMITMAX(temp, 255);410temp = (temp <<24) | addr;411snd_emu10k1_ptr_write(hw, DSL, ch, temp);412413/* clear filter delay memory */414snd_emu10k1_ptr_write(hw, Z1, ch, 0);415snd_emu10k1_ptr_write(hw, Z2, ch, 0);416417/* invalidate maps */418temp = (hw->silent_page.addr << 1) | MAP_PTI_MASK;419snd_emu10k1_ptr_write(hw, MAPA, ch, temp);420snd_emu10k1_ptr_write(hw, MAPB, ch, temp);421#if 0422/* cache */423{424unsigned int val, sample;425val = 32;426if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)427sample = 0x80808080;428else {429sample = 0;430val *= 2;431}432433/* cache */434snd_emu10k1_ptr_write(hw, CCR, ch, 0x1c << 16);435snd_emu10k1_ptr_write(hw, CDE, ch, sample);436snd_emu10k1_ptr_write(hw, CDF, ch, sample);437438/* invalidate maps */439temp = ((unsigned int)hw->silent_page.addr << 1) | MAP_PTI_MASK;440snd_emu10k1_ptr_write(hw, MAPA, ch, temp);441snd_emu10k1_ptr_write(hw, MAPB, ch, temp);442443/* fill cache */444val -= 4;445val <<= 25;446val |= 0x1c << 16;447snd_emu10k1_ptr_write(hw, CCR, ch, val);448}449#endif450451/* Q & current address (Q 4bit value, MSB) */452addr = vp->reg.start;453temp = vp->reg.parm.filterQ;454temp = (temp<<28) | addr;455if (vp->apitch < 0xe400)456temp |= CCCA_INTERPROM_0;457else {458unsigned int shift = (vp->apitch - 0xe000) >> 10;459temp |= shift << 25;460}461if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)462temp |= CCCA_8BITSELECT;463snd_emu10k1_ptr_write(hw, CCCA, ch, temp);464465/* reset volume */466temp = (unsigned int)vp->vtarget << 16;467snd_emu10k1_ptr_write(hw, VTFT, ch, temp | vp->ftarget);468snd_emu10k1_ptr_write(hw, CVCF, ch, temp | 0xff00);469return 0;470}471472/*473* Start envelope474*/475static void476trigger_voice(struct snd_emux_voice *vp)477{478unsigned int temp, ptarget;479struct snd_emu10k1 *hw;480struct snd_emu10k1_memblk *emem;481482hw = vp->hw;483484emem = (struct snd_emu10k1_memblk *)vp->block;485if (! emem || emem->mapped_page < 0)486return; /* not mapped */487488#if 0489ptarget = (unsigned int)vp->ptarget << 16;490#else491ptarget = IP_TO_CP(vp->apitch);492#endif493/* set pitch target and pan (volume) */494temp = ptarget | (vp->apan << 8) | vp->aaux;495snd_emu10k1_ptr_write(hw, PTRX, vp->ch, temp);496497/* pitch target */498snd_emu10k1_ptr_write(hw, CPF, vp->ch, ptarget);499500/* trigger voice */501snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, vp->reg.parm.voldcysus|DCYSUSV_CHANNELENABLE_MASK);502}503504#define MOD_SENSE 18505506/* set lfo1 modulation height and cutoff */507static void508set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)509{510unsigned short fmmod;511short pitch;512unsigned char cutoff;513int modulation;514515pitch = (char)(vp->reg.parm.fmmod>>8);516cutoff = (vp->reg.parm.fmmod & 0xff);517modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;518pitch += (MOD_SENSE * modulation) / 1200;519LIMITVALUE(pitch, -128, 127);520fmmod = ((unsigned char)pitch<<8) | cutoff;521snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, fmmod);522}523524/* set lfo2 pitch & frequency */525static void526set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)527{528unsigned short fm2frq2;529short pitch;530unsigned char freq;531int modulation;532533pitch = (char)(vp->reg.parm.fm2frq2>>8);534freq = vp->reg.parm.fm2frq2 & 0xff;535modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;536pitch += (MOD_SENSE * modulation) / 1200;537LIMITVALUE(pitch, -128, 127);538fm2frq2 = ((unsigned char)pitch<<8) | freq;539snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, fm2frq2);540}541542/* set filterQ */543static void544set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)545{546unsigned int val;547val = snd_emu10k1_ptr_read(hw, CCCA, vp->ch) & ~CCCA_RESONANCE;548val |= (vp->reg.parm.filterQ << 28);549snd_emu10k1_ptr_write(hw, CCCA, vp->ch, val);550}551552553