File Index
Files and directories
Sign in
FILE

emu8000_pcm.c

File preview with syntax highlighting.

linux-6.19.14/sound/isa/sb/emu8000_pcm.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * pcm emulation on emu8000 wavetable
  4 *
  5 *  Copyright (C) 2002 Takashi Iwai <tiwai@suse.de>
  6 */
  7
  8#include "emu8000_local.h"
  9
 10#include <linux/sched/signal.h>
 11#include <linux/init.h>
 12#include <linux/slab.h>
 13#include <sound/initval.h>
 14#include <sound/pcm.h>
 15
 16/*
 17 * define the following if you want to use this pcm with non-interleaved mode
 18 */
 19/* #define USE_NONINTERLEAVE */
 20
 21/* NOTE: for using the non-interleaved mode with alsa-lib, you have to set
 22 * mmap_emulation flag to 1 in your .asoundrc, such like
 23 *
 24 *	pcm.emu8k {
 25 *		type plug
 26 *		slave.pcm {
 27 *			type hw
 28 *			card 0
 29 *			device 1
 30 *			mmap_emulation 1
 31 *		}
 32 *	}
 33 *
 34 * besides, for the time being, the non-interleaved mode doesn't work well on
 35 * alsa-lib...
 36 */
 37
 38
 39struct snd_emu8k_pcm {
 40	struct snd_emu8000 *emu;
 41	struct snd_pcm_substream *substream;
 42
 43	unsigned int allocated_bytes;
 44	struct snd_util_memblk *block;
 45	unsigned int offset;
 46	unsigned int buf_size;
 47	unsigned int period_size;
 48	unsigned int loop_start[2];
 49	unsigned int pitch;
 50	int panning[2];
 51	int last_ptr;
 52	int period_pos;
 53	int voices;
 54	unsigned int dram_opened: 1;
 55	unsigned int running: 1;
 56	unsigned int timer_running: 1;
 57	struct timer_list timer;
 58	spinlock_t timer_lock;
 59};
 60
 61#define LOOP_BLANK_SIZE		8
 62
 63
 64/*
 65 * open up channels for the simultaneous data transfer and playback
 66 */
 67static int
 68emu8k_open_dram_for_pcm(struct snd_emu8000 *emu, int channels)
 69{
 70	int i;
 71
 72	/* reserve up to 2 voices for playback */
 73	snd_emux_lock_voice(emu->emu, 0);
 74	if (channels > 1)
 75		snd_emux_lock_voice(emu->emu, 1);
 76
 77	/* reserve 28 voices for loading */
 78	for (i = channels + 1; i < EMU8000_DRAM_VOICES; i++) {
 79		unsigned int mode = EMU8000_RAM_WRITE;
 80		snd_emux_lock_voice(emu->emu, i);
 81#ifndef USE_NONINTERLEAVE
 82		if (channels > 1 && (i & 1) != 0)
 83			mode |= EMU8000_RAM_RIGHT;
 84#endif
 85		snd_emu8000_dma_chan(emu, i, mode);
 86	}
 87
 88	/* assign voice 31 and 32 to ROM */
 89	EMU8000_VTFT_WRITE(emu, 30, 0);
 90	EMU8000_PSST_WRITE(emu, 30, 0x1d8);
 91	EMU8000_CSL_WRITE(emu, 30, 0x1e0);
 92	EMU8000_CCCA_WRITE(emu, 30, 0x1d8);
 93	EMU8000_VTFT_WRITE(emu, 31, 0);
 94	EMU8000_PSST_WRITE(emu, 31, 0x1d8);
 95	EMU8000_CSL_WRITE(emu, 31, 0x1e0);
 96	EMU8000_CCCA_WRITE(emu, 31, 0x1d8);
 97
 98	return 0;
 99}
