18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * synth callback routines for Emu10k1 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2000 Takashi Iwai <tiwai@suse.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/export.h> 98c2ecf20Sopenharmony_ci#include "emu10k1_synth_local.h" 108c2ecf20Sopenharmony_ci#include <sound/asoundef.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* voice status */ 138c2ecf20Sopenharmony_cienum { 148c2ecf20Sopenharmony_ci V_FREE=0, V_OFF, V_RELEASED, V_PLAYING, V_END 158c2ecf20Sopenharmony_ci}; 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* Keeps track of what we are finding */ 188c2ecf20Sopenharmony_cistruct best_voice { 198c2ecf20Sopenharmony_ci unsigned int time; 208c2ecf20Sopenharmony_ci int voice; 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * prototypes 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_cistatic void lookup_voices(struct snd_emux *emux, struct snd_emu10k1 *hw, 278c2ecf20Sopenharmony_ci struct best_voice *best, int active_only); 288c2ecf20Sopenharmony_cistatic struct snd_emux_voice *get_voice(struct snd_emux *emux, 298c2ecf20Sopenharmony_ci struct snd_emux_port *port); 308c2ecf20Sopenharmony_cistatic int start_voice(struct snd_emux_voice *vp); 318c2ecf20Sopenharmony_cistatic void trigger_voice(struct snd_emux_voice *vp); 328c2ecf20Sopenharmony_cistatic void release_voice(struct snd_emux_voice *vp); 338c2ecf20Sopenharmony_cistatic void update_voice(struct snd_emux_voice *vp, int update); 348c2ecf20Sopenharmony_cistatic void terminate_voice(struct snd_emux_voice *vp); 358c2ecf20Sopenharmony_cistatic void free_voice(struct snd_emux_voice *vp); 368c2ecf20Sopenharmony_cistatic void set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp); 378c2ecf20Sopenharmony_cistatic void set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp); 388c2ecf20Sopenharmony_cistatic void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* 418c2ecf20Sopenharmony_ci * Ensure a value is between two points 428c2ecf20Sopenharmony_ci * macro evaluates its args more than once, so changed to upper-case. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0) 458c2ecf20Sopenharmony_ci#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* 498c2ecf20Sopenharmony_ci * set up operators 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_cistatic const struct snd_emux_operators emu10k1_ops = { 528c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 538c2ecf20Sopenharmony_ci .get_voice = get_voice, 548c2ecf20Sopenharmony_ci .prepare = start_voice, 558c2ecf20Sopenharmony_ci .trigger = trigger_voice, 568c2ecf20Sopenharmony_ci .release = release_voice, 578c2ecf20Sopenharmony_ci .update = update_voice, 588c2ecf20Sopenharmony_ci .terminate = terminate_voice, 598c2ecf20Sopenharmony_ci .free_voice = free_voice, 608c2ecf20Sopenharmony_ci .sample_new = snd_emu10k1_sample_new, 618c2ecf20Sopenharmony_ci .sample_free = snd_emu10k1_sample_free, 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_civoid 658c2ecf20Sopenharmony_cisnd_emu10k1_ops_setup(struct snd_emux *emux) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci emux->ops = emu10k1_ops; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * get more voice for pcm 738c2ecf20Sopenharmony_ci * 748c2ecf20Sopenharmony_ci * terminate most inactive voice and give it as a pcm voice. 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * voice_lock is already held. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ciint 798c2ecf20Sopenharmony_cisnd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct snd_emux *emu; 828c2ecf20Sopenharmony_ci struct snd_emux_voice *vp; 838c2ecf20Sopenharmony_ci struct best_voice best[V_END]; 848c2ecf20Sopenharmony_ci int i; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci emu = hw->synth; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci lookup_voices(emu, hw, best, 1); /* no OFF voices */ 898c2ecf20Sopenharmony_ci for (i = 0; i < V_END; i++) { 908c2ecf20Sopenharmony_ci if (best[i].voice >= 0) { 918c2ecf20Sopenharmony_ci int ch; 928c2ecf20Sopenharmony_ci vp = &emu->voices[best[i].voice]; 938c2ecf20Sopenharmony_ci if ((ch = vp->ch) < 0) { 948c2ecf20Sopenharmony_ci /* 958c2ecf20Sopenharmony_ci dev_warn(emu->card->dev, 968c2ecf20Sopenharmony_ci "synth_get_voice: ch < 0 (%d) ??", i); 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci continue; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci vp->emu->num_voices--; 1018c2ecf20Sopenharmony_ci vp->ch = -1; 1028c2ecf20Sopenharmony_ci vp->state = SNDRV_EMUX_ST_OFF; 1038c2ecf20Sopenharmony_ci return ch; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* not found */ 1088c2ecf20Sopenharmony_ci return -ENOMEM; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* 1138c2ecf20Sopenharmony_ci * turn off the voice (not terminated) 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_cistatic void 1168c2ecf20Sopenharmony_cirelease_voice(struct snd_emux_voice *vp) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci int dcysusv; 1198c2ecf20Sopenharmony_ci struct snd_emu10k1 *hw; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci hw = vp->hw; 1228c2ecf20Sopenharmony_ci dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease; 1238c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv); 1248c2ecf20Sopenharmony_ci dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK; 1258c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, dcysusv); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* 1308c2ecf20Sopenharmony_ci * terminate the voice 1318c2ecf20Sopenharmony_ci */ 1328c2ecf20Sopenharmony_cistatic void 1338c2ecf20Sopenharmony_citerminate_voice(struct snd_emux_voice *vp) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct snd_emu10k1 *hw; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (snd_BUG_ON(!vp)) 1388c2ecf20Sopenharmony_ci return; 1398c2ecf20Sopenharmony_ci hw = vp->hw; 1408c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK); 1418c2ecf20Sopenharmony_ci if (vp->block) { 1428c2ecf20Sopenharmony_ci struct snd_emu10k1_memblk *emem; 1438c2ecf20Sopenharmony_ci emem = (struct snd_emu10k1_memblk *)vp->block; 1448c2ecf20Sopenharmony_ci if (emem->map_locked > 0) 1458c2ecf20Sopenharmony_ci emem->map_locked--; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/* 1508c2ecf20Sopenharmony_ci * release the voice to system 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_cistatic void 1538c2ecf20Sopenharmony_cifree_voice(struct snd_emux_voice *vp) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct snd_emu10k1 *hw; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci hw = vp->hw; 1588c2ecf20Sopenharmony_ci /* FIXME: emu10k1_synth is broken. */ 1598c2ecf20Sopenharmony_ci /* This can get called with hw == 0 */ 1608c2ecf20Sopenharmony_ci /* Problem apparent on plug, unplug then plug */ 1618c2ecf20Sopenharmony_ci /* on the Audigy 2 ZS Notebook. */ 1628c2ecf20Sopenharmony_ci if (hw && (vp->ch >= 0)) { 1638c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00); 1648c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK); 1658c2ecf20Sopenharmony_ci // snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0); 1668c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, VTFT, vp->ch, 0xffff); 1678c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, CVCF, vp->ch, 0xffff); 1688c2ecf20Sopenharmony_ci snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]); 1698c2ecf20Sopenharmony_ci vp->emu->num_voices--; 1708c2ecf20Sopenharmony_ci vp->ch = -1; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/* 1768c2ecf20Sopenharmony_ci * update registers 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_cistatic void 1798c2ecf20Sopenharmony_ciupdate_voice(struct snd_emux_voice *vp, int update) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct snd_emu10k1 *hw; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci hw = vp->hw; 1848c2ecf20Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_VOLUME) 1858c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, IFATN_ATTENUATION, vp->ch, vp->avol); 1868c2ecf20Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_PITCH) 1878c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch); 1888c2ecf20Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_PAN) { 1898c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_A, vp->ch, vp->apan); 1908c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux); 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_FMMOD) 1938c2ecf20Sopenharmony_ci set_fmmod(hw, vp); 1948c2ecf20Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_TREMFREQ) 1958c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq); 1968c2ecf20Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_FM2FRQ2) 1978c2ecf20Sopenharmony_ci set_fm2frq2(hw, vp); 1988c2ecf20Sopenharmony_ci if (update & SNDRV_EMUX_UPDATE_Q) 1998c2ecf20Sopenharmony_ci set_filterQ(hw, vp); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/* 2048c2ecf20Sopenharmony_ci * look up voice table - get the best voice in order of preference 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci/* spinlock held! */ 2078c2ecf20Sopenharmony_cistatic void 2088c2ecf20Sopenharmony_cilookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw, 2098c2ecf20Sopenharmony_ci struct best_voice *best, int active_only) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct snd_emux_voice *vp; 2128c2ecf20Sopenharmony_ci struct best_voice *bp; 2138c2ecf20Sopenharmony_ci int i; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci for (i = 0; i < V_END; i++) { 2168c2ecf20Sopenharmony_ci best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */ 2178c2ecf20Sopenharmony_ci best[i].voice = -1; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* 2218c2ecf20Sopenharmony_ci * Go through them all and get a best one to use. 2228c2ecf20Sopenharmony_ci * NOTE: could also look at volume and pick the quietest one. 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ci for (i = 0; i < emu->max_voices; i++) { 2258c2ecf20Sopenharmony_ci int state, val; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci vp = &emu->voices[i]; 2288c2ecf20Sopenharmony_ci state = vp->state; 2298c2ecf20Sopenharmony_ci if (state == SNDRV_EMUX_ST_OFF) { 2308c2ecf20Sopenharmony_ci if (vp->ch < 0) { 2318c2ecf20Sopenharmony_ci if (active_only) 2328c2ecf20Sopenharmony_ci continue; 2338c2ecf20Sopenharmony_ci bp = best + V_FREE; 2348c2ecf20Sopenharmony_ci } else 2358c2ecf20Sopenharmony_ci bp = best + V_OFF; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci else if (state == SNDRV_EMUX_ST_RELEASED || 2388c2ecf20Sopenharmony_ci state == SNDRV_EMUX_ST_PENDING) { 2398c2ecf20Sopenharmony_ci bp = best + V_RELEASED; 2408c2ecf20Sopenharmony_ci#if 1 2418c2ecf20Sopenharmony_ci val = snd_emu10k1_ptr_read(hw, CVCF_CURRENTVOL, vp->ch); 2428c2ecf20Sopenharmony_ci if (! val) 2438c2ecf20Sopenharmony_ci bp = best + V_OFF; 2448c2ecf20Sopenharmony_ci#endif 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci else if (state == SNDRV_EMUX_ST_STANDBY) 2478c2ecf20Sopenharmony_ci continue; 2488c2ecf20Sopenharmony_ci else if (state & SNDRV_EMUX_ST_ON) 2498c2ecf20Sopenharmony_ci bp = best + V_PLAYING; 2508c2ecf20Sopenharmony_ci else 2518c2ecf20Sopenharmony_ci continue; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* check if sample is finished playing (non-looping only) */ 2548c2ecf20Sopenharmony_ci if (bp != best + V_OFF && bp != best + V_FREE && 2558c2ecf20Sopenharmony_ci (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) { 2568c2ecf20Sopenharmony_ci val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch); 2578c2ecf20Sopenharmony_ci if (val >= vp->reg.loopstart) 2588c2ecf20Sopenharmony_ci bp = best + V_OFF; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (vp->time < bp->time) { 2628c2ecf20Sopenharmony_ci bp->time = vp->time; 2638c2ecf20Sopenharmony_ci bp->voice = i; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci/* 2698c2ecf20Sopenharmony_ci * get an empty voice 2708c2ecf20Sopenharmony_ci * 2718c2ecf20Sopenharmony_ci * emu->voice_lock is already held. 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_cistatic struct snd_emux_voice * 2748c2ecf20Sopenharmony_ciget_voice(struct snd_emux *emu, struct snd_emux_port *port) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct snd_emu10k1 *hw; 2778c2ecf20Sopenharmony_ci struct snd_emux_voice *vp; 2788c2ecf20Sopenharmony_ci struct best_voice best[V_END]; 2798c2ecf20Sopenharmony_ci int i; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci hw = emu->hw; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci lookup_voices(emu, hw, best, 0); 2848c2ecf20Sopenharmony_ci for (i = 0; i < V_END; i++) { 2858c2ecf20Sopenharmony_ci if (best[i].voice >= 0) { 2868c2ecf20Sopenharmony_ci vp = &emu->voices[best[i].voice]; 2878c2ecf20Sopenharmony_ci if (vp->ch < 0) { 2888c2ecf20Sopenharmony_ci /* allocate a voice */ 2898c2ecf20Sopenharmony_ci struct snd_emu10k1_voice *hwvoice; 2908c2ecf20Sopenharmony_ci if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, &hwvoice) < 0 || hwvoice == NULL) 2918c2ecf20Sopenharmony_ci continue; 2928c2ecf20Sopenharmony_ci vp->ch = hwvoice->number; 2938c2ecf20Sopenharmony_ci emu->num_voices++; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci return vp; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* not found */ 3008c2ecf20Sopenharmony_ci return NULL; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci/* 3048c2ecf20Sopenharmony_ci * prepare envelopes and LFOs 3058c2ecf20Sopenharmony_ci */ 3068c2ecf20Sopenharmony_cistatic int 3078c2ecf20Sopenharmony_cistart_voice(struct snd_emux_voice *vp) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci unsigned int temp; 3108c2ecf20Sopenharmony_ci int ch; 3118c2ecf20Sopenharmony_ci unsigned int addr, mapped_offset; 3128c2ecf20Sopenharmony_ci struct snd_midi_channel *chan; 3138c2ecf20Sopenharmony_ci struct snd_emu10k1 *hw; 3148c2ecf20Sopenharmony_ci struct snd_emu10k1_memblk *emem; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci hw = vp->hw; 3178c2ecf20Sopenharmony_ci ch = vp->ch; 3188c2ecf20Sopenharmony_ci if (snd_BUG_ON(ch < 0)) 3198c2ecf20Sopenharmony_ci return -EINVAL; 3208c2ecf20Sopenharmony_ci chan = vp->chan; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci emem = (struct snd_emu10k1_memblk *)vp->block; 3238c2ecf20Sopenharmony_ci if (emem == NULL) 3248c2ecf20Sopenharmony_ci return -EINVAL; 3258c2ecf20Sopenharmony_ci emem->map_locked++; 3268c2ecf20Sopenharmony_ci if (snd_emu10k1_memblk_map(hw, emem) < 0) { 3278c2ecf20Sopenharmony_ci /* dev_err(hw->card->devK, "emu: cannot map!\n"); */ 3288c2ecf20Sopenharmony_ci return -ENOMEM; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1; 3318c2ecf20Sopenharmony_ci vp->reg.start += mapped_offset; 3328c2ecf20Sopenharmony_ci vp->reg.end += mapped_offset; 3338c2ecf20Sopenharmony_ci vp->reg.loopstart += mapped_offset; 3348c2ecf20Sopenharmony_ci vp->reg.loopend += mapped_offset; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* set channel routing */ 3378c2ecf20Sopenharmony_ci /* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */ 3388c2ecf20Sopenharmony_ci if (hw->audigy) { 3398c2ecf20Sopenharmony_ci temp = FXBUS_MIDI_LEFT | (FXBUS_MIDI_RIGHT << 8) | 3408c2ecf20Sopenharmony_ci (FXBUS_MIDI_REVERB << 16) | (FXBUS_MIDI_CHORUS << 24); 3418c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, A_FXRT1, ch, temp); 3428c2ecf20Sopenharmony_ci } else { 3438c2ecf20Sopenharmony_ci temp = (FXBUS_MIDI_LEFT << 16) | (FXBUS_MIDI_RIGHT << 20) | 3448c2ecf20Sopenharmony_ci (FXBUS_MIDI_REVERB << 24) | (FXBUS_MIDI_CHORUS << 28); 3458c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, FXRT, ch, temp); 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* channel to be silent and idle */ 3498c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0x0000); 3508c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, VTFT, ch, 0x0000FFFF); 3518c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, CVCF, ch, 0x0000FFFF); 3528c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, PTRX, ch, 0); 3538c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, CPF, ch, 0); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* set pitch offset */ 3568c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* set envelope parameters */ 3598c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, ENVVAL, ch, vp->reg.parm.moddelay); 3608c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, ATKHLDM, ch, vp->reg.parm.modatkhld); 3618c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, DCYSUSM, ch, vp->reg.parm.moddcysus); 3628c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, ENVVOL, ch, vp->reg.parm.voldelay); 3638c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, ATKHLDV, ch, vp->reg.parm.volatkhld); 3648c2ecf20Sopenharmony_ci /* decay/sustain parameter for volume envelope is used 3658c2ecf20Sopenharmony_ci for triggerg the voice */ 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* cutoff and volume */ 3688c2ecf20Sopenharmony_ci temp = (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol; 3698c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, IFATN, vp->ch, temp); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* modulation envelope heights */ 3728c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, PEFE, ch, vp->reg.parm.pefe); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci /* lfo1/2 delay */ 3758c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, LFOVAL1, ch, vp->reg.parm.lfo1delay); 3768c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, LFOVAL2, ch, vp->reg.parm.lfo2delay); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* lfo1 pitch & cutoff shift */ 3798c2ecf20Sopenharmony_ci set_fmmod(hw, vp); 3808c2ecf20Sopenharmony_ci /* lfo1 volume & freq */ 3818c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq); 3828c2ecf20Sopenharmony_ci /* lfo2 pitch & freq */ 3838c2ecf20Sopenharmony_ci set_fm2frq2(hw, vp); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* reverb and loop start (reverb 8bit, MSB) */ 3868c2ecf20Sopenharmony_ci temp = vp->reg.parm.reverb; 3878c2ecf20Sopenharmony_ci temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10; 3888c2ecf20Sopenharmony_ci LIMITMAX(temp, 255); 3898c2ecf20Sopenharmony_ci addr = vp->reg.loopstart; 3908c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, PSST, vp->ch, (temp << 24) | addr); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* chorus & loop end (chorus 8bit, MSB) */ 3938c2ecf20Sopenharmony_ci addr = vp->reg.loopend; 3948c2ecf20Sopenharmony_ci temp = vp->reg.parm.chorus; 3958c2ecf20Sopenharmony_ci temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10; 3968c2ecf20Sopenharmony_ci LIMITMAX(temp, 255); 3978c2ecf20Sopenharmony_ci temp = (temp <<24) | addr; 3988c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, DSL, ch, temp); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* clear filter delay memory */ 4018c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, Z1, ch, 0); 4028c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, Z2, ch, 0); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* invalidate maps */ 4058c2ecf20Sopenharmony_ci temp = (hw->silent_page.addr << hw->address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0); 4068c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, MAPA, ch, temp); 4078c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, MAPB, ch, temp); 4088c2ecf20Sopenharmony_ci#if 0 4098c2ecf20Sopenharmony_ci /* cache */ 4108c2ecf20Sopenharmony_ci { 4118c2ecf20Sopenharmony_ci unsigned int val, sample; 4128c2ecf20Sopenharmony_ci val = 32; 4138c2ecf20Sopenharmony_ci if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS) 4148c2ecf20Sopenharmony_ci sample = 0x80808080; 4158c2ecf20Sopenharmony_ci else { 4168c2ecf20Sopenharmony_ci sample = 0; 4178c2ecf20Sopenharmony_ci val *= 2; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* cache */ 4218c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, CCR, ch, 0x1c << 16); 4228c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, CDE, ch, sample); 4238c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, CDF, ch, sample); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci /* invalidate maps */ 4268c2ecf20Sopenharmony_ci temp = ((unsigned int)hw->silent_page.addr << hw_address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0); 4278c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, MAPA, ch, temp); 4288c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, MAPB, ch, temp); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci /* fill cache */ 4318c2ecf20Sopenharmony_ci val -= 4; 4328c2ecf20Sopenharmony_ci val <<= 25; 4338c2ecf20Sopenharmony_ci val |= 0x1c << 16; 4348c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, CCR, ch, val); 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci#endif 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* Q & current address (Q 4bit value, MSB) */ 4398c2ecf20Sopenharmony_ci addr = vp->reg.start; 4408c2ecf20Sopenharmony_ci temp = vp->reg.parm.filterQ; 4418c2ecf20Sopenharmony_ci temp = (temp<<28) | addr; 4428c2ecf20Sopenharmony_ci if (vp->apitch < 0xe400) 4438c2ecf20Sopenharmony_ci temp |= CCCA_INTERPROM_0; 4448c2ecf20Sopenharmony_ci else { 4458c2ecf20Sopenharmony_ci unsigned int shift = (vp->apitch - 0xe000) >> 10; 4468c2ecf20Sopenharmony_ci temp |= shift << 25; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS) 4498c2ecf20Sopenharmony_ci temp |= CCCA_8BITSELECT; 4508c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, CCCA, ch, temp); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* reset volume */ 4538c2ecf20Sopenharmony_ci temp = (unsigned int)vp->vtarget << 16; 4548c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, VTFT, ch, temp | vp->ftarget); 4558c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, CVCF, ch, temp | 0xff00); 4568c2ecf20Sopenharmony_ci return 0; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci/* 4608c2ecf20Sopenharmony_ci * Start envelope 4618c2ecf20Sopenharmony_ci */ 4628c2ecf20Sopenharmony_cistatic void 4638c2ecf20Sopenharmony_citrigger_voice(struct snd_emux_voice *vp) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci unsigned int temp, ptarget; 4668c2ecf20Sopenharmony_ci struct snd_emu10k1 *hw; 4678c2ecf20Sopenharmony_ci struct snd_emu10k1_memblk *emem; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci hw = vp->hw; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci emem = (struct snd_emu10k1_memblk *)vp->block; 4728c2ecf20Sopenharmony_ci if (! emem || emem->mapped_page < 0) 4738c2ecf20Sopenharmony_ci return; /* not mapped */ 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci#if 0 4768c2ecf20Sopenharmony_ci ptarget = (unsigned int)vp->ptarget << 16; 4778c2ecf20Sopenharmony_ci#else 4788c2ecf20Sopenharmony_ci ptarget = IP_TO_CP(vp->apitch); 4798c2ecf20Sopenharmony_ci#endif 4808c2ecf20Sopenharmony_ci /* set pitch target and pan (volume) */ 4818c2ecf20Sopenharmony_ci temp = ptarget | (vp->apan << 8) | vp->aaux; 4828c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, PTRX, vp->ch, temp); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* pitch target */ 4858c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, CPF, vp->ch, ptarget); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* trigger voice */ 4888c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, vp->reg.parm.voldcysus|DCYSUSV_CHANNELENABLE_MASK); 4898c2ecf20Sopenharmony_ci} 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci#define MOD_SENSE 18 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci/* set lfo1 modulation height and cutoff */ 4948c2ecf20Sopenharmony_cistatic void 4958c2ecf20Sopenharmony_ciset_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci unsigned short fmmod; 4988c2ecf20Sopenharmony_ci short pitch; 4998c2ecf20Sopenharmony_ci unsigned char cutoff; 5008c2ecf20Sopenharmony_ci int modulation; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci pitch = (char)(vp->reg.parm.fmmod>>8); 5038c2ecf20Sopenharmony_ci cutoff = (vp->reg.parm.fmmod & 0xff); 5048c2ecf20Sopenharmony_ci modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; 5058c2ecf20Sopenharmony_ci pitch += (MOD_SENSE * modulation) / 1200; 5068c2ecf20Sopenharmony_ci LIMITVALUE(pitch, -128, 127); 5078c2ecf20Sopenharmony_ci fmmod = ((unsigned char)pitch<<8) | cutoff; 5088c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, fmmod); 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci/* set lfo2 pitch & frequency */ 5128c2ecf20Sopenharmony_cistatic void 5138c2ecf20Sopenharmony_ciset_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci unsigned short fm2frq2; 5168c2ecf20Sopenharmony_ci short pitch; 5178c2ecf20Sopenharmony_ci unsigned char freq; 5188c2ecf20Sopenharmony_ci int modulation; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci pitch = (char)(vp->reg.parm.fm2frq2>>8); 5218c2ecf20Sopenharmony_ci freq = vp->reg.parm.fm2frq2 & 0xff; 5228c2ecf20Sopenharmony_ci modulation = vp->chan->gm_modulation + vp->chan->midi_pressure; 5238c2ecf20Sopenharmony_ci pitch += (MOD_SENSE * modulation) / 1200; 5248c2ecf20Sopenharmony_ci LIMITVALUE(pitch, -128, 127); 5258c2ecf20Sopenharmony_ci fm2frq2 = ((unsigned char)pitch<<8) | freq; 5268c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, fm2frq2); 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci/* set filterQ */ 5308c2ecf20Sopenharmony_cistatic void 5318c2ecf20Sopenharmony_ciset_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci unsigned int val; 5348c2ecf20Sopenharmony_ci val = snd_emu10k1_ptr_read(hw, CCCA, vp->ch) & ~CCCA_RESONANCE; 5358c2ecf20Sopenharmony_ci val |= (vp->reg.parm.filterQ << 28); 5368c2ecf20Sopenharmony_ci snd_emu10k1_ptr_write(hw, CCCA, vp->ch, val); 5378c2ecf20Sopenharmony_ci} 538