Merge branch 'topic/msnd' into for-linus
This commit is contained in:
commit
4c5ecb7e59
|
@ -1172,6 +1172,54 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
|
|||
|
||||
This module supports multiple devices and PnP.
|
||||
|
||||
Module snd-msnd-classic
|
||||
-----------------------
|
||||
|
||||
Module for Turtle Beach MultiSound Classic, Tahiti or Monterey
|
||||
soundcards.
|
||||
|
||||
io - Port # for msnd-classic card
|
||||
irq - IRQ # for msnd-classic card
|
||||
mem - Memory address (0xb0000, 0xc8000, 0xd0000, 0xd8000,
|
||||
0xe0000 or 0xe8000)
|
||||
write_ndelay - enable write ndelay (default = 1)
|
||||
calibrate_signal - calibrate signal (default = 0)
|
||||
isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
|
||||
digital - Digital daughterboard present (default = 0)
|
||||
cfg - Config port (0x250, 0x260 or 0x270) default = PnP
|
||||
reset - Reset all devices
|
||||
mpu_io - MPU401 I/O port
|
||||
mpu_irq - MPU401 irq#
|
||||
ide_io0 - IDE port #0
|
||||
ide_io1 - IDE port #1
|
||||
ide_irq - IDE irq#
|
||||
joystick_io - Joystick I/O port
|
||||
|
||||
The driver requires firmware files "turtlebeach/msndinit.bin" and
|
||||
"turtlebeach/msndperm.bin" in the proper firmware directory.
|
||||
|
||||
See Documentation/sound/oss/MultiSound for important information
|
||||
about this driver. Note that it has been discontinued, but the
|
||||
Voyetra Turtle Beach knowledge base entry for it is still available
|
||||
at
|
||||
http://www.turtlebeach.com/site/kb_ftp/790.asp
|
||||
|
||||
Module snd-msnd-pinnacle
|
||||
------------------------
|
||||
|
||||
Module for Turtle Beach MultiSound Pinnacle/Fiji soundcards.
|
||||
|
||||
io - Port # for pinnacle/fiji card
|
||||
irq - IRQ # for pinnalce/fiji card
|
||||
mem - Memory address (0xb0000, 0xc8000, 0xd0000, 0xd8000,
|
||||
0xe0000 or 0xe8000)
|
||||
write_ndelay - enable write ndelay (default = 1)
|
||||
calibrate_signal - calibrate signal (default = 0)
|
||||
isapnp - ISA PnP detection - 0 = disable, 1 = enable (default)
|
||||
|
||||
The driver requires firmware files "turtlebeach/pndspini.bin" and
|
||||
"turtlebeach/pndsperm.bin" in the proper firmware directory.
|
||||
|
||||
Module snd-mtpav
|
||||
----------------
|
||||
|
||||
|
|
|
@ -402,5 +402,36 @@ config SND_WAVEFRONT_FIRMWARE_IN_KERNEL
|
|||
you need to install the firmware files from the
|
||||
alsa-firmware package.
|
||||
|
||||
config SND_MSND_PINNACLE
|
||||
tristate "Turtle Beach MultiSound Pinnacle/Fiji driver"
|
||||
depends on X86 && EXPERIMENTAL
|
||||
select FW_LOADER
|
||||
select SND_MPU401_UART
|
||||
select SND_PCM
|
||||
help
|
||||
Say Y to include support for Turtle Beach MultiSound Pinnacle/
|
||||
Fiji soundcards.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-msnd-pinnacle.
|
||||
|
||||
config SND_MSND_CLASSIC
|
||||
tristate "Support for Turtle Beach MultiSound Classic, Tahiti, Monterey"
|
||||
depends on X86 && EXPERIMENTAL
|
||||
select FW_LOADER
|
||||
select SND_MPU401_UART
|
||||
select SND_PCM
|
||||
help
|
||||
Say M here if you have a Turtle Beach MultiSound Classic, Tahiti or
|
||||
Monterey (not for the Pinnacle or Fiji).
|
||||
|
||||
See <file:Documentation/sound/oss/MultiSound> for important information
|
||||
about this driver. Note that it has been discontinued, but the
|
||||
Voyetra Turtle Beach knowledge base entry for it is still available
|
||||
at <http://www.turtlebeach.com/site/kb_ftp/790.asp>.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-msnd-classic.
|
||||
|
||||
endif # SND_ISA
|
||||
|
||||
|
|
|
@ -26,5 +26,5 @@ obj-$(CONFIG_SND_SC6000) += snd-sc6000.o
|
|||
obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o
|
||||
obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o
|
||||
|
||||
obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ opti9xx/ \
|
||||
obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ msnd/ opti9xx/ \
|
||||
sb/ wavefront/ wss/
|
||||
|
|
9
sound/isa/msnd/Makefile
Normal file
9
sound/isa/msnd/Makefile
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
snd-msnd-lib-objs := msnd.o msnd_midi.o msnd_pinnacle_mixer.o
|
||||
snd-msnd-pinnacle-objs := msnd_pinnacle.o
|
||||
snd-msnd-classic-objs := msnd_classic.o
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_MSND_PINNACLE) += snd-msnd-pinnacle.o snd-msnd-lib.o
|
||||
obj-$(CONFIG_SND_MSND_CLASSIC) += snd-msnd-classic.o snd-msnd-lib.o
|
||||
|
705
sound/isa/msnd/msnd.c
Normal file
705
sound/isa/msnd/msnd.c
Normal file
|
@ -0,0 +1,705 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* 2002/06/30 Karsten Wiese:
|
||||
* removed kernel-version dependencies.
|
||||
* ripped from linux kernel 2.4.18 (OSS Implementation) by me.
|
||||
* In the OSS Version, this file is compiled to a separate MODULE,
|
||||
* that is used by the pinnacle and the classic driver.
|
||||
* since there is no classic driver for alsa yet (i dont have a classic
|
||||
* & writing one blindfold is difficult) this file's object is statically
|
||||
* linked into the pinnacle-driver-module for now. look for the string
|
||||
* "uncomment this to make this a module again"
|
||||
* to do guess what.
|
||||
*
|
||||
* the following is a copy of the 2.4.18 OSS FREE file-heading comment:
|
||||
*
|
||||
* msnd.c - Driver Base
|
||||
*
|
||||
* Turtle Beach MultiSound Sound Card Driver for Linux
|
||||
*
|
||||
* Copyright (C) 1998 Andrew Veliath
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
********************************************************************/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "msnd.h"
|
||||
|
||||
#define LOGNAME "msnd"
|
||||
|
||||
|
||||
void snd_msnd_init_queue(void *base, int start, int size)
|
||||
{
|
||||
writew(PCTODSP_BASED(start), base + JQS_wStart);
|
||||
writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize);
|
||||
writew(0, base + JQS_wHead);
|
||||
writew(0, base + JQS_wTail);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msnd_init_queue);
|
||||
|
||||
static int snd_msnd_wait_TXDE(struct snd_msnd *dev)
|
||||
{
|
||||
unsigned int io = dev->io;
|
||||
int timeout = 1000;
|
||||
|
||||
while (timeout-- > 0)
|
||||
if (inb(io + HP_ISR) & HPISR_TXDE)
|
||||
return 0;
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int snd_msnd_wait_HC0(struct snd_msnd *dev)
|
||||
{
|
||||
unsigned int io = dev->io;
|
||||
int timeout = 1000;
|
||||
|
||||
while (timeout-- > 0)
|
||||
if (!(inb(io + HP_CVR) & HPCVR_HC))
|
||||
return 0;
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int snd_msnd_send_dsp_cmd(struct snd_msnd *dev, u8 cmd)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (snd_msnd_wait_HC0(dev) == 0) {
|
||||
outb(cmd, dev->io + HP_CVR);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
snd_printd(KERN_ERR LOGNAME ": Send DSP command timeout\n");
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msnd_send_dsp_cmd);
|
||||
|
||||
int snd_msnd_send_word(struct snd_msnd *dev, unsigned char high,
|
||||
unsigned char mid, unsigned char low)
|
||||
{
|
||||
unsigned int io = dev->io;
|
||||
|
||||
if (snd_msnd_wait_TXDE(dev) == 0) {
|
||||
outb(high, io + HP_TXH);
|
||||
outb(mid, io + HP_TXM);
|
||||
outb(low, io + HP_TXL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
snd_printd(KERN_ERR LOGNAME ": Send host word timeout\n");
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msnd_send_word);
|
||||
|
||||
int snd_msnd_upload_host(struct snd_msnd *dev, const u8 *bin, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (len % 3 != 0) {
|
||||
snd_printk(KERN_ERR LOGNAME
|
||||
": Upload host data not multiple of 3!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i += 3)
|
||||
if (snd_msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]))
|
||||
return -EIO;
|
||||
|
||||
inb(dev->io + HP_RXL);
|
||||
inb(dev->io + HP_CVR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msnd_upload_host);
|
||||
|
||||
int snd_msnd_enable_irq(struct snd_msnd *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (dev->irq_ref++)
|
||||
return 0;
|
||||
|
||||
snd_printdd(LOGNAME ": Enabling IRQ\n");
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (snd_msnd_wait_TXDE(dev) == 0) {
|
||||
outb(inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR);
|
||||
if (dev->type == msndClassic)
|
||||
outb(dev->irqid, dev->io + HP_IRQM);
|
||||
|
||||
outb(inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR);
|
||||
outb(inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR);
|
||||
enable_irq(dev->irq);
|
||||
snd_msnd_init_queue(dev->DSPQ, dev->dspq_data_buff,
|
||||
dev->dspq_buff_size);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
snd_printd(KERN_ERR LOGNAME ": Enable IRQ failed\n");
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msnd_enable_irq);
|
||||
|
||||
int snd_msnd_disable_irq(struct snd_msnd *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (--dev->irq_ref > 0)
|
||||
return 0;
|
||||
|
||||
if (dev->irq_ref < 0)
|
||||
snd_printd(KERN_WARNING LOGNAME ": IRQ ref count is %d\n",
|
||||
dev->irq_ref);
|
||||
|
||||
snd_printdd(LOGNAME ": Disabling IRQ\n");
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (snd_msnd_wait_TXDE(dev) == 0) {
|
||||
outb(inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR);
|
||||
if (dev->type == msndClassic)
|
||||
outb(HPIRQ_NONE, dev->io + HP_IRQM);
|
||||
disable_irq(dev->irq);
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
snd_printd(KERN_ERR LOGNAME ": Disable IRQ failed\n");
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msnd_disable_irq);
|
||||
|
||||
static inline long get_play_delay_jiffies(struct snd_msnd *chip, long size)
|
||||
{
|
||||
long tmp = (size * HZ * chip->play_sample_size) / 8;
|
||||
return tmp / (chip->play_sample_rate * chip->play_channels);
|
||||
}
|
||||
|
||||
static void snd_msnd_dsp_write_flush(struct snd_msnd *chip)
|
||||
{
|
||||
if (!(chip->mode & FMODE_WRITE) || !test_bit(F_WRITING, &chip->flags))
|
||||
return;
|
||||
set_bit(F_WRITEFLUSH, &chip->flags);
|
||||
/* interruptible_sleep_on_timeout(
|
||||
&chip->writeflush,
|
||||
get_play_delay_jiffies(&chip, chip->DAPF.len));*/
|
||||
clear_bit(F_WRITEFLUSH, &chip->flags);
|
||||
if (!signal_pending(current))
|
||||
schedule_timeout_interruptible(
|
||||
get_play_delay_jiffies(chip, chip->play_period_bytes));
|
||||
clear_bit(F_WRITING, &chip->flags);
|
||||
}
|
||||
|
||||
void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file)
|
||||
{
|
||||
if ((file ? file->f_mode : chip->mode) & FMODE_READ) {
|
||||
clear_bit(F_READING, &chip->flags);
|
||||
snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP);
|
||||
snd_msnd_disable_irq(chip);
|
||||
if (file) {
|
||||
snd_printd(KERN_INFO LOGNAME
|
||||
": Stopping read for %p\n", file);
|
||||
chip->mode &= ~FMODE_READ;
|
||||
}
|
||||
clear_bit(F_AUDIO_READ_INUSE, &chip->flags);
|
||||
}
|
||||
if ((file ? file->f_mode : chip->mode) & FMODE_WRITE) {
|
||||
if (test_bit(F_WRITING, &chip->flags)) {
|
||||
snd_msnd_dsp_write_flush(chip);
|
||||
snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP);
|
||||
}
|
||||
snd_msnd_disable_irq(chip);
|
||||
if (file) {
|
||||
snd_printd(KERN_INFO
|
||||
LOGNAME ": Stopping write for %p\n", file);
|
||||
chip->mode &= ~FMODE_WRITE;
|
||||
}
|
||||
clear_bit(F_AUDIO_WRITE_INUSE, &chip->flags);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msnd_dsp_halt);
|
||||
|
||||
|
||||
int snd_msnd_DARQ(struct snd_msnd *chip, int bank)
|
||||
{
|
||||
int /*size, n,*/ timeout = 3;
|
||||
u16 wTmp;
|
||||
/* void *DAQD; */
|
||||
|
||||
/* Increment the tail and check for queue wrap */
|
||||
wTmp = readw(chip->DARQ + JQS_wTail) + PCTODSP_OFFSET(DAQDS__size);
|
||||
if (wTmp > readw(chip->DARQ + JQS_wSize))
|
||||
wTmp = 0;
|
||||
while (wTmp == readw(chip->DARQ + JQS_wHead) && timeout--)
|
||||
udelay(1);
|
||||
|
||||
if (chip->capturePeriods == 2) {
|
||||
void *pDAQ = chip->mappedbase + DARQ_DATA_BUFF +
|
||||
bank * DAQDS__size + DAQDS_wStart;
|
||||
unsigned short offset = 0x3000 + chip->capturePeriodBytes;
|
||||
|
||||
if (readw(pDAQ) != PCTODSP_BASED(0x3000))
|
||||
offset = 0x3000;
|
||||
writew(PCTODSP_BASED(offset), pDAQ);
|
||||
}
|
||||
|
||||
writew(wTmp, chip->DARQ + JQS_wTail);
|
||||
|
||||
#if 0
|
||||
/* Get our digital audio queue struct */
|
||||
DAQD = bank * DAQDS__size + chip->mappedbase + DARQ_DATA_BUFF;
|
||||
|
||||
/* Get length of data */
|
||||
size = readw(DAQD + DAQDS_wSize);
|
||||
|
||||
/* Read data from the head (unprotected bank 1 access okay
|
||||
since this is only called inside an interrupt) */
|
||||
outb(HPBLKSEL_1, chip->io + HP_BLKS);
|
||||
n = msnd_fifo_write(&chip->DARF,
|
||||
(char *)(chip->base + bank * DAR_BUFF_SIZE),
|
||||
size, 0);
|
||||
if (n <= 0) {
|
||||
outb(HPBLKSEL_0, chip->io + HP_BLKS);
|
||||
return n;
|
||||
}
|
||||
outb(HPBLKSEL_0, chip->io + HP_BLKS);
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msnd_DARQ);
|
||||
|
||||
int snd_msnd_DAPQ(struct snd_msnd *chip, int start)
|
||||
{
|
||||
u16 DAPQ_tail;
|
||||
int protect = start, nbanks = 0;
|
||||
void *DAQD;
|
||||
static int play_banks_submitted;
|
||||
/* unsigned long flags;
|
||||
spin_lock_irqsave(&chip->lock, flags); not necessary */
|
||||
|
||||
DAPQ_tail = readw(chip->DAPQ + JQS_wTail);
|
||||
while (DAPQ_tail != readw(chip->DAPQ + JQS_wHead) || start) {
|
||||
int bank_num = DAPQ_tail / PCTODSP_OFFSET(DAQDS__size);
|
||||
|
||||
if (start) {
|
||||
start = 0;
|
||||
play_banks_submitted = 0;
|
||||
}
|
||||
|
||||
/* Get our digital audio queue struct */
|
||||
DAQD = bank_num * DAQDS__size + chip->mappedbase +
|
||||
DAPQ_DATA_BUFF;
|
||||
|
||||
/* Write size of this bank */
|
||||
writew(chip->play_period_bytes, DAQD + DAQDS_wSize);
|
||||
if (play_banks_submitted < 3)
|
||||
++play_banks_submitted;
|
||||
else if (chip->playPeriods == 2) {
|
||||
unsigned short offset = chip->play_period_bytes;
|
||||
|
||||
if (readw(DAQD + DAQDS_wStart) != PCTODSP_BASED(0x0))
|
||||
offset = 0;
|
||||
|
||||
writew(PCTODSP_BASED(offset), DAQD + DAQDS_wStart);
|
||||
}
|
||||
++nbanks;
|
||||
|
||||
/* Then advance the tail */
|
||||
/*
|
||||
if (protect)
|
||||
snd_printd(KERN_INFO "B %X %lX\n",
|
||||
bank_num, xtime.tv_usec);
|
||||
*/
|
||||
|
||||
DAPQ_tail = (++bank_num % 3) * PCTODSP_OFFSET(DAQDS__size);
|
||||
writew(DAPQ_tail, chip->DAPQ + JQS_wTail);
|
||||
/* Tell the DSP to play the bank */
|
||||
snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_START);
|
||||
if (protect)
|
||||
if (2 == bank_num)
|
||||
break;
|
||||
}
|
||||
/*
|
||||
if (protect)
|
||||
snd_printd(KERN_INFO "%lX\n", xtime.tv_usec);
|
||||
*/
|
||||
/* spin_unlock_irqrestore(&chip->lock, flags); not necessary */
|
||||
return nbanks;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msnd_DAPQ);
|
||||
|
||||
static void snd_msnd_play_reset_queue(struct snd_msnd *chip,
|
||||
unsigned int pcm_periods,
|
||||
unsigned int pcm_count)
|
||||
{
|
||||
int n;
|
||||
void *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF;
|
||||
|
||||
chip->last_playbank = -1;
|
||||
chip->playLimit = pcm_count * (pcm_periods - 1);
|
||||
chip->playPeriods = pcm_periods;
|
||||
writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DAPQ + JQS_wHead);
|
||||
writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DAPQ + JQS_wTail);
|
||||
|
||||
chip->play_period_bytes = pcm_count;
|
||||
|
||||
for (n = 0; n < pcm_periods; ++n, pDAQ += DAQDS__size) {
|
||||
writew(PCTODSP_BASED((u32)(pcm_count * n)),
|
||||
pDAQ + DAQDS_wStart);
|
||||
writew(0, pDAQ + DAQDS_wSize);
|
||||
writew(1, pDAQ + DAQDS_wFormat);
|
||||
writew(chip->play_sample_size, pDAQ + DAQDS_wSampleSize);
|
||||
writew(chip->play_channels, pDAQ + DAQDS_wChannels);
|
||||
writew(chip->play_sample_rate, pDAQ + DAQDS_wSampleRate);
|
||||
writew(HIMT_PLAY_DONE * 0x100 + n, pDAQ + DAQDS_wIntMsg);
|
||||
writew(n, pDAQ + DAQDS_wFlags);
|
||||
}
|
||||
}
|
||||
|
||||
static void snd_msnd_capture_reset_queue(struct snd_msnd *chip,
|
||||
unsigned int pcm_periods,
|
||||
unsigned int pcm_count)
|
||||
{
|
||||
int n;
|
||||
void *pDAQ;
|
||||
/* unsigned long flags; */
|
||||
|
||||
/* snd_msnd_init_queue(chip->DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE); */
|
||||
|
||||
chip->last_recbank = 2;
|
||||
chip->captureLimit = pcm_count * (pcm_periods - 1);
|
||||
chip->capturePeriods = pcm_periods;
|
||||
writew(PCTODSP_OFFSET(0 * DAQDS__size), chip->DARQ + JQS_wHead);
|
||||
writew(PCTODSP_OFFSET(chip->last_recbank * DAQDS__size),
|
||||
chip->DARQ + JQS_wTail);
|
||||
|
||||
#if 0 /* Critical section: bank 1 access. this is how the OSS driver does it:*/
|
||||
spin_lock_irqsave(&chip->lock, flags);
|
||||
outb(HPBLKSEL_1, chip->io + HP_BLKS);
|
||||
memset_io(chip->mappedbase, 0, DAR_BUFF_SIZE * 3);
|
||||
outb(HPBLKSEL_0, chip->io + HP_BLKS);
|
||||
spin_unlock_irqrestore(&chip->lock, flags);
|
||||
#endif
|
||||
|
||||
chip->capturePeriodBytes = pcm_count;
|
||||
snd_printdd("snd_msnd_capture_reset_queue() %i\n", pcm_count);
|
||||
|
||||
pDAQ = chip->mappedbase + DARQ_DATA_BUFF;
|
||||
|
||||
for (n = 0; n < pcm_periods; ++n, pDAQ += DAQDS__size) {
|
||||
u32 tmp = pcm_count * n;
|
||||
|
||||
writew(PCTODSP_BASED(tmp + 0x3000), pDAQ + DAQDS_wStart);
|
||||
writew(pcm_count, pDAQ + DAQDS_wSize);
|
||||
writew(1, pDAQ + DAQDS_wFormat);
|
||||
writew(chip->capture_sample_size, pDAQ + DAQDS_wSampleSize);
|
||||
writew(chip->capture_channels, pDAQ + DAQDS_wChannels);
|
||||
writew(chip->capture_sample_rate, pDAQ + DAQDS_wSampleRate);
|
||||
writew(HIMT_RECORD_DONE * 0x100 + n, pDAQ + DAQDS_wIntMsg);
|
||||
writew(n, pDAQ + DAQDS_wFlags);
|
||||
}
|
||||
}
|
||||
|
||||
static struct snd_pcm_hardware snd_msnd_playback = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_MMAP_VALID,
|
||||
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 0x3000,
|
||||
.period_bytes_min = 0x40,
|
||||
.period_bytes_max = 0x1800,
|
||||
.periods_min = 2,
|
||||
.periods_max = 3,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hardware snd_msnd_capture = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_MMAP_VALID,
|
||||
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
|
||||
.rates = SNDRV_PCM_RATE_8000_48000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = 0x3000,
|
||||
.period_bytes_min = 0x40,
|
||||
.period_bytes_max = 0x1800,
|
||||
.periods_min = 2,
|
||||
.periods_max = 3,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
|
||||
static int snd_msnd_playback_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
|
||||
|
||||
set_bit(F_AUDIO_WRITE_INUSE, &chip->flags);
|
||||
clear_bit(F_WRITING, &chip->flags);
|
||||
snd_msnd_enable_irq(chip);
|
||||
|
||||
runtime->dma_area = chip->mappedbase;
|
||||
runtime->dma_bytes = 0x3000;
|
||||
|
||||
chip->playback_substream = substream;
|
||||
runtime->hw = snd_msnd_playback;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msnd_playback_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
|
||||
|
||||
snd_msnd_disable_irq(chip);
|
||||
clear_bit(F_AUDIO_WRITE_INUSE, &chip->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int snd_msnd_playback_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
int i;
|
||||
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
|
||||
void *pDAQ = chip->mappedbase + DAPQ_DATA_BUFF;
|
||||
|
||||
chip->play_sample_size = snd_pcm_format_width(params_format(params));
|
||||
chip->play_channels = params_channels(params);
|
||||
chip->play_sample_rate = params_rate(params);
|
||||
|
||||
for (i = 0; i < 3; ++i, pDAQ += DAQDS__size) {
|
||||
writew(chip->play_sample_size, pDAQ + DAQDS_wSampleSize);
|
||||
writew(chip->play_channels, pDAQ + DAQDS_wChannels);
|
||||
writew(chip->play_sample_rate, pDAQ + DAQDS_wSampleRate);
|
||||
}
|
||||
/* dont do this here:
|
||||
* snd_msnd_calibrate_adc(chip->play_sample_rate);
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msnd_playback_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
|
||||
unsigned int pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
unsigned int pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
unsigned int pcm_periods = pcm_size / pcm_count;
|
||||
|
||||
snd_msnd_play_reset_queue(chip, pcm_periods, pcm_count);
|
||||
chip->playDMAPos = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msnd_playback_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
|
||||
int result = 0;
|
||||
|
||||
if (cmd == SNDRV_PCM_TRIGGER_START) {
|
||||
snd_printdd("snd_msnd_playback_trigger(START)\n");
|
||||
chip->banksPlayed = 0;
|
||||
set_bit(F_WRITING, &chip->flags);
|
||||
snd_msnd_DAPQ(chip, 1);
|
||||
} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
|
||||
snd_printdd("snd_msnd_playback_trigger(STop)\n");
|
||||
/* interrupt diagnostic, comment this out later */
|
||||
clear_bit(F_WRITING, &chip->flags);
|
||||
snd_msnd_send_dsp_cmd(chip, HDEX_PLAY_STOP);
|
||||
} else {
|
||||
snd_printd(KERN_ERR "snd_msnd_playback_trigger(?????)\n");
|
||||
result = -EINVAL;
|
||||
}
|
||||
|
||||
snd_printdd("snd_msnd_playback_trigger() ENDE\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
snd_msnd_playback_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
|
||||
|
||||
return bytes_to_frames(substream->runtime, chip->playDMAPos);
|
||||
}
|
||||
|
||||
|
||||
static struct snd_pcm_ops snd_msnd_playback_ops = {
|
||||
.open = snd_msnd_playback_open,
|
||||
.close = snd_msnd_playback_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_msnd_playback_hw_params,
|
||||
.prepare = snd_msnd_playback_prepare,
|
||||
.trigger = snd_msnd_playback_trigger,
|
||||
.pointer = snd_msnd_playback_pointer,
|
||||
};
|
||||
|
||||
static int snd_msnd_capture_open(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
|
||||
|
||||
set_bit(F_AUDIO_READ_INUSE, &chip->flags);
|
||||
snd_msnd_enable_irq(chip);
|
||||
runtime->dma_area = chip->mappedbase + 0x3000;
|
||||
runtime->dma_bytes = 0x3000;
|
||||
memset(runtime->dma_area, 0, runtime->dma_bytes);
|
||||
chip->capture_substream = substream;
|
||||
runtime->hw = snd_msnd_capture;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msnd_capture_close(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
|
||||
|
||||
snd_msnd_disable_irq(chip);
|
||||
clear_bit(F_AUDIO_READ_INUSE, &chip->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msnd_capture_prepare(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
|
||||
unsigned int pcm_size = snd_pcm_lib_buffer_bytes(substream);
|
||||
unsigned int pcm_count = snd_pcm_lib_period_bytes(substream);
|
||||
unsigned int pcm_periods = pcm_size / pcm_count;
|
||||
|
||||
snd_msnd_capture_reset_queue(chip, pcm_periods, pcm_count);
|
||||
chip->captureDMAPos = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msnd_capture_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
|
||||
|
||||
if (cmd == SNDRV_PCM_TRIGGER_START) {
|
||||
chip->last_recbank = -1;
|
||||
set_bit(F_READING, &chip->flags);
|
||||
if (snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_START) == 0)
|
||||
return 0;
|
||||
|
||||
clear_bit(F_READING, &chip->flags);
|
||||
} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
|
||||
clear_bit(F_READING, &chip->flags);
|
||||
snd_msnd_send_dsp_cmd(chip, HDEX_RECORD_STOP);
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
static snd_pcm_uframes_t
|
||||
snd_msnd_capture_pointer(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
|
||||
|
||||
return bytes_to_frames(runtime, chip->captureDMAPos);
|
||||
}
|
||||
|
||||
|
||||
static int snd_msnd_capture_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
int i;
|
||||
struct snd_msnd *chip = snd_pcm_substream_chip(substream);
|
||||
void *pDAQ = chip->mappedbase + DARQ_DATA_BUFF;
|
||||
|
||||
chip->capture_sample_size = snd_pcm_format_width(params_format(params));
|
||||
chip->capture_channels = params_channels(params);
|
||||
chip->capture_sample_rate = params_rate(params);
|
||||
|
||||
for (i = 0; i < 3; ++i, pDAQ += DAQDS__size) {
|
||||
writew(chip->capture_sample_size, pDAQ + DAQDS_wSampleSize);
|
||||
writew(chip->capture_channels, pDAQ + DAQDS_wChannels);
|
||||
writew(chip->capture_sample_rate, pDAQ + DAQDS_wSampleRate);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct snd_pcm_ops snd_msnd_capture_ops = {
|
||||
.open = snd_msnd_capture_open,
|
||||
.close = snd_msnd_capture_close,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_msnd_capture_hw_params,
|
||||
.prepare = snd_msnd_capture_prepare,
|
||||
.trigger = snd_msnd_capture_trigger,
|
||||
.pointer = snd_msnd_capture_pointer,
|
||||
};
|
||||
|
||||
|
||||
int snd_msnd_pcm(struct snd_card *card, int device,
|
||||
struct snd_pcm **rpcm)
|
||||
{
|
||||
struct snd_msnd *chip = card->private_data;
|
||||
struct snd_pcm *pcm;
|
||||
int err;
|
||||
|
||||
err = snd_pcm_new(card, "MSNDPINNACLE", device, 1, 1, &pcm);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_msnd_playback_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_msnd_capture_ops);
|
||||
|
||||
pcm->private_data = chip;
|
||||
strcpy(pcm->name, "Hurricane");
|
||||
|
||||
|
||||
if (rpcm)
|
||||
*rpcm = pcm;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msnd_pcm);
|
||||
|
||||
MODULE_DESCRIPTION("Common routines for Turtle Beach Multisound drivers");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
308
sound/isa/msnd/msnd.h
Normal file
308
sound/isa/msnd/msnd.h
Normal file
|
@ -0,0 +1,308 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* msnd.h
|
||||
*
|
||||
* Turtle Beach MultiSound Sound Card Driver for Linux
|
||||
*
|
||||
* Some parts of this header file were derived from the Turtle Beach
|
||||
* MultiSound Driver Development Kit.
|
||||
*
|
||||
* Copyright (C) 1998 Andrew Veliath
|
||||
* Copyright (C) 1993 Turtle Beach Systems, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
********************************************************************/
|
||||
#ifndef __MSND_H
|
||||
#define __MSND_H
|
||||
|
||||
#define DEFSAMPLERATE 44100
|
||||
#define DEFSAMPLESIZE SNDRV_PCM_FORMAT_S16
|
||||
#define DEFCHANNELS 1
|
||||
|
||||
#define SRAM_BANK_SIZE 0x8000
|
||||
#define SRAM_CNTL_START 0x7F00
|
||||
#define SMA_STRUCT_START 0x7F40
|
||||
|
||||
#define DSP_BASE_ADDR 0x4000
|
||||
#define DSP_BANK_BASE 0x4000
|
||||
|
||||
#define AGND 0x01
|
||||
#define SIGNAL 0x02
|
||||
|
||||
#define EXT_DSP_BIT_DCAL 0x0001
|
||||
#define EXT_DSP_BIT_MIDI_CON 0x0002
|
||||
|
||||
#define BUFFSIZE 0x8000
|
||||
#define HOSTQ_SIZE 0x40
|
||||
|
||||
#define DAP_BUFF_SIZE 0x2400
|
||||
|
||||
#define DAPQ_STRUCT_SIZE 0x10
|
||||
#define DARQ_STRUCT_SIZE 0x10
|
||||
#define DAPQ_BUFF_SIZE (3 * 0x10)
|
||||
#define DARQ_BUFF_SIZE (3 * 0x10)
|
||||
#define MODQ_BUFF_SIZE 0x400
|
||||
|
||||
#define DAPQ_DATA_BUFF 0x6C00
|
||||
#define DARQ_DATA_BUFF 0x6C30
|
||||
#define MODQ_DATA_BUFF 0x6C60
|
||||
#define MIDQ_DATA_BUFF 0x7060
|
||||
|
||||
#define DAPQ_OFFSET SRAM_CNTL_START
|
||||
#define DARQ_OFFSET (SRAM_CNTL_START + 0x08)
|
||||
#define MODQ_OFFSET (SRAM_CNTL_START + 0x10)
|
||||
#define MIDQ_OFFSET (SRAM_CNTL_START + 0x18)
|
||||
#define DSPQ_OFFSET (SRAM_CNTL_START + 0x20)
|
||||
|
||||
#define HP_ICR 0x00
|
||||
#define HP_CVR 0x01
|
||||
#define HP_ISR 0x02
|
||||
#define HP_IVR 0x03
|
||||
#define HP_NU 0x04
|
||||
#define HP_INFO 0x04
|
||||
#define HP_TXH 0x05
|
||||
#define HP_RXH 0x05
|
||||
#define HP_TXM 0x06
|
||||
#define HP_RXM 0x06
|
||||
#define HP_TXL 0x07
|
||||
#define HP_RXL 0x07
|
||||
|
||||
#define HP_ICR_DEF 0x00
|
||||
#define HP_CVR_DEF 0x12
|
||||
#define HP_ISR_DEF 0x06
|
||||
#define HP_IVR_DEF 0x0f
|
||||
#define HP_NU_DEF 0x00
|
||||
|
||||
#define HP_IRQM 0x09
|
||||
|
||||
#define HPR_BLRC 0x08
|
||||
#define HPR_SPR1 0x09
|
||||
#define HPR_SPR2 0x0A
|
||||
#define HPR_TCL0 0x0B
|
||||
#define HPR_TCL1 0x0C
|
||||
#define HPR_TCL2 0x0D
|
||||
#define HPR_TCL3 0x0E
|
||||
#define HPR_TCL4 0x0F
|
||||
|
||||
#define HPICR_INIT 0x80
|
||||
#define HPICR_HM1 0x40
|
||||
#define HPICR_HM0 0x20
|
||||
#define HPICR_HF1 0x10
|
||||
#define HPICR_HF0 0x08
|
||||
#define HPICR_TREQ 0x02
|
||||
#define HPICR_RREQ 0x01
|
||||
|
||||
#define HPCVR_HC 0x80
|
||||
|
||||
#define HPISR_HREQ 0x80
|
||||
#define HPISR_DMA 0x40
|
||||
#define HPISR_HF3 0x10
|
||||
#define HPISR_HF2 0x08
|
||||
#define HPISR_TRDY 0x04
|
||||
#define HPISR_TXDE 0x02
|
||||
#define HPISR_RXDF 0x01
|
||||
|
||||
#define HPIO_290 0
|
||||
#define HPIO_260 1
|
||||
#define HPIO_250 2
|
||||
#define HPIO_240 3
|
||||
#define HPIO_230 4
|
||||
#define HPIO_220 5
|
||||
#define HPIO_210 6
|
||||
#define HPIO_3E0 7
|
||||
|
||||
#define HPMEM_NONE 0
|
||||
#define HPMEM_B000 1
|
||||
#define HPMEM_C800 2
|
||||
#define HPMEM_D000 3
|
||||
#define HPMEM_D400 4
|
||||
#define HPMEM_D800 5
|
||||
#define HPMEM_E000 6
|
||||
#define HPMEM_E800 7
|
||||
|
||||
#define HPIRQ_NONE 0
|
||||
#define HPIRQ_5 1
|
||||
#define HPIRQ_7 2
|
||||
#define HPIRQ_9 3
|
||||
#define HPIRQ_10 4
|
||||
#define HPIRQ_11 5
|
||||
#define HPIRQ_12 6
|
||||
#define HPIRQ_15 7
|
||||
|
||||
#define HIMT_PLAY_DONE 0x00
|
||||
#define HIMT_RECORD_DONE 0x01
|
||||
#define HIMT_MIDI_EOS 0x02
|
||||
#define HIMT_MIDI_OUT 0x03
|
||||
|
||||
#define HIMT_MIDI_IN_UCHAR 0x0E
|
||||
#define HIMT_DSP 0x0F
|
||||
|
||||
#define HDEX_BASE 0x92
|
||||
#define HDEX_PLAY_START (0 + HDEX_BASE)
|
||||
#define HDEX_PLAY_STOP (1 + HDEX_BASE)
|
||||
#define HDEX_PLAY_PAUSE (2 + HDEX_BASE)
|
||||
#define HDEX_PLAY_RESUME (3 + HDEX_BASE)
|
||||
#define HDEX_RECORD_START (4 + HDEX_BASE)
|
||||
#define HDEX_RECORD_STOP (5 + HDEX_BASE)
|
||||
#define HDEX_MIDI_IN_START (6 + HDEX_BASE)
|
||||
#define HDEX_MIDI_IN_STOP (7 + HDEX_BASE)
|
||||
#define HDEX_MIDI_OUT_START (8 + HDEX_BASE)
|
||||
#define HDEX_MIDI_OUT_STOP (9 + HDEX_BASE)
|
||||
#define HDEX_AUX_REQ (10 + HDEX_BASE)
|
||||
|
||||
#define HDEXAR_CLEAR_PEAKS 1
|
||||
#define HDEXAR_IN_SET_POTS 2
|
||||
#define HDEXAR_AUX_SET_POTS 3
|
||||
#define HDEXAR_CAL_A_TO_D 4
|
||||
#define HDEXAR_RD_EXT_DSP_BITS 5
|
||||
|
||||
/* Pinnacle only HDEXAR defs */
|
||||
#define HDEXAR_SET_ANA_IN 0
|
||||
#define HDEXAR_SET_SYNTH_IN 4
|
||||
#define HDEXAR_READ_DAT_IN 5
|
||||
#define HDEXAR_MIC_SET_POTS 6
|
||||
#define HDEXAR_SET_DAT_IN 7
|
||||
|
||||
#define HDEXAR_SET_SYNTH_48 8
|
||||
#define HDEXAR_SET_SYNTH_44 9
|
||||
|
||||
#define HIWORD(l) ((u16)((((u32)(l)) >> 16) & 0xFFFF))
|
||||
#define LOWORD(l) ((u16)(u32)(l))
|
||||
#define HIBYTE(w) ((u8)(((u16)(w) >> 8) & 0xFF))
|
||||
#define LOBYTE(w) ((u8)(w))
|
||||
#define MAKELONG(low, hi) ((long)(((u16)(low))|(((u32)((u16)(hi)))<<16)))
|
||||
#define MAKEWORD(low, hi) ((u16)(((u8)(low))|(((u16)((u8)(hi)))<<8)))
|
||||
|
||||
#define PCTODSP_OFFSET(w) (u16)((w)/2)
|
||||
#define PCTODSP_BASED(w) (u16)(((w)/2) + DSP_BASE_ADDR)
|
||||
#define DSPTOPC_BASED(w) (((w) - DSP_BASE_ADDR) * 2)
|
||||
|
||||
#ifdef SLOWIO
|
||||
# undef outb
|
||||
# undef inb
|
||||
# define outb outb_p
|
||||
# define inb inb_p
|
||||
#endif
|
||||
|
||||
/* JobQueueStruct */
|
||||
#define JQS_wStart 0x00
|
||||
#define JQS_wSize 0x02
|
||||
#define JQS_wHead 0x04
|
||||
#define JQS_wTail 0x06
|
||||
#define JQS__size 0x08
|
||||
|
||||
/* DAQueueDataStruct */
|
||||
#define DAQDS_wStart 0x00
|
||||
#define DAQDS_wSize 0x02
|
||||
#define DAQDS_wFormat 0x04
|
||||
#define DAQDS_wSampleSize 0x06
|
||||
#define DAQDS_wChannels 0x08
|
||||
#define DAQDS_wSampleRate 0x0A
|
||||
#define DAQDS_wIntMsg 0x0C
|
||||
#define DAQDS_wFlags 0x0E
|
||||
#define DAQDS__size 0x10
|
||||
|
||||
#include <sound/pcm.h>
|
||||
|
||||
struct snd_msnd {
|
||||
void __iomem *mappedbase;
|
||||
int play_period_bytes;
|
||||
int playLimit;
|
||||
int playPeriods;
|
||||
int playDMAPos;
|
||||
int banksPlayed;
|
||||
int captureDMAPos;
|
||||
int capturePeriodBytes;
|
||||
int captureLimit;
|
||||
int capturePeriods;
|
||||
struct snd_card *card;
|
||||
void *msndmidi_mpu;
|
||||
struct snd_rawmidi *rmidi;
|
||||
|
||||
/* Hardware resources */
|
||||
long io;
|
||||
int memid, irqid;
|
||||
int irq, irq_ref;
|
||||
unsigned long base;
|
||||
|
||||
/* Motorola 56k DSP SMA */
|
||||
void __iomem *SMA;
|
||||
void __iomem *DAPQ;
|
||||
void __iomem *DARQ;
|
||||
void __iomem *MODQ;
|
||||
void __iomem *MIDQ;
|
||||
void __iomem *DSPQ;
|
||||
int dspq_data_buff, dspq_buff_size;
|
||||
|
||||
/* State variables */
|
||||
enum { msndClassic, msndPinnacle } type;
|
||||
mode_t mode;
|
||||
unsigned long flags;
|
||||
#define F_RESETTING 0
|
||||
#define F_HAVEDIGITAL 1
|
||||
#define F_AUDIO_WRITE_INUSE 2
|
||||
#define F_WRITING 3
|
||||
#define F_WRITEBLOCK 4
|
||||
#define F_WRITEFLUSH 5
|
||||
#define F_AUDIO_READ_INUSE 6
|
||||
#define F_READING 7
|
||||
#define F_READBLOCK 8
|
||||
#define F_EXT_MIDI_INUSE 9
|
||||
#define F_HDR_MIDI_INUSE 10
|
||||
#define F_DISABLE_WRITE_NDELAY 11
|
||||
spinlock_t lock;
|
||||
spinlock_t mixer_lock;
|
||||
int nresets;
|
||||
unsigned recsrc;
|
||||
#define LEVEL_ENTRIES 32
|
||||
int left_levels[LEVEL_ENTRIES];
|
||||
int right_levels[LEVEL_ENTRIES];
|
||||
int calibrate_signal;
|
||||
int play_sample_size, play_sample_rate, play_channels;
|
||||
int play_ndelay;
|
||||
int capture_sample_size, capture_sample_rate, capture_channels;
|
||||
int capture_ndelay;
|
||||
u8 bCurrentMidiPatch;
|
||||
|
||||
int last_playbank, last_recbank;
|
||||
struct snd_pcm_substream *playback_substream;
|
||||
struct snd_pcm_substream *capture_substream;
|
||||
|
||||
};
|
||||
|
||||
void snd_msnd_init_queue(void *base, int start, int size);
|
||||
|
||||
int snd_msnd_send_dsp_cmd(struct snd_msnd *chip, u8 cmd);
|
||||
int snd_msnd_send_word(struct snd_msnd *chip,
|
||||
unsigned char high,
|
||||
unsigned char mid,
|
||||
unsigned char low);
|
||||
int snd_msnd_upload_host(struct snd_msnd *chip,
|
||||
const u8 *bin, int len);
|
||||
int snd_msnd_enable_irq(struct snd_msnd *chip);
|
||||
int snd_msnd_disable_irq(struct snd_msnd *chip);
|
||||
void snd_msnd_dsp_halt(struct snd_msnd *chip, struct file *file);
|
||||
int snd_msnd_DAPQ(struct snd_msnd *chip, int start);
|
||||
int snd_msnd_DARQ(struct snd_msnd *chip, int start);
|
||||
int snd_msnd_pcm(struct snd_card *card, int device, struct snd_pcm **rpcm);
|
||||
|
||||
int snd_msndmidi_new(struct snd_card *card, int device);
|
||||
void snd_msndmidi_input_read(void *mpu);
|
||||
|
||||
void snd_msndmix_setup(struct snd_msnd *chip);
|
||||
int __devinit snd_msndmix_new(struct snd_card *card);
|
||||
int snd_msndmix_force_recsrc(struct snd_msnd *chip, int recsrc);
|
||||
#endif /* __MSND_H */
|
3
sound/isa/msnd/msnd_classic.c
Normal file
3
sound/isa/msnd/msnd_classic.c
Normal file
|
@ -0,0 +1,3 @@
|
|||
/* The work is in msnd_pinnacle.c, just define MSND_CLASSIC before it. */
|
||||
#define MSND_CLASSIC
|
||||
#include "msnd_pinnacle.c"
|
129
sound/isa/msnd/msnd_classic.h
Normal file
129
sound/isa/msnd/msnd_classic.h
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* msnd_classic.h
|
||||
*
|
||||
* Turtle Beach MultiSound Sound Card Driver for Linux
|
||||
*
|
||||
* Some parts of this header file were derived from the Turtle Beach
|
||||
* MultiSound Driver Development Kit.
|
||||
*
|
||||
* Copyright (C) 1998 Andrew Veliath
|
||||
* Copyright (C) 1993 Turtle Beach Systems, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
********************************************************************/
|
||||
#ifndef __MSND_CLASSIC_H
|
||||
#define __MSND_CLASSIC_H
|
||||
|
||||
#define DSP_NUMIO 0x10
|
||||
|
||||
#define HP_MEMM 0x08
|
||||
|
||||
#define HP_BITM 0x0E
|
||||
#define HP_WAIT 0x0D
|
||||
#define HP_DSPR 0x0A
|
||||
#define HP_PROR 0x0B
|
||||
#define HP_BLKS 0x0C
|
||||
|
||||
#define HPPRORESET_OFF 0
|
||||
#define HPPRORESET_ON 1
|
||||
|
||||
#define HPDSPRESET_OFF 0
|
||||
#define HPDSPRESET_ON 1
|
||||
|
||||
#define HPBLKSEL_0 0
|
||||
#define HPBLKSEL_1 1
|
||||
|
||||
#define HPWAITSTATE_0 0
|
||||
#define HPWAITSTATE_1 1
|
||||
|
||||
#define HPBITMODE_16 0
|
||||
#define HPBITMODE_8 1
|
||||
|
||||
#define HIDSP_INT_PLAY_UNDER 0x00
|
||||
#define HIDSP_INT_RECORD_OVER 0x01
|
||||
#define HIDSP_INPUT_CLIPPING 0x02
|
||||
#define HIDSP_MIDI_IN_OVER 0x10
|
||||
#define HIDSP_MIDI_OVERRUN_ERR 0x13
|
||||
|
||||
#define TIME_PRO_RESET_DONE 0x028A
|
||||
#define TIME_PRO_SYSEX 0x0040
|
||||
#define TIME_PRO_RESET 0x0032
|
||||
|
||||
#define DAR_BUFF_SIZE 0x2000
|
||||
|
||||
#define MIDQ_BUFF_SIZE 0x200
|
||||
#define DSPQ_BUFF_SIZE 0x40
|
||||
|
||||
#define DSPQ_DATA_BUFF 0x7260
|
||||
|
||||
#define MOP_SYNTH 0x10
|
||||
#define MOP_EXTOUT 0x32
|
||||
#define MOP_EXTTHRU 0x02
|
||||
#define MOP_OUTMASK 0x01
|
||||
|
||||
#define MIP_EXTIN 0x01
|
||||
#define MIP_SYNTH 0x00
|
||||
#define MIP_INMASK 0x32
|
||||
|
||||
/* Classic SMA Common Data */
|
||||
#define SMA_wCurrPlayBytes 0x0000
|
||||
#define SMA_wCurrRecordBytes 0x0002
|
||||
#define SMA_wCurrPlayVolLeft 0x0004
|
||||
#define SMA_wCurrPlayVolRight 0x0006
|
||||
#define SMA_wCurrInVolLeft 0x0008
|
||||
#define SMA_wCurrInVolRight 0x000a
|
||||
#define SMA_wUser_3 0x000c
|
||||
#define SMA_wUser_4 0x000e
|
||||
#define SMA_dwUser_5 0x0010
|
||||
#define SMA_dwUser_6 0x0014
|
||||
#define SMA_wUser_7 0x0018
|
||||
#define SMA_wReserved_A 0x001a
|
||||
#define SMA_wReserved_B 0x001c
|
||||
#define SMA_wReserved_C 0x001e
|
||||
#define SMA_wReserved_D 0x0020
|
||||
#define SMA_wReserved_E 0x0022
|
||||
#define SMA_wReserved_F 0x0024
|
||||
#define SMA_wReserved_G 0x0026
|
||||
#define SMA_wReserved_H 0x0028
|
||||
#define SMA_wCurrDSPStatusFlags 0x002a
|
||||
#define SMA_wCurrHostStatusFlags 0x002c
|
||||
#define SMA_wCurrInputTagBits 0x002e
|
||||
#define SMA_wCurrLeftPeak 0x0030
|
||||
#define SMA_wCurrRightPeak 0x0032
|
||||
#define SMA_wExtDSPbits 0x0034
|
||||
#define SMA_bExtHostbits 0x0036
|
||||
#define SMA_bBoardLevel 0x0037
|
||||
#define SMA_bInPotPosRight 0x0038
|
||||
#define SMA_bInPotPosLeft 0x0039
|
||||
#define SMA_bAuxPotPosRight 0x003a
|
||||
#define SMA_bAuxPotPosLeft 0x003b
|
||||
#define SMA_wCurrMastVolLeft 0x003c
|
||||
#define SMA_wCurrMastVolRight 0x003e
|
||||
#define SMA_bUser_12 0x0040
|
||||
#define SMA_bUser_13 0x0041
|
||||
#define SMA_wUser_14 0x0042
|
||||
#define SMA_wUser_15 0x0044
|
||||
#define SMA_wCalFreqAtoD 0x0046
|
||||
#define SMA_wUser_16 0x0048
|
||||
#define SMA_wUser_17 0x004a
|
||||
#define SMA__size 0x004c
|
||||
|
||||
#define INITCODEFILE "turtlebeach/msndinit.bin"
|
||||
#define PERMCODEFILE "turtlebeach/msndperm.bin"
|
||||
#define LONGNAME "MultiSound (Classic/Monterey/Tahiti)"
|
||||
|
||||
#endif /* __MSND_CLASSIC_H */
|
180
sound/isa/msnd/msnd_midi.c
Normal file
180
sound/isa/msnd/msnd_midi.c
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
|
||||
* Copyright (c) 2009 by Krzysztof Helt
|
||||
* Routines for control of MPU-401 in UART mode
|
||||
*
|
||||
* MPU-401 supports UART mode which is not capable generate transmit
|
||||
* interrupts thus output is done via polling. Also, if irq < 0, then
|
||||
* input is done also via polling. Do not expect good performance.
|
||||
*
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/errno.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/rawmidi.h>
|
||||
|
||||
#include "msnd.h"
|
||||
|
||||
#define MSNDMIDI_MODE_BIT_INPUT 0
|
||||
#define MSNDMIDI_MODE_BIT_OUTPUT 1
|
||||
#define MSNDMIDI_MODE_BIT_INPUT_TRIGGER 2
|
||||
#define MSNDMIDI_MODE_BIT_OUTPUT_TRIGGER 3
|
||||
|
||||
struct snd_msndmidi {
|
||||
struct snd_msnd *dev;
|
||||
|
||||
unsigned long mode; /* MSNDMIDI_MODE_XXXX */
|
||||
|
||||
struct snd_rawmidi_substream *substream_input;
|
||||
|
||||
spinlock_t input_lock;
|
||||
};
|
||||
|
||||
/*
|
||||
* input/output open/close - protected by open_mutex in rawmidi.c
|
||||
*/
|
||||
static int snd_msndmidi_input_open(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_msndmidi *mpu;
|
||||
|
||||
snd_printdd("snd_msndmidi_input_open()\n");
|
||||
|
||||
mpu = substream->rmidi->private_data;
|
||||
|
||||
mpu->substream_input = substream;
|
||||
|
||||
snd_msnd_enable_irq(mpu->dev);
|
||||
|
||||
snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_START);
|
||||
set_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msndmidi_input_close(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_msndmidi *mpu;
|
||||
|
||||
mpu = substream->rmidi->private_data;
|
||||
snd_msnd_send_dsp_cmd(mpu->dev, HDEX_MIDI_IN_STOP);
|
||||
clear_bit(MSNDMIDI_MODE_BIT_INPUT, &mpu->mode);
|
||||
mpu->substream_input = NULL;
|
||||
snd_msnd_disable_irq(mpu->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_msndmidi_input_drop(struct snd_msndmidi *mpu)
|
||||
{
|
||||
u16 tail;
|
||||
|
||||
tail = readw(mpu->dev->MIDQ + JQS_wTail);
|
||||
writew(tail, mpu->dev->MIDQ + JQS_wHead);
|
||||
}
|
||||
|
||||
/*
|
||||
* trigger input
|
||||
*/
|
||||
static void snd_msndmidi_input_trigger(struct snd_rawmidi_substream *substream,
|
||||
int up)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct snd_msndmidi *mpu;
|
||||
|
||||
snd_printdd("snd_msndmidi_input_trigger(, %i)\n", up);
|
||||
|
||||
mpu = substream->rmidi->private_data;
|
||||
spin_lock_irqsave(&mpu->input_lock, flags);
|
||||
if (up) {
|
||||
if (!test_and_set_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER,
|
||||
&mpu->mode))
|
||||
snd_msndmidi_input_drop(mpu);
|
||||
} else {
|
||||
clear_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode);
|
||||
}
|
||||
spin_unlock_irqrestore(&mpu->input_lock, flags);
|
||||
if (up)
|
||||
snd_msndmidi_input_read(mpu);
|
||||
}
|
||||
|
||||
void snd_msndmidi_input_read(void *mpuv)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct snd_msndmidi *mpu = mpuv;
|
||||
void *pwMIDQData = mpu->dev->mappedbase + MIDQ_DATA_BUFF;
|
||||
|
||||
spin_lock_irqsave(&mpu->input_lock, flags);
|
||||
while (readw(mpu->dev->MIDQ + JQS_wTail) !=
|
||||
readw(mpu->dev->MIDQ + JQS_wHead)) {
|
||||
u16 wTmp, val;
|
||||
val = readw(pwMIDQData + 2 * readw(mpu->dev->MIDQ + JQS_wHead));
|
||||
|
||||
if (test_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER,
|
||||
&mpu->mode))
|
||||
snd_rawmidi_receive(mpu->substream_input,
|
||||
(unsigned char *)&val, 1);
|
||||
|
||||
wTmp = readw(mpu->dev->MIDQ + JQS_wHead) + 1;
|
||||
if (wTmp > readw(mpu->dev->MIDQ + JQS_wSize))
|
||||
writew(0, mpu->dev->MIDQ + JQS_wHead);
|
||||
else
|
||||
writew(wTmp, mpu->dev->MIDQ + JQS_wHead);
|
||||
}
|
||||
spin_unlock_irqrestore(&mpu->input_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msndmidi_input_read);
|
||||
|
||||
static struct snd_rawmidi_ops snd_msndmidi_input = {
|
||||
.open = snd_msndmidi_input_open,
|
||||
.close = snd_msndmidi_input_close,
|
||||
.trigger = snd_msndmidi_input_trigger,
|
||||
};
|
||||
|
||||
static void snd_msndmidi_free(struct snd_rawmidi *rmidi)
|
||||
{
|
||||
struct snd_msndmidi *mpu = rmidi->private_data;
|
||||
kfree(mpu);
|
||||
}
|
||||
|
||||
int snd_msndmidi_new(struct snd_card *card, int device)
|
||||
{
|
||||
struct snd_msnd *chip = card->private_data;
|
||||
struct snd_msndmidi *mpu;
|
||||
struct snd_rawmidi *rmidi;
|
||||
int err;
|
||||
|
||||
err = snd_rawmidi_new(card, "MSND-MIDI", device, 1, 1, &rmidi);
|
||||
if (err < 0)
|
||||
return err;
|
||||
mpu = kcalloc(1, sizeof(*mpu), GFP_KERNEL);
|
||||
if (mpu == NULL) {
|
||||
snd_device_free(card, rmidi);
|
||||
return -ENOMEM;
|
||||
}
|
||||
mpu->dev = chip;
|
||||
chip->msndmidi_mpu = mpu;
|
||||
rmidi->private_data = mpu;
|
||||
rmidi->private_free = snd_msndmidi_free;
|
||||
spin_lock_init(&mpu->input_lock);
|
||||
strcpy(rmidi->name, "MSND MIDI");
|
||||
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
|
||||
&snd_msndmidi_input);
|
||||
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
|
||||
return 0;
|
||||
}
|
1238
sound/isa/msnd/msnd_pinnacle.c
Normal file
1238
sound/isa/msnd/msnd_pinnacle.c
Normal file
File diff suppressed because it is too large
Load diff
181
sound/isa/msnd/msnd_pinnacle.h
Normal file
181
sound/isa/msnd/msnd_pinnacle.h
Normal file
|
@ -0,0 +1,181 @@
|
|||
/*********************************************************************
|
||||
*
|
||||
* msnd_pinnacle.h
|
||||
*
|
||||
* Turtle Beach MultiSound Sound Card Driver for Linux
|
||||
*
|
||||
* Some parts of this header file were derived from the Turtle Beach
|
||||
* MultiSound Driver Development Kit.
|
||||
*
|
||||
* Copyright (C) 1998 Andrew Veliath
|
||||
* Copyright (C) 1993 Turtle Beach Systems, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
********************************************************************/
|
||||
#ifndef __MSND_PINNACLE_H
|
||||
#define __MSND_PINNACLE_H
|
||||
|
||||
#define DSP_NUMIO 0x08
|
||||
|
||||
#define IREG_LOGDEVICE 0x07
|
||||
#define IREG_ACTIVATE 0x30
|
||||
#define LD_ACTIVATE 0x01
|
||||
#define LD_DISACTIVATE 0x00
|
||||
#define IREG_EECONTROL 0x3F
|
||||
#define IREG_MEMBASEHI 0x40
|
||||
#define IREG_MEMBASELO 0x41
|
||||
#define IREG_MEMCONTROL 0x42
|
||||
#define IREG_MEMRANGEHI 0x43
|
||||
#define IREG_MEMRANGELO 0x44
|
||||
#define MEMTYPE_8BIT 0x00
|
||||
#define MEMTYPE_16BIT 0x02
|
||||
#define MEMTYPE_RANGE 0x00
|
||||
#define MEMTYPE_HIADDR 0x01
|
||||
#define IREG_IO0_BASEHI 0x60
|
||||
#define IREG_IO0_BASELO 0x61
|
||||
#define IREG_IO1_BASEHI 0x62
|
||||
#define IREG_IO1_BASELO 0x63
|
||||
#define IREG_IRQ_NUMBER 0x70
|
||||
#define IREG_IRQ_TYPE 0x71
|
||||
#define IRQTYPE_HIGH 0x02
|
||||
#define IRQTYPE_LOW 0x00
|
||||
#define IRQTYPE_LEVEL 0x01
|
||||
#define IRQTYPE_EDGE 0x00
|
||||
|
||||
#define HP_DSPR 0x04
|
||||
#define HP_BLKS 0x04
|
||||
|
||||
#define HPDSPRESET_OFF 2
|
||||
#define HPDSPRESET_ON 0
|
||||
|
||||
#define HPBLKSEL_0 2
|
||||
#define HPBLKSEL_1 3
|
||||
|
||||
#define HIMT_DAT_OFF 0x03
|
||||
|
||||
#define HIDSP_PLAY_UNDER 0x00
|
||||
#define HIDSP_INT_PLAY_UNDER 0x01
|
||||
#define HIDSP_SSI_TX_UNDER 0x02
|
||||
#define HIDSP_RECQ_OVERFLOW 0x08
|
||||
#define HIDSP_INT_RECORD_OVER 0x09
|
||||
#define HIDSP_SSI_RX_OVERFLOW 0x0a
|
||||
|
||||
#define HIDSP_MIDI_IN_OVER 0x10
|
||||
|
||||
#define HIDSP_MIDI_FRAME_ERR 0x11
|
||||
#define HIDSP_MIDI_PARITY_ERR 0x12
|
||||
#define HIDSP_MIDI_OVERRUN_ERR 0x13
|
||||
|
||||
#define HIDSP_INPUT_CLIPPING 0x20
|
||||
#define HIDSP_MIX_CLIPPING 0x30
|
||||
#define HIDSP_DAT_IN_OFF 0x21
|
||||
|
||||
#define TIME_PRO_RESET_DONE 0x028A
|
||||
#define TIME_PRO_SYSEX 0x001E
|
||||
#define TIME_PRO_RESET 0x0032
|
||||
|
||||
#define DAR_BUFF_SIZE 0x1000
|
||||
|
||||
#define MIDQ_BUFF_SIZE 0x800
|
||||
#define DSPQ_BUFF_SIZE 0x5A0
|
||||
|
||||
#define DSPQ_DATA_BUFF 0x7860
|
||||
|
||||
#define MOP_WAVEHDR 0
|
||||
#define MOP_EXTOUT 1
|
||||
#define MOP_HWINIT 0xfe
|
||||
#define MOP_NONE 0xff
|
||||
#define MOP_MAX 1
|
||||
|
||||
#define MIP_EXTIN 0
|
||||
#define MIP_WAVEHDR 1
|
||||
#define MIP_HWINIT 0xfe
|
||||
#define MIP_MAX 1
|
||||
|
||||
/* Pinnacle/Fiji SMA Common Data */
|
||||
#define SMA_wCurrPlayBytes 0x0000
|
||||
#define SMA_wCurrRecordBytes 0x0002
|
||||
#define SMA_wCurrPlayVolLeft 0x0004
|
||||
#define SMA_wCurrPlayVolRight 0x0006
|
||||
#define SMA_wCurrInVolLeft 0x0008
|
||||
#define SMA_wCurrInVolRight 0x000a
|
||||
#define SMA_wCurrMHdrVolLeft 0x000c
|
||||
#define SMA_wCurrMHdrVolRight 0x000e
|
||||
#define SMA_dwCurrPlayPitch 0x0010
|
||||
#define SMA_dwCurrPlayRate 0x0014
|
||||
#define SMA_wCurrMIDIIOPatch 0x0018
|
||||
#define SMA_wCurrPlayFormat 0x001a
|
||||
#define SMA_wCurrPlaySampleSize 0x001c
|
||||
#define SMA_wCurrPlayChannels 0x001e
|
||||
#define SMA_wCurrPlaySampleRate 0x0020
|
||||
#define SMA_wCurrRecordFormat 0x0022
|
||||
#define SMA_wCurrRecordSampleSize 0x0024
|
||||
#define SMA_wCurrRecordChannels 0x0026
|
||||
#define SMA_wCurrRecordSampleRate 0x0028
|
||||
#define SMA_wCurrDSPStatusFlags 0x002a
|
||||
#define SMA_wCurrHostStatusFlags 0x002c
|
||||
#define SMA_wCurrInputTagBits 0x002e
|
||||
#define SMA_wCurrLeftPeak 0x0030
|
||||
#define SMA_wCurrRightPeak 0x0032
|
||||
#define SMA_bMicPotPosLeft 0x0034
|
||||
#define SMA_bMicPotPosRight 0x0035
|
||||
#define SMA_bMicPotMaxLeft 0x0036
|
||||
#define SMA_bMicPotMaxRight 0x0037
|
||||
#define SMA_bInPotPosLeft 0x0038
|
||||
#define SMA_bInPotPosRight 0x0039
|
||||
#define SMA_bAuxPotPosLeft 0x003a
|
||||
#define SMA_bAuxPotPosRight 0x003b
|
||||
#define SMA_bInPotMaxLeft 0x003c
|
||||
#define SMA_bInPotMaxRight 0x003d
|
||||
#define SMA_bAuxPotMaxLeft 0x003e
|
||||
#define SMA_bAuxPotMaxRight 0x003f
|
||||
#define SMA_bInPotMaxMethod 0x0040
|
||||
#define SMA_bAuxPotMaxMethod 0x0041
|
||||
#define SMA_wCurrMastVolLeft 0x0042
|
||||
#define SMA_wCurrMastVolRight 0x0044
|
||||
#define SMA_wCalFreqAtoD 0x0046
|
||||
#define SMA_wCurrAuxVolLeft 0x0048
|
||||
#define SMA_wCurrAuxVolRight 0x004a
|
||||
#define SMA_wCurrPlay1VolLeft 0x004c
|
||||
#define SMA_wCurrPlay1VolRight 0x004e
|
||||
#define SMA_wCurrPlay2VolLeft 0x0050
|
||||
#define SMA_wCurrPlay2VolRight 0x0052
|
||||
#define SMA_wCurrPlay3VolLeft 0x0054
|
||||
#define SMA_wCurrPlay3VolRight 0x0056
|
||||
#define SMA_wCurrPlay4VolLeft 0x0058
|
||||
#define SMA_wCurrPlay4VolRight 0x005a
|
||||
#define SMA_wCurrPlay1PeakLeft 0x005c
|
||||
#define SMA_wCurrPlay1PeakRight 0x005e
|
||||
#define SMA_wCurrPlay2PeakLeft 0x0060
|
||||
#define SMA_wCurrPlay2PeakRight 0x0062
|
||||
#define SMA_wCurrPlay3PeakLeft 0x0064
|
||||
#define SMA_wCurrPlay3PeakRight 0x0066
|
||||
#define SMA_wCurrPlay4PeakLeft 0x0068
|
||||
#define SMA_wCurrPlay4PeakRight 0x006a
|
||||
#define SMA_wCurrPlayPeakLeft 0x006c
|
||||
#define SMA_wCurrPlayPeakRight 0x006e
|
||||
#define SMA_wCurrDATSR 0x0070
|
||||
#define SMA_wCurrDATRXCHNL 0x0072
|
||||
#define SMA_wCurrDATTXCHNL 0x0074
|
||||
#define SMA_wCurrDATRXRate 0x0076
|
||||
#define SMA_dwDSPPlayCount 0x0078
|
||||
#define SMA__size 0x007c
|
||||
|
||||
#define INITCODEFILE "turtlebeach/pndspini.bin"
|
||||
#define PERMCODEFILE "turtlebeach/pndsperm.bin"
|
||||
#define LONGNAME "MultiSound (Pinnacle/Fiji)"
|
||||
|
||||
#endif /* __MSND_PINNACLE_H */
|
343
sound/isa/msnd/msnd_pinnacle_mixer.c
Normal file
343
sound/isa/msnd/msnd_pinnacle_mixer.c
Normal file
|
@ -0,0 +1,343 @@
|
|||
/***************************************************************************
|
||||
msnd_pinnacle_mixer.c - description
|
||||
-------------------
|
||||
begin : Fre Jun 7 2002
|
||||
copyright : (C) 2002 by karsten wiese
|
||||
email : annabellesgarden@yahoo.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/control.h>
|
||||
#include "msnd.h"
|
||||
#include "msnd_pinnacle.h"
|
||||
|
||||
|
||||
#define MSND_MIXER_VOLUME 0
|
||||
#define MSND_MIXER_PCM 1
|
||||
#define MSND_MIXER_AUX 2 /* Input source 1 (aux1) */
|
||||
#define MSND_MIXER_IMIX 3 /* Recording monitor */
|
||||
#define MSND_MIXER_SYNTH 4
|
||||
#define MSND_MIXER_SPEAKER 5
|
||||
#define MSND_MIXER_LINE 6
|
||||
#define MSND_MIXER_MIC 7
|
||||
#define MSND_MIXER_RECLEV 11 /* Recording level */
|
||||
#define MSND_MIXER_IGAIN 12 /* Input gain */
|
||||
#define MSND_MIXER_OGAIN 13 /* Output gain */
|
||||
#define MSND_MIXER_DIGITAL 17 /* Digital (input) 1 */
|
||||
|
||||
/* Device mask bits */
|
||||
|
||||
#define MSND_MASK_VOLUME (1 << MSND_MIXER_VOLUME)
|
||||
#define MSND_MASK_SYNTH (1 << MSND_MIXER_SYNTH)
|
||||
#define MSND_MASK_PCM (1 << MSND_MIXER_PCM)
|
||||
#define MSND_MASK_SPEAKER (1 << MSND_MIXER_SPEAKER)
|
||||
#define MSND_MASK_LINE (1 << MSND_MIXER_LINE)
|
||||
#define MSND_MASK_MIC (1 << MSND_MIXER_MIC)
|
||||
#define MSND_MASK_IMIX (1 << MSND_MIXER_IMIX)
|
||||
#define MSND_MASK_RECLEV (1 << MSND_MIXER_RECLEV)
|
||||
#define MSND_MASK_IGAIN (1 << MSND_MIXER_IGAIN)
|
||||
#define MSND_MASK_OGAIN (1 << MSND_MIXER_OGAIN)
|
||||
#define MSND_MASK_AUX (1 << MSND_MIXER_AUX)
|
||||
#define MSND_MASK_DIGITAL (1 << MSND_MIXER_DIGITAL)
|
||||
|
||||
static int snd_msndmix_info_mux(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
static char *texts[3] = {
|
||||
"Analog", "MASS", "SPDIF",
|
||||
};
|
||||
struct snd_msnd *chip = snd_kcontrol_chip(kcontrol);
|
||||
unsigned items = test_bit(F_HAVEDIGITAL, &chip->flags) ? 3 : 2;
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.enumerated.items = items;
|
||||
if (uinfo->value.enumerated.item >= items)
|
||||
uinfo->value.enumerated.item = items - 1;
|
||||
strcpy(uinfo->value.enumerated.name,
|
||||
texts[uinfo->value.enumerated.item]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msndmix_get_mux(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_msnd *chip = snd_kcontrol_chip(kcontrol);
|
||||
/* MSND_MASK_IMIX is the default */
|
||||
ucontrol->value.enumerated.item[0] = 0;
|
||||
|
||||
if (chip->recsrc & MSND_MASK_SYNTH) {
|
||||
ucontrol->value.enumerated.item[0] = 1;
|
||||
} else if ((chip->recsrc & MSND_MASK_DIGITAL) &&
|
||||
test_bit(F_HAVEDIGITAL, &chip->flags)) {
|
||||
ucontrol->value.enumerated.item[0] = 2;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msndmix_set_mux(struct snd_msnd *chip, int val)
|
||||
{
|
||||
unsigned newrecsrc;
|
||||
int change;
|
||||
unsigned char msndbyte;
|
||||
|
||||
switch (val) {
|
||||
case 0:
|
||||
newrecsrc = MSND_MASK_IMIX;
|
||||
msndbyte = HDEXAR_SET_ANA_IN;
|
||||
break;
|
||||
case 1:
|
||||
newrecsrc = MSND_MASK_SYNTH;
|
||||
msndbyte = HDEXAR_SET_SYNTH_IN;
|
||||
break;
|
||||
case 2:
|
||||
newrecsrc = MSND_MASK_DIGITAL;
|
||||
msndbyte = HDEXAR_SET_DAT_IN;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
change = newrecsrc != chip->recsrc;
|
||||
if (change) {
|
||||
change = 0;
|
||||
if (!snd_msnd_send_word(chip, 0, 0, msndbyte))
|
||||
if (!snd_msnd_send_dsp_cmd(chip, HDEX_AUX_REQ)) {
|
||||
chip->recsrc = newrecsrc;
|
||||
change = 1;
|
||||
}
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
||||
static int snd_msndmix_put_mux(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
|
||||
return snd_msndmix_set_mux(msnd, ucontrol->value.enumerated.item[0]);
|
||||
}
|
||||
|
||||
|
||||
static int snd_msndmix_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 = 100;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msndmix_volume_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
|
||||
int addr = kcontrol->private_value;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&msnd->mixer_lock, flags);
|
||||
ucontrol->value.integer.value[0] = msnd->left_levels[addr] * 100;
|
||||
ucontrol->value.integer.value[0] /= 0xFFFF;
|
||||
ucontrol->value.integer.value[1] = msnd->right_levels[addr] * 100;
|
||||
ucontrol->value.integer.value[1] /= 0xFFFF;
|
||||
spin_unlock_irqrestore(&msnd->mixer_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define update_volm(a, b) \
|
||||
do { \
|
||||
writew((dev->left_levels[a] >> 1) * \
|
||||
readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \
|
||||
dev->SMA + SMA_##b##Left); \
|
||||
writew((dev->right_levels[a] >> 1) * \
|
||||
readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \
|
||||
dev->SMA + SMA_##b##Right); \
|
||||
} while (0);
|
||||
|
||||
#define update_potm(d, s, ar) \
|
||||
do { \
|
||||
writeb((dev->left_levels[d] >> 8) * \
|
||||
readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \
|
||||
dev->SMA + SMA_##s##Left); \
|
||||
writeb((dev->right_levels[d] >> 8) * \
|
||||
readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \
|
||||
dev->SMA + SMA_##s##Right); \
|
||||
if (snd_msnd_send_word(dev, 0, 0, ar) == 0) \
|
||||
snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); \
|
||||
} while (0);
|
||||
|
||||
#define update_pot(d, s, ar) \
|
||||
do { \
|
||||
writeb(dev->left_levels[d] >> 8, \
|
||||
dev->SMA + SMA_##s##Left); \
|
||||
writeb(dev->right_levels[d] >> 8, \
|
||||
dev->SMA + SMA_##s##Right); \
|
||||
if (snd_msnd_send_word(dev, 0, 0, ar) == 0) \
|
||||
snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); \
|
||||
} while (0);
|
||||
|
||||
|
||||
static int snd_msndmix_set(struct snd_msnd *dev, int d, int left, int right)
|
||||
{
|
||||
int bLeft, bRight;
|
||||
int wLeft, wRight;
|
||||
int updatemaster = 0;
|
||||
|
||||
if (d >= LEVEL_ENTRIES)
|
||||
return -EINVAL;
|
||||
|
||||
bLeft = left * 0xff / 100;
|
||||
wLeft = left * 0xffff / 100;
|
||||
|
||||
bRight = right * 0xff / 100;
|
||||
wRight = right * 0xffff / 100;
|
||||
|
||||
dev->left_levels[d] = wLeft;
|
||||
dev->right_levels[d] = wRight;
|
||||
|
||||
switch (d) {
|
||||
/* master volume unscaled controls */
|
||||
case MSND_MIXER_LINE: /* line pot control */
|
||||
/* scaled by IMIX in digital mix */
|
||||
writeb(bLeft, dev->SMA + SMA_bInPotPosLeft);
|
||||
writeb(bRight, dev->SMA + SMA_bInPotPosRight);
|
||||
if (snd_msnd_send_word(dev, 0, 0, HDEXAR_IN_SET_POTS) == 0)
|
||||
snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);
|
||||
break;
|
||||
case MSND_MIXER_MIC: /* mic pot control */
|
||||
if (dev->type == msndClassic)
|
||||
return -EINVAL;
|
||||
/* scaled by IMIX in digital mix */
|
||||
writeb(bLeft, dev->SMA + SMA_bMicPotPosLeft);
|
||||
writeb(bRight, dev->SMA + SMA_bMicPotPosRight);
|
||||
if (snd_msnd_send_word(dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0)
|
||||
snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ);
|
||||
break;
|
||||
case MSND_MIXER_VOLUME: /* master volume */
|
||||
writew(wLeft, dev->SMA + SMA_wCurrMastVolLeft);
|
||||
writew(wRight, dev->SMA + SMA_wCurrMastVolRight);
|
||||
/* fall through */
|
||||
|
||||
case MSND_MIXER_AUX: /* aux pot control */
|
||||
/* scaled by master volume */
|
||||
/* fall through */
|
||||
|
||||
/* digital controls */
|
||||
case MSND_MIXER_SYNTH: /* synth vol (dsp mix) */
|
||||
case MSND_MIXER_PCM: /* pcm vol (dsp mix) */
|
||||
case MSND_MIXER_IMIX: /* input monitor (dsp mix) */
|
||||
/* scaled by master volume */
|
||||
updatemaster = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (updatemaster) {
|
||||
/* update master volume scaled controls */
|
||||
update_volm(MSND_MIXER_PCM, wCurrPlayVol);
|
||||
update_volm(MSND_MIXER_IMIX, wCurrInVol);
|
||||
if (dev->type == msndPinnacle)
|
||||
update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol);
|
||||
update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_msndmix_volume_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol);
|
||||
int change, addr = kcontrol->private_value;
|
||||
int left, right;
|
||||
unsigned long flags;
|
||||
|
||||
left = ucontrol->value.integer.value[0] % 101;
|
||||
right = ucontrol->value.integer.value[1] % 101;
|
||||
spin_lock_irqsave(&msnd->mixer_lock, flags);
|
||||
change = msnd->left_levels[addr] != left
|
||||
|| msnd->right_levels[addr] != right;
|
||||
snd_msndmix_set(msnd, addr, left, right);
|
||||
spin_unlock_irqrestore(&msnd->mixer_lock, flags);
|
||||
return change;
|
||||
}
|
||||
|
||||
|
||||
#define DUMMY_VOLUME(xname, xindex, addr) \
|
||||
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
|
||||
.info = snd_msndmix_volume_info, \
|
||||
.get = snd_msndmix_volume_get, .put = snd_msndmix_volume_put, \
|
||||
.private_value = addr }
|
||||
|
||||
|
||||
static struct snd_kcontrol_new snd_msnd_controls[] = {
|
||||
DUMMY_VOLUME("Master Volume", 0, MSND_MIXER_VOLUME),
|
||||
DUMMY_VOLUME("PCM Volume", 0, MSND_MIXER_PCM),
|
||||
DUMMY_VOLUME("Aux Volume", 0, MSND_MIXER_AUX),
|
||||
DUMMY_VOLUME("Line Volume", 0, MSND_MIXER_LINE),
|
||||
DUMMY_VOLUME("Mic Volume", 0, MSND_MIXER_MIC),
|
||||
DUMMY_VOLUME("Monitor", 0, MSND_MIXER_IMIX),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "Capture Source",
|
||||
.info = snd_msndmix_info_mux,
|
||||
.get = snd_msndmix_get_mux,
|
||||
.put = snd_msndmix_put_mux,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int __devinit snd_msndmix_new(struct snd_card *card)
|
||||
{
|
||||
struct snd_msnd *chip = card->private_data;
|
||||
unsigned int idx;
|
||||
int err;
|
||||
|
||||
if (snd_BUG_ON(!chip))
|
||||
return -EINVAL;
|
||||
spin_lock_init(&chip->mixer_lock);
|
||||
strcpy(card->mixername, "MSND Pinnacle Mixer");
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++)
|
||||
err = snd_ctl_add(card,
|
||||
snd_ctl_new1(snd_msnd_controls + idx, chip));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msndmix_new);
|
||||
|
||||
void snd_msndmix_setup(struct snd_msnd *dev)
|
||||
{
|
||||
update_pot(MSND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS);
|
||||
update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS);
|
||||
update_volm(MSND_MIXER_PCM, wCurrPlayVol);
|
||||
update_volm(MSND_MIXER_IMIX, wCurrInVol);
|
||||
if (dev->type == msndPinnacle) {
|
||||
update_pot(MSND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS);
|
||||
update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msndmix_setup);
|
||||
|
||||
int snd_msndmix_force_recsrc(struct snd_msnd *dev, int recsrc)
|
||||
{
|
||||
dev->recsrc = -1;
|
||||
return snd_msndmix_set_mux(dev, recsrc);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_msndmix_force_recsrc);
|
Loading…
Reference in a new issue