656 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * pcm emulation on emu8000 wavetable4 *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 mode18 */19/* #define USE_NONINTERLEAVE */20 21/* NOTE: for using the non-interleaved mode with alsa-lib, you have to set22 * mmap_emulation flag to 1 in your .asoundrc, such like23 *24 * pcm.emu8k {25 * type plug26 * slave.pcm {27 * type hw28 * card 029 * device 130 * mmap_emulation 131 * }32 * }33 *34 * besides, for the time being, the non-interleaved mode doesn't work well on35 * 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 862 63 64/*65 * open up channels for the simultaneous data transfer and playback66 */67static int68emu8k_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_NONINTERLEAVE82 if (channels > 1 && (i & 1) != 0)83 mode |= EMU8000_RAM_RIGHT;84#endif85 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 void104snd_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 channels117 */118static void119emu8k_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 4096137 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_NONINTERLEAVE149 .info = SNDRV_PCM_INFO_NONINTERLEAVED,150#else151 .info = SNDRV_PCM_INFO_INTERLEAVED,152#endif153 .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 register170 */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 handler181 * 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 = from_timer(rec, t, timer);186 int ptr, delta;187 188 spin_lock(&rec->timer_lock);189 /* update the current pointer */190 ptr = emu8k_get_curpos(rec, 0);191 if (ptr < rec->last_ptr)192 delta = ptr + rec->buf_size - rec->last_ptr;193 else194 delta = ptr - rec->last_ptr;195 rec->period_pos += delta;196 rec->last_ptr = ptr;197 198 /* reprogram timer */199 mod_timer(&rec->timer, jiffies + 1);200 201 /* update period */202 if (rec->period_pos >= (int)rec->period_size) {203 rec->period_pos %= rec->period_size;204 spin_unlock(&rec->timer_lock);205 snd_pcm_period_elapsed(rec->substream);206 return;207 }208 spin_unlock(&rec->timer_lock);209}210 211 212/*213 * open pcm214 * creating an instance here215 */216static int emu8k_pcm_open(struct snd_pcm_substream *subs)217{218 struct snd_emu8000 *emu = snd_pcm_substream_chip(subs);219 struct snd_emu8k_pcm *rec;220 struct snd_pcm_runtime *runtime = subs->runtime;221 222 rec = kzalloc(sizeof(*rec), GFP_KERNEL);223 if (! rec)224 return -ENOMEM;225 226 rec->emu = emu;227 rec->substream = subs;228 runtime->private_data = rec;229 230 spin_lock_init(&rec->timer_lock);231 timer_setup(&rec->timer, emu8k_pcm_timer_func, 0);232 233 runtime->hw = emu8k_pcm_hw;234 runtime->hw.buffer_bytes_max = emu->mem_size - LOOP_BLANK_SIZE * 3;235 runtime->hw.period_bytes_max = runtime->hw.buffer_bytes_max / 2;236 237 /* use timer to update periods.. (specified in msec) */238 snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME,239 DIV_ROUND_UP(1000000, HZ), UINT_MAX);240 241 return 0;242}243 244static int emu8k_pcm_close(struct snd_pcm_substream *subs)245{246 struct snd_emu8k_pcm *rec = subs->runtime->private_data;247 kfree(rec);248 subs->runtime->private_data = NULL;249 return 0;250}251 252/*253 * calculate pitch target254 */255static int calc_pitch_target(int pitch)256{257 int ptarget = 1 << (pitch >> 12);258 if (pitch & 0x800) ptarget += (ptarget * 0x102e) / 0x2710;259 if (pitch & 0x400) ptarget += (ptarget * 0x764) / 0x2710;260 if (pitch & 0x200) ptarget += (ptarget * 0x389) / 0x2710;261 ptarget += (ptarget >> 1);262 if (ptarget > 0xffff) ptarget = 0xffff;263 return ptarget;264}265 266/*267 * set up the voice268 */269static void setup_voice(struct snd_emu8k_pcm *rec, int ch)270{271 struct snd_emu8000 *hw = rec->emu;272 unsigned int temp;273 274 /* channel to be silent and idle */275 EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080);276 EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF);277 EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF);278 EMU8000_PTRX_WRITE(hw, ch, 0);279 EMU8000_CPF_WRITE(hw, ch, 0);280 281 /* pitch offset */282 EMU8000_IP_WRITE(hw, ch, rec->pitch);283 /* set envelope parameters */284 EMU8000_ENVVAL_WRITE(hw, ch, 0x8000);285 EMU8000_ATKHLD_WRITE(hw, ch, 0x7f7f);286 EMU8000_DCYSUS_WRITE(hw, ch, 0x7f7f);287 EMU8000_ENVVOL_WRITE(hw, ch, 0x8000);288 EMU8000_ATKHLDV_WRITE(hw, ch, 0x7f7f);289 /* decay/sustain parameter for volume envelope is used290 for triggerg the voice */291 /* modulation envelope heights */292 EMU8000_PEFE_WRITE(hw, ch, 0x0);293 /* lfo1/2 delay */294 EMU8000_LFO1VAL_WRITE(hw, ch, 0x8000);295 EMU8000_LFO2VAL_WRITE(hw, ch, 0x8000);296 /* lfo1 pitch & cutoff shift */297 EMU8000_FMMOD_WRITE(hw, ch, 0);298 /* lfo1 volume & freq */299 EMU8000_TREMFRQ_WRITE(hw, ch, 0);300 /* lfo2 pitch & freq */301 EMU8000_FM2FRQ2_WRITE(hw, ch, 0);302 /* pan & loop start */303 temp = rec->panning[ch];304 temp = (temp <<24) | ((unsigned int)rec->loop_start[ch] - 1);305 EMU8000_PSST_WRITE(hw, ch, temp);306 /* chorus & loop end (chorus 8bit, MSB) */307 temp = 0; // chorus308 temp = (temp << 24) | ((unsigned int)rec->loop_start[ch] + rec->buf_size - 1);309 EMU8000_CSL_WRITE(hw, ch, temp);310 /* Q & current address (Q 4bit value, MSB) */311 temp = 0; // filterQ312 temp = (temp << 28) | ((unsigned int)rec->loop_start[ch] - 1);313 EMU8000_CCCA_WRITE(hw, ch, temp);314 /* clear unknown registers */315 EMU8000_00A0_WRITE(hw, ch, 0);316 EMU8000_0080_WRITE(hw, ch, 0);317}318 319/*320 * trigger the voice321 */322static void start_voice(struct snd_emu8k_pcm *rec, int ch)323{324 unsigned long flags;325 struct snd_emu8000 *hw = rec->emu;326 unsigned int temp, aux;327 int pt = calc_pitch_target(rec->pitch);328 329 /* cutoff and volume */330 EMU8000_IFATN_WRITE(hw, ch, 0xff00);331 EMU8000_VTFT_WRITE(hw, ch, 0xffff);332 EMU8000_CVCF_WRITE(hw, ch, 0xffff);333 /* trigger envelope */334 EMU8000_DCYSUSV_WRITE(hw, ch, 0x7f7f);335 /* set reverb and pitch target */336 temp = 0; // reverb337 if (rec->panning[ch] == 0)338 aux = 0xff;339 else340 aux = (-rec->panning[ch]) & 0xff;341 temp = (temp << 8) | (pt << 16) | aux;342 EMU8000_PTRX_WRITE(hw, ch, temp);343 EMU8000_CPF_WRITE(hw, ch, pt << 16);344 345 /* start timer */346 spin_lock_irqsave(&rec->timer_lock, flags);347 if (! rec->timer_running) {348 mod_timer(&rec->timer, jiffies + 1);349 rec->timer_running = 1;350 }351 spin_unlock_irqrestore(&rec->timer_lock, flags);352}353 354/*355 * stop the voice immediately356 */357static void stop_voice(struct snd_emu8k_pcm *rec, int ch)358{359 unsigned long flags;360 struct snd_emu8000 *hw = rec->emu;361 362 EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F);363 364 /* stop timer */365 spin_lock_irqsave(&rec->timer_lock, flags);366 if (rec->timer_running) {367 del_timer(&rec->timer);368 rec->timer_running = 0;369 }370 spin_unlock_irqrestore(&rec->timer_lock, flags);371}372 373static int emu8k_pcm_trigger(struct snd_pcm_substream *subs, int cmd)374{375 struct snd_emu8k_pcm *rec = subs->runtime->private_data;376 int ch;377 378 switch (cmd) {379 case SNDRV_PCM_TRIGGER_START:380 for (ch = 0; ch < rec->voices; ch++)381 start_voice(rec, ch);382 rec->running = 1;383 break;384 case SNDRV_PCM_TRIGGER_STOP:385 rec->running = 0;386 for (ch = 0; ch < rec->voices; ch++)387 stop_voice(rec, ch);388 break;389 default:390 return -EINVAL;391 }392 return 0;393}394 395 396/*397 * copy / silence ops398 */399 400/*401 * this macro should be inserted in the copy/silence loops402 * to reduce the latency. without this, the system will hang up403 * during the whole loop.404 */405#define CHECK_SCHEDULER() \406do { \407 cond_resched();\408 if (signal_pending(current))\409 return -EAGAIN;\410} while (0)411 412#define GET_VAL(sval, iter) \413 do { \414 if (!iter) \415 sval = 0; \416 else if (copy_from_iter(&sval, 2, iter) != 2) \417 return -EFAULT; \418 } while (0)419 420#ifdef USE_NONINTERLEAVE421 422#define LOOP_WRITE(rec, offset, iter, count) \423 do { \424 struct snd_emu8000 *emu = (rec)->emu; \425 snd_emu8000_write_wait(emu, 1); \426 EMU8000_SMALW_WRITE(emu, offset); \427 while (count > 0) { \428 unsigned short sval; \429 CHECK_SCHEDULER(); \430 GET_VAL(sval, iter); \431 EMU8000_SMLD_WRITE(emu, sval); \432 count--; \433 } \434 } while (0)435 436/* copy one channel block */437static int emu8k_pcm_copy(struct snd_pcm_substream *subs,438 int voice, unsigned long pos,439 struct iov_iter *src, unsigned long count)440{441 struct snd_emu8k_pcm *rec = subs->runtime->private_data;442 443 /* convert to word unit */444 pos = (pos << 1) + rec->loop_start[voice];445 count <<= 1;446 LOOP_WRITE(rec, pos, src, count);447 return 0;448}449 450/* make a channel block silence */451static int emu8k_pcm_silence(struct snd_pcm_substream *subs,452 int voice, unsigned long pos, unsigned long count)453{454 struct snd_emu8k_pcm *rec = subs->runtime->private_data;455 456 /* convert to word unit */457 pos = (pos << 1) + rec->loop_start[voice];458 count <<= 1;459 LOOP_WRITE(rec, pos, NULL, count);460 return 0;461}462 463#else /* interleave */464 465#define LOOP_WRITE(rec, pos, iter, count) \466 do { \467 struct snd_emu8000 *emu = rec->emu; \468 snd_emu8000_write_wait(emu, 1); \469 EMU8000_SMALW_WRITE(emu, pos + rec->loop_start[0]); \470 if (rec->voices > 1) \471 EMU8000_SMARW_WRITE(emu, pos + rec->loop_start[1]); \472 while (count > 0) { \473 unsigned short sval; \474 CHECK_SCHEDULER(); \475 GET_VAL(sval, iter); \476 EMU8000_SMLD_WRITE(emu, sval); \477 if (rec->voices > 1) { \478 CHECK_SCHEDULER(); \479 GET_VAL(sval, iter); \480 EMU8000_SMRD_WRITE(emu, sval); \481 } \482 count--; \483 } \484 } while (0)485 486 487/*488 * copy the interleaved data can be done easily by using489 * DMA "left" and "right" channels on emu8k engine.490 */491static int emu8k_pcm_copy(struct snd_pcm_substream *subs,492 int voice, unsigned long pos,493 struct iov_iter *src, unsigned long count)494{495 struct snd_emu8k_pcm *rec = subs->runtime->private_data;496 497 /* convert to frames */498 pos = bytes_to_frames(subs->runtime, pos);499 count = bytes_to_frames(subs->runtime, count);500 LOOP_WRITE(rec, pos, src, count);501 return 0;502}503 504static int emu8k_pcm_silence(struct snd_pcm_substream *subs,505 int voice, unsigned long pos, unsigned long count)506{507 struct snd_emu8k_pcm *rec = subs->runtime->private_data;508 509 /* convert to frames */510 pos = bytes_to_frames(subs->runtime, pos);511 count = bytes_to_frames(subs->runtime, count);512 LOOP_WRITE(rec, pos, NULL, count);513 return 0;514}515#endif516 517 518/*519 * allocate a memory block520 */521static int emu8k_pcm_hw_params(struct snd_pcm_substream *subs,522 struct snd_pcm_hw_params *hw_params)523{524 struct snd_emu8k_pcm *rec = subs->runtime->private_data;525 526 if (rec->block) {527 /* reallocation - release the old block */528 snd_util_mem_free(rec->emu->memhdr, rec->block);529 rec->block = NULL;530 }531 532 rec->allocated_bytes = params_buffer_bytes(hw_params) + LOOP_BLANK_SIZE * 4;533 rec->block = snd_util_mem_alloc(rec->emu->memhdr, rec->allocated_bytes);534 if (! rec->block)535 return -ENOMEM;536 rec->offset = EMU8000_DRAM_OFFSET + (rec->block->offset >> 1); /* in word */537 /* at least dma_bytes must be set for non-interleaved mode */538 subs->dma_buffer.bytes = params_buffer_bytes(hw_params);539 540 return 0;541}542 543/*544 * free the memory block545 */546static int emu8k_pcm_hw_free(struct snd_pcm_substream *subs)547{548 struct snd_emu8k_pcm *rec = subs->runtime->private_data;549 550 if (rec->block) {551 int ch;552 for (ch = 0; ch < rec->voices; ch++)553 stop_voice(rec, ch); // to be sure554 if (rec->dram_opened)555 emu8k_close_dram(rec->emu);556 snd_util_mem_free(rec->emu->memhdr, rec->block);557 rec->block = NULL;558 }559 return 0;560}561 562/*563 */564static int emu8k_pcm_prepare(struct snd_pcm_substream *subs)565{566 struct snd_emu8k_pcm *rec = subs->runtime->private_data;567 568 rec->pitch = 0xe000 + calc_rate_offset(subs->runtime->rate);569 rec->last_ptr = 0;570 rec->period_pos = 0;571 572 rec->buf_size = subs->runtime->buffer_size;573 rec->period_size = subs->runtime->period_size;574 rec->voices = subs->runtime->channels;575 rec->loop_start[0] = rec->offset + LOOP_BLANK_SIZE;576 if (rec->voices > 1)577 rec->loop_start[1] = rec->loop_start[0] + rec->buf_size + LOOP_BLANK_SIZE;578 if (rec->voices > 1) {579 rec->panning[0] = 0xff;580 rec->panning[1] = 0x00;581 } else582 rec->panning[0] = 0x80;583 584 if (! rec->dram_opened) {585 int err, i, ch;586 587 snd_emux_terminate_all(rec->emu->emu);588 err = emu8k_open_dram_for_pcm(rec->emu, rec->voices);589 if (err)590 return err;591 rec->dram_opened = 1;592 593 /* clear loop blanks */594 snd_emu8000_write_wait(rec->emu, 0);595 EMU8000_SMALW_WRITE(rec->emu, rec->offset);596 for (i = 0; i < LOOP_BLANK_SIZE; i++)597 EMU8000_SMLD_WRITE(rec->emu, 0);598 for (ch = 0; ch < rec->voices; ch++) {599 EMU8000_SMALW_WRITE(rec->emu, rec->loop_start[ch] + rec->buf_size);600 for (i = 0; i < LOOP_BLANK_SIZE; i++)601 EMU8000_SMLD_WRITE(rec->emu, 0);602 }603 }604 605 setup_voice(rec, 0);606 if (rec->voices > 1)607 setup_voice(rec, 1);608 return 0;609}610 611static snd_pcm_uframes_t emu8k_pcm_pointer(struct snd_pcm_substream *subs)612{613 struct snd_emu8k_pcm *rec = subs->runtime->private_data;614 if (rec->running)615 return emu8k_get_curpos(rec, 0);616 return 0;617}618 619 620static const struct snd_pcm_ops emu8k_pcm_ops = {621 .open = emu8k_pcm_open,622 .close = emu8k_pcm_close,623 .hw_params = emu8k_pcm_hw_params,624 .hw_free = emu8k_pcm_hw_free,625 .prepare = emu8k_pcm_prepare,626 .trigger = emu8k_pcm_trigger,627 .pointer = emu8k_pcm_pointer,628 .copy = emu8k_pcm_copy,629 .fill_silence = emu8k_pcm_silence,630};631 632 633static void snd_emu8000_pcm_free(struct snd_pcm *pcm)634{635 struct snd_emu8000 *emu = pcm->private_data;636 emu->pcm = NULL;637}638 639int snd_emu8000_pcm_new(struct snd_card *card, struct snd_emu8000 *emu, int index)640{641 struct snd_pcm *pcm;642 int err;643 644 err = snd_pcm_new(card, "Emu8000 PCM", index, 1, 0, &pcm);645 if (err < 0)646 return err;647 pcm->private_data = emu;648 pcm->private_free = snd_emu8000_pcm_free;649 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &emu8k_pcm_ops);650 emu->pcm = pcm;651 652 snd_device_register(card, pcm);653 654 return 0;655}656