100
101/*
102 */
103static void
104snd_emu8000_write_wait(struct snd_emu8000 *emu, int can_schedule)
105{
106	while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
107		if (can_schedule) {
108			schedule_timeout_interruptible(1);
109			if (signal_pending(current))
110				break;
111		}
112	}
113}
114
115/*
116 * close all channels
117 */
118static void
119emu8k_close_dram(struct snd_emu8000 *emu)
120{
121	int i;
122
123	for (i = 0; i < 2; i++)
124		snd_emux_unlock_voice(emu->emu, i);
125	for (; i < EMU8000_DRAM_VOICES; i++) {
126		snd_emu8000_dma_chan(emu, i, EMU8000_RAM_CLOSE);
127		snd_emux_unlock_voice(emu->emu, i);
128	}
129}
130
131/*
132 * convert Hz to AWE32 rate offset (see emux/soundfont.c)
133 */
134
135#define OFFSET_SAMPLERATE	1011119		/* base = 44100 */
136#define SAMPLERATE_RATIO	4096
137
138static int calc_rate_offset(int hz)
139{
140	return snd_sf_linear_to_log(hz, OFFSET_SAMPLERATE, SAMPLERATE_RATIO);
141}
142
143
144/*
145 */
146
147static const struct snd_pcm_hardware emu8k_pcm_hw = {
148#ifdef USE_NONINTERLEAVE
149	.info =			SNDRV_PCM_INFO_NONINTERLEAVED,
150#else
151	.info =			SNDRV_PCM_INFO_INTERLEAVED,
152#endif
153	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
154	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
155	.rate_min =		4000,
156	.rate_max =		48000,
157	.channels_min =		1,
158	.channels_max =		2,
159	.buffer_bytes_max =	(128*1024),
160	.period_bytes_min =	1024,
161	.period_bytes_max =	(128*1024),
162	.periods_min =		2,
163	.periods_max =		1024,
164	.fifo_size =		0,
165
166};
167
168/*
169 * get the current position at the given channel from CCCA register
170 */
171static inline int emu8k_get_curpos(struct snd_emu8k_pcm *rec, int ch)
172{
173	int val = EMU8000_CCCA_READ(rec->emu, ch) & 0xfffffff;
174	val -= rec->loop_start[ch] - 1;
175	return val;
176}
177
178
179/*
180 * timer interrupt handler
181 * check the current position and update the period if necessary.
182 */
183static void emu8k_pcm_timer_func(struct timer_list *t)
184{
185	struct snd_emu8k_pcm *rec = timer_container_of(rec, t, timer);
186	int ptr, delta;
187	bool period_elapsed = false;
188
189	scoped_guard(spinlock, &rec->timer_lock) {
190		/* update the current pointer */
191		ptr = emu8k_get_curpos(rec, 0);
192		if (ptr < rec->last_ptr)
193			delta = ptr + rec->buf_size - rec->last_ptr;
194		else
195			delta = ptr - rec->last_ptr;
196		rec->period_pos += delta;
197		rec->last_ptr = ptr;
198
199		/* reprogram timer */
200		mod_timer(&rec->timer, jiffies + 1);
201
202		/* update period */
203		if (rec->period_pos >= (int)rec->period_size) {
204			rec->period_pos %= rec->period_size;
205			period_elapsed = true;
206		}
207	}
208
209	if (period_elapsed)
210		snd_pcm_period_elapsed(rec->substream);
211}
212
213
214/*
215 * open pcm
216 * creating an instance here
217 */
218static int emu8k_pcm_open(struct snd_pcm_substream *subs)
219{
220	struct snd_emu8000 *emu = snd_pcm_substream_chip(subs);
221	struct snd_emu8k_pcm *rec;
222	struct snd_pcm_runtime *runtime = subs->runtime;
223
224	rec = kzalloc(sizeof(*rec), GFP_KERNEL);
225	if (! rec)
226		return -ENOMEM;
227
228	rec->emu = emu;
229	rec->substream = subs;
230	runtime->private_data = rec;
231
232	spin_lock_init(&rec->timer_lock);
233	timer_setup(&rec->timer, emu8k_pcm_timer_func, 0);
234
235	runtime->hw = emu8k_pcm_hw;
236	runtime->hw.buffer_bytes_max = emu->mem_size - LOOP_BLANK_SIZE * 3;
237	runtime->hw.period_bytes_max = runtime->hw.buffer_bytes_max / 2;
238
239	/* use timer to update periods.. (specified in msec) */
240	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
241				     DIV_ROUND_UP(1000000, HZ), UINT_MAX);
242
243	return 0;
244}
245
246static int emu8k_pcm_close(struct snd_pcm_substream *subs)
247{
248	struct snd_emu8k_pcm *rec = subs->runtime->private_data;
249	kfree(rec);
250	subs->runtime->private_data = NULL;
251	return 0;
252}
253
254/*
255 * calculate pitch target
256 */
257static int calc_pitch_target(int pitch)
258{
259	int ptarget = 1 << (pitch >> 12);
260	if (pitch & 0x800) ptarget += (ptarget * 0x102e) / 0x2710;
261	if (pitch & 0x400) ptarget += (ptarget * 0x764) / 0x2710;
262	if (pitch & 0x200) ptarget += (ptarget * 0x389) / 0x2710;
263	ptarget += (ptarget >> 1);
264	if (ptarget > 0xffff) ptarget = 0xffff;
265	return ptarget;
266}
267
268/*
269 * set up the voice
270 */
271static void setup_voice(struct snd_emu8k_pcm *rec, int ch)
272{
273	struct snd_emu8000 *hw = rec->emu;
274	unsigned int temp;
275
276	/* channel to be silent and idle */
277	EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080);
278	EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF);
279	EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF);
280	EMU8000_PTRX_WRITE(hw, ch, 0);
281	EMU8000_CPF_WRITE(hw, ch, 0);
282
283	/* pitch offset */
284	EMU8000_IP_WRITE(hw, ch, rec->pitch);
285	/* set envelope parameters */
286	EMU8000_ENVVAL_WRITE(hw, ch, 0x8000);
287	EMU8000_ATKHLD_WRITE(hw, ch, 0x7f7f);
288	EMU8000_DCYSUS_WRITE(hw, ch, 0x7f7f);
289	EMU8000_ENVVOL_WRITE(hw, ch, 0x8000);
290	EMU8000_ATKHLDV_WRITE(hw, ch, 0x7f7f);
291	/* decay/sustain parameter for volume envelope is used
292	   for triggerg the voice */
293	/* modulation envelope heights */
294	EMU8000_PEFE_WRITE(hw, ch, 0x0);
295	/* lfo1/2 delay */
296	EMU8000_LFO1VAL_WRITE(hw, ch, 0x8000);
297	EMU8000_LFO2VAL_WRITE(hw, ch, 0x8000);
298	/* lfo1 pitch & cutoff shift */
299	EMU8000_FMMOD_WRITE(hw, ch, 0);
300	/* lfo1 volume & freq */
301	EMU8000_TREMFRQ_WRITE(hw, ch, 0);
302	/* lfo2 pitch & freq */
303	EMU8000_FM2FRQ2_WRITE(hw, ch, 0);
304	/* pan & loop start */
305	temp = rec->panning[ch];
306	temp = (temp <<24) | ((unsigned int)rec->loop_start[ch] - 1);
307	EMU8000_PSST_WRITE(hw, ch, temp);
308	/* chorus & loop end (chorus 8bit, MSB) */
309	temp = 0; // chorus
310	temp = (temp << 24) | ((unsigned int)rec->loop_start[ch] + rec->buf_size - 1);
311	EMU8000_CSL_WRITE(hw, ch, temp);
312	/* Q & current address (Q 4bit value, MSB) */
313	temp = 0; // filterQ
314	temp = (temp << 28) | ((unsigned int)rec->loop_start[ch] - 1);
315	EMU8000_CCCA_WRITE(hw, ch, temp);
316	/* clear unknown registers */
317	EMU8000_00A0_WRITE(hw, ch, 0);
318	EMU8000_0080_WRITE(hw, ch, 0);
319}
320
321/*
322 * trigger the voice
323 */
324static void start_voice(struct snd_emu8k_pcm *rec, int ch)
325{
326	struct snd_emu8000 *hw = rec->emu;
327	unsigned int temp, aux;
328	int pt = calc_pitch_target(rec->pitch);
329
330	/* cutoff and volume */
331	EMU8000_IFATN_WRITE(hw, ch, 0xff00);
332	EMU8000_VTFT_WRITE(hw, ch, 0xffff);
333	EMU8000_CVCF_WRITE(hw, ch, 0xffff);
334	/* trigger envelope */
335	EMU8000_DCYSUSV_WRITE(hw, ch, 0x7f7f);
336	/* set reverb and pitch target */
337	temp = 0; // reverb
338	if (rec->panning[ch] == 0)
339		aux = 0xff;
340	else
341		aux = (-rec->panning[ch]) & 0xff;
342	temp = (temp << 8) | (pt << 16) | aux;
343	EMU8000_PTRX_WRITE(hw, ch, temp);
344	EMU8000_CPF_WRITE(hw, ch, pt << 16);
345
346	/* start timer */
347	guard(spinlock_irqsave)(&rec->timer_lock);
348	if (! rec->timer_running) {
349		mod_timer(&rec->timer, jiffies + 1);
350		rec->timer_running = 1;
351	}
352}
353
354/*
355 * stop the voice immediately
356 */
357static void stop_voice(struct snd_emu8k_pcm *rec, int ch)
358{
359	struct snd_emu8000 *hw = rec->emu;
360
361	EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F);
362
363	/* stop timer */
364	guard(spinlock_irqsave)(&rec->timer_lock);
365	if (rec->timer_running) {
366		timer_delete(&rec->timer);
367		rec->timer_running = 0;
368	}
369}
370
371static int emu8k_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
372{
373	struct snd_emu8k_pcm *rec = subs->runtime->private_data;
374	int ch;
375
376	switch (cmd) {
377	case SNDRV_PCM_TRIGGER_START:
378		for (ch = 0; ch < rec->voices; ch++)
379			start_voice(rec, ch);
380		rec->running = 1;
381		break;
382	case SNDRV_PCM_TRIGGER_STOP:
383		rec->running = 0;
384		for (ch = 0; ch < rec->voices; ch++)
385			stop_voice(rec, ch);
386		break;
387	default:
388		return -EINVAL;
389	}
390	return 0;
391}
392
393
394/*
395 * copy / silence ops
396 */
397
398/*
399 * this macro should be inserted in the copy/silence loops
400 * to reduce the latency.  without this, the system will hang up
401 * during the whole loop.
402 */
403#define CHECK_SCHEDULER() \
404do { \
405	cond_resched();\
406	if (signal_pending(current))\
407		return -EAGAIN;\
408} while (0)
409
410#define GET_VAL(sval, iter)						\
411	do {								\
412		if (!iter)						\
413			sval = 0;					\
414		else if (copy_from_iter(&sval, 2, iter) != 2)		\
415			return -EFAULT;					\
416	} while (0)
417
418#ifdef USE_NONINTERLEAVE
419
420#define LOOP_WRITE(rec, offset, iter, count)			\
421	do {							\
422		struct snd_emu8000 *emu = (rec)->emu;		\
423		snd_emu8000_write_wait(emu, 1);			\
424		EMU8000_SMALW_WRITE(emu, offset);		\
425		while (count > 0) {				\
426			unsigned short sval;			\
427			CHECK_SCHEDULER();			\
428			GET_VAL(sval, iter);			\
429			EMU8000_SMLD_WRITE(emu, sval);		\
430			count--;				\
431		}						\
432	} while (0)
433
434/* copy one channel block */
435static int emu8k_pcm_copy(struct snd_pcm_substream *subs,
436			  int voice, unsigned long pos,
437			  struct iov_iter *src, unsigned long count)
438{
439	struct snd_emu8k_pcm *rec = subs->runtime->private_data;
440
441	/* convert to word unit */
442	pos = (pos << 1) + rec->loop_start[voice];
443	count <<= 1;
444	LOOP_WRITE(rec, pos, src, count);
445	return 0;
446}
447
448/* make a channel block silence */
449static int emu8k_pcm_silence(struct snd_pcm_substream *subs,
450			     int voice, unsigned long pos, unsigned long count)
451{
452	struct snd_emu8k_pcm *rec = subs->runtime->private_data;
453
454	/* convert to word unit */
455	pos = (pos << 1) + rec->loop_start[voice];
456	count <<= 1;
457	LOOP_WRITE(rec, pos, NULL, count);
458	return 0;
459}
460
461#else /* interleave */
462
463#define LOOP_WRITE(rec, pos, iter, count)				\
464	do {								\
465		struct snd_emu8000 *emu = rec->emu;			\
466		snd_emu8000_write_wait(emu, 1);				\
467		EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]);	\
468		if (rec->voices > 1)					\
469			EMU8000_SMARW_WRITE(emu, pos + rec->loop_start[1]); \
470		while (count > 0) {					\
471			unsigned short sval;				\
472			CHECK_SCHEDULER();				\
473			GET_VAL(sval, iter);				\
474			EMU8000_SMLD_WRITE(emu, sval);			\
475			if (rec->voices > 1) {				\
476				CHECK_SCHEDULER();			\
477				GET_VAL(sval, iter);			\
478				EMU8000_SMRD_WRITE(emu, sval);		\
479			}						\
480			count--;					\
481		}							\
482	} while (0)
483
484
485/*
486 * copy the interleaved data can be done easily by using
487 * DMA "left" and "right" channels on emu8k engine.
488 */
489static int emu8k_pcm_copy(struct snd_pcm_substream *subs,
490			  int voice, unsigned long pos,
491			  struct iov_iter *src, unsigned long count)
492{
493	struct snd_emu8k_pcm *rec = subs->runtime->private_data;
494
495	/* convert to frames */
496	pos = bytes_to_frames(subs->runtime, pos);
497	count = bytes_to_frames(subs->runtime, count);
498	LOOP_WRITE(rec, pos, src, count);
499	return 0;
500}
501
502static int emu8k_pcm_silence(struct snd_pcm_substream *subs,
503			     int voice, unsigned long pos, unsigned long count)
504{
505	struct snd_emu8k_pcm *rec = subs->runtime->private_data;
506
507	/* convert to frames */
508	pos = bytes_to_frames(subs->runtime, pos);
509	count = bytes_to_frames(subs->runtime, count);
510	LOOP_WRITE(rec, pos, NULL, count);
511	return 0;
512}
513#endif
514
515
516/*
517 * allocate a memory block
518 */
519static int emu8k_pcm_hw_params(struct snd_pcm_substream *subs,
520			       struct snd_pcm_hw_params *hw_params)
521{
522	struct snd_emu8k_pcm *rec = subs->runtime->private_data;
523
524	if (rec->block) {
525		/* reallocation - release the old block */
526		snd_util_mem_free(rec->emu->memhdr, rec->block);
527		rec->block = NULL;
528	}
529
530	rec->allocated_bytes = params_buffer_bytes(hw_params) + LOOP_BLANK_SIZE * 4;
531	rec->block = snd_util_mem_alloc(rec->emu->memhdr, rec->allocated_bytes);
532	if (! rec->block)
533		return -ENOMEM;
534	rec->offset = EMU8000_DRAM_OFFSET + (rec->block->offset >> 1); /* in word */
535	/* at least dma_bytes must be set for non-interleaved mode */
536	subs->dma_buffer.bytes = params_buffer_bytes(hw_params);
537
538	return 0;
539}
540
541/*
542 * free the memory block
543 */
544static int emu8k_pcm_hw_free(struct snd_pcm_substream *subs)
545{
546	struct snd_emu8k_pcm *rec = subs->runtime->private_data;
547
548	if (rec->block) {
549		int ch;
550		for (ch = 0; ch < rec->voices; ch++)
551			stop_voice(rec, ch); // to be sure
552		if (rec->dram_opened)
553			emu8k_close_dram(rec->emu);
554		snd_util_mem_free(rec->emu->memhdr, rec->block);
555		rec->block = NULL;
556	}
557	return 0;
558}
559
560/*
561 */
562static int emu8k_pcm_prepare(struct snd_pcm_substream *subs)
563{
564	struct snd_emu8k_pcm *rec = subs->runtime->private_data;
565
566	rec->pitch = 0xe000 + calc_rate_offset(subs->runtime->rate);
567	rec->last_ptr = 0;
568	rec->period_pos = 0;
569
570	rec->buf_size = subs->runtime->buffer_size;
571	rec->period_size = subs->runtime->period_size;
572	rec->voices = subs->runtime->channels;
573	rec->loop_start[0] = rec->offset + LOOP_BLANK_SIZE;
574	if (rec->voices > 1)
575		rec->loop_start[1] = rec->loop_start[0] + rec->buf_size + LOOP_BLANK_SIZE;
576	if (rec->voices > 1) {
577		rec->panning[0] = 0xff;
578		rec->panning[1] = 0x00;
579	} else
580		rec->panning[0] = 0x80;
581
582	if (! rec->dram_opened) {
583		int err, i, ch;
584
585		snd_emux_terminate_all(rec->emu->emu);
586		err = emu8k_open_dram_for_pcm(rec->emu, rec->voices);
587		if (err)
588			return err;
589		rec->dram_opened = 1;
590
591		/* clear loop blanks */
592		snd_emu8000_write_wait(rec->emu, 0);
593		EMU8000_SMALW_WRITE(rec->emu, rec->offset);
594		for (i = 0; i < LOOP_BLANK_SIZE; i++)
595			EMU8000_SMLD_WRITE(rec->emu, 0);
596		for (ch = 0; ch < rec->voices; ch++) {
597			EMU8000_SMALW_WRITE(rec->emu, rec->loop_start[ch] + rec->buf_size);
598			for (i = 0; i < LOOP_BLANK_SIZE; i++)
599				EMU8000_SMLD_WRITE(rec->emu, 0);
600		}
601	}
602
603	setup_voice(rec, 0);
604	if (rec->voices > 1)
605		setup_voice(rec, 1);
606	return 0;
607}
608
609static snd_pcm_uframes_t emu8k_pcm_pointer(struct snd_pcm_substream *subs)
610{
611	struct snd_emu8k_pcm *rec = subs->runtime->private_data;
612	if (rec->running)
613		return emu8k_get_curpos(rec, 0);
614	return 0;
615}
616
617
618static const struct snd_pcm_ops emu8k_pcm_ops = {
619	.open =		emu8k_pcm_open,
620	.close =	emu8k_pcm_close,
621	.hw_params =	emu8k_pcm_hw_params,
622	.hw_free =	emu8k_pcm_hw_free,
623	.prepare =	emu8k_pcm_prepare,
624	.trigger =	emu8k_pcm_trigger,
625	.pointer =	emu8k_pcm_pointer,
626	.copy =		emu8k_pcm_copy,
627	.fill_silence =	emu8k_pcm_silence,
628};
629
630
631static void snd_emu8000_pcm_free(struct snd_pcm *pcm)
632{
633	struct snd_emu8000 *emu = pcm->private_data;
634	emu->pcm = NULL;
635}
636
637int snd_emu8000_pcm_new(struct snd_card *card, struct snd_emu8000 *emu, int index)
638{
639	struct snd_pcm *pcm;
640	int err;
641
642	err = snd_pcm_new(card, "Emu8000 PCM", index, 1, 0, &pcm);
643	if (err < 0)
644		return err;
645	pcm->private_data = emu;
646	pcm->private_free = snd_emu8000_pcm_free;
647	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &emu8k_pcm_ops);
648	emu->pcm = pcm;
649
650	snd_device_register(card, pcm);
651
652	return 0;
653}
~/ Read-only file preview.