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