linux/sound/isa/sb/emu8000_patch.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  Patch routines for the emu8000 (AWE32/64)
   4 *
   5 *  Copyright (C) 1999 Steve Ratcliffe
   6 *  Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
   7 */
   8
   9#include "emu8000_local.h"
  10
  11#include <linux/sched/signal.h>
  12#include <linux/uaccess.h>
  13#include <linux/moduleparam.h>
  14
  15static int emu8000_reset_addr;
  16module_param(emu8000_reset_addr, int, 0444);
  17MODULE_PARM_DESC(emu8000_reset_addr, "reset write address at each time (makes slowdown)");
  18
  19
  20/*
  21 * Open up channels.
  22 */
  23static int
  24snd_emu8000_open_dma(struct snd_emu8000 *emu, int write)
  25{
  26        int i;
  27
  28        /* reserve all 30 voices for loading */
  29        for (i = 0; i < EMU8000_DRAM_VOICES; i++) {
  30                snd_emux_lock_voice(emu->emu, i);
  31                snd_emu8000_dma_chan(emu, i, write);
  32        }
  33
  34        /* assign voice 31 and 32 to ROM */
  35        EMU8000_VTFT_WRITE(emu, 30, 0);
  36        EMU8000_PSST_WRITE(emu, 30, 0x1d8);
  37        EMU8000_CSL_WRITE(emu, 30, 0x1e0);
  38        EMU8000_CCCA_WRITE(emu, 30, 0x1d8);
  39        EMU8000_VTFT_WRITE(emu, 31, 0);
  40        EMU8000_PSST_WRITE(emu, 31, 0x1d8);
  41        EMU8000_CSL_WRITE(emu, 31, 0x1e0);
  42        EMU8000_CCCA_WRITE(emu, 31, 0x1d8);
  43
  44        return 0;
  45}
  46
  47/*
  48 * Close all dram channels.
  49 */
  50static void
  51snd_emu8000_close_dma(struct snd_emu8000 *emu)
  52{
  53        int i;
  54
  55        for (i = 0; i < EMU8000_DRAM_VOICES; i++) {
  56                snd_emu8000_dma_chan(emu, i, EMU8000_RAM_CLOSE);
  57                snd_emux_unlock_voice(emu->emu, i);
  58        }
  59}
  60
  61/*
  62 */
  63
  64#define BLANK_LOOP_START        4
  65#define BLANK_LOOP_END          8
  66#define BLANK_LOOP_SIZE         12
  67#define BLANK_HEAD_SIZE         48
  68
  69/*
  70 * Read a word from userland, taking care of conversions from
  71 * 8bit samples etc.
  72 */
  73static unsigned short
  74read_word(const void __user *buf, int offset, int mode)
  75{
  76        unsigned short c;
  77        if (mode & SNDRV_SFNT_SAMPLE_8BITS) {
  78                unsigned char cc;
  79                get_user(cc, (unsigned char __user *)buf + offset);
  80                c = cc << 8; /* convert 8bit -> 16bit */
  81        } else {
  82#ifdef SNDRV_LITTLE_ENDIAN
  83                get_user(c, (unsigned short __user *)buf + offset);
  84#else
  85                unsigned short cc;
  86                get_user(cc, (unsigned short __user *)buf + offset);
  87                c = swab16(cc);
  88#endif
  89        }
  90        if (mode & SNDRV_SFNT_SAMPLE_UNSIGNED)
  91                c ^= 0x8000; /* unsigned -> signed */
  92        return c;
  93}
  94
  95/*
  96 */
  97static void
  98snd_emu8000_write_wait(struct snd_emu8000 *emu)
  99{
 100        while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
 101                schedule_timeout_interruptible(1);
 102                if (signal_pending(current))
 103                        break;
 104        }
 105}
 106
 107/*
 108 * write sample word data
 109 *
 110 * You should not have to keep resetting the address each time
 111 * as the chip is supposed to step on the next address automatically.
 112 * It mostly does, but during writes of some samples at random it
 113 * completely loses words (every one in 16 roughly but with no
 114 * obvious pattern).
 115 *
 116 * This is therefore much slower than need be, but is at least
 117 * working.
 118 */
 119static inline void
 120write_word(struct snd_emu8000 *emu, int *offset, unsigned short data)
 121{
 122        if (emu8000_reset_addr) {
 123                if (emu8000_reset_addr > 1)
 124                        snd_emu8000_write_wait(emu);
 125                EMU8000_SMALW_WRITE(emu, *offset);
 126        }
 127        EMU8000_SMLD_WRITE(emu, data);
 128        *offset += 1;
 129}
 130
 131/*
 132 * Write the sample to EMU800 memory.  This routine is invoked out of
 133 * the generic soundfont routines as a callback.
 134 */
 135int
 136snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
 137                       struct snd_util_memhdr *hdr,
 138                       const void __user *data, long count)
 139{
 140        int  i;
 141        int  rc;
 142        int  offset;
 143        int  truesize;
 144        int  dram_offset, dram_start;
 145        struct snd_emu8000 *emu;
 146
 147        emu = rec->hw;
 148        if (snd_BUG_ON(!sp))
 149                return -EINVAL;
 150
 151        if (sp->v.size == 0)
 152                return 0;
 153
 154        /* be sure loop points start < end */
 155        if (sp->v.loopstart > sp->v.loopend)
 156                swap(sp->v.loopstart, sp->v.loopend);
 157
 158        /* compute true data size to be loaded */
 159        truesize = sp->v.size;
 160        if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
 161                truesize += sp->v.loopend - sp->v.loopstart;
 162        if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
 163                truesize += BLANK_LOOP_SIZE;
 164
 165        sp->block = snd_util_mem_alloc(hdr, truesize * 2);
 166        if (sp->block == NULL) {
 167                /*snd_printd("EMU8000: out of memory\n");*/
 168                /* not ENOMEM (for compatibility) */
 169                return -ENOSPC;
 170        }
 171
 172        if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) {
 173                if (!access_ok(data, sp->v.size))
 174                        return -EFAULT;
 175        } else {
 176                if (!access_ok(data, sp->v.size * 2))
 177                        return -EFAULT;
 178        }
 179
 180        /* recalculate address offset */
 181        sp->v.end -= sp->v.start;
 182        sp->v.loopstart -= sp->v.start;
 183        sp->v.loopend -= sp->v.start;
 184        sp->v.start = 0;
 185
 186        /* dram position (in word) -- mem_offset is byte */
 187        dram_offset = EMU8000_DRAM_OFFSET + (sp->block->offset >> 1);
 188        dram_start = dram_offset;
 189
 190        /* set the total size (store onto obsolete checksum value) */
 191        sp->v.truesize = truesize * 2; /* in bytes */
 192
 193        snd_emux_terminate_all(emu->emu);
 194        if ((rc = snd_emu8000_open_dma(emu, EMU8000_RAM_WRITE)) != 0)
 195                return rc;
 196
 197        /* Set the address to start writing at */
 198        snd_emu8000_write_wait(emu);
 199        EMU8000_SMALW_WRITE(emu, dram_offset);
 200
 201        /*snd_emu8000_init_fm(emu);*/
 202
 203#if 0
 204        /* first block - write 48 samples for silence */
 205        if (! sp->block->offset) {
 206                for (i = 0; i < BLANK_HEAD_SIZE; i++) {
 207                        write_word(emu, &dram_offset, 0);
 208                }
 209        }
 210#endif
 211
 212        offset = 0;
 213        for (i = 0; i < sp->v.size; i++) {
 214                unsigned short s;
 215
 216                s = read_word(data, offset, sp->v.mode_flags);
 217                offset++;
 218                write_word(emu, &dram_offset, s);
 219
 220                /* we may take too long time in this loop.
 221                 * so give controls back to kernel if needed.
 222                 */
 223                cond_resched();
 224
 225                if (i == sp->v.loopend &&
 226                    (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)))
 227                {
 228                        int looplen = sp->v.loopend - sp->v.loopstart;
 229                        int k;
 230
 231                        /* copy reverse loop */
 232                        for (k = 1; k <= looplen; k++) {
 233                                s = read_word(data, offset - k, sp->v.mode_flags);
 234                                write_word(emu, &dram_offset, s);
 235                        }
 236                        if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
 237                                sp->v.loopend += looplen;
 238                        } else {
 239                                sp->v.loopstart += looplen;
 240                                sp->v.loopend += looplen;
 241                        }
 242                        sp->v.end += looplen;
 243                }
 244        }
 245
 246        /* if no blank loop is attached in the sample, add it */
 247        if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
 248                for (i = 0; i < BLANK_LOOP_SIZE; i++) {
 249                        write_word(emu, &dram_offset, 0);
 250                }
 251                if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
 252                        sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
 253                        sp->v.loopend = sp->v.end + BLANK_LOOP_END;
 254                }
 255        }
 256
 257        /* add dram offset */
 258        sp->v.start += dram_start;
 259        sp->v.end += dram_start;
 260        sp->v.loopstart += dram_start;
 261        sp->v.loopend += dram_start;
 262
 263        snd_emu8000_close_dma(emu);
 264        snd_emu8000_init_fm(emu);
 265
 266        return 0;
 267}
 268
 269/*
 270 * free a sample block
 271 */
 272int
 273snd_emu8000_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
 274                        struct snd_util_memhdr *hdr)
 275{
 276        if (sp->block) {
 277                snd_util_mem_free(hdr, sp->block);
 278                sp->block = NULL;
 279        }
 280        return 0;
 281}
 282
 283
 284/*
 285 * sample_reset callback - terminate voices
 286 */
 287void
 288snd_emu8000_sample_reset(struct snd_emux *rec)
 289{
 290        snd_emux_terminate_all(rec);
 291}
 292