// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) by Jaroslav Kysela <perex@perex.cz> * and (c) 1999 Steve Ratcliffe <steve@parabola.demon.co.uk> * Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de> * * Routines for control of EMU8000 chip
*/
/* * The following routines read and write registers on the emu8000. They * should always be called via the EMU8000*READ/WRITE macros and never * directly. The macros handle the port number and command word.
*/ /* Write a word */ void snd_emu8000_poke(struct snd_emu8000 *emu, unsignedint port, unsignedint reg, unsignedint val)
{ unsignedlong flags;
spin_lock_irqsave(&emu->reg_lock, flags); if (reg != emu->last_reg) {
outw((unsignedshort)reg, EMU8000_PTR(emu)); /* Set register */
emu->last_reg = reg;
}
outw((unsignedshort)val, port); /* Send data */
spin_unlock_irqrestore(&emu->reg_lock, flags);
}
/* Read a word */ unsignedshort snd_emu8000_peek(struct snd_emu8000 *emu, unsignedint port, unsignedint reg)
{ unsignedshort res; unsignedlong flags;
spin_lock_irqsave(&emu->reg_lock, flags); if (reg != emu->last_reg) {
outw((unsignedshort)reg, EMU8000_PTR(emu)); /* Set register */
emu->last_reg = reg;
}
res = inw(port); /* Read data */
spin_unlock_irqrestore(&emu->reg_lock, flags); return res;
}
/* Write a double word */ void snd_emu8000_poke_dw(struct snd_emu8000 *emu, unsignedint port, unsignedint reg, unsignedint val)
{ unsignedlong flags;
spin_lock_irqsave(&emu->reg_lock, flags); if (reg != emu->last_reg) {
outw((unsignedshort)reg, EMU8000_PTR(emu)); /* Set register */
emu->last_reg = reg;
}
outw((unsignedshort)val, port); /* Send low word of data */
outw((unsignedshort)(val>>16), port+2); /* Send high word of data */
spin_unlock_irqrestore(&emu->reg_lock, flags);
}
/* Read a double word */ unsignedint snd_emu8000_peek_dw(struct snd_emu8000 *emu, unsignedint port, unsignedintreg)
{ unsignedshort low; unsignedint res; unsignedlong flags;
spin_lock_irqsave(&emu->reg_lock, flags); if (reg != emu->last_reg) {
outw((unsignedshort)reg, EMU8000_PTR(emu)); /* Set register */
emu->last_reg = reg;
}
low = inw(port); /* Read low word of data */
res = low + (inw(port+2) << 16);
spin_unlock_irqrestore(&emu->reg_lock, flags); return res;
}
/* * Set up / close a channel to be used for DMA.
*/ /*exported*/ void
snd_emu8000_dma_chan(struct snd_emu8000 *emu, int ch, int mode)
{ unsigned right_bit = (mode & EMU8000_RAM_RIGHT) ? 0x01000000 : 0;
mode &= EMU8000_RAM_MODE_MASK; if (mode == EMU8000_RAM_CLOSE) {
EMU8000_CCCA_WRITE(emu, ch, 0);
EMU8000_DCYSUSV_WRITE(emu, ch, 0x807F); return;
}
EMU8000_DCYSUSV_WRITE(emu, ch, 0x80);
EMU8000_VTFT_WRITE(emu, ch, 0);
EMU8000_CVCF_WRITE(emu, ch, 0);
EMU8000_PTRX_WRITE(emu, ch, 0x40000000);
EMU8000_CPF_WRITE(emu, ch, 0x40000000);
EMU8000_PSST_WRITE(emu, ch, 0);
EMU8000_CSL_WRITE(emu, ch, 0); if (mode == EMU8000_RAM_WRITE) /* DMA write */
EMU8000_CCCA_WRITE(emu, ch, 0x06000000 | right_bit); else/* DMA read */
EMU8000_CCCA_WRITE(emu, ch, 0x04000000 | right_bit);
}
/*
*/ staticvoid
snd_emu8000_read_wait(struct snd_emu8000 *emu)
{ while ((EMU8000_SMALR_READ(emu) & 0x80000000) != 0) {
schedule_timeout_interruptible(1); if (signal_pending(current)) break;
}
}
/*
*/ staticvoid
snd_emu8000_write_wait(struct snd_emu8000 *emu)
{ while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
schedule_timeout_interruptible(1); if (signal_pending(current)) break;
}
}
/* * detect a card at the given port
*/ staticint
snd_emu8000_detect(struct snd_emu8000 *emu)
{ /* Initialise */
EMU8000_HWCF1_WRITE(emu, 0x0059);
EMU8000_HWCF2_WRITE(emu, 0x0020);
EMU8000_HWCF3_WRITE(emu, 0x0000); /* Check for a recognisable emu8000 */ /* if ((EMU8000_U1_READ(emu) & 0x000f) != 0x000c) return -ENODEV;
*/ if ((EMU8000_HWCF1_READ(emu) & 0x007e) != 0x0058) return -ENODEV; if ((EMU8000_HWCF2_READ(emu) & 0x0003) != 0x0003) return -ENODEV;
/* send an initialization array * Taken from the oss driver, not obvious from the doc how this * is meant to work
*/ staticvoid
send_array(struct snd_emu8000 *emu, constunsignedshort *data, int size)
{ int i; constunsignedshort *p;
p = data; for (i = 0; i < size; i++, p++)
EMU8000_INIT1_WRITE(emu, i, *p); for (i = 0; i < size; i++, p++)
EMU8000_INIT2_WRITE(emu, i, *p); for (i = 0; i < size; i++, p++)
EMU8000_INIT3_WRITE(emu, i, *p); for (i = 0; i < size; i++, p++)
EMU8000_INIT4_WRITE(emu, i, *p);
}
/* * Send initialization arrays to start up, this just follows the * initialisation sequence in the adip.
*/ staticvoid
init_arrays(struct snd_emu8000 *emu)
{
send_array(emu, init1, ARRAY_SIZE(init1)/4);
/* * Size the onboard memory. * This is written so as not to need arbitrary delays after the write. It * seems that the only way to do this is to use the one channel and keep * reallocating between read and write.
*/ staticvoid
size_dram(struct snd_emu8000 *emu)
{ int i, size;
if (emu->dram_checked) return;
size = 0;
/* write out a magic number */
snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);
snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_READ);
EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET);
EMU8000_SMLD_WRITE(emu, UNIQUE_ID1);
snd_emu8000_init_fm(emu); /* This must really be here and not 2 lines back even */
snd_emu8000_write_wait(emu);
/* * Detect first 512 KiB. If a write succeeds at the beginning of a * 512 KiB page we assume that the whole page is there.
*/
EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET);
EMU8000_SMLD_READ(emu); /* discard stale data */ if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1) goto skip_detect; /* No RAM */
snd_emu8000_read_wait(emu);
/* Write a unique data on the test address. * if the address is out of range, the data is written on * 0x200000(=EMU8000_DRAM_OFFSET). Then the id word is * changed by this data.
*/ /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_WRITE);*/
EMU8000_SMALW_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1));
EMU8000_SMLD_WRITE(emu, UNIQUE_ID2);
snd_emu8000_write_wait(emu);
/* * read the data on the just written DRAM address * if not the same then we have reached the end of ram.
*/ /*snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_READ);*/
EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET + (size>>1)); /*snd_emu8000_read_wait(emu);*/
EMU8000_SMLD_READ(emu); /* discard stale data */ if (EMU8000_SMLD_READ(emu) != UNIQUE_ID2) break; /* no memory at this address */
snd_emu8000_read_wait(emu);
/* * If it is the same it could be that the address just * wraps back to the beginning; so check to see if the * initial value has been overwritten.
*/
EMU8000_SMALR_WRITE(emu, EMU8000_DRAM_OFFSET);
EMU8000_SMLD_READ(emu); /* discard stale data */ if (EMU8000_SMLD_READ(emu) != UNIQUE_ID1) break; /* we must have wrapped around */
snd_emu8000_read_wait(emu);
/* Otherwise, it's valid memory. */
}
skip_detect: /* wait until FULL bit in SMAxW register is false */ for (i = 0; i < 10000; i++) { if ((EMU8000_SMALW_READ(emu) & 0x80000000) == 0) break;
schedule_timeout_interruptible(1); if (signal_pending(current)) break;
}
snd_emu8000_dma_chan(emu, 0, EMU8000_RAM_CLOSE);
snd_emu8000_dma_chan(emu, 1, EMU8000_RAM_CLOSE);
pr_info("EMU8000 [0x%lx]: %d KiB on-board DRAM detected\n",
emu->port1, size/1024);
emu->mem_size = size;
emu->dram_checked = 1;
}
/* * Initiailise the FM section. You have to do this to use sample RAM * and therefore lose 2 voices.
*/ /*exported*/ void
snd_emu8000_init_fm(struct snd_emu8000 *emu)
{ unsignedlong flags;
/* Initialize the last two channels for DRAM refresh and producing
the reverb and chorus effects for Yamaha OPL-3 synthesizer */
/* 31: FM left channel, 0xffffe0-0xffffe8 */
EMU8000_DCYSUSV_WRITE(emu, 30, 0x80);
EMU8000_PSST_WRITE(emu, 30, 0xFFFFFFE0); /* full left */
EMU8000_CSL_WRITE(emu, 30, 0x00FFFFE8 | (emu->fm_chorus_depth << 24));
EMU8000_PTRX_WRITE(emu, 30, (emu->fm_reverb_depth << 8));
EMU8000_CPF_WRITE(emu, 30, 0);
EMU8000_CCCA_WRITE(emu, 30, 0x00FFFFE3);
/* 32: FM right channel, 0xfffff0-0xfffff8 */
EMU8000_DCYSUSV_WRITE(emu, 31, 0x80);
EMU8000_PSST_WRITE(emu, 31, 0x00FFFFF0); /* full right */
EMU8000_CSL_WRITE(emu, 31, 0x00FFFFF8 | (emu->fm_chorus_depth << 24));
EMU8000_PTRX_WRITE(emu, 31, (emu->fm_reverb_depth << 8));
EMU8000_CPF_WRITE(emu, 31, 0x8000);
EMU8000_CCCA_WRITE(emu, 31, 0x00FFFFF3);
/* * set Emu8000 digital equalizer; from 0 to 11 [-12dB - 12dB]
*/ /*exported*/ void
snd_emu8000_update_equalizer(struct snd_emu8000 *emu)
{ unsignedshort w; int bass = emu->bass_level; int treble = emu->treble_level;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.