Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+25
View File
@@ -0,0 +1,25 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
#
snd-gus-lib-y := gus_main.o \
gus_io.o gus_irq.o gus_timer.o \
gus_mem.o gus_mem_proc.o gus_dram.o gus_dma.o gus_volume.o \
gus_pcm.o gus_mixer.o \
gus_uart.o \
gus_reset.o
snd-gusclassic-y := gusclassic.o
snd-gusextreme-y := gusextreme.o
snd-gusmax-y := gusmax.o
snd-interwave-y := interwave.o
snd-interwave-stb-y := interwave-stb.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_GUSCLASSIC) += snd-gusclassic.o snd-gus-lib.o
obj-$(CONFIG_SND_GUSMAX) += snd-gusmax.o snd-gus-lib.o
obj-$(CONFIG_SND_GUSEXTREME) += snd-gusextreme.o snd-gus-lib.o
obj-$(CONFIG_SND_INTERWAVE) += snd-interwave.o snd-gus-lib.o
obj-$(CONFIG_SND_INTERWAVE_STB) += snd-interwave-stb.o snd-gus-lib.o
+268
View File
@@ -0,0 +1,268 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for GF1 DMA control
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*/
#include <asm/dma.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/gus.h>
static void snd_gf1_dma_ack(struct snd_gus_card * gus)
{
guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, 0x00);
snd_gf1_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL);
}
static void snd_gf1_dma_program(struct snd_gus_card * gus,
unsigned int addr,
unsigned long buf_addr,
unsigned int count,
unsigned int cmd)
{
unsigned int address;
unsigned char dma_cmd;
unsigned int address_high;
dev_dbg(gus->card->dev,
"dma_transfer: addr=0x%x, buf=0x%lx, count=0x%x\n",
addr, buf_addr, count);
if (gus->gf1.dma1 > 3) {
if (gus->gf1.enh_mode) {
address = addr >> 1;
} else {
if (addr & 0x1f) {
dev_dbg(gus->card->dev,
"%s: unaligned address (0x%x)?\n",
__func__, addr);
return;
}
address = (addr & 0x000c0000) | ((addr & 0x0003ffff) >> 1);
}
} else {
address = addr;
}
dma_cmd = SNDRV_GF1_DMA_ENABLE | (unsigned short) cmd;
#if 0
dma_cmd |= 0x08;
#endif
if (dma_cmd & SNDRV_GF1_DMA_16BIT) {
count++;
count &= ~1; /* align */
}
if (gus->gf1.dma1 > 3) {
dma_cmd |= SNDRV_GF1_DMA_WIDTH16;
count++;
count &= ~1; /* align */
}
snd_gf1_dma_ack(gus);
snd_dma_program(gus->gf1.dma1, buf_addr, count, dma_cmd & SNDRV_GF1_DMA_READ ? DMA_MODE_READ : DMA_MODE_WRITE);
#if 0
dev_dbg(gus->card->dev,
"address = 0x%x, count = 0x%x, dma_cmd = 0x%x\n",
address << 1, count, dma_cmd);
#endif
guard(spinlock_irqsave)(&gus->reg_lock);
if (gus->gf1.enh_mode) {
address_high = ((address >> 16) & 0x000000f0) | (address & 0x0000000f);
snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_HIGH, (unsigned char) address_high);
} else
snd_gf1_write16(gus, SNDRV_GF1_GW_DRAM_DMA_LOW, (unsigned short) (address >> 4));
snd_gf1_write8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL, dma_cmd);
}
static struct snd_gf1_dma_block *snd_gf1_dma_next_block(struct snd_gus_card * gus)
{
struct snd_gf1_dma_block *block;
/* PCM block have bigger priority than synthesizer one */
if (gus->gf1.dma_data_pcm) {
block = gus->gf1.dma_data_pcm;
if (gus->gf1.dma_data_pcm_last == block) {
gus->gf1.dma_data_pcm =
gus->gf1.dma_data_pcm_last = NULL;
} else {
gus->gf1.dma_data_pcm = block->next;
}
} else if (gus->gf1.dma_data_synth) {
block = gus->gf1.dma_data_synth;
if (gus->gf1.dma_data_synth_last == block) {
gus->gf1.dma_data_synth =
gus->gf1.dma_data_synth_last = NULL;
} else {
gus->gf1.dma_data_synth = block->next;
}
} else {
block = NULL;
}
if (block) {
gus->gf1.dma_ack = block->ack;
gus->gf1.dma_private_data = block->private_data;
}
return block;
}
static void snd_gf1_dma_interrupt(struct snd_gus_card * gus)
{
struct snd_gf1_dma_block *block;
snd_gf1_dma_ack(gus);
if (gus->gf1.dma_ack)
gus->gf1.dma_ack(gus, gus->gf1.dma_private_data);
scoped_guard(spinlock, &gus->dma_lock) {
if (gus->gf1.dma_data_pcm == NULL &&
gus->gf1.dma_data_synth == NULL) {
gus->gf1.dma_ack = NULL;
gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER;
return;
}
block = snd_gf1_dma_next_block(gus);
}
if (!block)
return;
snd_gf1_dma_program(gus, block->addr, block->buf_addr, block->count, (unsigned short) block->cmd);
kfree(block);
#if 0
dev_dbg(gus->card->dev,
"program dma (IRQ) - addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
block->addr, block->buf_addr, block->count, block->cmd);
#endif
}
int snd_gf1_dma_init(struct snd_gus_card * gus)
{
guard(mutex)(&gus->dma_mutex);
gus->gf1.dma_shared++;
if (gus->gf1.dma_shared > 1)
return 0;
gus->gf1.interrupt_handler_dma_write = snd_gf1_dma_interrupt;
gus->gf1.dma_data_pcm =
gus->gf1.dma_data_pcm_last =
gus->gf1.dma_data_synth =
gus->gf1.dma_data_synth_last = NULL;
return 0;
}
int snd_gf1_dma_done(struct snd_gus_card * gus)
{
struct snd_gf1_dma_block *block;
guard(mutex)(&gus->dma_mutex);
gus->gf1.dma_shared--;
if (!gus->gf1.dma_shared) {
snd_dma_disable(gus->gf1.dma1);
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_WRITE);
snd_gf1_dma_ack(gus);
while ((block = gus->gf1.dma_data_pcm)) {
gus->gf1.dma_data_pcm = block->next;
kfree(block);
}
while ((block = gus->gf1.dma_data_synth)) {
gus->gf1.dma_data_synth = block->next;
kfree(block);
}
gus->gf1.dma_data_pcm_last =
gus->gf1.dma_data_synth_last = NULL;
}
return 0;
}
void snd_gf1_dma_suspend(struct snd_gus_card *gus)
{
struct snd_gf1_dma_block *block;
guard(mutex)(&gus->dma_mutex);
if (!gus->gf1.dma_shared)
return;
snd_dma_disable(gus->gf1.dma1);
snd_gf1_dma_ack(gus);
if (gus->gf1.dma_ack)
gus->gf1.dma_ack(gus, gus->gf1.dma_private_data);
gus->gf1.dma_ack = NULL;
gus->gf1.dma_private_data = NULL;
while ((block = gus->gf1.dma_data_pcm)) {
gus->gf1.dma_data_pcm = block->next;
if (block->ack)
block->ack(gus, block->private_data);
kfree(block);
}
while ((block = gus->gf1.dma_data_synth)) {
gus->gf1.dma_data_synth = block->next;
if (block->ack)
block->ack(gus, block->private_data);
kfree(block);
}
gus->gf1.dma_data_pcm_last = NULL;
gus->gf1.dma_data_synth_last = NULL;
gus->gf1.dma_flags &= ~SNDRV_GF1_DMA_TRIGGER;
}
int snd_gf1_dma_transfer_block(struct snd_gus_card * gus,
struct snd_gf1_dma_block * __block,
int atomic,
int synth)
{
struct snd_gf1_dma_block *block;
struct snd_gf1_dma_block *free_block = NULL;
block = kmalloc(sizeof(*block), atomic ? GFP_ATOMIC : GFP_KERNEL);
if (!block)
return -ENOMEM;
*block = *__block;
block->next = NULL;
dev_dbg(gus->card->dev,
"addr = 0x%x, buffer = 0x%lx, count = 0x%x, cmd = 0x%x\n",
block->addr, (long) block->buffer, block->count,
block->cmd);
dev_dbg(gus->card->dev,
"gus->gf1.dma_data_pcm_last = 0x%lx\n",
(long)gus->gf1.dma_data_pcm_last);
dev_dbg(gus->card->dev,
"gus->gf1.dma_data_pcm = 0x%lx\n",
(long)gus->gf1.dma_data_pcm);
scoped_guard(spinlock_irqsave, &gus->dma_lock) {
if (synth) {
if (gus->gf1.dma_data_synth_last) {
gus->gf1.dma_data_synth_last->next = block;
gus->gf1.dma_data_synth_last = block;
} else {
gus->gf1.dma_data_synth =
gus->gf1.dma_data_synth_last = block;
}
} else {
if (gus->gf1.dma_data_pcm_last) {
gus->gf1.dma_data_pcm_last->next = block;
gus->gf1.dma_data_pcm_last = block;
} else {
gus->gf1.dma_data_pcm =
gus->gf1.dma_data_pcm_last = block;
}
}
if (!(gus->gf1.dma_flags & SNDRV_GF1_DMA_TRIGGER)) {
gus->gf1.dma_flags |= SNDRV_GF1_DMA_TRIGGER;
free_block = snd_gf1_dma_next_block(gus);
}
}
if (free_block) {
snd_gf1_dma_program(gus, free_block->addr, free_block->buf_addr,
free_block->count,
(unsigned short)free_block->cmd);
kfree(free_block);
}
return 0;
}
+83
View File
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* DRAM access routines
*/
#include <linux/time.h>
#include <sound/core.h>
#include <sound/gus.h>
#include <sound/info.h>
static int snd_gus_dram_poke(struct snd_gus_card *gus, char __user *_buffer,
unsigned int address, unsigned int size)
{
unsigned int size1, size2;
char buffer[256], *pbuffer;
while (size > 0) {
size1 = size > sizeof(buffer) ? sizeof(buffer) : size;
if (copy_from_user(buffer, _buffer, size1))
return -EFAULT;
if (gus->interwave) {
guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
snd_gf1_dram_addr(gus, address);
outsb(GUSP(gus, DRAM), buffer, size1);
address += size1;
} else {
pbuffer = buffer;
size2 = size1;
while (size2--)
snd_gf1_poke(gus, address++, *pbuffer++);
}
size -= size1;
_buffer += size1;
}
return 0;
}
int snd_gus_dram_write(struct snd_gus_card *gus, char __user *buffer,
unsigned int address, unsigned int size)
{
return snd_gus_dram_poke(gus, buffer, address, size);
}
static int snd_gus_dram_peek(struct snd_gus_card *gus, char __user *_buffer,
unsigned int address, unsigned int size,
int rom)
{
unsigned int size1, size2;
char buffer[256], *pbuffer;
while (size > 0) {
size1 = size > sizeof(buffer) ? sizeof(buffer) : size;
if (gus->interwave) {
guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, rom ? 0x03 : 0x01);
snd_gf1_dram_addr(gus, address);
insb(GUSP(gus, DRAM), buffer, size1);
snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
address += size1;
} else {
pbuffer = buffer;
size2 = size1;
while (size2--)
*pbuffer++ = snd_gf1_peek(gus, address++);
}
if (copy_to_user(_buffer, buffer, size1))
return -EFAULT;
size -= size1;
_buffer += size1;
}
return 0;
}
int snd_gus_dram_read(struct snd_gus_card *gus, char __user *buffer,
unsigned int address, unsigned int size,
int rom)
{
return snd_gus_dram_peek(gus, buffer, address, size, rom);
}
+340
View File
@@ -0,0 +1,340 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* I/O routines for GF1/InterWave synthesizer chips
*/
#include <linux/delay.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/gus.h>
void snd_gf1_delay(struct snd_gus_card * gus)
{
int i;
for (i = 0; i < 6; i++) {
mb();
inb(GUSP(gus, DRAM));
}
}
/*
* =======================================================================
*/
/*
* ok.. stop of control registers (wave & ramp) need some special things..
* big UltraClick (tm) elimination...
*/
static inline void __snd_gf1_ctrl_stop(struct snd_gus_card * gus, unsigned char reg)
{
unsigned char value;
outb(reg | 0x80, gus->gf1.reg_regsel);
mb();
value = inb(gus->gf1.reg_data8);
mb();
outb(reg, gus->gf1.reg_regsel);
mb();
outb((value | 0x03) & ~(0x80 | 0x20), gus->gf1.reg_data8);
mb();
}
static inline void __snd_gf1_write8(struct snd_gus_card * gus,
unsigned char reg,
unsigned char data)
{
outb(reg, gus->gf1.reg_regsel);
mb();
outb(data, gus->gf1.reg_data8);
mb();
}
static inline unsigned char __snd_gf1_look8(struct snd_gus_card * gus,
unsigned char reg)
{
outb(reg, gus->gf1.reg_regsel);
mb();
return inb(gus->gf1.reg_data8);
}
static inline void __snd_gf1_write16(struct snd_gus_card * gus,
unsigned char reg, unsigned int data)
{
outb(reg, gus->gf1.reg_regsel);
mb();
outw((unsigned short) data, gus->gf1.reg_data16);
mb();
}
static inline unsigned short __snd_gf1_look16(struct snd_gus_card * gus,
unsigned char reg)
{
outb(reg, gus->gf1.reg_regsel);
mb();
return inw(gus->gf1.reg_data16);
}
static inline void __snd_gf1_adlib_write(struct snd_gus_card * gus,
unsigned char reg, unsigned char data)
{
outb(reg, gus->gf1.reg_timerctrl);
inb(gus->gf1.reg_timerctrl);
inb(gus->gf1.reg_timerctrl);
outb(data, gus->gf1.reg_timerdata);
inb(gus->gf1.reg_timerctrl);
inb(gus->gf1.reg_timerctrl);
}
static inline void __snd_gf1_write_addr(struct snd_gus_card * gus, unsigned char reg,
unsigned int addr, int w_16bit)
{
if (gus->gf1.enh_mode) {
if (w_16bit)
addr = ((addr >> 1) & ~0x0000000f) | (addr & 0x0000000f);
__snd_gf1_write8(gus, SNDRV_GF1_VB_UPPER_ADDRESS, (unsigned char) ((addr >> 26) & 0x03));
} else if (w_16bit)
addr = (addr & 0x00c0000f) | ((addr & 0x003ffff0) >> 1);
__snd_gf1_write16(gus, reg, (unsigned short) (addr >> 11));
__snd_gf1_write16(gus, reg + 1, (unsigned short) (addr << 5));
}
static inline unsigned int __snd_gf1_read_addr(struct snd_gus_card * gus,
unsigned char reg, short w_16bit)
{
unsigned int res;
res = ((unsigned int) __snd_gf1_look16(gus, reg | 0x80) << 11) & 0xfff800;
res |= ((unsigned int) __snd_gf1_look16(gus, (reg + 1) | 0x80) >> 5) & 0x0007ff;
if (gus->gf1.enh_mode) {
res |= (unsigned int) __snd_gf1_look8(gus, SNDRV_GF1_VB_UPPER_ADDRESS | 0x80) << 26;
if (w_16bit)
res = ((res << 1) & 0xffffffe0) | (res & 0x0000000f);
} else if (w_16bit)
res = ((res & 0x001ffff0) << 1) | (res & 0x00c0000f);
return res;
}
/*
* =======================================================================
*/
void snd_gf1_ctrl_stop(struct snd_gus_card * gus, unsigned char reg)
{
__snd_gf1_ctrl_stop(gus, reg);
}
void snd_gf1_write8(struct snd_gus_card * gus,
unsigned char reg,
unsigned char data)
{
__snd_gf1_write8(gus, reg, data);
}
unsigned char snd_gf1_look8(struct snd_gus_card * gus, unsigned char reg)
{
return __snd_gf1_look8(gus, reg);
}
void snd_gf1_write16(struct snd_gus_card * gus,
unsigned char reg,
unsigned int data)
{
__snd_gf1_write16(gus, reg, data);
}
unsigned short snd_gf1_look16(struct snd_gus_card * gus, unsigned char reg)
{
return __snd_gf1_look16(gus, reg);
}
void snd_gf1_adlib_write(struct snd_gus_card * gus,
unsigned char reg,
unsigned char data)
{
__snd_gf1_adlib_write(gus, reg, data);
}
void snd_gf1_write_addr(struct snd_gus_card * gus, unsigned char reg,
unsigned int addr, short w_16bit)
{
__snd_gf1_write_addr(gus, reg, addr, w_16bit);
}
unsigned int snd_gf1_read_addr(struct snd_gus_card * gus,
unsigned char reg,
short w_16bit)
{
return __snd_gf1_read_addr(gus, reg, w_16bit);
}
/*
*/
void snd_gf1_i_ctrl_stop(struct snd_gus_card * gus, unsigned char reg)
{
guard(spinlock_irqsave)(&gus->reg_lock);
__snd_gf1_ctrl_stop(gus, reg);
}
void snd_gf1_i_write8(struct snd_gus_card * gus,
unsigned char reg,
unsigned char data)
{
guard(spinlock_irqsave)(&gus->reg_lock);
__snd_gf1_write8(gus, reg, data);
}
unsigned char snd_gf1_i_look8(struct snd_gus_card * gus, unsigned char reg)
{
guard(spinlock_irqsave)(&gus->reg_lock);
return __snd_gf1_look8(gus, reg);
}
void snd_gf1_i_write16(struct snd_gus_card * gus,
unsigned char reg,
unsigned int data)
{
guard(spinlock_irqsave)(&gus->reg_lock);
__snd_gf1_write16(gus, reg, data);
}
unsigned short snd_gf1_i_look16(struct snd_gus_card * gus, unsigned char reg)
{
guard(spinlock_irqsave)(&gus->reg_lock);
return __snd_gf1_look16(gus, reg);
}
void snd_gf1_dram_addr(struct snd_gus_card * gus, unsigned int addr)
{
outb(0x43, gus->gf1.reg_regsel);
mb();
outw((unsigned short) addr, gus->gf1.reg_data16);
mb();
outb(0x44, gus->gf1.reg_regsel);
mb();
outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
mb();
}
void snd_gf1_poke(struct snd_gus_card * gus, unsigned int addr, unsigned char data)
{
guard(spinlock_irqsave)(&gus->reg_lock);
outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
mb();
outw((unsigned short) addr, gus->gf1.reg_data16);
mb();
outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
mb();
outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
mb();
outb(data, gus->gf1.reg_dram);
}
unsigned char snd_gf1_peek(struct snd_gus_card * gus, unsigned int addr)
{
guard(spinlock_irqsave)(&gus->reg_lock);
outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
mb();
outw((unsigned short) addr, gus->gf1.reg_data16);
mb();
outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
mb();
outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
mb();
return inb(gus->gf1.reg_dram);
}
#if 0
void snd_gf1_pokew(struct snd_gus_card * gus, unsigned int addr, unsigned short data)
{
if (!gus->interwave)
dev_dbg(gus->card->dev, "%s - GF1!!!\n", __func__);
guard(spinlock_irqsave)(&gus->reg_lock);
outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
mb();
outw((unsigned short) addr, gus->gf1.reg_data16);
mb();
outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
mb();
outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
mb();
outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel);
mb();
outw(data, gus->gf1.reg_data16);
}
unsigned short snd_gf1_peekw(struct snd_gus_card * gus, unsigned int addr)
{
if (!gus->interwave)
dev_dbg(gus->card->dev, "%s - GF1!!!\n", __func__);
guard(spinlock_irqsave)(&gus->reg_lock);
outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
mb();
outw((unsigned short) addr, gus->gf1.reg_data16);
mb();
outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
mb();
outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
mb();
outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel);
mb();
return inw(gus->gf1.reg_data16);
}
void snd_gf1_dram_setmem(struct snd_gus_card * gus, unsigned int addr,
unsigned short value, unsigned int count)
{
unsigned long port;
if (!gus->interwave)
dev_dbg(gus->card->dev, "%s - GF1!!!\n", __func__);
addr &= ~1;
count >>= 1;
port = GUSP(gus, GF1DATALOW);
guard(spinlock_irqsave)(&gus->reg_lock);
outb(SNDRV_GF1_GW_DRAM_IO_LOW, gus->gf1.reg_regsel);
mb();
outw((unsigned short) addr, gus->gf1.reg_data16);
mb();
outb(SNDRV_GF1_GB_DRAM_IO_HIGH, gus->gf1.reg_regsel);
mb();
outb((unsigned char) (addr >> 16), gus->gf1.reg_data8);
mb();
outb(SNDRV_GF1_GW_DRAM_IO16, gus->gf1.reg_regsel);
while (count--)
outw(value, port);
}
#endif /* 0 */
void snd_gf1_select_active_voices(struct snd_gus_card * gus)
{
unsigned short voices;
static const unsigned short voices_tbl[32 - 14 + 1] =
{
44100, 41160, 38587, 36317, 34300, 32494, 30870, 29400, 28063, 26843,
25725, 24696, 23746, 22866, 22050, 21289, 20580, 19916, 19293
};
voices = gus->gf1.active_voices;
if (voices > 32)
voices = 32;
if (voices < 14)
voices = 14;
if (gus->gf1.enh_mode)
voices = 32;
gus->gf1.active_voices = voices;
gus->gf1.playback_freq =
gus->gf1.enh_mode ? 44100 : voices_tbl[voices - 14];
if (!gus->gf1.enh_mode) {
snd_gf1_i_write8(gus, SNDRV_GF1_GB_ACTIVE_VOICES, 0xc0 | (voices - 1));
udelay(100);
}
}
+130
View File
@@ -0,0 +1,130 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routine for IRQ handling from GF1/InterWave chip
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*/
#include <sound/core.h>
#include <sound/info.h>
#include <sound/gus.h>
#ifdef CONFIG_SND_DEBUG
#define STAT_ADD(x) ((x)++)
#else
#define STAT_ADD(x) while (0) { ; }
#endif
irqreturn_t snd_gus_interrupt(int irq, void *dev_id)
{
struct snd_gus_card * gus = dev_id;
unsigned char status;
int loop = 100;
int handled = 0;
__again:
status = inb(gus->gf1.reg_irqstat);
if (status == 0)
return IRQ_RETVAL(handled);
handled = 1;
if (status & 0x02) {
STAT_ADD(gus->gf1.interrupt_stat_midi_in);
if (gus->gf1.interrupt_handler_midi_in)
gus->gf1.interrupt_handler_midi_in(gus);
}
if (status & 0x01) {
STAT_ADD(gus->gf1.interrupt_stat_midi_out);
if (gus->gf1.interrupt_handler_midi_out)
gus->gf1.interrupt_handler_midi_out(gus);
}
if (status & (0x20 | 0x40)) {
unsigned int already, _current_;
unsigned char voice_status, voice;
struct snd_gus_voice *pvoice;
already = 0;
while (((voice_status = snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ)) & 0xc0) != 0xc0) {
voice = voice_status & 0x1f;
_current_ = 1 << voice;
if (already & _current_)
continue; /* multi request */
already |= _current_; /* mark request */
#if 0
dev_dbg(gus->card->dev,
"voice = %i, voice_status = 0x%x, voice_verify = %i\n",
voice, voice_status, inb(GUSP(gus, GF1PAGE)));
#endif
pvoice = &gus->gf1.voices[voice];
if (pvoice->use) {
if (!(voice_status & 0x80)) { /* voice position IRQ */
STAT_ADD(pvoice->interrupt_stat_wave);
pvoice->handler_wave(gus, pvoice);
}
if (!(voice_status & 0x40)) { /* volume ramp IRQ */
STAT_ADD(pvoice->interrupt_stat_volume);
pvoice->handler_volume(gus, pvoice);
}
} else {
STAT_ADD(gus->gf1.interrupt_stat_voice_lost);
snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
snd_gf1_i_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
}
}
}
if (status & 0x04) {
STAT_ADD(gus->gf1.interrupt_stat_timer1);
if (gus->gf1.interrupt_handler_timer1)
gus->gf1.interrupt_handler_timer1(gus);
}
if (status & 0x08) {
STAT_ADD(gus->gf1.interrupt_stat_timer2);
if (gus->gf1.interrupt_handler_timer2)
gus->gf1.interrupt_handler_timer2(gus);
}
if (status & 0x80) {
if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_DRAM_DMA_CONTROL) & 0x40) {
STAT_ADD(gus->gf1.interrupt_stat_dma_write);
if (gus->gf1.interrupt_handler_dma_write)
gus->gf1.interrupt_handler_dma_write(gus);
}
if (snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL) & 0x40) {
STAT_ADD(gus->gf1.interrupt_stat_dma_read);
if (gus->gf1.interrupt_handler_dma_read)
gus->gf1.interrupt_handler_dma_read(gus);
}
}
if (--loop > 0)
goto __again;
return IRQ_NONE;
}
#ifdef CONFIG_SND_DEBUG
static void snd_gus_irq_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_gus_card *gus;
struct snd_gus_voice *pvoice;
int idx;
gus = entry->private_data;
snd_iprintf(buffer, "midi out = %u\n", gus->gf1.interrupt_stat_midi_out);
snd_iprintf(buffer, "midi in = %u\n", gus->gf1.interrupt_stat_midi_in);
snd_iprintf(buffer, "timer1 = %u\n", gus->gf1.interrupt_stat_timer1);
snd_iprintf(buffer, "timer2 = %u\n", gus->gf1.interrupt_stat_timer2);
snd_iprintf(buffer, "dma write = %u\n", gus->gf1.interrupt_stat_dma_write);
snd_iprintf(buffer, "dma read = %u\n", gus->gf1.interrupt_stat_dma_read);
snd_iprintf(buffer, "voice lost = %u\n", gus->gf1.interrupt_stat_voice_lost);
for (idx = 0; idx < 32; idx++) {
pvoice = &gus->gf1.voices[idx];
snd_iprintf(buffer, "voice %i: wave = %u, volume = %u\n",
idx,
pvoice->interrupt_stat_wave,
pvoice->interrupt_stat_volume);
}
}
void snd_gus_irq_profile_init(struct snd_gus_card *gus)
{
snd_card_ro_proc_new(gus->card, "gusirq", gus, snd_gus_irq_info_read);
}
#endif
+481
View File
@@ -0,0 +1,481 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for Gravis UltraSound soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/gus.h>
#include <sound/control.h>
#include <asm/dma.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Routines for Gravis UltraSound soundcards");
MODULE_LICENSE("GPL");
static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches);
static int snd_gus_joystick_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 31;
return 0;
}
static int snd_gus_joystick_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = gus->joystick_dac & 31;
return 0;
}
static int snd_gus_joystick_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
int change;
unsigned char nval;
nval = ucontrol->value.integer.value[0] & 31;
guard(spinlock_irqsave)(&gus->reg_lock);
change = gus->joystick_dac != nval;
gus->joystick_dac = nval;
snd_gf1_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac);
return change;
}
static const struct snd_kcontrol_new snd_gus_joystick_control = {
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "Joystick Speed",
.info = snd_gus_joystick_info,
.get = snd_gus_joystick_get,
.put = snd_gus_joystick_put
};
static void snd_gus_init_control(struct snd_gus_card *gus)
{
if (!gus->ace_flag)
snd_ctl_add(gus->card, snd_ctl_new1(&snd_gus_joystick_control, gus));
}
/*
*
*/
static int snd_gus_free(struct snd_gus_card *gus)
{
if (gus->gf1.res_port2 == NULL)
goto __hw_end;
snd_gf1_stop(gus);
snd_gus_init_dma_irq(gus, 0);
__hw_end:
release_and_free_resource(gus->gf1.res_port1);
release_and_free_resource(gus->gf1.res_port2);
if (gus->gf1.irq >= 0)
free_irq(gus->gf1.irq, (void *) gus);
if (gus->gf1.dma1 >= 0) {
disable_dma(gus->gf1.dma1);
free_dma(gus->gf1.dma1);
}
if (!gus->equal_dma && gus->gf1.dma2 >= 0) {
disable_dma(gus->gf1.dma2);
free_dma(gus->gf1.dma2);
}
kfree(gus);
return 0;
}
static int snd_gus_dev_free(struct snd_device *device)
{
struct snd_gus_card *gus = device->device_data;
return snd_gus_free(gus);
}
int snd_gus_create(struct snd_card *card,
unsigned long port,
int irq, int dma1, int dma2,
int timer_dev,
int voices,
int pcm_channels,
int effect,
struct snd_gus_card **rgus)
{
struct snd_gus_card *gus;
int err;
static const struct snd_device_ops ops = {
.dev_free = snd_gus_dev_free,
};
*rgus = NULL;
gus = kzalloc_obj(*gus);
if (gus == NULL)
return -ENOMEM;
spin_lock_init(&gus->reg_lock);
spin_lock_init(&gus->voice_alloc);
spin_lock_init(&gus->active_voice_lock);
spin_lock_init(&gus->event_lock);
spin_lock_init(&gus->dma_lock);
spin_lock_init(&gus->pcm_volume_level_lock);
spin_lock_init(&gus->uart_cmd_lock);
mutex_init(&gus->dma_mutex);
gus->gf1.irq = -1;
gus->gf1.dma1 = -1;
gus->gf1.dma2 = -1;
gus->card = card;
gus->gf1.port = port;
/* fill register variables for speedup */
gus->gf1.reg_page = GUSP(gus, GF1PAGE);
gus->gf1.reg_regsel = GUSP(gus, GF1REGSEL);
gus->gf1.reg_data8 = GUSP(gus, GF1DATAHIGH);
gus->gf1.reg_data16 = GUSP(gus, GF1DATALOW);
gus->gf1.reg_irqstat = GUSP(gus, IRQSTAT);
gus->gf1.reg_dram = GUSP(gus, DRAM);
gus->gf1.reg_timerctrl = GUSP(gus, TIMERCNTRL);
gus->gf1.reg_timerdata = GUSP(gus, TIMERDATA);
/* allocate resources */
gus->gf1.res_port1 = request_region(port, 16, "GUS GF1 (Adlib/SB)");
if (!gus->gf1.res_port1) {
dev_err(card->dev, "gus: can't grab SB port 0x%lx\n", port);
snd_gus_free(gus);
return -EBUSY;
}
gus->gf1.res_port2 = request_region(port + 0x100, 12, "GUS GF1 (Synth)");
if (!gus->gf1.res_port2) {
dev_err(card->dev, "gus: can't grab synth port 0x%lx\n", port + 0x100);
snd_gus_free(gus);
return -EBUSY;
}
if (irq >= 0 && request_irq(irq, snd_gus_interrupt, 0, "GUS GF1", (void *) gus)) {
dev_err(card->dev, "gus: can't grab irq %d\n", irq);
snd_gus_free(gus);
return -EBUSY;
}
gus->gf1.irq = irq;
card->sync_irq = irq;
if (request_dma(dma1, "GUS - 1")) {
dev_err(card->dev, "gus: can't grab DMA1 %d\n", dma1);
snd_gus_free(gus);
return -EBUSY;
}
gus->gf1.dma1 = dma1;
if (dma2 >= 0 && dma1 != dma2) {
if (request_dma(dma2, "GUS - 2")) {
dev_err(card->dev, "gus: can't grab DMA2 %d\n", dma2);
snd_gus_free(gus);
return -EBUSY;
}
gus->gf1.dma2 = dma2;
} else {
gus->gf1.dma2 = gus->gf1.dma1;
gus->equal_dma = 1;
}
gus->timer_dev = timer_dev;
if (voices < 14)
voices = 14;
if (voices > 32)
voices = 32;
if (pcm_channels < 0)
pcm_channels = 0;
if (pcm_channels > 8)
pcm_channels = 8;
pcm_channels++;
pcm_channels &= ~1;
gus->gf1.effect = effect ? 1 : 0;
gus->gf1.active_voices = voices;
gus->gf1.pcm_channels = pcm_channels;
gus->gf1.volume_ramp = 25;
gus->gf1.smooth_pan = 1;
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, gus, &ops);
if (err < 0) {
snd_gus_free(gus);
return err;
}
*rgus = gus;
return 0;
}
/*
* Memory detection routine for plain GF1 soundcards
*/
static int snd_gus_detect_memory(struct snd_gus_card * gus)
{
int l, idx, local;
unsigned char d;
snd_gf1_poke(gus, 0L, 0xaa);
snd_gf1_poke(gus, 1L, 0x55);
if (snd_gf1_peek(gus, 0L) != 0xaa || snd_gf1_peek(gus, 1L) != 0x55) {
dev_err(gus->card->dev,
"plain GF1 card at 0x%lx without onboard DRAM?\n",
gus->gf1.port);
return -ENOMEM;
}
for (idx = 1, d = 0xab; idx < 4; idx++, d++) {
local = idx << 18;
snd_gf1_poke(gus, local, d);
snd_gf1_poke(gus, local + 1, d + 1);
if (snd_gf1_peek(gus, local) != d ||
snd_gf1_peek(gus, local + 1) != d + 1 ||
snd_gf1_peek(gus, 0L) != 0xaa)
break;
}
#if 1
gus->gf1.memory = idx << 18;
#else
gus->gf1.memory = 256 * 1024;
#endif
for (l = 0, local = gus->gf1.memory; l < 4; l++, local -= 256 * 1024) {
gus->gf1.mem_alloc.banks_8[l].address =
gus->gf1.mem_alloc.banks_8[l].size = 0;
gus->gf1.mem_alloc.banks_16[l].address = l << 18;
gus->gf1.mem_alloc.banks_16[l].size = local > 0 ? 256 * 1024 : 0;
}
gus->gf1.mem_alloc.banks_8[0].size = gus->gf1.memory;
return 0; /* some memory were detected */
}
static int snd_gus_init_dma_irq(struct snd_gus_card * gus, int latches)
{
struct snd_card *card;
int irq, dma1, dma2;
static const unsigned char irqs[16] =
{0, 0, 1, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7};
static const unsigned char dmas[8] =
{6, 1, 0, 2, 0, 3, 4, 5};
if (snd_BUG_ON(!gus))
return -EINVAL;
card = gus->card;
if (snd_BUG_ON(!card))
return -EINVAL;
gus->mix_cntrl_reg &= 0xf8;
gus->mix_cntrl_reg |= 0x01; /* disable MIC, LINE IN, enable LINE OUT */
if (gus->codec_flag || gus->ess_flag) {
gus->mix_cntrl_reg &= ~1; /* enable LINE IN */
gus->mix_cntrl_reg |= 4; /* enable MIC */
}
dma1 = gus->gf1.dma1;
dma1 = abs(dma1);
dma1 = dmas[dma1 & 7];
dma2 = gus->gf1.dma2;
dma2 = abs(dma2);
dma2 = dmas[dma2 & 7];
dma1 |= gus->equal_dma ? 0x40 : (dma2 << 3);
if ((dma1 & 7) == 0 || (dma2 & 7) == 0) {
dev_err(gus->card->dev, "Error! DMA isn't defined.\n");
return -EINVAL;
}
irq = gus->gf1.irq;
irq = abs(irq);
irq = irqs[irq & 0x0f];
if (irq == 0) {
dev_err(gus->card->dev, "Error! IRQ isn't defined.\n");
return -EINVAL;
}
irq |= 0x40;
#if 0
card->mixer.mix_ctrl_reg |= 0x10;
#endif
scoped_guard(spinlock_irqsave, &gus->reg_lock) {
outb(5, GUSP(gus, REGCNTRLS));
outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
outb(0x00, GUSP(gus, IRQDMACNTRLREG));
outb(0, GUSP(gus, REGCNTRLS));
}
udelay(100);
scoped_guard(spinlock_irqsave, &gus->reg_lock) {
outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
outb(dma1, GUSP(gus, IRQDMACNTRLREG));
if (latches) {
outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
outb(irq, GUSP(gus, IRQDMACNTRLREG));
}
}
udelay(100);
scoped_guard(spinlock_irqsave, &gus->reg_lock) {
outb(0x00 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
outb(dma1, GUSP(gus, IRQDMACNTRLREG));
if (latches) {
outb(0x40 | gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
outb(irq, GUSP(gus, IRQDMACNTRLREG));
}
}
snd_gf1_delay(gus);
if (latches)
gus->mix_cntrl_reg |= 0x08; /* enable latches */
else
gus->mix_cntrl_reg &= ~0x08; /* disable latches */
scoped_guard(spinlock_irqsave, &gus->reg_lock) {
outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
outb(0, GUSP(gus, GF1PAGE));
}
return 0;
}
static int snd_gus_check_version(struct snd_gus_card * gus)
{
unsigned char val, rev;
struct snd_card *card;
card = gus->card;
scoped_guard(spinlock_irqsave, &gus->reg_lock) {
outb(0x20, GUSP(gus, REGCNTRLS));
val = inb(GUSP(gus, REGCNTRLS));
rev = inb(GUSP(gus, BOARDVERSION));
}
dev_dbg(card->dev, "GF1 [0x%lx] init - val = 0x%x, rev = 0x%x\n", gus->gf1.port, val, rev);
strscpy(card->driver, "GUS");
strscpy(card->longname, "Gravis UltraSound Classic (2.4)");
if ((val != 255 && (val & 0x06)) || (rev >= 5 && rev != 255)) {
if (rev >= 5 && rev <= 9) {
gus->ics_flag = 1;
if (rev == 5)
gus->ics_flipped = 1;
card->longname[27] = '3';
card->longname[29] = rev == 5 ? '5' : '7';
}
if (rev >= 10 && rev != 255) {
if (rev >= 10 && rev <= 11) {
strscpy(card->driver, "GUS MAX");
strscpy(card->longname, "Gravis UltraSound MAX");
gus->max_flag = 1;
} else if (rev == 0x30) {
strscpy(card->driver, "GUS ACE");
strscpy(card->longname, "Gravis UltraSound Ace");
gus->ace_flag = 1;
} else if (rev == 0x50) {
strscpy(card->driver, "GUS Extreme");
strscpy(card->longname, "Gravis UltraSound Extreme");
gus->ess_flag = 1;
} else {
dev_err(card->dev,
"unknown GF1 revision number at 0x%lx - 0x%x (0x%x)\n",
gus->gf1.port, rev, val);
dev_err(card->dev,
" please - report to <perex@perex.cz>\n");
}
}
}
strscpy(card->shortname, card->longname, sizeof(card->shortname));
gus->uart_enable = 1; /* standard GUSes doesn't have midi uart trouble */
snd_gus_init_control(gus);
return 0;
}
int snd_gus_initialize(struct snd_gus_card *gus)
{
int err;
if (!gus->interwave) {
err = snd_gus_check_version(gus);
if (err < 0) {
dev_err(gus->card->dev, "version check failed\n");
return err;
}
err = snd_gus_detect_memory(gus);
if (err < 0)
return err;
}
err = snd_gus_init_dma_irq(gus, 1);
if (err < 0)
return err;
snd_gf1_start(gus);
gus->initialized = 1;
return 0;
}
int snd_gus_suspend(struct snd_gus_card *gus)
{
int err;
if (gus->pcm) {
err = snd_pcm_suspend_all(gus->pcm);
if (err < 0)
return err;
}
err = snd_gf1_suspend(gus);
if (err < 0)
return err;
snd_power_change_state(gus->card, SNDRV_CTL_POWER_D3hot);
return 0;
}
EXPORT_SYMBOL(snd_gus_suspend);
int snd_gus_resume(struct snd_gus_card *gus)
{
int err;
err = snd_gus_init_dma_irq(gus, 1);
if (err < 0)
return err;
err = snd_gf1_resume(gus);
if (err < 0)
return err;
snd_power_change_state(gus->card, SNDRV_CTL_POWER_D0);
return 0;
}
EXPORT_SYMBOL(snd_gus_resume);
/* gus_io.c */
EXPORT_SYMBOL(snd_gf1_delay);
EXPORT_SYMBOL(snd_gf1_write8);
EXPORT_SYMBOL(snd_gf1_look8);
EXPORT_SYMBOL(snd_gf1_write16);
EXPORT_SYMBOL(snd_gf1_look16);
EXPORT_SYMBOL(snd_gf1_i_write8);
EXPORT_SYMBOL(snd_gf1_i_look8);
EXPORT_SYMBOL(snd_gf1_i_look16);
EXPORT_SYMBOL(snd_gf1_dram_addr);
EXPORT_SYMBOL(snd_gf1_write_addr);
EXPORT_SYMBOL(snd_gf1_poke);
EXPORT_SYMBOL(snd_gf1_peek);
/* gus_reset.c */
EXPORT_SYMBOL(snd_gf1_alloc_voice);
EXPORT_SYMBOL(snd_gf1_free_voice);
EXPORT_SYMBOL(snd_gf1_ctrl_stop);
EXPORT_SYMBOL(snd_gf1_stop_voice);
/* gus_mixer.c */
EXPORT_SYMBOL(snd_gf1_new_mixer);
/* gus_pcm.c */
EXPORT_SYMBOL(snd_gf1_pcm_new);
/* gus.c */
EXPORT_SYMBOL(snd_gus_create);
EXPORT_SYMBOL(snd_gus_initialize);
/* gus_irq.c */
EXPORT_SYMBOL(snd_gus_interrupt);
/* gus_uart.c */
EXPORT_SYMBOL(snd_gf1_rawmidi_new);
/* gus_dram.c */
EXPORT_SYMBOL(snd_gus_dram_write);
EXPORT_SYMBOL(snd_gus_dram_read);
/* gus_volume.c */
EXPORT_SYMBOL(snd_gf1_lvol_to_gvol_raw);
EXPORT_SYMBOL(snd_gf1_translate_freq);
/* gus_mem.c */
EXPORT_SYMBOL(snd_gf1_mem_alloc);
EXPORT_SYMBOL(snd_gf1_mem_xfree);
EXPORT_SYMBOL(snd_gf1_mem_free);
+316
View File
@@ -0,0 +1,316 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* GUS's memory allocation routines / bottom layer
*/
#include <linux/slab.h>
#include <linux/string.h>
#include <sound/core.h>
#include <sound/gus.h>
#include <sound/info.h>
#ifdef CONFIG_SND_DEBUG
static void snd_gf1_mem_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer);
#endif
static struct snd_gf1_mem_block *
snd_gf1_mem_xalloc(struct snd_gf1_mem *alloc, struct snd_gf1_mem_block *block,
const char *name)
{
struct snd_gf1_mem_block *pblock, *nblock;
nblock = kmalloc_obj(struct snd_gf1_mem_block);
if (nblock == NULL)
return NULL;
*nblock = *block;
nblock->name = kstrdup(name, GFP_KERNEL);
if (!nblock->name) {
kfree(nblock);
return NULL;
}
pblock = alloc->first;
while (pblock) {
if (pblock->ptr > nblock->ptr) {
nblock->prev = pblock->prev;
nblock->next = pblock;
pblock->prev = nblock;
if (pblock == alloc->first)
alloc->first = nblock;
else
nblock->prev->next = nblock;
return nblock;
}
pblock = pblock->next;
}
nblock->next = NULL;
if (alloc->last == NULL) {
nblock->prev = NULL;
alloc->first = alloc->last = nblock;
} else {
nblock->prev = alloc->last;
alloc->last->next = nblock;
alloc->last = nblock;
}
return nblock;
}
int snd_gf1_mem_xfree(struct snd_gf1_mem * alloc, struct snd_gf1_mem_block * block)
{
if (block->share) { /* ok.. shared block */
block->share--;
return 0;
}
if (alloc->first == block) {
alloc->first = block->next;
if (block->next)
block->next->prev = NULL;
} else {
block->prev->next = block->next;
if (block->next)
block->next->prev = block->prev;
}
if (alloc->last == block) {
alloc->last = block->prev;
if (block->prev)
block->prev->next = NULL;
} else {
block->next->prev = block->prev;
if (block->prev)
block->prev->next = block->next;
}
kfree(block->name);
kfree(block);
return 0;
}
static struct snd_gf1_mem_block *snd_gf1_mem_look(struct snd_gf1_mem * alloc,
unsigned int address)
{
struct snd_gf1_mem_block *block;
for (block = alloc->first; block; block = block->next) {
if (block->ptr == address) {
return block;
}
}
return NULL;
}
static struct snd_gf1_mem_block *snd_gf1_mem_share(struct snd_gf1_mem * alloc,
unsigned int *share_id)
{
struct snd_gf1_mem_block *block;
if (!share_id[0] && !share_id[1] &&
!share_id[2] && !share_id[3])
return NULL;
for (block = alloc->first; block; block = block->next)
if (!memcmp(share_id, block->share_id,
sizeof(block->share_id)))
return block;
return NULL;
}
static int snd_gf1_mem_find(struct snd_gf1_mem * alloc,
struct snd_gf1_mem_block * block,
unsigned int size, int w_16, int align)
{
struct snd_gf1_bank_info *info = w_16 ? alloc->banks_16 : alloc->banks_8;
unsigned int idx, boundary;
int size1;
struct snd_gf1_mem_block *pblock;
unsigned int ptr1, ptr2;
if (w_16 && align < 2)
align = 2;
block->flags = w_16 ? SNDRV_GF1_MEM_BLOCK_16BIT : 0;
block->owner = SNDRV_GF1_MEM_OWNER_DRIVER;
block->share = 0;
block->share_id[0] = block->share_id[1] =
block->share_id[2] = block->share_id[3] = 0;
block->name = NULL;
block->prev = block->next = NULL;
for (pblock = alloc->first, idx = 0; pblock; pblock = pblock->next) {
while (pblock->ptr >= (boundary = info[idx].address + info[idx].size))
idx++;
while (pblock->ptr + pblock->size >= (boundary = info[idx].address + info[idx].size))
idx++;
ptr2 = boundary;
if (pblock->next) {
if (pblock->ptr + pblock->size == pblock->next->ptr)
continue;
if (pblock->next->ptr < boundary)
ptr2 = pblock->next->ptr;
}
ptr1 = ALIGN(pblock->ptr + pblock->size, align);
if (ptr1 >= ptr2)
continue;
size1 = ptr2 - ptr1;
if ((int)size <= size1) {
block->ptr = ptr1;
block->size = size;
return 0;
}
}
while (++idx < 4) {
if (size <= info[idx].size) {
/* I assume that bank address is already aligned.. */
block->ptr = info[idx].address;
block->size = size;
return 0;
}
}
return -ENOMEM;
}
struct snd_gf1_mem_block *snd_gf1_mem_alloc(struct snd_gf1_mem * alloc, int owner,
char *name, int size, int w_16, int align,
unsigned int *share_id)
{
struct snd_gf1_mem_block block, *nblock;
guard(mutex)(&alloc->memory_mutex);
if (share_id != NULL) {
nblock = snd_gf1_mem_share(alloc, share_id);
if (nblock != NULL) {
if (size != (int)nblock->size) {
/* TODO: remove in the future */
pr_err("%s - share: sizes differ\n", __func__);
goto __std;
}
nblock->share++;
return NULL;
}
}
__std:
if (snd_gf1_mem_find(alloc, &block, size, w_16, align) < 0)
return NULL;
if (share_id != NULL)
memcpy(&block.share_id, share_id, sizeof(block.share_id));
block.owner = owner;
nblock = snd_gf1_mem_xalloc(alloc, &block, name);
return nblock;
}
int snd_gf1_mem_free(struct snd_gf1_mem * alloc, unsigned int address)
{
struct snd_gf1_mem_block *block;
guard(mutex)(&alloc->memory_mutex);
block = snd_gf1_mem_look(alloc, address);
if (block)
return snd_gf1_mem_xfree(alloc, block);
return -EINVAL;
}
int snd_gf1_mem_init(struct snd_gus_card * gus)
{
struct snd_gf1_mem *alloc;
struct snd_gf1_mem_block block;
alloc = &gus->gf1.mem_alloc;
mutex_init(&alloc->memory_mutex);
alloc->first = alloc->last = NULL;
if (!gus->gf1.memory)
return 0;
memset(&block, 0, sizeof(block));
block.owner = SNDRV_GF1_MEM_OWNER_DRIVER;
if (gus->gf1.enh_mode) {
block.ptr = 0;
block.size = 1024;
if (!snd_gf1_mem_xalloc(alloc, &block, "InterWave LFOs"))
return -ENOMEM;
}
block.ptr = gus->gf1.default_voice_address;
block.size = 4;
if (!snd_gf1_mem_xalloc(alloc, &block, "Voice default (NULL's)"))
return -ENOMEM;
#ifdef CONFIG_SND_DEBUG
snd_card_ro_proc_new(gus->card, "gusmem", gus, snd_gf1_mem_info_read);
#endif
return 0;
}
int snd_gf1_mem_done(struct snd_gus_card * gus)
{
struct snd_gf1_mem *alloc;
struct snd_gf1_mem_block *block, *nblock;
alloc = &gus->gf1.mem_alloc;
block = alloc->first;
while (block) {
nblock = block->next;
snd_gf1_mem_xfree(alloc, block);
block = nblock;
}
return 0;
}
#ifdef CONFIG_SND_DEBUG
static void snd_gf1_mem_info_read(struct snd_info_entry *entry,
struct snd_info_buffer *buffer)
{
struct snd_gus_card *gus;
struct snd_gf1_mem *alloc;
struct snd_gf1_mem_block *block;
unsigned int total, used;
int i;
gus = entry->private_data;
alloc = &gus->gf1.mem_alloc;
guard(mutex)(&alloc->memory_mutex);
snd_iprintf(buffer, "8-bit banks : \n ");
for (i = 0; i < 4; i++)
snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_8[i].address, alloc->banks_8[i].size >> 10, i + 1 < 4 ? "," : "");
snd_iprintf(buffer, "\n"
"16-bit banks : \n ");
for (i = total = 0; i < 4; i++) {
snd_iprintf(buffer, "0x%06x (%04ik)%s", alloc->banks_16[i].address, alloc->banks_16[i].size >> 10, i + 1 < 4 ? "," : "");
total += alloc->banks_16[i].size;
}
snd_iprintf(buffer, "\n");
used = 0;
for (block = alloc->first, i = 0; block; block = block->next, i++) {
used += block->size;
snd_iprintf(buffer, "Block %i onboard 0x%x size %i (0x%x):\n", i, block->ptr, block->size, block->size);
if (block->share ||
block->share_id[0] || block->share_id[1] ||
block->share_id[2] || block->share_id[3])
snd_iprintf(buffer, " Share : %i [id0 0x%x] [id1 0x%x] [id2 0x%x] [id3 0x%x]\n",
block->share,
block->share_id[0], block->share_id[1],
block->share_id[2], block->share_id[3]);
snd_iprintf(buffer, " Flags :%s\n",
block->flags & SNDRV_GF1_MEM_BLOCK_16BIT ? " 16-bit" : "");
snd_iprintf(buffer, " Owner : ");
switch (block->owner) {
case SNDRV_GF1_MEM_OWNER_DRIVER:
snd_iprintf(buffer, "driver - %s\n", block->name);
break;
case SNDRV_GF1_MEM_OWNER_WAVE_SIMPLE:
snd_iprintf(buffer, "SIMPLE wave\n");
break;
case SNDRV_GF1_MEM_OWNER_WAVE_GF1:
snd_iprintf(buffer, "GF1 wave\n");
break;
case SNDRV_GF1_MEM_OWNER_WAVE_IWFFFF:
snd_iprintf(buffer, "IWFFFF wave\n");
break;
default:
snd_iprintf(buffer, "unknown\n");
}
}
snd_iprintf(buffer, " Total: memory = %i, used = %i, free = %i\n",
total, used, total - used);
#if 0
ultra_iprintf(buffer, " Verify: free = %i, max 8-bit block = %i, max 16-bit block = %i\n",
ultra_memory_free_size(card, &card->gf1.mem_alloc),
ultra_memory_free_block(card, &card->gf1.mem_alloc, 0),
ultra_memory_free_block(card, &card->gf1.mem_alloc, 1));
#endif
}
#endif
+87
View File
@@ -0,0 +1,87 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* GUS's memory access via proc filesystem
*/
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/gus.h>
#include <sound/info.h>
struct gus_proc_private {
int rom; /* data are in ROM */
unsigned int address;
unsigned int size;
struct snd_gus_card * gus;
};
static ssize_t snd_gf1_mem_proc_dump(struct snd_info_entry *entry,
void *file_private_data,
struct file *file, char __user *buf,
size_t count, loff_t pos)
{
struct gus_proc_private *priv = entry->private_data;
struct snd_gus_card *gus = priv->gus;
int err;
err = snd_gus_dram_read(gus, buf, pos, count, priv->rom);
if (err < 0)
return err;
return count;
}
static void snd_gf1_mem_proc_free(struct snd_info_entry *entry)
{
struct gus_proc_private *priv = entry->private_data;
kfree(priv);
}
static const struct snd_info_entry_ops snd_gf1_mem_proc_ops = {
.read = snd_gf1_mem_proc_dump,
};
int snd_gf1_mem_proc_init(struct snd_gus_card * gus)
{
int idx;
char name[16];
struct gus_proc_private *priv;
struct snd_info_entry *entry;
for (idx = 0; idx < 4; idx++) {
if (gus->gf1.mem_alloc.banks_8[idx].size > 0) {
priv = kzalloc_obj(*priv);
if (priv == NULL)
return -ENOMEM;
priv->gus = gus;
sprintf(name, "gus-ram-%i", idx);
if (! snd_card_proc_new(gus->card, name, &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = priv;
entry->private_free = snd_gf1_mem_proc_free;
entry->c.ops = &snd_gf1_mem_proc_ops;
priv->address = gus->gf1.mem_alloc.banks_8[idx].address;
priv->size = entry->size = gus->gf1.mem_alloc.banks_8[idx].size;
}
}
}
for (idx = 0; idx < 4; idx++) {
if (gus->gf1.rom_present & (1 << idx)) {
priv = kzalloc_obj(*priv);
if (priv == NULL)
return -ENOMEM;
priv->rom = 1;
priv->gus = gus;
sprintf(name, "gus-rom-%i", idx);
if (! snd_card_proc_new(gus->card, name, &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = priv;
entry->private_free = snd_gf1_mem_proc_free;
entry->c.ops = &snd_gf1_mem_proc_ops;
priv->address = idx * 4096 * 1024;
priv->size = entry->size = gus->gf1.rom_memory;
}
}
}
return 0;
}
+171
View File
@@ -0,0 +1,171 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of ICS 2101 chip and "mixer" in GF1 chip
*/
#include <linux/time.h>
#include <linux/wait.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/gus.h>
/*
*
*/
#define GF1_SINGLE(xname, xindex, shift, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.info = snd_gf1_info_single, \
.get = snd_gf1_get_single, .put = snd_gf1_put_single, \
.private_value = shift | (invert << 8) }
#define snd_gf1_info_single snd_ctl_boolean_mono_info
static int snd_gf1_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
int shift = kcontrol->private_value & 0xff;
int invert = (kcontrol->private_value >> 8) & 1;
ucontrol->value.integer.value[0] = (gus->mix_cntrl_reg >> shift) & 1;
if (invert)
ucontrol->value.integer.value[0] ^= 1;
return 0;
}
static int snd_gf1_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
int shift = kcontrol->private_value & 0xff;
int invert = (kcontrol->private_value >> 8) & 1;
int change;
unsigned char oval, nval;
nval = ucontrol->value.integer.value[0] & 1;
if (invert)
nval ^= 1;
nval <<= shift;
guard(spinlock_irqsave)(&gus->reg_lock);
oval = gus->mix_cntrl_reg;
nval = (oval & ~(1 << shift)) | nval;
change = nval != oval;
outb(gus->mix_cntrl_reg = nval, GUSP(gus, MIXCNTRLREG));
outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE));
return change;
}
#define ICS_DOUBLE(xname, xindex, addr) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
.info = snd_ics_info_double, \
.get = snd_ics_get_double, .put = snd_ics_put_double, \
.private_value = addr }
static int snd_ics_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 127;
return 0;
}
static int snd_ics_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
int addr = kcontrol->private_value & 0xff;
unsigned char left, right;
guard(spinlock_irqsave)(&gus->reg_lock);
left = gus->gf1.ics_regs[addr][0];
right = gus->gf1.ics_regs[addr][1];
ucontrol->value.integer.value[0] = left & 127;
ucontrol->value.integer.value[1] = right & 127;
return 0;
}
static int snd_ics_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
int addr = kcontrol->private_value & 0xff;
int change;
unsigned char val1, val2, oval1, oval2;
val1 = ucontrol->value.integer.value[0] & 127;
val2 = ucontrol->value.integer.value[1] & 127;
guard(spinlock_irqsave)(&gus->reg_lock);
oval1 = gus->gf1.ics_regs[addr][0];
oval2 = gus->gf1.ics_regs[addr][1];
change = val1 != oval1 || val2 != oval2;
gus->gf1.ics_regs[addr][0] = val1;
gus->gf1.ics_regs[addr][1] = val2;
if (gus->ics_flag && gus->ics_flipped &&
(addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV))
swap(val1, val2);
addr <<= 3;
outb(addr | 0, GUSP(gus, MIXCNTRLPORT));
outb(1, GUSP(gus, MIXDATAPORT));
outb(addr | 2, GUSP(gus, MIXCNTRLPORT));
outb((unsigned char) val1, GUSP(gus, MIXDATAPORT));
outb(addr | 1, GUSP(gus, MIXCNTRLPORT));
outb(2, GUSP(gus, MIXDATAPORT));
outb(addr | 3, GUSP(gus, MIXCNTRLPORT));
outb((unsigned char) val2, GUSP(gus, MIXDATAPORT));
return change;
}
static const struct snd_kcontrol_new snd_gf1_controls[] = {
GF1_SINGLE("Master Playback Switch", 0, 1, 1),
GF1_SINGLE("Line Switch", 0, 0, 1),
GF1_SINGLE("Mic Switch", 0, 2, 0)
};
static const struct snd_kcontrol_new snd_ics_controls[] = {
GF1_SINGLE("Master Playback Switch", 0, 1, 1),
ICS_DOUBLE("Master Playback Volume", 0, SNDRV_ICS_MASTER_DEV),
ICS_DOUBLE("Synth Playback Volume", 0, SNDRV_ICS_GF1_DEV),
GF1_SINGLE("Line Switch", 0, 0, 1),
ICS_DOUBLE("Line Playback Volume", 0, SNDRV_ICS_LINE_DEV),
GF1_SINGLE("Mic Switch", 0, 2, 0),
ICS_DOUBLE("Mic Playback Volume", 0, SNDRV_ICS_MIC_DEV),
ICS_DOUBLE("CD Playback Volume", 0, SNDRV_ICS_CD_DEV)
};
int snd_gf1_new_mixer(struct snd_gus_card * gus)
{
struct snd_card *card;
unsigned int idx, max;
int err;
if (snd_BUG_ON(!gus))
return -EINVAL;
card = gus->card;
if (snd_BUG_ON(!card))
return -EINVAL;
if (gus->ics_flag)
snd_component_add(card, "ICS2101");
if (card->mixername[0] == '\0') {
strscpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1");
} else {
if (gus->ics_flag)
strcat(card->mixername, ",ICS2101");
strcat(card->mixername, ",GF1");
}
if (!gus->ics_flag) {
max = gus->ess_flag ? 1 : ARRAY_SIZE(snd_gf1_controls);
for (idx = 0; idx < max; idx++) {
err = snd_ctl_add(card, snd_ctl_new1(&snd_gf1_controls[idx], gus));
if (err < 0)
return err;
}
} else {
for (idx = 0; idx < ARRAY_SIZE(snd_ics_controls); idx++) {
err = snd_ctl_add(card, snd_ctl_new1(&snd_ics_controls[idx], gus));
if (err < 0)
return err;
}
}
return 0;
}
+862
View File
@@ -0,0 +1,862 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for control of GF1 chip (PCM things)
*
* InterWave chips supports interleaved DMA, but this feature isn't used in
* this code.
*
* This code emulates autoinit DMA transfer for playback, recording by GF1
* chip doesn't support autoinit DMA.
*/
#include <asm/dma.h>
#include <linux/slab.h>
#include <linux/sched/signal.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/gus.h>
#include <sound/pcm_params.h>
#include "gus_tables.h"
/* maximum rate */
#define SNDRV_GF1_PCM_RATE 48000
#define SNDRV_GF1_PCM_PFLG_NONE 0
#define SNDRV_GF1_PCM_PFLG_ACTIVE (1<<0)
#define SNDRV_GF1_PCM_PFLG_NEUTRAL (2<<0)
struct gus_pcm_private {
struct snd_gus_card * gus;
struct snd_pcm_substream *substream;
spinlock_t lock;
unsigned int voices;
struct snd_gus_voice *pvoices[2];
unsigned int memory;
unsigned short flags;
unsigned char voice_ctrl, ramp_ctrl;
unsigned int bpos;
unsigned int blocks;
unsigned int block_size;
unsigned int dma_size;
wait_queue_head_t sleep;
atomic_t dma_count;
int final_volume;
};
static void snd_gf1_pcm_block_change_ack(struct snd_gus_card * gus, void *private_data)
{
struct gus_pcm_private *pcmp = private_data;
if (pcmp) {
atomic_dec(&pcmp->dma_count);
wake_up(&pcmp->sleep);
}
}
static int snd_gf1_pcm_block_change(struct snd_pcm_substream *substream,
unsigned int offset,
unsigned int addr,
unsigned int count)
{
struct snd_gf1_dma_block block;
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
count += offset & 31;
offset &= ~31;
memset(&block, 0, sizeof(block));
block.cmd = SNDRV_GF1_DMA_IRQ;
if (snd_pcm_format_unsigned(runtime->format))
block.cmd |= SNDRV_GF1_DMA_UNSIGNED;
if (snd_pcm_format_width(runtime->format) == 16)
block.cmd |= SNDRV_GF1_DMA_16BIT;
block.addr = addr & ~31;
block.buffer = runtime->dma_area + offset;
block.buf_addr = runtime->dma_addr + offset;
block.count = count;
block.private_data = pcmp;
block.ack = snd_gf1_pcm_block_change_ack;
if (!snd_gf1_dma_transfer_block(pcmp->gus, &block, 0, 0))
atomic_inc(&pcmp->dma_count);
return 0;
}
static void snd_gf1_pcm_trigger_up(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
struct snd_gus_card * gus = pcmp->gus;
unsigned char voice_ctrl, ramp_ctrl;
unsigned short rate;
unsigned int curr, begin, end;
unsigned short vol;
unsigned char pan;
unsigned int voice;
scoped_guard(spinlock_irqsave, &pcmp->lock) {
if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE)
return;
pcmp->flags |= SNDRV_GF1_PCM_PFLG_ACTIVE;
pcmp->final_volume = 0;
}
rate = snd_gf1_translate_freq(gus, runtime->rate << 4);
/* enable WAVE IRQ */
voice_ctrl = snd_pcm_format_width(runtime->format) == 16 ? 0x24 : 0x20;
/* enable RAMP IRQ + rollover */
ramp_ctrl = 0x24;
if (pcmp->blocks == 1) {
voice_ctrl |= 0x08; /* loop enable */
ramp_ctrl &= ~0x04; /* disable rollover */
}
for (voice = 0; voice < pcmp->voices; voice++) {
begin = pcmp->memory + voice * (pcmp->dma_size / runtime->channels);
curr = begin + (pcmp->bpos * pcmp->block_size) / runtime->channels;
end = curr + (pcmp->block_size / runtime->channels);
end -= snd_pcm_format_width(runtime->format) == 16 ? 2 : 1;
pan = runtime->channels == 2 ? (!voice ? 1 : 14) : 8;
vol = !voice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, pan);
snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, rate);
snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, begin << 4, voice_ctrl & 4);
snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4);
snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, curr << 4, voice_ctrl & 4);
snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, SNDRV_GF1_MIN_VOLUME << 4);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0x2f);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, vol >> 8);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
if (!gus->gf1.enh_mode) {
snd_gf1_delay(gus);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
}
}
guard(spinlock_irqsave)(&gus->reg_lock);
for (voice = 0; voice < pcmp->voices; voice++) {
snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
if (gus->gf1.enh_mode)
snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, 0x00); /* deactivate voice */
snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
voice_ctrl &= ~0x20;
}
voice_ctrl |= 0x20;
if (!gus->gf1.enh_mode) {
snd_gf1_delay(gus);
for (voice = 0; voice < pcmp->voices; voice++) {
snd_gf1_select_voice(gus, pcmp->pvoices[voice]->number);
snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
voice_ctrl &= ~0x20; /* disable IRQ for next voice */
}
}
}
static void snd_gf1_pcm_interrupt_wave(struct snd_gus_card * gus,
struct snd_gus_voice *pvoice)
{
struct gus_pcm_private * pcmp;
struct snd_pcm_runtime *runtime;
unsigned char voice_ctrl, ramp_ctrl;
unsigned int idx;
unsigned int end, step;
if (!pvoice->private_data) {
dev_dbg(gus->card->dev, "%s: unknown wave irq?\n", __func__);
snd_gf1_smart_stop_voice(gus, pvoice->number);
return;
}
pcmp = pvoice->private_data;
if (pcmp == NULL) {
dev_dbg(gus->card->dev, "%s: unknown wave irq?\n", __func__);
snd_gf1_smart_stop_voice(gus, pvoice->number);
return;
}
gus = pcmp->gus;
runtime = pcmp->substream->runtime;
scoped_guard(spinlock, &gus->reg_lock) {
snd_gf1_select_voice(gus, pvoice->number);
voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & ~0x8b;
ramp_ctrl = (snd_gf1_read8(gus, SNDRV_GF1_VB_VOLUME_CONTROL) & ~0xa4) | 0x03;
#if 0
snd_gf1_select_voice(gus, pvoice->number);
dev_dbg(gus->card->dev, "position = 0x%x\n",
(snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
snd_gf1_select_voice(gus, pcmp->pvoices[1]->number);
dev_dbg(gus->card->dev, "position = 0x%x\n",
(snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4));
snd_gf1_select_voice(gus, pvoice->number);
#endif
pcmp->bpos++;
pcmp->bpos %= pcmp->blocks;
if (pcmp->bpos + 1 >= pcmp->blocks) { /* last block? */
voice_ctrl |= 0x08; /* enable loop */
} else {
ramp_ctrl |= 0x04; /* enable rollover */
}
end = pcmp->memory + (((pcmp->bpos + 1) * pcmp->block_size) / runtime->channels);
end -= voice_ctrl & 4 ? 2 : 1;
step = pcmp->dma_size / runtime->channels;
voice_ctrl |= 0x20;
if (!pcmp->final_volume) {
ramp_ctrl |= 0x20;
ramp_ctrl &= ~0x03;
}
for (idx = 0; idx < pcmp->voices; idx++, end += step) {
snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number);
snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, end << 4, voice_ctrl & 4);
snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
voice_ctrl &= ~0x20;
}
if (!gus->gf1.enh_mode) {
snd_gf1_delay(gus);
voice_ctrl |= 0x20;
for (idx = 0; idx < pcmp->voices; idx++) {
snd_gf1_select_voice(gus, pcmp->pvoices[idx]->number);
snd_gf1_write8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL, voice_ctrl);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, ramp_ctrl);
voice_ctrl &= ~0x20;
}
}
}
snd_pcm_period_elapsed(pcmp->substream);
#if 0
if ((runtime->flags & SNDRV_PCM_FLG_MMAP) &&
*runtime->state == SNDRV_PCM_STATE_RUNNING) {
end = pcmp->bpos * pcmp->block_size;
if (runtime->channels > 1) {
snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + (end / 2), pcmp->block_size / 2);
snd_gf1_pcm_block_change(pcmp->substream, end + (pcmp->block_size / 2), pcmp->memory + (pcmp->dma_size / 2) + (end / 2), pcmp->block_size / 2);
} else {
snd_gf1_pcm_block_change(pcmp->substream, end, pcmp->memory + end, pcmp->block_size);
}
}
#endif
}
static void snd_gf1_pcm_interrupt_volume(struct snd_gus_card * gus,
struct snd_gus_voice * pvoice)
{
unsigned short vol;
int cvoice;
struct gus_pcm_private *pcmp = pvoice->private_data;
/* stop ramp, but leave rollover bit untouched */
scoped_guard(spinlock, &gus->reg_lock) {
snd_gf1_select_voice(gus, pvoice->number);
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
}
if (pcmp == NULL)
return;
/* are we active? */
if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE))
return;
/* load real volume - better precision */
cvoice = pcmp->pvoices[0] == pvoice ? 0 : 1;
if (pcmp->substream == NULL)
return;
vol = !cvoice ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
guard(spinlock)(&gus->reg_lock);
snd_gf1_select_voice(gus, pvoice->number);
snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol);
pcmp->final_volume = 1;
}
static void snd_gf1_pcm_volume_change(struct snd_gus_card * gus)
{
}
static int snd_gf1_pcm_poke_block(struct snd_gus_card *gus, unsigned char *buf,
unsigned int pos, unsigned int count,
int w16, int invert)
{
unsigned int len;
while (count > 0) {
len = count;
if (len > 512) /* limit, to allow IRQ */
len = 512;
count -= len;
if (gus->interwave) {
guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01 | (invert ? 0x08 : 0x00));
snd_gf1_dram_addr(gus, pos);
if (w16) {
outb(SNDRV_GF1_GW_DRAM_IO16, GUSP(gus, GF1REGSEL));
outsw(GUSP(gus, GF1DATALOW), buf, len >> 1);
} else {
outsb(GUSP(gus, DRAM), buf, len);
}
buf += 512;
pos += 512;
} else {
invert = invert ? 0x80 : 0x00;
if (w16) {
len >>= 1;
while (len--) {
snd_gf1_poke(gus, pos++, *buf++);
snd_gf1_poke(gus, pos++, *buf++ ^ invert);
}
} else {
while (len--)
snd_gf1_poke(gus, pos++, *buf++ ^ invert);
}
}
if (count > 0 && !in_interrupt()) {
schedule_timeout_interruptible(1);
if (signal_pending(current))
return -EAGAIN;
}
}
return 0;
}
static int get_bpos(struct gus_pcm_private *pcmp, int voice, unsigned int pos,
unsigned int len)
{
unsigned int bpos = pos + (voice * (pcmp->dma_size / 2));
if (snd_BUG_ON(bpos > pcmp->dma_size))
return -EIO;
if (snd_BUG_ON(bpos + len > pcmp->dma_size))
return -EIO;
return bpos;
}
static int playback_copy_ack(struct snd_pcm_substream *substream,
unsigned int bpos, unsigned int len)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
struct snd_gus_card *gus = pcmp->gus;
int w16, invert;
if (len > 32)
return snd_gf1_pcm_block_change(substream, bpos,
pcmp->memory + bpos, len);
w16 = (snd_pcm_format_width(runtime->format) == 16);
invert = snd_pcm_format_unsigned(runtime->format);
return snd_gf1_pcm_poke_block(gus, runtime->dma_area + bpos,
pcmp->memory + bpos, len, w16, invert);
}
static int snd_gf1_pcm_playback_copy(struct snd_pcm_substream *substream,
int voice, unsigned long pos,
struct iov_iter *src, unsigned long count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
unsigned int len = count;
int bpos;
bpos = get_bpos(pcmp, voice, pos, len);
if (bpos < 0)
return bpos;
if (copy_from_iter(runtime->dma_area + bpos, len, src) != len)
return -EFAULT;
return playback_copy_ack(substream, bpos, len);
}
static int snd_gf1_pcm_playback_silence(struct snd_pcm_substream *substream,
int voice, unsigned long pos,
unsigned long count)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
unsigned int len = count;
int bpos;
bpos = get_bpos(pcmp, voice, pos, len);
if (bpos < 0)
return bpos;
snd_pcm_format_set_silence(runtime->format, runtime->dma_area + bpos,
bytes_to_samples(runtime, count));
return playback_copy_ack(substream, bpos, len);
}
static int snd_gf1_pcm_playback_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
if (runtime->buffer_changed) {
struct snd_gf1_mem_block *block;
if (pcmp->memory > 0) {
snd_gf1_mem_free(&gus->gf1.mem_alloc, pcmp->memory);
pcmp->memory = 0;
}
block = snd_gf1_mem_alloc(&gus->gf1.mem_alloc,
SNDRV_GF1_MEM_OWNER_DRIVER,
"GF1 PCM",
runtime->dma_bytes, 1, 32,
NULL);
if (!block)
return -ENOMEM;
pcmp->memory = block->ptr;
}
pcmp->voices = params_channels(hw_params);
if (pcmp->pvoices[0] == NULL) {
pcmp->pvoices[0] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0);
if (!pcmp->pvoices[0])
return -ENOMEM;
pcmp->pvoices[0]->handler_wave = snd_gf1_pcm_interrupt_wave;
pcmp->pvoices[0]->handler_volume = snd_gf1_pcm_interrupt_volume;
pcmp->pvoices[0]->volume_change = snd_gf1_pcm_volume_change;
pcmp->pvoices[0]->private_data = pcmp;
}
if (pcmp->voices > 1 && pcmp->pvoices[1] == NULL) {
pcmp->pvoices[1] = snd_gf1_alloc_voice(pcmp->gus, SNDRV_GF1_VOICE_TYPE_PCM, 0, 0);
if (!pcmp->pvoices[1])
return -ENOMEM;
pcmp->pvoices[1]->handler_wave = snd_gf1_pcm_interrupt_wave;
pcmp->pvoices[1]->handler_volume = snd_gf1_pcm_interrupt_volume;
pcmp->pvoices[1]->volume_change = snd_gf1_pcm_volume_change;
pcmp->pvoices[1]->private_data = pcmp;
} else if (pcmp->voices == 1) {
if (pcmp->pvoices[1]) {
snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]);
pcmp->pvoices[1] = NULL;
}
}
return 0;
}
static int snd_gf1_pcm_playback_hw_free(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
if (pcmp->pvoices[0]) {
snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[0]);
pcmp->pvoices[0] = NULL;
}
if (pcmp->pvoices[1]) {
snd_gf1_free_voice(pcmp->gus, pcmp->pvoices[1]);
pcmp->pvoices[1] = NULL;
}
if (pcmp->memory > 0) {
snd_gf1_mem_free(&pcmp->gus->gf1.mem_alloc, pcmp->memory);
pcmp->memory = 0;
}
return 0;
}
static int snd_gf1_pcm_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
pcmp->bpos = 0;
pcmp->dma_size = snd_pcm_lib_buffer_bytes(substream);
pcmp->block_size = snd_pcm_lib_period_bytes(substream);
pcmp->blocks = pcmp->dma_size / pcmp->block_size;
return 0;
}
static int snd_gf1_pcm_playback_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
int voice;
if (cmd == SNDRV_PCM_TRIGGER_START) {
snd_gf1_pcm_trigger_up(substream);
} else if (cmd == SNDRV_PCM_TRIGGER_STOP ||
cmd == SNDRV_PCM_TRIGGER_SUSPEND) {
scoped_guard(spinlock, &pcmp->lock) {
pcmp->flags &= ~SNDRV_GF1_PCM_PFLG_ACTIVE;
}
voice = pcmp->pvoices[0]->number;
snd_gf1_stop_voices(gus, voice, voice);
if (pcmp->pvoices[1]) {
voice = pcmp->pvoices[1]->number;
snd_gf1_stop_voices(gus, voice, voice);
}
} else {
return -EINVAL;
}
return 0;
}
static snd_pcm_uframes_t snd_gf1_pcm_playback_pointer(struct snd_pcm_substream *substream)
{
struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
unsigned int pos;
unsigned char voice_ctrl;
pos = 0;
guard(spinlock)(&gus->reg_lock);
if (pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE) {
snd_gf1_select_voice(gus, pcmp->pvoices[0]->number);
voice_ctrl = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
pos = (snd_gf1_read_addr(gus, SNDRV_GF1_VA_CURRENT, voice_ctrl & 4) >> 4) - pcmp->memory;
if (substream->runtime->channels > 1)
pos <<= 1;
pos = bytes_to_frames(runtime, pos);
}
return pos;
}
static const struct snd_ratnum clock = {
.num = 9878400/16,
.den_min = 2,
.den_max = 257,
.den_step = 1,
};
static const struct snd_pcm_hw_constraint_ratnums hw_constraints_clocks = {
.nrats = 1,
.rats = &clock,
};
static int snd_gf1_pcm_capture_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
gus->c_dma_size = params_buffer_bytes(hw_params);
gus->c_period_size = params_period_bytes(hw_params);
gus->c_pos = 0;
gus->gf1.pcm_rcntrl_reg = 0x21; /* IRQ at end, enable & start */
if (params_channels(hw_params) > 1)
gus->gf1.pcm_rcntrl_reg |= 2;
if (gus->gf1.dma2 > 3)
gus->gf1.pcm_rcntrl_reg |= 4;
if (snd_pcm_format_unsigned(params_format(hw_params)))
gus->gf1.pcm_rcntrl_reg |= 0x80;
return 0;
}
static int snd_gf1_pcm_capture_prepare(struct snd_pcm_substream *substream)
{
struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RECORD_RATE, runtime->rate_den - 2);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); /* disable sampling */
snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */
snd_dma_program(gus->gf1.dma2, runtime->dma_addr, gus->c_period_size, DMA_MODE_READ);
return 0;
}
static int snd_gf1_pcm_capture_trigger(struct snd_pcm_substream *substream,
int cmd)
{
struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
int val;
if (cmd == SNDRV_PCM_TRIGGER_START) {
val = gus->gf1.pcm_rcntrl_reg;
} else if (cmd == SNDRV_PCM_TRIGGER_STOP ||
cmd == SNDRV_PCM_TRIGGER_SUSPEND) {
val = 0;
} else {
return -EINVAL;
}
guard(spinlock)(&gus->reg_lock);
snd_gf1_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, val);
snd_gf1_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL);
return 0;
}
static snd_pcm_uframes_t snd_gf1_pcm_capture_pointer(struct snd_pcm_substream *substream)
{
struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
int pos = snd_dma_pointer(gus->gf1.dma2, gus->c_period_size);
pos = bytes_to_frames(substream->runtime, (gus->c_pos + pos) % gus->c_dma_size);
return pos;
}
static void snd_gf1_pcm_interrupt_dma_read(struct snd_gus_card * gus)
{
snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0); /* disable sampling */
snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL); /* Sampling Control Register */
if (gus->pcm_cap_substream != NULL) {
snd_gf1_pcm_capture_prepare(gus->pcm_cap_substream);
snd_gf1_pcm_capture_trigger(gus->pcm_cap_substream, SNDRV_PCM_TRIGGER_START);
gus->c_pos += gus->c_period_size;
snd_pcm_period_elapsed(gus->pcm_cap_substream);
}
}
static const struct snd_pcm_hardware snd_gf1_pcm_playback =
{
.info = SNDRV_PCM_INFO_NONINTERLEAVED,
.formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
.rate_min = 5510,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = (128*1024),
.period_bytes_min = 64,
.period_bytes_max = (128*1024),
.periods_min = 1,
.periods_max = 1024,
.fifo_size = 0,
};
static const struct snd_pcm_hardware snd_gf1_pcm_capture =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8,
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
.rate_min = 5510,
.rate_max = 44100,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = (128*1024),
.period_bytes_min = 64,
.period_bytes_max = (128*1024),
.periods_min = 1,
.periods_max = 1024,
.fifo_size = 0,
};
static void snd_gf1_pcm_playback_free(struct snd_pcm_runtime *runtime)
{
kfree(runtime->private_data);
}
static int snd_gf1_pcm_playback_open(struct snd_pcm_substream *substream)
{
struct gus_pcm_private *pcmp;
struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
int err;
pcmp = kzalloc_obj(*pcmp);
if (pcmp == NULL)
return -ENOMEM;
pcmp->gus = gus;
spin_lock_init(&pcmp->lock);
init_waitqueue_head(&pcmp->sleep);
atomic_set(&pcmp->dma_count, 0);
runtime->private_data = pcmp;
runtime->private_free = snd_gf1_pcm_playback_free;
#if 0
dev_dbg(gus->card->dev,
"playback.buffer = 0x%lx, gf1.pcm_buffer = 0x%lx\n",
(long) pcm->playback.buffer, (long) gus->gf1.pcm_buffer);
#endif
err = snd_gf1_dma_init(gus);
if (err < 0)
return err;
pcmp->flags = SNDRV_GF1_PCM_PFLG_NONE;
pcmp->substream = substream;
runtime->hw = snd_gf1_pcm_playback;
snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.buffer_bytes_max);
snd_pcm_limit_isa_dma_size(gus->gf1.dma1, &runtime->hw.period_bytes_max);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 64);
return 0;
}
static int snd_gf1_pcm_playback_close(struct snd_pcm_substream *substream)
{
struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct gus_pcm_private *pcmp = runtime->private_data;
if (!wait_event_timeout(pcmp->sleep, (atomic_read(&pcmp->dma_count) <= 0), 2*HZ))
dev_err(gus->card->dev, "gf1 pcm - serious DMA problem\n");
snd_gf1_dma_done(gus);
return 0;
}
static int snd_gf1_pcm_capture_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
gus->gf1.interrupt_handler_dma_read = snd_gf1_pcm_interrupt_dma_read;
gus->pcm_cap_substream = substream;
substream->runtime->hw = snd_gf1_pcm_capture;
snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.buffer_bytes_max);
snd_pcm_limit_isa_dma_size(gus->gf1.dma2, &runtime->hw.period_bytes_max);
snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&hw_constraints_clocks);
return 0;
}
static int snd_gf1_pcm_capture_close(struct snd_pcm_substream *substream)
{
struct snd_gus_card *gus = snd_pcm_substream_chip(substream);
gus->pcm_cap_substream = NULL;
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_DMA_READ);
return 0;
}
static int snd_gf1_pcm_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 127;
return 0;
}
static int snd_gf1_pcm_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
guard(spinlock_irqsave)(&gus->pcm_volume_level_lock);
ucontrol->value.integer.value[0] = gus->gf1.pcm_volume_level_left1;
ucontrol->value.integer.value[1] = gus->gf1.pcm_volume_level_right1;
return 0;
}
static int snd_gf1_pcm_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
{
struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol);
int change;
unsigned int idx;
unsigned short val1, val2, vol;
struct gus_pcm_private *pcmp;
struct snd_gus_voice *pvoice;
val1 = ucontrol->value.integer.value[0] & 127;
val2 = ucontrol->value.integer.value[1] & 127;
scoped_guard(spinlock_irqsave, &gus->pcm_volume_level_lock) {
change = val1 != gus->gf1.pcm_volume_level_left1 ||
val2 != gus->gf1.pcm_volume_level_right1;
gus->gf1.pcm_volume_level_left1 = val1;
gus->gf1.pcm_volume_level_right1 = val2;
gus->gf1.pcm_volume_level_left = snd_gf1_lvol_to_gvol_raw(val1 << 9) << 4;
gus->gf1.pcm_volume_level_right = snd_gf1_lvol_to_gvol_raw(val2 << 9) << 4;
}
/* are we active? */
scoped_guard(spinlock_irqsave, &gus->voice_alloc) {
for (idx = 0; idx < 32; idx++) {
pvoice = &gus->gf1.voices[idx];
if (!pvoice->pcm)
continue;
pcmp = pvoice->private_data;
if (!(pcmp->flags & SNDRV_GF1_PCM_PFLG_ACTIVE))
continue;
/* load real volume - better precision */
guard(spinlock)(&gus->reg_lock);
snd_gf1_select_voice(gus, pvoice->number);
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
vol = pvoice == pcmp->pvoices[0] ? gus->gf1.pcm_volume_level_left : gus->gf1.pcm_volume_level_right;
snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, vol);
pcmp->final_volume = 1;
}
}
return change;
}
static const struct snd_kcontrol_new snd_gf1_pcm_volume_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
.info = snd_gf1_pcm_volume_info,
.get = snd_gf1_pcm_volume_get,
.put = snd_gf1_pcm_volume_put
};
static const struct snd_kcontrol_new snd_gf1_pcm_volume_control1 =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "GPCM Playback Volume",
.info = snd_gf1_pcm_volume_info,
.get = snd_gf1_pcm_volume_get,
.put = snd_gf1_pcm_volume_put
};
static const struct snd_pcm_ops snd_gf1_pcm_playback_ops = {
.open = snd_gf1_pcm_playback_open,
.close = snd_gf1_pcm_playback_close,
.hw_params = snd_gf1_pcm_playback_hw_params,
.hw_free = snd_gf1_pcm_playback_hw_free,
.prepare = snd_gf1_pcm_playback_prepare,
.trigger = snd_gf1_pcm_playback_trigger,
.pointer = snd_gf1_pcm_playback_pointer,
.copy = snd_gf1_pcm_playback_copy,
.fill_silence = snd_gf1_pcm_playback_silence,
};
static const struct snd_pcm_ops snd_gf1_pcm_capture_ops = {
.open = snd_gf1_pcm_capture_open,
.close = snd_gf1_pcm_capture_close,
.hw_params = snd_gf1_pcm_capture_hw_params,
.prepare = snd_gf1_pcm_capture_prepare,
.trigger = snd_gf1_pcm_capture_trigger,
.pointer = snd_gf1_pcm_capture_pointer,
};
int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index)
{
struct snd_card *card;
struct snd_kcontrol *kctl;
struct snd_pcm *pcm;
struct snd_pcm_substream *substream;
int capture, err;
card = gus->card;
capture = !gus->interwave && !gus->ess_flag && !gus->ace_flag ? 1 : 0;
err = snd_pcm_new(card,
gus->interwave ? "AMD InterWave" : "GF1",
pcm_dev,
gus->gf1.pcm_channels / 2,
capture,
&pcm);
if (err < 0)
return err;
pcm->private_data = gus;
/* playback setup */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_gf1_pcm_playback_ops);
for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
snd_pcm_set_managed_buffer(substream, SNDRV_DMA_TYPE_DEV,
card->dev,
64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024);
pcm->info_flags = 0;
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
if (capture) {
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_gf1_pcm_capture_ops);
if (gus->gf1.dma2 == gus->gf1.dma1)
pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX;
snd_pcm_set_managed_buffer(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
SNDRV_DMA_TYPE_DEV, card->dev,
64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024);
}
strscpy(pcm->name, pcm->id);
if (gus->interwave) {
sprintf(pcm->name + strlen(pcm->name), " rev %c", gus->revision + 'A');
}
strcat(pcm->name, " (synth)");
gus->pcm = pcm;
if (gus->codec_flag)
kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control1, gus);
else
kctl = snd_ctl_new1(&snd_gf1_pcm_volume_control, gus);
if (!kctl)
return -ENOMEM;
kctl->id.index = control_index;
err = snd_ctl_add(card, kctl);
if (err < 0)
return err;
return 0;
}
+401
View File
@@ -0,0 +1,401 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*/
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/gus.h>
/*
* ok.. default interrupt handlers...
*/
static void snd_gf1_default_interrupt_handler_midi_out(struct snd_gus_card * gus)
{
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd &= ~0x20);
}
static void snd_gf1_default_interrupt_handler_midi_in(struct snd_gus_card * gus)
{
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd &= ~0x80);
}
static void snd_gf1_default_interrupt_handler_timer1(struct snd_gus_card * gus)
{
snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, gus->gf1.timer_enabled &= ~4);
}
static void snd_gf1_default_interrupt_handler_timer2(struct snd_gus_card * gus)
{
snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, gus->gf1.timer_enabled &= ~8);
}
static void snd_gf1_default_interrupt_handler_wave_and_volume(struct snd_gus_card * gus, struct snd_gus_voice * voice)
{
snd_gf1_i_ctrl_stop(gus, 0x00);
snd_gf1_i_ctrl_stop(gus, 0x0d);
}
static void snd_gf1_default_interrupt_handler_dma_write(struct snd_gus_card * gus)
{
snd_gf1_i_write8(gus, 0x41, 0x00);
}
static void snd_gf1_default_interrupt_handler_dma_read(struct snd_gus_card * gus)
{
snd_gf1_i_write8(gus, 0x49, 0x00);
}
void snd_gf1_set_default_handlers(struct snd_gus_card * gus, unsigned int what)
{
if (what & SNDRV_GF1_HANDLER_MIDI_OUT)
gus->gf1.interrupt_handler_midi_out = snd_gf1_default_interrupt_handler_midi_out;
if (what & SNDRV_GF1_HANDLER_MIDI_IN)
gus->gf1.interrupt_handler_midi_in = snd_gf1_default_interrupt_handler_midi_in;
if (what & SNDRV_GF1_HANDLER_TIMER1)
gus->gf1.interrupt_handler_timer1 = snd_gf1_default_interrupt_handler_timer1;
if (what & SNDRV_GF1_HANDLER_TIMER2)
gus->gf1.interrupt_handler_timer2 = snd_gf1_default_interrupt_handler_timer2;
if (what & SNDRV_GF1_HANDLER_VOICE) {
struct snd_gus_voice *voice;
voice = &gus->gf1.voices[what & 0xffff];
voice->handler_wave =
voice->handler_volume = snd_gf1_default_interrupt_handler_wave_and_volume;
voice->handler_effect = NULL;
voice->volume_change = NULL;
}
if (what & SNDRV_GF1_HANDLER_DMA_WRITE)
gus->gf1.interrupt_handler_dma_write = snd_gf1_default_interrupt_handler_dma_write;
if (what & SNDRV_GF1_HANDLER_DMA_READ)
gus->gf1.interrupt_handler_dma_read = snd_gf1_default_interrupt_handler_dma_read;
}
/*
*/
static void snd_gf1_clear_regs(struct snd_gus_card * gus)
{
guard(spinlock_irqsave)(&gus->reg_lock);
inb(GUSP(gus, IRQSTAT));
snd_gf1_write8(gus, 0x41, 0); /* DRAM DMA Control Register */
snd_gf1_write8(gus, 0x45, 0); /* Timer Control */
snd_gf1_write8(gus, 0x49, 0); /* Sampling Control Register */
}
static void snd_gf1_look_regs(struct snd_gus_card * gus)
{
guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_look8(gus, 0x41); /* DRAM DMA Control Register */
snd_gf1_look8(gus, 0x49); /* Sampling Control Register */
inb(GUSP(gus, IRQSTAT));
snd_gf1_read8(gus, 0x0f); /* IRQ Source Register */
}
/*
* put selected GF1 voices to initial stage...
*/
void snd_gf1_smart_stop_voice(struct snd_gus_card * gus, unsigned short voice)
{
guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_select_voice(gus, voice);
#if 0
dev_dbg(gus->card->dev,
" -%i- smart stop voice - volume = 0x%x\n",
voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME));
#endif
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
}
void snd_gf1_stop_voice(struct snd_gus_card * gus, unsigned short voice)
{
guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_select_voice(gus, voice);
#if 0
dev_dbg(gus->card->dev,
" -%i- stop voice - volume = 0x%x\n",
voice, snd_gf1_i_read16(gus, SNDRV_GF1_VW_VOLUME));
#endif
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL);
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL);
if (gus->gf1.enh_mode)
snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0);
}
static void snd_gf1_clear_voices(struct snd_gus_card * gus, unsigned short v_min,
unsigned short v_max)
{
unsigned int daddr;
unsigned short i, w_16;
daddr = gus->gf1.default_voice_address << 4;
for (i = v_min; i <= v_max; i++) {
#if 0
if (gus->gf1.syn_voices)
gus->gf1.syn_voices[i].flags = ~VFLG_DYNAMIC;
#endif
guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_select_voice(gus, i);
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_ADDRESS_CONTROL); /* Voice Control Register = voice stop */
snd_gf1_ctrl_stop(gus, SNDRV_GF1_VB_VOLUME_CONTROL); /* Volume Ramp Control Register = ramp off */
if (gus->gf1.enh_mode)
snd_gf1_write8(gus, SNDRV_GF1_VB_MODE, gus->gf1.memory ? 0x02 : 0x82); /* Deactivate voice */
w_16 = snd_gf1_read8(gus, SNDRV_GF1_VB_ADDRESS_CONTROL) & 0x04;
snd_gf1_write16(gus, SNDRV_GF1_VW_FREQUENCY, 0x400);
snd_gf1_write_addr(gus, SNDRV_GF1_VA_START, daddr, w_16);
snd_gf1_write_addr(gus, SNDRV_GF1_VA_END, daddr, w_16);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, 0);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, 0);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 0);
snd_gf1_write16(gus, SNDRV_GF1_VW_VOLUME, 0);
snd_gf1_write_addr(gus, SNDRV_GF1_VA_CURRENT, daddr, w_16);
snd_gf1_write8(gus, SNDRV_GF1_VB_PAN, 7);
if (gus->gf1.enh_mode) {
snd_gf1_write8(gus, SNDRV_GF1_VB_ACCUMULATOR, 0);
snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME, 0);
snd_gf1_write16(gus, SNDRV_GF1_VW_EFFECT_VOLUME_FINAL, 0);
}
}
}
void snd_gf1_stop_voices(struct snd_gus_card * gus, unsigned short v_min, unsigned short v_max)
{
short i, ramp_ok;
unsigned short ramp_end;
if (!in_interrupt()) { /* this can't be done in interrupt */
for (i = v_min, ramp_ok = 0; i <= v_max; i++) {
guard(spinlock_irqsave)(&gus->reg_lock);
snd_gf1_select_voice(gus, i);
ramp_end = snd_gf1_read16(gus, 9) >> 8;
if (ramp_end > SNDRV_GF1_MIN_OFFSET) {
ramp_ok++;
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_RATE, 20); /* ramp rate */
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_START, SNDRV_GF1_MIN_OFFSET); /* ramp start */
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_END, ramp_end); /* ramp end */
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40); /* ramp down */
if (gus->gf1.enh_mode) {
snd_gf1_delay(gus);
snd_gf1_write8(gus, SNDRV_GF1_VB_VOLUME_CONTROL, 0x40);
}
}
}
msleep_interruptible(50);
}
snd_gf1_clear_voices(gus, v_min, v_max);
}
static void snd_gf1_alloc_voice_use(struct snd_gus_card * gus,
struct snd_gus_voice * pvoice,
int type, int client, int port)
{
pvoice->use = 1;
switch (type) {
case SNDRV_GF1_VOICE_TYPE_PCM:
gus->gf1.pcm_alloc_voices++;
pvoice->pcm = 1;
break;
case SNDRV_GF1_VOICE_TYPE_SYNTH:
pvoice->synth = 1;
pvoice->client = client;
pvoice->port = port;
break;
case SNDRV_GF1_VOICE_TYPE_MIDI:
pvoice->midi = 1;
pvoice->client = client;
pvoice->port = port;
break;
}
}
struct snd_gus_voice *snd_gf1_alloc_voice(struct snd_gus_card * gus, int type, int client, int port)
{
struct snd_gus_voice *pvoice;
int idx;
guard(spinlock_irqsave)(&gus->voice_alloc);
if (type == SNDRV_GF1_VOICE_TYPE_PCM) {
if (gus->gf1.pcm_alloc_voices >= gus->gf1.pcm_channels)
return NULL;
}
for (idx = 0; idx < 32; idx++) {
pvoice = &gus->gf1.voices[idx];
if (!pvoice->use) {
snd_gf1_alloc_voice_use(gus, pvoice, type, client, port);
return pvoice;
}
}
for (idx = 0; idx < 32; idx++) {
pvoice = &gus->gf1.voices[idx];
if (pvoice->midi && !pvoice->client) {
snd_gf1_clear_voices(gus, pvoice->number, pvoice->number);
snd_gf1_alloc_voice_use(gus, pvoice, type, client, port);
return pvoice;
}
}
return NULL;
}
void snd_gf1_free_voice(struct snd_gus_card * gus, struct snd_gus_voice *voice)
{
void (*private_free)(struct snd_gus_voice *voice);
if (voice == NULL || !voice->use)
return;
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | voice->number);
snd_gf1_clear_voices(gus, voice->number, voice->number);
scoped_guard(spinlock_irqsave, &gus->voice_alloc) {
private_free = voice->private_free;
voice->private_free = NULL;
voice->private_data = NULL;
if (voice->pcm)
gus->gf1.pcm_alloc_voices--;
voice->use = voice->pcm = 0;
voice->sample_ops = NULL;
}
if (private_free)
private_free(voice);
}
static void snd_gf1_init_software_state(struct snd_gus_card *gus)
{
unsigned int i;
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_ALL);
for (i = 0; i < 32; i++) {
gus->gf1.voices[i].number = i;
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_VOICE | i);
}
}
static void snd_gf1_hw_start(struct snd_gus_card *gus, bool initial)
{
unsigned int i;
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */
udelay(160);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* disable IRQ & DAC */
udelay(160);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_JOYSTICK_DAC_LEVEL, gus->joystick_dac);
if (initial) {
snd_gf1_init_software_state(gus);
snd_gf1_uart_cmd(gus, 0x03);
} else {
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
outb(0x03, GUSP(gus, MIDICTRL));
}
if (gus->gf1.enh_mode) { /* enhanced mode !!!! */
snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
}
snd_gf1_clear_regs(gus);
snd_gf1_select_active_voices(gus);
snd_gf1_delay(gus);
gus->gf1.default_voice_address = gus->gf1.memory > 0 ? 0 : 512 - 8;
gus->gf1.hw_lfo = 0;
gus->gf1.sw_lfo = 0;
/* initialize LFOs & clear LFOs memory */
if (gus->gf1.enh_mode && gus->gf1.memory) {
gus->gf1.hw_lfo = 1;
gus->gf1.default_voice_address += 1024;
} else {
gus->gf1.sw_lfo = 1;
}
if (gus->gf1.memory > 0)
for (i = 0; i < 4; i++)
snd_gf1_poke(gus, gus->gf1.default_voice_address + i, 0);
snd_gf1_clear_regs(gus);
snd_gf1_clear_voices(gus, 0, 31);
snd_gf1_look_regs(gus);
udelay(160);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 7); /* Reset Register = IRQ enable, DAC enable */
udelay(160);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 7); /* Reset Register = IRQ enable, DAC enable */
if (gus->gf1.enh_mode) { /* enhanced mode !!!! */
snd_gf1_i_write8(gus, SNDRV_GF1_GB_GLOBAL_MODE, snd_gf1_i_look8(gus, SNDRV_GF1_GB_GLOBAL_MODE) | 0x01);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_MEMORY_CONTROL, 0x01);
}
while ((snd_gf1_i_read8(gus, SNDRV_GF1_GB_VOICES_IRQ) & 0xc0) != 0xc0);
scoped_guard(spinlock_irqsave, &gus->reg_lock) {
outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE));
outb(gus->mix_cntrl_reg, GUSP(gus, MIXCNTRLREG));
}
}
int snd_gf1_start(struct snd_gus_card *gus)
{
/*
* Probe-time startup initializes both GF1 hardware and the
* software state that suspend/resume keeps across PM cycles.
*/
snd_gf1_hw_start(gus, true);
snd_gf1_timers_init(gus);
snd_gf1_look_regs(gus);
snd_gf1_mem_init(gus);
snd_gf1_mem_proc_init(gus);
#ifdef CONFIG_SND_DEBUG
snd_gus_irq_profile_init(gus);
#endif
#if 0
if (gus->pnp_flag) {
if (gus->chip.playback_fifo_size > 0)
snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_RECORD_BASE_ADDR, gus->chip.playback_fifo_block->ptr >> 8);
if (gus->chip.record_fifo_size > 0)
snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_PLAY_BASE_ADDR, gus->chip.record_fifo_block->ptr >> 8);
snd_gf1_i_write16(gus, SNDRV_GF1_GW_FIFO_SIZE, gus->chip.interwave_fifo_reg);
}
#endif
return 0;
}
/*
* call this function only by shutdown of driver
*/
int snd_gf1_stop(struct snd_gus_card * gus)
{
snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0); /* stop all timers */
snd_gf1_stop_voices(gus, 0, 31); /* stop all voices */
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* disable IRQ & DAC */
snd_gf1_timers_done(gus);
snd_gf1_mem_done(gus);
return 0;
}
int snd_gf1_suspend(struct snd_gus_card *gus)
{
snd_gf1_dma_suspend(gus);
snd_gf1_uart_suspend(gus);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, 0);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL, 0);
snd_gf1_i_look8(gus, SNDRV_GF1_GB_REC_DMA_CONTROL);
snd_gf1_stop_voices(gus, 0, 31);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1);
snd_dma_disable(gus->gf1.dma2);
return 0;
}
int snd_gf1_resume(struct snd_gus_card *gus)
{
snd_gf1_hw_start(gus, false);
snd_gf1_timers_resume(gus);
snd_gf1_uart_resume(gus);
return 0;
}
+75
View File
@@ -0,0 +1,75 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*/
#define SNDRV_GF1_SCALE_TABLE_SIZE 128
#define SNDRV_GF1_ATTEN_TABLE_SIZE 128
#ifdef __GUS_TABLES_ALLOC__
#if 0
unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE] =
{
8372, 8870, 9397, 9956, 10548, 11175,
11840, 12544, 13290, 14080, 14917, 15804,
16744, 17740, 18795, 19912, 21096, 22351,
23680, 25088, 26580, 28160, 29834, 31609,
33488, 35479, 37589, 39824, 42192, 44701,
47359, 50175, 53159, 56320, 59669, 63217,
66976, 70959, 75178, 79649, 84385, 89402,
94719, 100351, 106318, 112640, 119338, 126434,
133952, 141918, 150356, 159297, 168769, 178805,
189437, 200702, 212636, 225280, 238676, 252868,
267905, 283835, 300713, 318594, 337539, 357610,
378874, 401403, 425272, 450560, 477352, 505737,
535809, 567670, 601425, 637188, 675077, 715219,
757749, 802807, 850544, 901120, 954703, 1011473,
1071618, 1135340, 1202851, 1274376, 1350154, 1430439,
1515497, 1605613, 1701088, 1802240, 1909407, 2022946,
2143237, 2270680, 2405702, 2548752, 2700309, 2860878,
3030994, 3211227, 3402176, 3604480, 3818814, 4045892,
4286473, 4541360, 4811404, 5097505, 5400618, 5721755,
6061989, 6422453, 6804352, 7208960, 7637627, 8091784,
8572947, 9082720, 9622807, 10195009, 10801236, 11443511,
12123977, 12844906
};
#endif /* 0 */
unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE] = {
4095 /* 0 */,1789 /* 1 */,1533 /* 2 */,1383 /* 3 */,1277 /* 4 */,
1195 /* 5 */,1127 /* 6 */,1070 /* 7 */,1021 /* 8 */,978 /* 9 */,
939 /* 10 */,903 /* 11 */,871 /* 12 */,842 /* 13 */,814 /* 14 */,
789 /* 15 */,765 /* 16 */,743 /* 17 */,722 /* 18 */,702 /* 19 */,
683 /* 20 */,665 /* 21 */,647 /* 22 */,631 /* 23 */,615 /* 24 */,
600 /* 25 */,586 /* 26 */,572 /* 27 */,558 /* 28 */,545 /* 29 */,
533 /* 30 */,521 /* 31 */,509 /* 32 */,498 /* 33 */,487 /* 34 */,
476 /* 35 */,466 /* 36 */,455 /* 37 */,446 /* 38 */,436 /* 39 */,
427 /* 40 */,418 /* 41 */,409 /* 42 */,400 /* 43 */,391 /* 44 */,
383 /* 45 */,375 /* 46 */,367 /* 47 */,359 /* 48 */,352 /* 49 */,
344 /* 50 */,337 /* 51 */,330 /* 52 */,323 /* 53 */,316 /* 54 */,
309 /* 55 */,302 /* 56 */,296 /* 57 */,289 /* 58 */,283 /* 59 */,
277 /* 60 */,271 /* 61 */,265 /* 62 */,259 /* 63 */,253 /* 64 */,
247 /* 65 */,242 /* 66 */,236 /* 67 */,231 /* 68 */,225 /* 69 */,
220 /* 70 */,215 /* 71 */,210 /* 72 */,205 /* 73 */,199 /* 74 */,
195 /* 75 */,190 /* 76 */,185 /* 77 */,180 /* 78 */,175 /* 79 */,
171 /* 80 */,166 /* 81 */,162 /* 82 */,157 /* 83 */,153 /* 84 */,
148 /* 85 */,144 /* 86 */,140 /* 87 */,135 /* 88 */,131 /* 89 */,
127 /* 90 */,123 /* 91 */,119 /* 92 */,115 /* 93 */,111 /* 94 */,
107 /* 95 */,103 /* 96 */,100 /* 97 */,96 /* 98 */,92 /* 99 */,
88 /* 100 */,85 /* 101 */,81 /* 102 */,77 /* 103 */,74 /* 104 */,
70 /* 105 */,67 /* 106 */,63 /* 107 */,60 /* 108 */,56 /* 109 */,
53 /* 110 */,50 /* 111 */,46 /* 112 */,43 /* 113 */,40 /* 114 */,
37 /* 115 */,33 /* 116 */,30 /* 117 */,27 /* 118 */,24 /* 119 */,
21 /* 120 */,18 /* 121 */,15 /* 122 */,12 /* 123 */,9 /* 124 */,
6 /* 125 */,3 /* 126 */,0 /* 127 */,
};
#else
extern unsigned int snd_gf1_scale_table[SNDRV_GF1_SCALE_TABLE_SIZE];
extern unsigned short snd_gf1_atten_table[SNDRV_GF1_ATTEN_TABLE_SIZE];
#endif
+194
View File
@@ -0,0 +1,194 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Routines for Gravis UltraSound soundcards - Timers
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*
* GUS have similar timers as AdLib (OPL2/OPL3 chips).
*/
#include <linux/time.h>
#include <sound/core.h>
#include <sound/gus.h>
/*
* Timer 1 - 80us
*/
static int snd_gf1_timer1_start(struct snd_timer * timer)
{
unsigned char tmp;
unsigned int ticks;
struct snd_gus_card *gus;
gus = snd_timer_chip(timer);
guard(spinlock_irqsave)(&gus->reg_lock);
ticks = timer->sticks;
tmp = (gus->gf1.timer_enabled |= 4);
snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_1, 256 - ticks); /* timer 1 count */
snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 1 IRQ */
snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */
return 0;
}
static int snd_gf1_timer1_stop(struct snd_timer * timer)
{
unsigned char tmp;
struct snd_gus_card *gus;
gus = snd_timer_chip(timer);
guard(spinlock_irqsave)(&gus->reg_lock);
tmp = (gus->gf1.timer_enabled &= ~4);
snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */
return 0;
}
/*
* Timer 2 - 320us
*/
static int snd_gf1_timer2_start(struct snd_timer * timer)
{
unsigned char tmp;
unsigned int ticks;
struct snd_gus_card *gus;
gus = snd_timer_chip(timer);
guard(spinlock_irqsave)(&gus->reg_lock);
ticks = timer->sticks;
tmp = (gus->gf1.timer_enabled |= 8);
snd_gf1_write8(gus, SNDRV_GF1_GB_ADLIB_TIMER_2, 256 - ticks); /* timer 2 count */
snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* enable timer 2 IRQ */
snd_gf1_adlib_write(gus, 0x04, tmp >> 2); /* timer 2 start */
return 0;
}
static int snd_gf1_timer2_stop(struct snd_timer * timer)
{
unsigned char tmp;
struct snd_gus_card *gus;
gus = snd_timer_chip(timer);
guard(spinlock_irqsave)(&gus->reg_lock);
tmp = (gus->gf1.timer_enabled &= ~8);
snd_gf1_write8(gus, SNDRV_GF1_GB_SOUND_BLASTER_CONTROL, tmp); /* disable timer #1 */
return 0;
}
/*
*/
static void snd_gf1_interrupt_timer1(struct snd_gus_card * gus)
{
struct snd_timer *timer = gus->gf1.timer1;
if (timer == NULL)
return;
snd_timer_interrupt(timer, timer->sticks);
}
static void snd_gf1_interrupt_timer2(struct snd_gus_card * gus)
{
struct snd_timer *timer = gus->gf1.timer2;
if (timer == NULL)
return;
snd_timer_interrupt(timer, timer->sticks);
}
/*
*/
static const struct snd_timer_hardware snd_gf1_timer1 =
{
.flags = SNDRV_TIMER_HW_STOP,
.resolution = 80000,
.ticks = 256,
.start = snd_gf1_timer1_start,
.stop = snd_gf1_timer1_stop,
};
static const struct snd_timer_hardware snd_gf1_timer2 =
{
.flags = SNDRV_TIMER_HW_STOP,
.resolution = 320000,
.ticks = 256,
.start = snd_gf1_timer2_start,
.stop = snd_gf1_timer2_stop,
};
static void snd_gf1_timer1_free(struct snd_timer *timer)
{
struct snd_gus_card *gus = timer->private_data;
gus->gf1.timer1 = NULL;
}
static void snd_gf1_timer2_free(struct snd_timer *timer)
{
struct snd_gus_card *gus = timer->private_data;
gus->gf1.timer2 = NULL;
}
void snd_gf1_timers_init(struct snd_gus_card * gus)
{
struct snd_timer *timer;
struct snd_timer_id tid;
if (gus->gf1.timer1 != NULL || gus->gf1.timer2 != NULL)
return;
gus->gf1.interrupt_handler_timer1 = snd_gf1_interrupt_timer1;
gus->gf1.interrupt_handler_timer2 = snd_gf1_interrupt_timer2;
tid.dev_class = SNDRV_TIMER_CLASS_CARD;
tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
tid.card = gus->card->number;
tid.device = gus->timer_dev;
tid.subdevice = 0;
if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) {
strscpy(timer->name, "GF1 timer #1");
timer->private_data = gus;
timer->private_free = snd_gf1_timer1_free;
timer->hw = snd_gf1_timer1;
}
gus->gf1.timer1 = timer;
tid.device++;
if (snd_timer_new(gus->card, "GF1 timer", &tid, &timer) >= 0) {
strscpy(timer->name, "GF1 timer #2");
timer->private_data = gus;
timer->private_free = snd_gf1_timer2_free;
timer->hw = snd_gf1_timer2;
}
gus->gf1.timer2 = timer;
}
void snd_gf1_timers_done(struct snd_gus_card * gus)
{
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_TIMER1 | SNDRV_GF1_HANDLER_TIMER2);
if (gus->gf1.timer1) {
snd_device_free(gus->card, gus->gf1.timer1);
gus->gf1.timer1 = NULL;
}
if (gus->gf1.timer2) {
snd_device_free(gus->card, gus->gf1.timer2);
gus->gf1.timer2 = NULL;
}
}
void snd_gf1_timers_resume(struct snd_gus_card *gus)
{
if (gus->gf1.timer1) {
gus->gf1.interrupt_handler_timer1 = snd_gf1_interrupt_timer1;
if (gus->gf1.timer_enabled & 4)
snd_gf1_timer1_start(gus->gf1.timer1);
}
if (gus->gf1.timer2) {
gus->gf1.interrupt_handler_timer2 = snd_gf1_interrupt_timer2;
if (gus->gf1.timer_enabled & 8)
snd_gf1_timer2_start(gus->gf1.timer2);
}
}
+281
View File
@@ -0,0 +1,281 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
* Routines for the GF1 MIDI interface - like UART 6850
*/
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <sound/core.h>
#include <sound/gus.h>
static void snd_gf1_interrupt_midi_in(struct snd_gus_card * gus)
{
int count;
unsigned char stat, byte;
__always_unused unsigned char data;
unsigned long flags;
count = 10;
while (count) {
spin_lock_irqsave(&gus->uart_cmd_lock, flags);
stat = snd_gf1_uart_stat(gus);
if (!(stat & 0x01)) { /* data in Rx FIFO? */
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
count--;
continue;
}
count = 100; /* arm counter to new value */
data = snd_gf1_uart_get(gus);
if (!(gus->gf1.uart_cmd & 0x80)) {
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
continue;
}
if (stat & 0x10) { /* framing error */
gus->gf1.uart_framing++;
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
continue;
}
byte = snd_gf1_uart_get(gus);
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
snd_rawmidi_receive(gus->midi_substream_input, &byte, 1);
if (stat & 0x20) {
gus->gf1.uart_overrun++;
}
}
}
static void snd_gf1_interrupt_midi_out(struct snd_gus_card * gus)
{
char byte;
/* try unlock output */
if (snd_gf1_uart_stat(gus) & 0x01)
snd_gf1_interrupt_midi_in(gus);
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (snd_gf1_uart_stat(gus) & 0x02) { /* Tx FIFO free? */
if (snd_rawmidi_transmit(gus->midi_substream_output, &byte, 1) != 1) { /* no other bytes or error */
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20); /* disable Tx interrupt */
} else {
snd_gf1_uart_put(gus, byte);
}
}
}
static void snd_gf1_uart_reset(struct snd_gus_card * gus, int close)
{
snd_gf1_uart_cmd(gus, 0x03); /* reset */
if (!close && gus->uart_enable) {
udelay(160);
snd_gf1_uart_cmd(gus, 0x00); /* normal operations */
}
}
static int snd_gf1_uart_output_open(struct snd_rawmidi_substream *substream)
{
struct snd_gus_card *gus;
gus = substream->rmidi->private_data;
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (!(gus->gf1.uart_cmd & 0x80)) { /* input active? */
snd_gf1_uart_reset(gus, 0);
}
gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out;
gus->midi_substream_output = substream;
#if 0
dev_dbg(gus->card->dev,
"write init - cmd = 0x%x, stat = 0x%x\n",
gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
#endif
return 0;
}
static int snd_gf1_uart_input_open(struct snd_rawmidi_substream *substream)
{
struct snd_gus_card *gus;
int i;
gus = substream->rmidi->private_data;
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out) {
snd_gf1_uart_reset(gus, 0);
}
gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in;
gus->midi_substream_input = substream;
if (gus->uart_enable) {
for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++)
snd_gf1_uart_get(gus); /* clean Rx */
if (i >= 1000)
dev_err(gus->card->dev, "gus midi uart init read - cleanup error\n");
}
#if 0
dev_dbg(gus->card->dev,
"read init - enable = %i, cmd = 0x%x, stat = 0x%x\n",
gus->uart_enable, gus->gf1.uart_cmd, snd_gf1_uart_stat(gus));
dev_dbg(gus->card->dev,
"[0x%x] reg (ctrl/status) = 0x%x, reg (data) = 0x%x (page = 0x%x)\n",
gus->gf1.port + 0x100, inb(gus->gf1.port + 0x100),
inb(gus->gf1.port + 0x101), inb(gus->gf1.port + 0x102));
#endif
return 0;
}
static int snd_gf1_uart_output_close(struct snd_rawmidi_substream *substream)
{
struct snd_gus_card *gus;
gus = substream->rmidi->private_data;
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (gus->gf1.interrupt_handler_midi_in != snd_gf1_interrupt_midi_in)
snd_gf1_uart_reset(gus, 1);
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_OUT);
gus->midi_substream_output = NULL;
return 0;
}
static int snd_gf1_uart_input_close(struct snd_rawmidi_substream *substream)
{
struct snd_gus_card *gus;
gus = substream->rmidi->private_data;
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (gus->gf1.interrupt_handler_midi_out != snd_gf1_interrupt_midi_out)
snd_gf1_uart_reset(gus, 1);
snd_gf1_set_default_handlers(gus, SNDRV_GF1_HANDLER_MIDI_IN);
gus->midi_substream_input = NULL;
return 0;
}
static void snd_gf1_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
{
struct snd_gus_card *gus;
gus = substream->rmidi->private_data;
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (up) {
if ((gus->gf1.uart_cmd & 0x80) == 0)
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x80); /* enable Rx interrupts */
} else {
if (gus->gf1.uart_cmd & 0x80)
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x80); /* disable Rx interrupts */
}
}
static void snd_gf1_uart_output_trigger(struct snd_rawmidi_substream *substream, int up)
{
unsigned long flags;
struct snd_gus_card *gus;
char byte;
int timeout;
gus = substream->rmidi->private_data;
spin_lock_irqsave(&gus->uart_cmd_lock, flags);
if (up) {
if ((gus->gf1.uart_cmd & 0x20) == 0) {
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
/* wait for empty Rx - Tx is probably unlocked */
timeout = 10000;
while (timeout-- > 0 && snd_gf1_uart_stat(gus) & 0x01);
/* Tx FIFO free? */
spin_lock_irqsave(&gus->uart_cmd_lock, flags);
if (gus->gf1.uart_cmd & 0x20) {
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
return;
}
if (snd_gf1_uart_stat(gus) & 0x02) {
if (snd_rawmidi_transmit(substream, &byte, 1) != 1) {
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
return;
}
snd_gf1_uart_put(gus, byte);
}
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd | 0x20); /* enable Tx interrupt */
}
} else {
if (gus->gf1.uart_cmd & 0x20)
snd_gf1_uart_cmd(gus, gus->gf1.uart_cmd & ~0x20);
}
spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
}
static const struct snd_rawmidi_ops snd_gf1_uart_output =
{
.open = snd_gf1_uart_output_open,
.close = snd_gf1_uart_output_close,
.trigger = snd_gf1_uart_output_trigger,
};
static const struct snd_rawmidi_ops snd_gf1_uart_input =
{
.open = snd_gf1_uart_input_open,
.close = snd_gf1_uart_input_close,
.trigger = snd_gf1_uart_input_trigger,
};
int snd_gf1_rawmidi_new(struct snd_gus_card *gus, int device)
{
struct snd_rawmidi *rmidi;
int err;
err = snd_rawmidi_new(gus->card, "GF1", device, 1, 1, &rmidi);
if (err < 0)
return err;
strscpy(rmidi->name, gus->interwave ? "AMD InterWave" : "GF1");
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_gf1_uart_output);
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_gf1_uart_input);
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
rmidi->private_data = gus;
gus->midi_uart = rmidi;
return err;
}
void snd_gf1_uart_suspend(struct snd_gus_card *gus)
{
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
outb(0x03, GUSP(gus, MIDICTRL));
}
void snd_gf1_uart_resume(struct snd_gus_card *gus)
{
unsigned short uart_cmd;
bool active;
int i;
scoped_guard(spinlock_irqsave, &gus->uart_cmd_lock) {
active = gus->midi_substream_input || gus->midi_substream_output;
}
if (!active)
return;
/* snd_gf1_hw_start() already left MIDICTRL in reset. */
usleep_range(160, 200);
guard(spinlock_irqsave)(&gus->uart_cmd_lock);
if (!gus->midi_substream_input && !gus->midi_substream_output)
return;
if (gus->midi_substream_output)
gus->gf1.interrupt_handler_midi_out = snd_gf1_interrupt_midi_out;
if (gus->midi_substream_input)
gus->gf1.interrupt_handler_midi_in = snd_gf1_interrupt_midi_in;
if (!gus->uart_enable)
return;
uart_cmd = gus->gf1.uart_cmd;
snd_gf1_uart_cmd(gus, 0x00);
if (gus->midi_substream_input) {
for (i = 0; i < 1000 && (snd_gf1_uart_stat(gus) & 0x01); i++)
snd_gf1_uart_get(gus);
if (i >= 1000)
dev_err(gus->card->dev,
"gus midi uart resume - cleanup error\n");
}
snd_gf1_uart_cmd(gus, uart_cmd);
}
+205
View File
@@ -0,0 +1,205 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*/
#include <linux/time.h>
#include <linux/export.h>
#include <sound/core.h>
#include <sound/gus.h>
#define __GUS_TABLES_ALLOC__
#include "gus_tables.h"
EXPORT_SYMBOL(snd_gf1_atten_table); /* for snd-gus-synth module */
unsigned short snd_gf1_lvol_to_gvol_raw(unsigned int vol)
{
unsigned short e, m, tmp;
if (vol > 65535)
vol = 65535;
tmp = vol;
e = 7;
if (tmp < 128) {
while (e > 0 && tmp < (1 << e))
e--;
} else {
while (tmp > 255) {
tmp >>= 1;
e++;
}
}
m = vol - (1 << e);
if (m > 0) {
if (e > 8)
m >>= e - 8;
else if (e < 8)
m <<= 8 - e;
m &= 255;
}
return (e << 8) | m;
}
#if 0
unsigned int snd_gf1_gvol_to_lvol_raw(unsigned short gf1_vol)
{
unsigned int rvol;
unsigned short e, m;
if (!gf1_vol)
return 0;
e = gf1_vol >> 8;
m = (unsigned char) gf1_vol;
rvol = 1 << e;
if (e > 8)
return rvol | (m << (e - 8));
return rvol | (m >> (8 - e));
}
unsigned int snd_gf1_calc_ramp_rate(struct snd_gus_card * gus,
unsigned short start,
unsigned short end,
unsigned int us)
{
static const unsigned char vol_rates[19] =
{
23, 24, 26, 28, 29, 31, 32, 34,
36, 37, 39, 40, 42, 44, 45, 47,
49, 50, 52
};
unsigned short range, increment, value, i;
start >>= 4;
end >>= 4;
if (start < end)
us /= end - start;
else
us /= start - end;
range = 4;
value = gus->gf1.enh_mode ?
vol_rates[0] :
vol_rates[gus->gf1.active_voices - 14];
for (i = 0; i < 3; i++) {
if (us < value) {
range = i;
break;
} else
value <<= 3;
}
if (range == 4) {
range = 3;
increment = 1;
} else
increment = (value + (value >> 1)) / us;
return (range << 6) | (increment & 0x3f);
}
#endif /* 0 */
unsigned short snd_gf1_translate_freq(struct snd_gus_card * gus, unsigned int freq16)
{
freq16 >>= 3;
if (freq16 < 50)
freq16 = 50;
if (freq16 & 0xf8000000) {
freq16 = ~0xf8000000;
dev_err(gus->card->dev, "%s: overflow - freq = 0x%x\n",
__func__, freq16);
}
return ((freq16 << 9) + (gus->gf1.playback_freq >> 1)) / gus->gf1.playback_freq;
}
#if 0
short snd_gf1_compute_vibrato(short cents, unsigned short fc_register)
{
static const short vibrato_table[] =
{
0, 0, 32, 592, 61, 1175, 93, 1808,
124, 2433, 152, 3007, 182, 3632, 213, 4290,
241, 4834, 255, 5200
};
long depth;
const short *vi1, *vi2;
short pcents, v1;
pcents = cents < 0 ? -cents : cents;
for (vi1 = vibrato_table, vi2 = vi1 + 2; pcents > *vi2; vi1 = vi2, vi2 += 2);
v1 = *(vi1 + 1);
/* The FC table above is a list of pairs. The first number in the pair */
/* is the cents index from 0-255 cents, and the second number in the */
/* pair is the FC adjustment needed to change the pitch by the indexed */
/* number of cents. The table was created for an FC of 32768. */
/* The following expression does a linear interpolation against the */
/* approximated log curve in the table above, and then scales the number */
/* by the FC before the LFO. This calculation also adjusts the output */
/* value to produce the appropriate depth for the hardware. The depth */
/* is 2 * desired FC + 1. */
depth = (((int) (*(vi2 + 1) - *vi1) * (pcents - *vi1) / (*vi2 - *vi1)) + v1) * fc_register >> 14;
if (depth)
depth++;
if (depth > 255)
depth = 255;
return cents < 0 ? -(short) depth : (short) depth;
}
unsigned short snd_gf1_compute_pitchbend(unsigned short pitchbend, unsigned short sens)
{
static const long log_table[] = {1024, 1085, 1149, 1218, 1290, 1367, 1448, 1534, 1625, 1722, 1825, 1933};
int wheel, sensitivity;
unsigned int mantissa, f1, f2;
unsigned short semitones, f1_index, f2_index, f1_power, f2_power;
char bend_down = 0;
int bend;
if (!sens)
return 1024;
wheel = (int) pitchbend - 8192;
sensitivity = ((int) sens * wheel) / 128;
if (sensitivity < 0) {
bend_down = 1;
sensitivity = -sensitivity;
}
semitones = (unsigned int) (sensitivity >> 13);
mantissa = sensitivity % 8192;
f1_index = semitones % 12;
f2_index = (semitones + 1) % 12;
f1_power = semitones / 12;
f2_power = (semitones + 1) / 12;
f1 = log_table[f1_index] << f1_power;
f2 = log_table[f2_index] << f2_power;
bend = (int) ((((f2 - f1) * mantissa) >> 13) + f1);
if (bend_down)
bend = 1048576L / bend;
return bend;
}
unsigned short snd_gf1_compute_freq(unsigned int freq,
unsigned int rate,
unsigned short mix_rate)
{
unsigned int fc;
int scale = 0;
while (freq >= 4194304L) {
scale++;
freq >>= 1;
}
fc = (freq << 10) / rate;
if (fc > 97391L) {
fc = 97391;
pr_err("patch: (1) fc frequency overflow - %u\n", fc);
}
fc = (fc * 44100UL) / mix_rate;
while (scale--)
fc <<= 1;
if (fc > 65535L) {
fc = 65535;
pr_err("patch: (2) fc frequency overflow - %u\n", fc);
}
return (unsigned short) fc;
}
#endif /* 0 */
+226
View File
@@ -0,0 +1,226 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Gravis UltraSound Classic soundcard
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*/
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <linux/module.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/gus.h>
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>
#define CRD_NAME "Gravis UltraSound Classic"
#define DEV_NAME "gusclassic"
MODULE_DESCRIPTION(CRD_NAME);
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x230,0x240,0x250,0x260 */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 3,5,9,11,12,15 */
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */
static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */
static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29};
/* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */
static int channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24};
static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for " CRD_NAME " driver.");
module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for " CRD_NAME " driver.");
module_param_array(joystick_dac, int, NULL, 0444);
MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for " CRD_NAME " driver.");
module_param_array(channels, int, NULL, 0444);
MODULE_PARM_DESC(channels, "GF1 channels for " CRD_NAME " driver.");
module_param_array(pcm_channels, int, NULL, 0444);
MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for " CRD_NAME " driver.");
static int snd_gusclassic_match(struct device *dev, unsigned int n)
{
return enable[n];
}
static int snd_gusclassic_create(struct snd_card *card,
struct device *dev, unsigned int n,
struct snd_gus_card **rgus)
{
static const long possible_ports[] = {0x220, 0x230, 0x240, 0x250, 0x260};
static const int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, 4, -1};
static const int possible_dmas[] = {5, 6, 7, 1, 3, -1};
int i, error;
if (irq[n] == SNDRV_AUTO_IRQ) {
irq[n] = snd_legacy_find_free_irq(possible_irqs);
if (irq[n] < 0) {
dev_err(dev, "unable to find a free IRQ\n");
return -EBUSY;
}
}
if (dma1[n] == SNDRV_AUTO_DMA) {
dma1[n] = snd_legacy_find_free_dma(possible_dmas);
if (dma1[n] < 0) {
dev_err(dev, "unable to find a free DMA1\n");
return -EBUSY;
}
}
if (dma2[n] == SNDRV_AUTO_DMA) {
dma2[n] = snd_legacy_find_free_dma(possible_dmas);
if (dma2[n] < 0) {
dev_err(dev, "unable to find a free DMA2\n");
return -EBUSY;
}
}
if (port[n] != SNDRV_AUTO_PORT)
return snd_gus_create(card, port[n], irq[n], dma1[n], dma2[n],
0, channels[n], pcm_channels[n], 0, rgus);
i = 0;
do {
port[n] = possible_ports[i];
error = snd_gus_create(card, port[n], irq[n], dma1[n], dma2[n],
0, channels[n], pcm_channels[n], 0, rgus);
} while (error < 0 && ++i < ARRAY_SIZE(possible_ports));
return error;
}
static int snd_gusclassic_detect(struct snd_gus_card *gus)
{
unsigned char d;
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */
d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
if ((d & 0x07) != 0) {
dev_dbg(gus->card->dev, "[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
udelay(160);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */
udelay(160);
d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
if ((d & 0x07) != 1) {
dev_dbg(gus->card->dev, "[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
return 0;
}
static int snd_gusclassic_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
struct snd_gus_card *gus;
int error;
error = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE, 0, &card);
if (error < 0)
return error;
if (pcm_channels[n] < 2)
pcm_channels[n] = 2;
error = snd_gusclassic_create(card, dev, n, &gus);
if (error < 0)
return error;
card->private_data = gus;
error = snd_gusclassic_detect(gus);
if (error < 0)
return error;
gus->joystick_dac = joystick_dac[n];
error = snd_gus_initialize(gus);
if (error < 0)
return error;
error = -ENODEV;
if (gus->max_flag || gus->ess_flag) {
dev_err(dev, "GUS Classic or ACE soundcard was "
"not detected at 0x%lx\n", gus->gf1.port);
return error;
}
error = snd_gf1_new_mixer(gus);
if (error < 0)
return error;
error = snd_gf1_pcm_new(gus, 0, 0);
if (error < 0)
return error;
if (!gus->ace_flag) {
error = snd_gf1_rawmidi_new(gus, 0);
if (error < 0)
return error;
}
sprintf(card->longname + strlen(card->longname),
" at 0x%lx, irq %d, dma %d",
gus->gf1.port, gus->gf1.irq, gus->gf1.dma1);
if (gus->gf1.dma2 >= 0)
sprintf(card->longname + strlen(card->longname),
"&%d", gus->gf1.dma2);
error = snd_card_register(card);
if (error < 0)
return error;
dev_set_drvdata(dev, card);
return 0;
}
#ifdef CONFIG_PM
static int snd_gusclassic_suspend(struct device *dev, unsigned int n,
pm_message_t state)
{
struct snd_card *card = dev_get_drvdata(dev);
return snd_gus_suspend(card->private_data);
}
static int snd_gusclassic_resume(struct device *dev, unsigned int n)
{
struct snd_card *card = dev_get_drvdata(dev);
return snd_gus_resume(card->private_data);
}
#endif
static struct isa_driver snd_gusclassic_driver = {
.match = snd_gusclassic_match,
.probe = snd_gusclassic_probe,
#ifdef CONFIG_PM
.suspend = snd_gusclassic_suspend,
.resume = snd_gusclassic_resume,
#endif
.driver = {
.name = DEV_NAME
}
};
module_isa_driver(snd_gusclassic_driver, SNDRV_CARDS);
+375
View File
@@ -0,0 +1,375 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Gravis UltraSound Extreme soundcards
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*/
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <linux/module.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/gus.h>
#include <sound/es1688.h>
#include <sound/mpu401.h>
#include <sound/opl3.h>
#define SNDRV_LEGACY_AUTO_PROBE
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>
#define CRD_NAME "Gravis UltraSound Extreme"
#define DEV_NAME "gusextreme"
MODULE_DESCRIPTION(CRD_NAME);
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x240,0x260 */
static long gf1_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x210,0x220,0x230,0x240,0x250,0x260,0x270 */
static long mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS) - 1] = -1}; /* 0x300,0x310,0x320 */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */
static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 5,7,9,10 */
static int gf1_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */
static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 0,1,3 */
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29};
/* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */
static int channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24};
static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
struct snd_gusextreme {
struct snd_es1688 es1688;
struct snd_gus_card *gus;
};
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
module_param_hw_array(gf1_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(gf1_port, "GF1 port # for " CRD_NAME " driver (optional).");
module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
module_param_hw_array(gf1_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(gf1_irq, "GF1 IRQ # for " CRD_NAME " driver.");
module_param_hw_array(dma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma8, "8-bit DMA # for " CRD_NAME " driver.");
module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "GF1 DMA # for " CRD_NAME " driver.");
module_param_array(joystick_dac, int, NULL, 0444);
MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for " CRD_NAME " driver.");
module_param_array(channels, int, NULL, 0444);
MODULE_PARM_DESC(channels, "GF1 channels for " CRD_NAME " driver.");
module_param_array(pcm_channels, int, NULL, 0444);
MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for " CRD_NAME " driver.");
static int snd_gusextreme_match(struct device *dev, unsigned int n)
{
return enable[n];
}
static int snd_gusextreme_es1688_create(struct snd_card *card,
struct snd_es1688 *chip,
struct device *dev, unsigned int n)
{
static const long possible_ports[] = {0x220, 0x240, 0x260};
static const int possible_irqs[] = {5, 9, 10, 7, -1};
static const int possible_dmas[] = {1, 3, 0, -1};
int i, error;
if (irq[n] == SNDRV_AUTO_IRQ) {
irq[n] = snd_legacy_find_free_irq(possible_irqs);
if (irq[n] < 0) {
dev_err(dev, "unable to find a free IRQ for ES1688\n");
return -EBUSY;
}
}
if (dma8[n] == SNDRV_AUTO_DMA) {
dma8[n] = snd_legacy_find_free_dma(possible_dmas);
if (dma8[n] < 0) {
dev_err(dev, "unable to find a free DMA for ES1688\n");
return -EBUSY;
}
}
if (port[n] != SNDRV_AUTO_PORT)
return snd_es1688_create(card, chip, port[n], mpu_port[n],
irq[n], mpu_irq[n], dma8[n], ES1688_HW_1688);
i = 0;
do {
port[n] = possible_ports[i];
error = snd_es1688_create(card, chip, port[n], mpu_port[n],
irq[n], mpu_irq[n], dma8[n], ES1688_HW_1688);
} while (error < 0 && ++i < ARRAY_SIZE(possible_ports));
return error;
}
static int snd_gusextreme_gus_card_create(struct snd_card *card,
struct device *dev, unsigned int n,
struct snd_gus_card **rgus)
{
static const int possible_irqs[] = {11, 12, 15, 9, 5, 7, 3, -1};
static const int possible_dmas[] = {5, 6, 7, 3, 1, -1};
if (gf1_irq[n] == SNDRV_AUTO_IRQ) {
gf1_irq[n] = snd_legacy_find_free_irq(possible_irqs);
if (gf1_irq[n] < 0) {
dev_err(dev, "unable to find a free IRQ for GF1\n");
return -EBUSY;
}
}
if (dma1[n] == SNDRV_AUTO_DMA) {
dma1[n] = snd_legacy_find_free_dma(possible_dmas);
if (dma1[n] < 0) {
dev_err(dev, "unable to find a free DMA for GF1\n");
return -EBUSY;
}
}
return snd_gus_create(card, gf1_port[n], gf1_irq[n], dma1[n], -1,
0, channels[n], pcm_channels[n], 0, rgus);
}
static void snd_gusextreme_enable_gf1(struct snd_gus_card *gus,
struct snd_es1688 *es1688)
{
/*
* This is main stuff - enable access to GF1 chip...
* I'm not sure, if this will work for card which have
* ES1688 chip in another place than 0x220.
*
* I used reverse-engineering in DOSEMU. [--jk]
*
* ULTRINIT.EXE:
* 0x230 = 0,2,3
* 0x240 = 2,0,1
* 0x250 = 2,0,3
* 0x260 = 2,2,1
*/
scoped_guard(spinlock_irqsave, &es1688->mixer_lock) {
snd_es1688_mixer_write(es1688, 0x40, 0x0b); /* don't change!!! */
}
scoped_guard(spinlock_irqsave, &es1688->reg_lock) {
outb(gus->gf1.port & 0x040 ? 2 : 0, ES1688P(es1688, INIT1));
outb(0, 0x201);
outb(gus->gf1.port & 0x020 ? 2 : 0, ES1688P(es1688, INIT1));
outb(0, 0x201);
outb(gus->gf1.port & 0x010 ? 3 : 1, ES1688P(es1688, INIT1));
}
}
static int snd_gusextreme_detect(struct snd_gus_card *gus,
struct snd_es1688 *es1688)
{
unsigned char d;
snd_gusextreme_enable_gf1(gus, es1688);
udelay(100);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */
d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
if ((d & 0x07) != 0) {
dev_dbg(gus->card->dev, "[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
return -EIO;
}
udelay(160);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */
udelay(160);
d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
if ((d & 0x07) != 1) {
dev_dbg(gus->card->dev, "[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
return -EIO;
}
return 0;
}
static int snd_gusextreme_mixer(struct snd_card *card)
{
struct snd_ctl_elem_id id1, id2;
int error;
memset(&id1, 0, sizeof(id1));
memset(&id2, 0, sizeof(id2));
id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
/* reassign AUX to SYNTHESIZER */
strscpy(id1.name, "Aux Playback Volume");
strscpy(id2.name, "Synth Playback Volume");
error = snd_ctl_rename_id(card, &id1, &id2);
if (error < 0)
return error;
/* reassign Master Playback Switch to Synth Playback Switch */
strscpy(id1.name, "Master Playback Switch");
strscpy(id2.name, "Synth Playback Switch");
error = snd_ctl_rename_id(card, &id1, &id2);
if (error < 0)
return error;
return 0;
}
static int snd_gusextreme_probe(struct device *dev, unsigned int n)
{
struct snd_card *card;
struct snd_gus_card *gus;
struct snd_gusextreme *gusextreme;
struct snd_es1688 *es1688;
struct snd_opl3 *opl3;
int error;
error = snd_devm_card_new(dev, index[n], id[n], THIS_MODULE,
sizeof(*gusextreme), &card);
if (error < 0)
return error;
gusextreme = card->private_data;
es1688 = &gusextreme->es1688;
if (mpu_port[n] == SNDRV_AUTO_PORT)
mpu_port[n] = 0;
if (mpu_irq[n] > 15)
mpu_irq[n] = -1;
error = snd_gusextreme_es1688_create(card, es1688, dev, n);
if (error < 0)
return error;
if (gf1_port[n] < 0)
gf1_port[n] = es1688->port + 0x20;
error = snd_gusextreme_gus_card_create(card, dev, n, &gus);
if (error < 0)
return error;
gusextreme->gus = gus;
error = snd_gusextreme_detect(gus, es1688);
if (error < 0)
return error;
gus->joystick_dac = joystick_dac[n];
error = snd_gus_initialize(gus);
if (error < 0)
return error;
error = -ENODEV;
if (!gus->ess_flag) {
dev_err(dev, "GUS Extreme soundcard was not "
"detected at 0x%lx\n", gus->gf1.port);
return error;
}
gus->codec_flag = 1;
error = snd_es1688_pcm(card, es1688, 0);
if (error < 0)
return error;
error = snd_es1688_mixer(card, es1688);
if (error < 0)
return error;
snd_component_add(card, "ES1688");
if (pcm_channels[n] > 0) {
error = snd_gf1_pcm_new(gus, 1, 1);
if (error < 0)
return error;
}
error = snd_gf1_new_mixer(gus);
if (error < 0)
return error;
error = snd_gusextreme_mixer(card);
if (error < 0)
return error;
if (snd_opl3_create(card, es1688->port, es1688->port + 2,
OPL3_HW_OPL3, 0, &opl3) < 0)
dev_warn(dev, "opl3 not detected at 0x%lx\n", es1688->port);
else {
error = snd_opl3_hwdep_new(opl3, 0, 2, NULL);
if (error < 0)
return error;
}
if (es1688->mpu_port >= 0x300) {
error = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688,
es1688->mpu_port, 0, mpu_irq[n], NULL);
if (error < 0)
return error;
}
sprintf(card->longname, "Gravis UltraSound Extreme at 0x%lx, "
"irq %i&%i, dma %i&%i", es1688->port,
gus->gf1.irq, es1688->irq, gus->gf1.dma1, es1688->dma8);
error = snd_card_register(card);
if (error < 0)
return error;
dev_set_drvdata(dev, card);
return 0;
}
#ifdef CONFIG_PM
static int snd_gusextreme_suspend(struct device *dev, unsigned int n,
pm_message_t state)
{
struct snd_card *card = dev_get_drvdata(dev);
struct snd_gusextreme *gusextreme = card->private_data;
return snd_gus_suspend(gusextreme->gus);
}
static int snd_gusextreme_resume(struct device *dev, unsigned int n)
{
struct snd_card *card = dev_get_drvdata(dev);
struct snd_gusextreme *gusextreme = card->private_data;
int err;
err = snd_es1688_reset(&gusextreme->es1688);
if (err < 0)
return err;
snd_gusextreme_enable_gf1(gusextreme->gus, &gusextreme->es1688);
usleep_range(100, 200);
return snd_gus_resume(gusextreme->gus);
}
#endif
static struct isa_driver snd_gusextreme_driver = {
.match = snd_gusextreme_match,
.probe = snd_gusextreme_probe,
#ifdef CONFIG_PM
.suspend = snd_gusextreme_suspend,
.resume = snd_gusextreme_resume,
#endif
.driver = {
.name = DEV_NAME
}
};
module_isa_driver(snd_gusextreme_driver, SNDRV_CARDS);
+368
View File
@@ -0,0 +1,368 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Gravis UltraSound MAX soundcard
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
*/
#include <linux/init.h>
#include <linux/err.h>
#include <linux/isa.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <linux/module.h>
#include <asm/dma.h>
#include <sound/core.h>
#include <sound/gus.h>
#include <sound/wss.h>
#define SNDRV_LEGACY_FIND_FREE_IRQ
#define SNDRV_LEGACY_FIND_FREE_DMA
#include <sound/initval.h>
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Gravis UltraSound MAX");
MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x220,0x230,0x240,0x250,0x260 */
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ; /* 2,3,5,9,11,12,15 */
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */
static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA; /* 1,3,5,6,7 */
static int joystick_dac[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 29};
/* 0 to 31, (0.59V-4.52V or 0.389V-2.98V) */
static int channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 24};
static int pcm_channels[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for GUS MAX soundcard.");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for GUS MAX soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable GUS MAX soundcard.");
module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for GUS MAX driver.");
module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for GUS MAX driver.");
module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for GUS MAX driver.");
module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for GUS MAX driver.");
module_param_array(joystick_dac, int, NULL, 0444);
MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS MAX driver.");
module_param_array(channels, int, NULL, 0444);
MODULE_PARM_DESC(channels, "Used GF1 channels for GUS MAX driver.");
module_param_array(pcm_channels, int, NULL, 0444);
MODULE_PARM_DESC(pcm_channels, "Reserved PCM channels for GUS MAX driver.");
struct snd_gusmax {
int irq;
struct snd_card *card;
struct snd_gus_card *gus;
struct snd_wss *wss;
unsigned short gus_status_reg;
unsigned short pcm_status_reg;
};
static int snd_gusmax_detect(struct snd_gus_card *gus)
{
unsigned char d;
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 0); /* reset GF1 */
d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
if ((d & 0x07) != 0) {
dev_dbg(gus->card->dev, "[0x%lx] check 1 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
udelay(160);
snd_gf1_i_write8(gus, SNDRV_GF1_GB_RESET, 1); /* release reset */
udelay(160);
d = snd_gf1_i_look8(gus, SNDRV_GF1_GB_RESET);
if ((d & 0x07) != 1) {
dev_dbg(gus->card->dev, "[0x%lx] check 2 failed - 0x%x\n", gus->gf1.port, d);
return -ENODEV;
}
return 0;
}
static irqreturn_t snd_gusmax_interrupt(int irq, void *dev_id)
{
struct snd_gusmax *maxcard = dev_id;
int loop, max = 5;
int handled = 0;
do {
loop = 0;
if (inb(maxcard->gus_status_reg)) {
handled = 1;
snd_gus_interrupt(irq, maxcard->gus);
loop++;
}
if (inb(maxcard->pcm_status_reg) & 0x01) { /* IRQ bit is set? */
handled = 1;
snd_wss_interrupt(irq, maxcard->wss);
loop++;
}
} while (loop && --max > 0);
return IRQ_RETVAL(handled);
}
static void snd_gusmax_init(int dev, struct snd_card *card,
struct snd_gus_card *gus)
{
gus->equal_irq = 1;
gus->codec_flag = 1;
gus->joystick_dac = joystick_dac[dev];
/* init control register */
gus->max_cntrl_val = (gus->gf1.port >> 4) & 0x0f;
if (gus->gf1.dma1 > 3)
gus->max_cntrl_val |= 0x10;
if (gus->gf1.dma2 > 3)
gus->max_cntrl_val |= 0x20;
gus->max_cntrl_val |= 0x40;
outb(gus->max_cntrl_val, GUSP(gus, MAXCNTRLPORT));
}
static int snd_gusmax_mixer(struct snd_wss *chip)
{
struct snd_card *card = chip->card;
struct snd_ctl_elem_id id1, id2;
int err;
memset(&id1, 0, sizeof(id1));
memset(&id2, 0, sizeof(id2));
id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
/* reassign AUXA to SYNTHESIZER */
strscpy(id1.name, "Aux Playback Switch");
strscpy(id2.name, "Synth Playback Switch");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0)
return err;
strscpy(id1.name, "Aux Playback Volume");
strscpy(id2.name, "Synth Playback Volume");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0)
return err;
/* reassign AUXB to CD */
strscpy(id1.name, "Aux Playback Switch"); id1.index = 1;
strscpy(id2.name, "CD Playback Switch");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0)
return err;
strscpy(id1.name, "Aux Playback Volume");
strscpy(id2.name, "CD Playback Volume");
err = snd_ctl_rename_id(card, &id1, &id2);
if (err < 0)
return err;
#if 0
/* reassign Mono Input to MIC */
if (snd_mixer_group_rename(mixer,
SNDRV_MIXER_IN_MONO, 0,
SNDRV_MIXER_IN_MIC, 0) < 0)
goto __error;
if (snd_mixer_elem_rename(mixer,
SNDRV_MIXER_IN_MONO, 0, SNDRV_MIXER_ETYPE_INPUT,
SNDRV_MIXER_IN_MIC, 0) < 0)
goto __error;
if (snd_mixer_elem_rename(mixer,
"Mono Capture Volume", 0, SNDRV_MIXER_ETYPE_VOLUME1,
"Mic Capture Volume", 0) < 0)
goto __error;
if (snd_mixer_elem_rename(mixer,
"Mono Capture Switch", 0, SNDRV_MIXER_ETYPE_SWITCH1,
"Mic Capture Switch", 0) < 0)
goto __error;
#endif
return 0;
}
static int snd_gusmax_match(struct device *pdev, unsigned int dev)
{
return enable[dev];
}
static int snd_gusmax_probe(struct device *pdev, unsigned int dev)
{
static const int possible_irqs[] = {5, 11, 12, 9, 7, 15, 3, -1};
static const int possible_dmas[] = {5, 6, 7, 1, 3, -1};
int xirq, xdma1, xdma2, err;
struct snd_card *card;
struct snd_gus_card *gus = NULL;
struct snd_wss *wss;
struct snd_gusmax *maxcard;
err = snd_devm_card_new(pdev, index[dev], id[dev], THIS_MODULE,
sizeof(struct snd_gusmax), &card);
if (err < 0)
return err;
maxcard = card->private_data;
maxcard->card = card;
maxcard->irq = -1;
xirq = irq[dev];
if (xirq == SNDRV_AUTO_IRQ) {
xirq = snd_legacy_find_free_irq(possible_irqs);
if (xirq < 0) {
dev_err(pdev, "unable to find a free IRQ\n");
return -EBUSY;
}
}
xdma1 = dma1[dev];
if (xdma1 == SNDRV_AUTO_DMA) {
xdma1 = snd_legacy_find_free_dma(possible_dmas);
if (xdma1 < 0) {
dev_err(pdev, "unable to find a free DMA1\n");
return -EBUSY;
}
}
xdma2 = dma2[dev];
if (xdma2 == SNDRV_AUTO_DMA) {
xdma2 = snd_legacy_find_free_dma(possible_dmas);
if (xdma2 < 0) {
dev_err(pdev, "unable to find a free DMA2\n");
return -EBUSY;
}
}
if (port[dev] != SNDRV_AUTO_PORT) {
err = snd_gus_create(card,
port[dev],
-xirq, xdma1, xdma2,
0, channels[dev],
pcm_channels[dev],
0, &gus);
} else {
static const unsigned long possible_ports[] = {
0x220, 0x230, 0x240, 0x250, 0x260
};
int i;
for (i = 0; i < ARRAY_SIZE(possible_ports); i++) {
err = snd_gus_create(card,
possible_ports[i],
-xirq, xdma1, xdma2,
0, channels[dev],
pcm_channels[dev],
0, &gus);
if (err >= 0) {
port[dev] = possible_ports[i];
break;
}
}
}
if (err < 0)
return err;
err = snd_gusmax_detect(gus);
if (err < 0)
return err;
maxcard->gus_status_reg = gus->gf1.reg_irqstat;
maxcard->pcm_status_reg = gus->gf1.port + 0x10c + 2;
snd_gusmax_init(dev, card, gus);
err = snd_gus_initialize(gus);
if (err < 0)
return err;
if (!gus->max_flag) {
dev_err(pdev, "GUS MAX soundcard was not detected at 0x%lx\n", gus->gf1.port);
return -ENODEV;
}
if (devm_request_irq(card->dev, xirq, snd_gusmax_interrupt, 0,
"GUS MAX", (void *)maxcard)) {
dev_err(pdev, "unable to grab IRQ %d\n", xirq);
return -EBUSY;
}
maxcard->irq = xirq;
card->sync_irq = maxcard->irq;
err = snd_wss_create(card,
gus->gf1.port + 0x10c, -1, xirq,
xdma2 < 0 ? xdma1 : xdma2, xdma1,
WSS_HW_DETECT,
WSS_HWSHARE_IRQ |
WSS_HWSHARE_DMA1 |
WSS_HWSHARE_DMA2,
&wss);
if (err < 0)
return err;
err = snd_wss_pcm(wss, 0);
if (err < 0)
return err;
err = snd_wss_mixer(wss);
if (err < 0)
return err;
err = snd_wss_timer(wss, 2);
if (err < 0)
return err;
if (pcm_channels[dev] > 0) {
err = snd_gf1_pcm_new(gus, 1, 1);
if (err < 0)
return err;
}
err = snd_gusmax_mixer(wss);
if (err < 0)
return err;
err = snd_gf1_rawmidi_new(gus, 0);
if (err < 0)
return err;
sprintf(card->longname + strlen(card->longname), " at 0x%lx, irq %i, dma %i", gus->gf1.port, xirq, xdma1);
if (xdma2 >= 0)
sprintf(card->longname + strlen(card->longname), "&%i", xdma2);
err = snd_card_register(card);
if (err < 0)
return err;
maxcard->gus = gus;
maxcard->wss = wss;
dev_set_drvdata(pdev, card);
return 0;
}
#ifdef CONFIG_PM
static int snd_gusmax_suspend(struct device *dev, unsigned int n,
pm_message_t state)
{
struct snd_card *card = dev_get_drvdata(dev);
struct snd_gusmax *maxcard = card->private_data;
maxcard->wss->suspend(maxcard->wss);
return snd_gus_suspend(maxcard->gus);
}
static int snd_gusmax_resume(struct device *dev, unsigned int n)
{
struct snd_card *card = dev_get_drvdata(dev);
struct snd_gusmax *maxcard = card->private_data;
/* Restore the board routing latch before resuming the codec and GF1. */
outb(maxcard->gus->max_cntrl_val, GUSP(maxcard->gus, MAXCNTRLPORT));
maxcard->wss->resume(maxcard->wss);
return snd_gus_resume(maxcard->gus);
}
#endif
#define DEV_NAME "gusmax"
static struct isa_driver snd_gusmax_driver = {
.match = snd_gusmax_match,
.probe = snd_gusmax_probe,
#ifdef CONFIG_PM
.suspend = snd_gusmax_suspend,
.resume = snd_gusmax_resume,
#endif
.driver = {
.name = DEV_NAME
},
};
module_isa_driver(snd_gusmax_driver, SNDRV_CARDS);
@@ -0,0 +1,3 @@
// SPDX-License-Identifier: GPL-2.0
#define SNDRV_STB
#include "interwave.c"
File diff suppressed because it is too large Load Diff