Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/isa/sb/emu8000_pcm.c
29266 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* pcm emulation on emu8000 wavetable
4
*
5
* Copyright (C) 2002 Takashi Iwai <[email protected]>
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
39
struct 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
*/
67
static int
68
emu8k_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
*/
103
static void
104
snd_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
*/
118
static void
119
emu8k_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
138
static 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
147
static 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
*/
171
static 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
*/
183
static 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
*/
218
static 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
246
static 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
*/
257
static 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
*/
271
static 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
*/
324
static 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
*/
357
static 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
371
static 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() \
404
do { \
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 */
435
static 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 */
449
static 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
*/
489
static 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
502
static 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
*/
519
static 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
*/
544
static 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
*/
562
static 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
609
static 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
618
static 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
631
static void snd_emu8000_pcm_free(struct snd_pcm *pcm)
632
{
633
struct snd_emu8000 *emu = pcm->private_data;
634
emu->pcm = NULL;
635
}
636
637
int 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
}
654
655