~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/isa/sb/emu8000_pcm.c

Version: ~ [ linux-5.4-rc7 ] ~ [ linux-5.3.10 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.83 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.153 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.200 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.200 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.76 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * pcm emulation on emu8000 wavetable
  3  *
  4  *  Copyright (C) 2002 Takashi Iwai <tiwai@suse.de>
  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         mod_timer(&rec->timer, jiffies + 1);
211 
212         /* update period */
213         if (rec->period_pos >= (int)rec->period_size) {
214                 rec->period_pos %= rec->period_size;
215                 spin_unlock(&rec->timer_lock);
216                 snd_pcm_period_elapsed(rec->substream);
217                 return;
218         }
219         spin_unlock(&rec->timer_lock);
220 }
221 
222 
223 /*
224  * open pcm
225  * creating an instance here
226  */
227 static int emu8k_pcm_open(struct snd_pcm_substream *subs)
228 {
229         struct snd_emu8000 *emu = snd_pcm_substream_chip(subs);
230         struct snd_emu8k_pcm *rec;
231         struct snd_pcm_runtime *runtime = subs->runtime;
232 
233         rec = kzalloc(sizeof(*rec), GFP_KERNEL);
234         if (! rec)
235                 return -ENOMEM;
236 
237         rec->emu = emu;
238         rec->substream = subs;
239         runtime->private_data = rec;
240 
241         spin_lock_init(&rec->timer_lock);
242         setup_timer(&rec->timer, emu8k_pcm_timer_func, (unsigned long)rec);
243 
244         runtime->hw = emu8k_pcm_hw;
245         runtime->hw.buffer_bytes_max = emu->mem_size - LOOP_BLANK_SIZE * 3;
246         runtime->hw.period_bytes_max = runtime->hw.buffer_bytes_max / 2;
247 
248         /* use timer to update periods.. (specified in msec) */
249         snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
250                                      (1000000 + HZ - 1) / HZ, UINT_MAX);
251 
252         return 0;
253 }
254 
255 static int emu8k_pcm_close(struct snd_pcm_substream *subs)
256 {
257         struct snd_emu8k_pcm *rec = subs->runtime->private_data;
258         kfree(rec);
259         subs->runtime->private_data = NULL;
260         return 0;
261 }
262 
263 /*
264  * calculate pitch target
265  */
266 static int calc_pitch_target(int pitch)
267 {
268         int ptarget = 1 << (pitch >> 12);
269         if (pitch & 0x800) ptarget += (ptarget * 0x102e) / 0x2710;
270         if (pitch & 0x400) ptarget += (ptarget * 0x764) / 0x2710;
271         if (pitch & 0x200) ptarget += (ptarget * 0x389) / 0x2710;
272         ptarget += (ptarget >> 1);
273         if (ptarget > 0xffff) ptarget = 0xffff;
274         return ptarget;
275 }
276 
277 /*
278  * set up the voice
279  */
280 static void setup_voice(struct snd_emu8k_pcm *rec, int ch)
281 {
282         struct snd_emu8000 *hw = rec->emu;
283         unsigned int temp;
284 
285         /* channel to be silent and idle */
286         EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080);
287         EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF);
288         EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF);
289         EMU8000_PTRX_WRITE(hw, ch, 0);
290         EMU8000_CPF_WRITE(hw, ch, 0);
291 
292         /* pitch offset */
293         EMU8000_IP_WRITE(hw, ch, rec->pitch);
294         /* set envelope parameters */
295         EMU8000_ENVVAL_WRITE(hw, ch, 0x8000);
296         EMU8000_ATKHLD_WRITE(hw, ch, 0x7f7f);
297         EMU8000_DCYSUS_WRITE(hw, ch, 0x7f7f);
298         EMU8000_ENVVOL_WRITE(hw, ch, 0x8000);
299         EMU8000_ATKHLDV_WRITE(hw, ch, 0x7f7f);
300         /* decay/sustain parameter for volume envelope is used
301            for triggerg the voice */
302         /* modulation envelope heights */
303         EMU8000_PEFE_WRITE(hw, ch, 0x0);
304         /* lfo1/2 delay */
305         EMU8000_LFO1VAL_WRITE(hw, ch, 0x8000);
306         EMU8000_LFO2VAL_WRITE(hw, ch, 0x8000);
307         /* lfo1 pitch & cutoff shift */
308         EMU8000_FMMOD_WRITE(hw, ch, 0);
309         /* lfo1 volume & freq */
310         EMU8000_TREMFRQ_WRITE(hw, ch, 0);
311         /* lfo2 pitch & freq */
312         EMU8000_FM2FRQ2_WRITE(hw, ch, 0);
313         /* pan & loop start */
314         temp = rec->panning[ch];
315         temp = (temp <<24) | ((unsigned int)rec->loop_start[ch] - 1);
316         EMU8000_PSST_WRITE(hw, ch, temp);
317         /* chorus & loop end (chorus 8bit, MSB) */
318         temp = 0; // chorus
319         temp = (temp << 24) | ((unsigned int)rec->loop_start[ch] + rec->buf_size - 1);
320         EMU8000_CSL_WRITE(hw, ch, temp);
321         /* Q & current address (Q 4bit value, MSB) */
322         temp = 0; // filterQ
323         temp = (temp << 28) | ((unsigned int)rec->loop_start[ch] - 1);
324         EMU8000_CCCA_WRITE(hw, ch, temp);
325         /* clear unknown registers */
326         EMU8000_00A0_WRITE(hw, ch, 0);
327         EMU8000_0080_WRITE(hw, ch, 0);
328 }
329 
330 /*
331  * trigger the voice
332  */
333 static void start_voice(struct snd_emu8k_pcm *rec, int ch)
334 {
335         unsigned long flags;
336         struct snd_emu8000 *hw = rec->emu;
337         unsigned int temp, aux;
338         int pt = calc_pitch_target(rec->pitch);
339 
340         /* cutoff and volume */
341         EMU8000_IFATN_WRITE(hw, ch, 0xff00);
342         EMU8000_VTFT_WRITE(hw, ch, 0xffff);
343         EMU8000_CVCF_WRITE(hw, ch, 0xffff);
344         /* trigger envelope */
345         EMU8000_DCYSUSV_WRITE(hw, ch, 0x7f7f);
346         /* set reverb and pitch target */
347         temp = 0; // reverb
348         if (rec->panning[ch] == 0)
349                 aux = 0xff;
350         else
351                 aux = (-rec->panning[ch]) & 0xff;
352         temp = (temp << 8) | (pt << 16) | aux;
353         EMU8000_PTRX_WRITE(hw, ch, temp);
354         EMU8000_CPF_WRITE(hw, ch, pt << 16);
355 
356         /* start timer */
357         spin_lock_irqsave(&rec->timer_lock, flags);
358         if (! rec->timer_running) {
359                 mod_timer(&rec->timer, jiffies + 1);
360                 rec->timer_running = 1;
361         }
362         spin_unlock_irqrestore(&rec->timer_lock, flags);
363 }
364 
365 /*
366  * stop the voice immediately
367  */
368 static void stop_voice(struct snd_emu8k_pcm *rec, int ch)
369 {
370         unsigned long flags;
371         struct snd_emu8000 *hw = rec->emu;
372 
373         EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F);
374 
375         /* stop timer */
376         spin_lock_irqsave(&rec->timer_lock, flags);
377         if (rec->timer_running) {
378                 del_timer(&rec->timer);
379                 rec->timer_running = 0;
380         }
381         spin_unlock_irqrestore(&rec->timer_lock, flags);
382 }
383 
384 static int emu8k_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
385 {
386         struct snd_emu8k_pcm *rec = subs->runtime->private_data;
387         int ch;
388 
389         switch (cmd) {
390         case SNDRV_PCM_TRIGGER_START:
391                 for (ch = 0; ch < rec->voices; ch++)
392                         start_voice(rec, ch);
393                 rec->running = 1;
394                 break;
395         case SNDRV_PCM_TRIGGER_STOP:
396                 rec->running = 0;
397                 for (ch = 0; ch < rec->voices; ch++)
398                         stop_voice(rec, ch);
399                 break;
400         default:
401                 return -EINVAL;
402         }
403         return 0;
404 }
405 
406 
407 /*
408  * copy / silence ops
409  */
410 
411 /*
412  * this macro should be inserted in the copy/silence loops
413  * to reduce the latency.  without this, the system will hang up
414  * during the whole loop.
415  */
416 #define CHECK_SCHEDULER() \
417 do { \
418         cond_resched();\
419         if (signal_pending(current))\
420                 return -EAGAIN;\
421 } while (0)
422 
423 
424 #ifdef USE_NONINTERLEAVE
425 /* copy one channel block */
426 static int emu8k_transfer_block(struct snd_emu8000 *emu, int offset, unsigned short *buf, int count)
427 {
428         EMU8000_SMALW_WRITE(emu, offset);
429         while (count > 0) {
430                 unsigned short sval;
431                 CHECK_SCHEDULER();
432                 if (get_user(sval, buf))
433                         return -EFAULT;
434                 EMU8000_SMLD_WRITE(emu, sval);
435                 buf++;
436                 count--;
437         }
438         return 0;
439 }
440 
441 static int emu8k_pcm_copy(struct snd_pcm_substream *subs,
442                           int voice,
443                           snd_pcm_uframes_t pos,
444                           void *src,
445                           snd_pcm_uframes_t count)
446 {
447         struct snd_emu8k_pcm *rec = subs->runtime->private_data;
448         struct snd_emu8000 *emu = rec->emu;
449 
450         snd_emu8000_write_wait(emu, 1);
451         if (voice == -1) {
452                 unsigned short *buf = src;
453                 int i, err;
454                 count /= rec->voices;
455                 for (i = 0; i < rec->voices; i++) {
456                         err = emu8k_transfer_block(emu, pos + rec->loop_start[i], buf, count);
457                         if (err < 0)
458                                 return err;
459                         buf += count;
460                 }
461                 return 0;
462         } else {
463                 return emu8k_transfer_block(emu, pos + rec->loop_start[voice], src, count);
464         }
465 }
466 
467 /* make a channel block silence */
468 static int emu8k_silence_block(struct snd_emu8000 *emu, int offset, int count)
469 {
470         EMU8000_SMALW_WRITE(emu, offset);
471         while (count > 0) {
472                 CHECK_SCHEDULER();
473                 EMU8000_SMLD_WRITE(emu, 0);
474                 count--;
475         }
476         return 0;
477 }
478 
479 static int emu8k_pcm_silence(struct snd_pcm_substream *subs,
480                              int voice,
481                              snd_pcm_uframes_t pos,
482                              snd_pcm_uframes_t count)
483 {
484         struct snd_emu8k_pcm *rec = subs->runtime->private_data;
485         struct snd_emu8000 *emu = rec->emu;
486 
487         snd_emu8000_write_wait(emu, 1);
488         if (voice == -1 && rec->voices == 1)
489                 voice = 0;
490         if (voice == -1) {
491                 int err;
492                 err = emu8k_silence_block(emu, pos + rec->loop_start[0], count / 2);
493                 if (err < 0)
494                         return err;
495                 return emu8k_silence_block(emu, pos + rec->loop_start[1], count / 2);
496         } else {
497                 return emu8k_silence_block(emu, pos + rec->loop_start[voice], count);
498         }
499 }
500 
501 #else /* interleave */
502 
503 /*
504  * copy the interleaved data can be done easily by using
505  * DMA "left" and "right" channels on emu8k engine.
506  */
507 static int emu8k_pcm_copy(struct snd_pcm_substream *subs,
508                           int voice,
509                           snd_pcm_uframes_t pos,
510                           void __user *src,
511                           snd_pcm_uframes_t count)
512 {
513         struct snd_emu8k_pcm *rec = subs->runtime->private_data;
514         struct snd_emu8000 *emu = rec->emu;
515         unsigned short __user *buf = src;
516 
517         snd_emu8000_write_wait(emu, 1);
518         EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]);
519         if (rec->voices > 1)
520                 EMU8000_SMARW_WRITE(emu, pos + rec->loop_start[1]);
521 
522         while (count-- > 0) {
523                 unsigned short sval;
524                 CHECK_SCHEDULER();
525                 if (get_user(sval, buf))
526                         return -EFAULT;
527                 EMU8000_SMLD_WRITE(emu, sval);
528                 buf++;
529                 if (rec->voices > 1) {
530                         CHECK_SCHEDULER();
531                         if (get_user(sval, buf))
532                                 return -EFAULT;
533                         EMU8000_SMRD_WRITE(emu, sval);
534                         buf++;
535                 }
536         }
537         return 0;
538 }
539 
540 static int emu8k_pcm_silence(struct snd_pcm_substream *subs,
541                              int voice,
542                              snd_pcm_uframes_t pos,
543                              snd_pcm_uframes_t count)
544 {
545         struct snd_emu8k_pcm *rec = subs->runtime->private_data;
546         struct snd_emu8000 *emu = rec->emu;
547 
548         snd_emu8000_write_wait(emu, 1);
549         EMU8000_SMALW_WRITE(emu, rec->loop_start[0] + pos);
550         if (rec->voices > 1)
551                 EMU8000_SMARW_WRITE(emu, rec->loop_start[1] + pos);
552         while (count-- > 0) {
553                 CHECK_SCHEDULER();
554                 EMU8000_SMLD_WRITE(emu, 0);
555                 if (rec->voices > 1) {
556                         CHECK_SCHEDULER();
557                         EMU8000_SMRD_WRITE(emu, 0);
558                 }
559         }
560         return 0;
561 }
562 #endif
563 
564 
565 /*
566  * allocate a memory block
567  */
568 static int emu8k_pcm_hw_params(struct snd_pcm_substream *subs,
569                                struct snd_pcm_hw_params *hw_params)
570 {
571         struct snd_emu8k_pcm *rec = subs->runtime->private_data;
572 
573         if (rec->block) {
574                 /* reallocation - release the old block */
575                 snd_util_mem_free(rec->emu->memhdr, rec->block);
576                 rec->block = NULL;
577         }
578 
579         rec->allocated_bytes = params_buffer_bytes(hw_params) + LOOP_BLANK_SIZE * 4;
580         rec->block = snd_util_mem_alloc(rec->emu->memhdr, rec->allocated_bytes);
581         if (! rec->block)
582                 return -ENOMEM;
583         rec->offset = EMU8000_DRAM_OFFSET + (rec->block->offset >> 1); /* in word */
584         /* at least dma_bytes must be set for non-interleaved mode */
585         subs->dma_buffer.bytes = params_buffer_bytes(hw_params);
586 
587         return 0;
588 }
589 
590 /*
591  * free the memory block
592  */
593 static int emu8k_pcm_hw_free(struct snd_pcm_substream *subs)
594 {
595         struct snd_emu8k_pcm *rec = subs->runtime->private_data;
596 
597         if (rec->block) {
598                 int ch;
599                 for (ch = 0; ch < rec->voices; ch++)
600                         stop_voice(rec, ch); // to be sure
601                 if (rec->dram_opened)
602                         emu8k_close_dram(rec->emu);
603                 snd_util_mem_free(rec->emu->memhdr, rec->block);
604                 rec->block = NULL;
605         }
606         return 0;
607 }
608 
609 /*
610  */
611 static int emu8k_pcm_prepare(struct snd_pcm_substream *subs)
612 {
613         struct snd_emu8k_pcm *rec = subs->runtime->private_data;
614 
615         rec->pitch = 0xe000 + calc_rate_offset(subs->runtime->rate);
616         rec->last_ptr = 0;
617         rec->period_pos = 0;
618 
619         rec->buf_size = subs->runtime->buffer_size;
620         rec->period_size = subs->runtime->period_size;
621         rec->voices = subs->runtime->channels;
622         rec->loop_start[0] = rec->offset + LOOP_BLANK_SIZE;
623         if (rec->voices > 1)
624                 rec->loop_start[1] = rec->loop_start[0] + rec->buf_size + LOOP_BLANK_SIZE;
625         if (rec->voices > 1) {
626                 rec->panning[0] = 0xff;
627                 rec->panning[1] = 0x00;
628         } else
629                 rec->panning[0] = 0x80;
630 
631         if (! rec->dram_opened) {
632                 int err, i, ch;
633 
634                 snd_emux_terminate_all(rec->emu->emu);
635                 if ((err = emu8k_open_dram_for_pcm(rec->emu, rec->voices)) != 0)
636                         return err;
637                 rec->dram_opened = 1;
638 
639                 /* clear loop blanks */
640                 snd_emu8000_write_wait(rec->emu, 0);
641                 EMU8000_SMALW_WRITE(rec->emu, rec->offset);
642                 for (i = 0; i < LOOP_BLANK_SIZE; i++)
643                         EMU8000_SMLD_WRITE(rec->emu, 0);
644                 for (ch = 0; ch < rec->voices; ch++) {
645                         EMU8000_SMALW_WRITE(rec->emu, rec->loop_start[ch] + rec->buf_size);
646                         for (i = 0; i < LOOP_BLANK_SIZE; i++)
647                                 EMU8000_SMLD_WRITE(rec->emu, 0);
648                 }
649         }
650 
651         setup_voice(rec, 0);
652         if (rec->voices > 1)
653                 setup_voice(rec, 1);
654         return 0;
655 }
656 
657 static snd_pcm_uframes_t emu8k_pcm_pointer(struct snd_pcm_substream *subs)
658 {
659         struct snd_emu8k_pcm *rec = subs->runtime->private_data;
660         if (rec->running)
661                 return emu8k_get_curpos(rec, 0);
662         return 0;
663 }
664 
665 
666 static struct snd_pcm_ops emu8k_pcm_ops = {
667         .open =         emu8k_pcm_open,
668         .close =        emu8k_pcm_close,
669         .ioctl =        snd_pcm_lib_ioctl,
670         .hw_params =    emu8k_pcm_hw_params,
671         .hw_free =      emu8k_pcm_hw_free,
672         .prepare =      emu8k_pcm_prepare,
673         .trigger =      emu8k_pcm_trigger,
674         .pointer =      emu8k_pcm_pointer,
675         .copy =         emu8k_pcm_copy,
676         .silence =      emu8k_pcm_silence,
677 };
678 
679 
680 static void snd_emu8000_pcm_free(struct snd_pcm *pcm)
681 {
682         struct snd_emu8000 *emu = pcm->private_data;
683         emu->pcm = NULL;
684 }
685 
686 int snd_emu8000_pcm_new(struct snd_card *card, struct snd_emu8000 *emu, int index)
687 {
688         struct snd_pcm *pcm;
689         int err;
690 
691         if ((err = snd_pcm_new(card, "Emu8000 PCM", index, 1, 0, &pcm)) < 0)
692                 return err;
693         pcm->private_data = emu;
694         pcm->private_free = snd_emu8000_pcm_free;
695         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &emu8k_pcm_ops);
696         emu->pcm = pcm;
697 
698         snd_device_register(card, pcm);
699 
700         return 0;
701 }
702 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp