cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

emu10k1_callback.c (13965B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  synth callback routines for Emu10k1
      4 *
      5 *  Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
      6 */
      7
      8#include <linux/export.h>
      9#include "emu10k1_synth_local.h"
     10#include <sound/asoundef.h>
     11
     12/* voice status */
     13enum {
     14	V_FREE=0, V_OFF, V_RELEASED, V_PLAYING, V_END
     15};
     16
     17/* Keeps track of what we are finding */
     18struct best_voice {
     19	unsigned int time;
     20	int voice;
     21};
     22
     23/*
     24 * prototypes
     25 */
     26static void lookup_voices(struct snd_emux *emux, struct snd_emu10k1 *hw,
     27			  struct best_voice *best, int active_only);
     28static struct snd_emux_voice *get_voice(struct snd_emux *emux,
     29					struct snd_emux_port *port);
     30static int start_voice(struct snd_emux_voice *vp);
     31static void trigger_voice(struct snd_emux_voice *vp);
     32static void release_voice(struct snd_emux_voice *vp);
     33static void update_voice(struct snd_emux_voice *vp, int update);
     34static void terminate_voice(struct snd_emux_voice *vp);
     35static void free_voice(struct snd_emux_voice *vp);
     36static void set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
     37static void set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
     38static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
     39
     40/*
     41 * Ensure a value is between two points
     42 * macro evaluates its args more than once, so changed to upper-case.
     43 */
     44#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
     45#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
     46
     47
     48/*
     49 * set up operators
     50 */
     51static const struct snd_emux_operators emu10k1_ops = {
     52	.owner =	THIS_MODULE,
     53	.get_voice =	get_voice,
     54	.prepare =	start_voice,
     55	.trigger =	trigger_voice,
     56	.release =	release_voice,
     57	.update =	update_voice,
     58	.terminate =	terminate_voice,
     59	.free_voice =	free_voice,
     60	.sample_new =	snd_emu10k1_sample_new,
     61	.sample_free =	snd_emu10k1_sample_free,
     62};
     63
     64void
     65snd_emu10k1_ops_setup(struct snd_emux *emux)
     66{
     67	emux->ops = emu10k1_ops;
     68}
     69
     70
     71/*
     72 * get more voice for pcm
     73 *
     74 * terminate most inactive voice and give it as a pcm voice.
     75 *
     76 * voice_lock is already held.
     77 */
     78int
     79snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
     80{
     81	struct snd_emux *emu;
     82	struct snd_emux_voice *vp;
     83	struct best_voice best[V_END];
     84	int i;
     85
     86	emu = hw->synth;
     87
     88	lookup_voices(emu, hw, best, 1); /* no OFF voices */
     89	for (i = 0; i < V_END; i++) {
     90		if (best[i].voice >= 0) {
     91			int ch;
     92			vp = &emu->voices[best[i].voice];
     93			ch = vp->ch;
     94			if (ch < 0) {
     95				/*
     96				dev_warn(emu->card->dev,
     97				       "synth_get_voice: ch < 0 (%d) ??", i);
     98				*/
     99				continue;
    100			}
    101			vp->emu->num_voices--;
    102			vp->ch = -1;
    103			vp->state = SNDRV_EMUX_ST_OFF;
    104			return ch;
    105		}
    106	}
    107
    108	/* not found */
    109	return -ENOMEM;
    110}
    111
    112
    113/*
    114 * turn off the voice (not terminated)
    115 */
    116static void
    117release_voice(struct snd_emux_voice *vp)
    118{
    119	int dcysusv;
    120	struct snd_emu10k1 *hw;
    121	
    122	hw = vp->hw;
    123	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;
    124	snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv);
    125	dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK;
    126	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, dcysusv);
    127}
    128
    129
    130/*
    131 * terminate the voice
    132 */
    133static void
    134terminate_voice(struct snd_emux_voice *vp)
    135{
    136	struct snd_emu10k1 *hw;
    137	
    138	if (snd_BUG_ON(!vp))
    139		return;
    140	hw = vp->hw;
    141	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
    142	if (vp->block) {
    143		struct snd_emu10k1_memblk *emem;
    144		emem = (struct snd_emu10k1_memblk *)vp->block;
    145		if (emem->map_locked > 0)
    146			emem->map_locked--;
    147	}
    148}
    149
    150/*
    151 * release the voice to system
    152 */
    153static void
    154free_voice(struct snd_emux_voice *vp)
    155{
    156	struct snd_emu10k1 *hw;
    157	
    158	hw = vp->hw;
    159	/* FIXME: emu10k1_synth is broken. */
    160	/* This can get called with hw == 0 */
    161	/* Problem apparent on plug, unplug then plug */
    162	/* on the Audigy 2 ZS Notebook. */
    163	if (hw && (vp->ch >= 0)) {
    164		snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00);
    165		snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
    166		// snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0);
    167		snd_emu10k1_ptr_write(hw, VTFT, vp->ch, 0xffff);
    168		snd_emu10k1_ptr_write(hw, CVCF, vp->ch, 0xffff);
    169		snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]);
    170		vp->emu->num_voices--;
    171		vp->ch = -1;
    172	}
    173}
    174
    175
    176/*
    177 * update registers
    178 */
    179static void
    180update_voice(struct snd_emux_voice *vp, int update)
    181{
    182	struct snd_emu10k1 *hw;
    183	
    184	hw = vp->hw;
    185	if (update & SNDRV_EMUX_UPDATE_VOLUME)
    186		snd_emu10k1_ptr_write(hw, IFATN_ATTENUATION, vp->ch, vp->avol);
    187	if (update & SNDRV_EMUX_UPDATE_PITCH)
    188		snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);
    189	if (update & SNDRV_EMUX_UPDATE_PAN) {
    190		snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_A, vp->ch, vp->apan);
    191		snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux);
    192	}
    193	if (update & SNDRV_EMUX_UPDATE_FMMOD)
    194		set_fmmod(hw, vp);
    195	if (update & SNDRV_EMUX_UPDATE_TREMFREQ)
    196		snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
    197	if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)
    198		set_fm2frq2(hw, vp);
    199	if (update & SNDRV_EMUX_UPDATE_Q)
    200		set_filterQ(hw, vp);
    201}
    202
    203
    204/*
    205 * look up voice table - get the best voice in order of preference
    206 */
    207/* spinlock held! */
    208static void
    209lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw,
    210	      struct best_voice *best, int active_only)
    211{
    212	struct snd_emux_voice *vp;
    213	struct best_voice *bp;
    214	int  i;
    215
    216	for (i = 0; i < V_END; i++) {
    217		best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */
    218		best[i].voice = -1;
    219	}
    220
    221	/*
    222	 * Go through them all and get a best one to use.
    223	 * NOTE: could also look at volume and pick the quietest one.
    224	 */
    225	for (i = 0; i < emu->max_voices; i++) {
    226		int state, val;
    227
    228		vp = &emu->voices[i];
    229		state = vp->state;
    230		if (state == SNDRV_EMUX_ST_OFF) {
    231			if (vp->ch < 0) {
    232				if (active_only)
    233					continue;
    234				bp = best + V_FREE;
    235			} else
    236				bp = best + V_OFF;
    237		}
    238		else if (state == SNDRV_EMUX_ST_RELEASED ||
    239			 state == SNDRV_EMUX_ST_PENDING) {
    240			bp = best + V_RELEASED;
    241#if 1
    242			val = snd_emu10k1_ptr_read(hw, CVCF_CURRENTVOL, vp->ch);
    243			if (! val)
    244				bp = best + V_OFF;
    245#endif
    246		}
    247		else if (state == SNDRV_EMUX_ST_STANDBY)
    248			continue;
    249		else if (state & SNDRV_EMUX_ST_ON)
    250			bp = best + V_PLAYING;
    251		else
    252			continue;
    253
    254		/* check if sample is finished playing (non-looping only) */
    255		if (bp != best + V_OFF && bp != best + V_FREE &&
    256		    (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {
    257			val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch);
    258			if (val >= vp->reg.loopstart)
    259				bp = best + V_OFF;
    260		}
    261
    262		if (vp->time < bp->time) {
    263			bp->time = vp->time;
    264			bp->voice = i;
    265		}
    266	}
    267}
    268
    269/*
    270 * get an empty voice
    271 *
    272 * emu->voice_lock is already held.
    273 */
    274static struct snd_emux_voice *
    275get_voice(struct snd_emux *emu, struct snd_emux_port *port)
    276{
    277	struct snd_emu10k1 *hw;
    278	struct snd_emux_voice *vp;
    279	struct best_voice best[V_END];
    280	int i;
    281
    282	hw = emu->hw;
    283
    284	lookup_voices(emu, hw, best, 0);
    285	for (i = 0; i < V_END; i++) {
    286		if (best[i].voice >= 0) {
    287			vp = &emu->voices[best[i].voice];
    288			if (vp->ch < 0) {
    289				/* allocate a voice */
    290				struct snd_emu10k1_voice *hwvoice;
    291				if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, &hwvoice) < 0 || hwvoice == NULL)
    292					continue;
    293				vp->ch = hwvoice->number;
    294				emu->num_voices++;
    295			}
    296			return vp;
    297		}
    298	}
    299
    300	/* not found */
    301	return NULL;
    302}
    303
    304/*
    305 * prepare envelopes and LFOs
    306 */
    307static int
    308start_voice(struct snd_emux_voice *vp)
    309{
    310	unsigned int temp;
    311	int ch;
    312	unsigned int addr, mapped_offset;
    313	struct snd_midi_channel *chan;
    314	struct snd_emu10k1 *hw;
    315	struct snd_emu10k1_memblk *emem;
    316	
    317	hw = vp->hw;
    318	ch = vp->ch;
    319	if (snd_BUG_ON(ch < 0))
    320		return -EINVAL;
    321	chan = vp->chan;
    322
    323	emem = (struct snd_emu10k1_memblk *)vp->block;
    324	if (emem == NULL)
    325		return -EINVAL;
    326	emem->map_locked++;
    327	if (snd_emu10k1_memblk_map(hw, emem) < 0) {
    328		/* dev_err(hw->card->devK, "emu: cannot map!\n"); */
    329		return -ENOMEM;
    330	}
    331	mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1;
    332	vp->reg.start += mapped_offset;
    333	vp->reg.end += mapped_offset;
    334	vp->reg.loopstart += mapped_offset;
    335	vp->reg.loopend += mapped_offset;
    336
    337	/* set channel routing */
    338	/* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */
    339	if (hw->audigy) {
    340		temp = FXBUS_MIDI_LEFT | (FXBUS_MIDI_RIGHT << 8) | 
    341			(FXBUS_MIDI_REVERB << 16) | (FXBUS_MIDI_CHORUS << 24);
    342		snd_emu10k1_ptr_write(hw, A_FXRT1, ch, temp);
    343	} else {
    344		temp = (FXBUS_MIDI_LEFT << 16) | (FXBUS_MIDI_RIGHT << 20) | 
    345			(FXBUS_MIDI_REVERB << 24) | (FXBUS_MIDI_CHORUS << 28);
    346		snd_emu10k1_ptr_write(hw, FXRT, ch, temp);
    347	}
    348
    349	/* channel to be silent and idle */
    350	snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0x0000);
    351	snd_emu10k1_ptr_write(hw, VTFT, ch, 0x0000FFFF);
    352	snd_emu10k1_ptr_write(hw, CVCF, ch, 0x0000FFFF);
    353	snd_emu10k1_ptr_write(hw, PTRX, ch, 0);
    354	snd_emu10k1_ptr_write(hw, CPF, ch, 0);
    355
    356	/* set pitch offset */
    357	snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);
    358
    359	/* set envelope parameters */
    360	snd_emu10k1_ptr_write(hw, ENVVAL, ch, vp->reg.parm.moddelay);
    361	snd_emu10k1_ptr_write(hw, ATKHLDM, ch, vp->reg.parm.modatkhld);
    362	snd_emu10k1_ptr_write(hw, DCYSUSM, ch, vp->reg.parm.moddcysus);
    363	snd_emu10k1_ptr_write(hw, ENVVOL, ch, vp->reg.parm.voldelay);
    364	snd_emu10k1_ptr_write(hw, ATKHLDV, ch, vp->reg.parm.volatkhld);
    365	/* decay/sustain parameter for volume envelope is used
    366	   for triggerg the voice */
    367
    368	/* cutoff and volume */
    369	temp = (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol;
    370	snd_emu10k1_ptr_write(hw, IFATN, vp->ch, temp);
    371
    372	/* modulation envelope heights */
    373	snd_emu10k1_ptr_write(hw, PEFE, ch, vp->reg.parm.pefe);
    374
    375	/* lfo1/2 delay */
    376	snd_emu10k1_ptr_write(hw, LFOVAL1, ch, vp->reg.parm.lfo1delay);
    377	snd_emu10k1_ptr_write(hw, LFOVAL2, ch, vp->reg.parm.lfo2delay);
    378
    379	/* lfo1 pitch & cutoff shift */
    380	set_fmmod(hw, vp);
    381	/* lfo1 volume & freq */
    382	snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
    383	/* lfo2 pitch & freq */
    384	set_fm2frq2(hw, vp);
    385
    386	/* reverb and loop start (reverb 8bit, MSB) */
    387	temp = vp->reg.parm.reverb;
    388	temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;
    389	LIMITMAX(temp, 255);
    390	addr = vp->reg.loopstart;
    391	snd_emu10k1_ptr_write(hw, PSST, vp->ch, (temp << 24) | addr);
    392
    393	/* chorus & loop end (chorus 8bit, MSB) */
    394	addr = vp->reg.loopend;
    395	temp = vp->reg.parm.chorus;
    396	temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;
    397	LIMITMAX(temp, 255);
    398	temp = (temp <<24) | addr;
    399	snd_emu10k1_ptr_write(hw, DSL, ch, temp);
    400
    401	/* clear filter delay memory */
    402	snd_emu10k1_ptr_write(hw, Z1, ch, 0);
    403	snd_emu10k1_ptr_write(hw, Z2, ch, 0);
    404
    405	/* invalidate maps */
    406	temp = (hw->silent_page.addr << hw->address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
    407	snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
    408	snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
    409#if 0
    410	/* cache */
    411	{
    412		unsigned int val, sample;
    413		val = 32;
    414		if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
    415			sample = 0x80808080;
    416		else {
    417			sample = 0;
    418			val *= 2;
    419		}
    420
    421		/* cache */
    422		snd_emu10k1_ptr_write(hw, CCR, ch, 0x1c << 16);
    423		snd_emu10k1_ptr_write(hw, CDE, ch, sample);
    424		snd_emu10k1_ptr_write(hw, CDF, ch, sample);
    425
    426		/* invalidate maps */
    427		temp = ((unsigned int)hw->silent_page.addr << hw_address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
    428		snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
    429		snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
    430		
    431		/* fill cache */
    432		val -= 4;
    433		val <<= 25;
    434		val |= 0x1c << 16;
    435		snd_emu10k1_ptr_write(hw, CCR, ch, val);
    436	}
    437#endif
    438
    439	/* Q & current address (Q 4bit value, MSB) */
    440	addr = vp->reg.start;
    441	temp = vp->reg.parm.filterQ;
    442	temp = (temp<<28) | addr;
    443	if (vp->apitch < 0xe400)
    444		temp |= CCCA_INTERPROM_0;
    445	else {
    446		unsigned int shift = (vp->apitch - 0xe000) >> 10;
    447		temp |= shift << 25;
    448	}
    449	if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
    450		temp |= CCCA_8BITSELECT;
    451	snd_emu10k1_ptr_write(hw, CCCA, ch, temp);
    452
    453	/* reset volume */
    454	temp = (unsigned int)vp->vtarget << 16;
    455	snd_emu10k1_ptr_write(hw, VTFT, ch, temp | vp->ftarget);
    456	snd_emu10k1_ptr_write(hw, CVCF, ch, temp | 0xff00);
    457	return 0;
    458}
    459
    460/*
    461 * Start envelope
    462 */
    463static void
    464trigger_voice(struct snd_emux_voice *vp)
    465{
    466	unsigned int temp, ptarget;
    467	struct snd_emu10k1 *hw;
    468	struct snd_emu10k1_memblk *emem;
    469	
    470	hw = vp->hw;
    471
    472	emem = (struct snd_emu10k1_memblk *)vp->block;
    473	if (! emem || emem->mapped_page < 0)
    474		return; /* not mapped */
    475
    476#if 0
    477	ptarget = (unsigned int)vp->ptarget << 16;
    478#else
    479	ptarget = IP_TO_CP(vp->apitch);
    480#endif
    481	/* set pitch target and pan (volume) */
    482	temp = ptarget | (vp->apan << 8) | vp->aaux;
    483	snd_emu10k1_ptr_write(hw, PTRX, vp->ch, temp);
    484
    485	/* pitch target */
    486	snd_emu10k1_ptr_write(hw, CPF, vp->ch, ptarget);
    487
    488	/* trigger voice */
    489	snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, vp->reg.parm.voldcysus|DCYSUSV_CHANNELENABLE_MASK);
    490}
    491
    492#define MOD_SENSE 18
    493
    494/* set lfo1 modulation height and cutoff */
    495static void
    496set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
    497{
    498	unsigned short fmmod;
    499	short pitch;
    500	unsigned char cutoff;
    501	int modulation;
    502
    503	pitch = (char)(vp->reg.parm.fmmod>>8);
    504	cutoff = (vp->reg.parm.fmmod & 0xff);
    505	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
    506	pitch += (MOD_SENSE * modulation) / 1200;
    507	LIMITVALUE(pitch, -128, 127);
    508	fmmod = ((unsigned char)pitch<<8) | cutoff;
    509	snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, fmmod);
    510}
    511
    512/* set lfo2 pitch & frequency */
    513static void
    514set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
    515{
    516	unsigned short fm2frq2;
    517	short pitch;
    518	unsigned char freq;
    519	int modulation;
    520
    521	pitch = (char)(vp->reg.parm.fm2frq2>>8);
    522	freq = vp->reg.parm.fm2frq2 & 0xff;
    523	modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
    524	pitch += (MOD_SENSE * modulation) / 1200;
    525	LIMITVALUE(pitch, -128, 127);
    526	fm2frq2 = ((unsigned char)pitch<<8) | freq;
    527	snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, fm2frq2);
    528}
    529
    530/* set filterQ */
    531static void
    532set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
    533{
    534	unsigned int val;
    535	val = snd_emu10k1_ptr_read(hw, CCCA, vp->ch) & ~CCCA_RESONANCE;
    536	val |= (vp->reg.parm.filterQ << 28);
    537	snd_emu10k1_ptr_write(hw, CCCA, vp->ch, val);
    538}