2009-01-24 12:35:28 +00:00
|
|
|
/*********************************************************************
|
|
|
|
*
|
|
|
|
* Linux multisound pinnacle/fiji driver for ALSA.
|
|
|
|
*
|
|
|
|
* 2002/06/30 Karsten Wiese:
|
|
|
|
* for now this is only used to build a pinnacle / fiji driver.
|
|
|
|
* the OSS parent of this code is designed to also support
|
|
|
|
* the multisound classic via the file msnd_classic.c.
|
|
|
|
* to make it easier for some brave heart to implemt classic
|
|
|
|
* support in alsa, i left all the MSND_CLASSIC tokens in this file.
|
|
|
|
* but for now this untested & undone.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* ripped from linux kernel 2.4.18 by Karsten Wiese.
|
|
|
|
*
|
|
|
|
* the following is a copy of the 2.4.18 OSS FREE file-heading comment:
|
|
|
|
*
|
|
|
|
* Turtle Beach MultiSound Sound Card Driver for Linux
|
|
|
|
* msnd_pinnacle.c / msnd_classic.c
|
|
|
|
*
|
|
|
|
* -- If MSND_CLASSIC is defined:
|
|
|
|
*
|
|
|
|
* -> driver for Turtle Beach Classic/Monterey/Tahiti
|
|
|
|
*
|
|
|
|
* -- Else
|
|
|
|
*
|
|
|
|
* -> driver for Turtle Beach Pinnacle/Fiji
|
|
|
|
*
|
|
|
|
* 12-3-2000 Modified IO port validation Steve Sycamore
|
|
|
|
*
|
|
|
|
* 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/module.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/ioport.h>
|
|
|
|
#include <linux/firmware.h>
|
|
|
|
#include <linux/isa.h>
|
|
|
|
#include <linux/isapnp.h>
|
|
|
|
#include <linux/irq.h>
|
|
|
|
#include <linux/io.h>
|
|
|
|
|
|
|
|
#include <sound/core.h>
|
|
|
|
#include <sound/initval.h>
|
|
|
|
#include <sound/asound.h>
|
|
|
|
#include <sound/pcm.h>
|
|
|
|
#include <sound/mpu401.h>
|
|
|
|
|
|
|
|
#ifdef MSND_CLASSIC
|
|
|
|
# ifndef __alpha__
|
|
|
|
# define SLOWIO
|
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
#include "msnd.h"
|
|
|
|
#ifdef MSND_CLASSIC
|
|
|
|
# include "msnd_classic.h"
|
|
|
|
# define LOGNAME "msnd_classic"
|
2013-11-12 07:06:20 +00:00
|
|
|
# define DEV_NAME "msnd-classic"
|
2009-01-24 12:35:28 +00:00
|
|
|
#else
|
|
|
|
# include "msnd_pinnacle.h"
|
|
|
|
# define LOGNAME "snd_msnd_pinnacle"
|
2013-11-12 07:06:20 +00:00
|
|
|
# define DEV_NAME "msnd-pinnacle"
|
2009-01-24 12:35:28 +00:00
|
|
|
#endif
|
|
|
|
|
2012-12-06 17:35:21 +00:00
|
|
|
static void set_default_audio_parameters(struct snd_msnd *chip)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
chip->play_sample_size = DEFSAMPLESIZE;
|
|
|
|
chip->play_sample_rate = DEFSAMPLERATE;
|
|
|
|
chip->play_channels = DEFCHANNELS;
|
|
|
|
chip->capture_sample_size = DEFSAMPLESIZE;
|
|
|
|
chip->capture_sample_rate = DEFSAMPLERATE;
|
|
|
|
chip->capture_channels = DEFCHANNELS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void snd_msnd_eval_dsp_msg(struct snd_msnd *chip, u16 wMessage)
|
|
|
|
{
|
|
|
|
switch (HIBYTE(wMessage)) {
|
|
|
|
case HIMT_PLAY_DONE: {
|
|
|
|
if (chip->banksPlayed < 3)
|
|
|
|
snd_printdd("%08X: HIMT_PLAY_DONE: %i\n",
|
|
|
|
(unsigned)jiffies, LOBYTE(wMessage));
|
|
|
|
|
|
|
|
if (chip->last_playbank == LOBYTE(wMessage)) {
|
|
|
|
snd_printdd("chip.last_playbank == LOBYTE(wMessage)\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
chip->banksPlayed++;
|
|
|
|
|
|
|
|
if (test_bit(F_WRITING, &chip->flags))
|
|
|
|
snd_msnd_DAPQ(chip, 0);
|
|
|
|
|
|
|
|
chip->last_playbank = LOBYTE(wMessage);
|
|
|
|
chip->playDMAPos += chip->play_period_bytes;
|
|
|
|
if (chip->playDMAPos > chip->playLimit)
|
|
|
|
chip->playDMAPos = 0;
|
|
|
|
snd_pcm_period_elapsed(chip->playback_substream);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case HIMT_RECORD_DONE:
|
|
|
|
if (chip->last_recbank == LOBYTE(wMessage))
|
|
|
|
break;
|
|
|
|
chip->last_recbank = LOBYTE(wMessage);
|
|
|
|
chip->captureDMAPos += chip->capturePeriodBytes;
|
|
|
|
if (chip->captureDMAPos > (chip->captureLimit))
|
|
|
|
chip->captureDMAPos = 0;
|
|
|
|
|
|
|
|
if (test_bit(F_READING, &chip->flags))
|
|
|
|
snd_msnd_DARQ(chip, chip->last_recbank);
|
|
|
|
|
|
|
|
snd_pcm_period_elapsed(chip->capture_substream);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HIMT_DSP:
|
|
|
|
switch (LOBYTE(wMessage)) {
|
|
|
|
#ifndef MSND_CLASSIC
|
|
|
|
case HIDSP_PLAY_UNDER:
|
|
|
|
#endif
|
|
|
|
case HIDSP_INT_PLAY_UNDER:
|
|
|
|
snd_printd(KERN_WARNING LOGNAME ": Play underflow %i\n",
|
|
|
|
chip->banksPlayed);
|
|
|
|
if (chip->banksPlayed > 2)
|
|
|
|
clear_bit(F_WRITING, &chip->flags);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HIDSP_INT_RECORD_OVER:
|
|
|
|
snd_printd(KERN_WARNING LOGNAME ": Record overflow\n");
|
|
|
|
clear_bit(F_READING, &chip->flags);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
snd_printd(KERN_WARNING LOGNAME
|
|
|
|
": DSP message %d 0x%02x\n",
|
|
|
|
LOBYTE(wMessage), LOBYTE(wMessage));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case HIMT_MIDI_IN_UCHAR:
|
|
|
|
if (chip->msndmidi_mpu)
|
|
|
|
snd_msndmidi_input_read(chip->msndmidi_mpu);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
snd_printd(KERN_WARNING LOGNAME ": HIMT message %d 0x%02x\n",
|
|
|
|
HIBYTE(wMessage), HIBYTE(wMessage));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static irqreturn_t snd_msnd_interrupt(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct snd_msnd *chip = dev_id;
|
|
|
|
void *pwDSPQData = chip->mappedbase + DSPQ_DATA_BUFF;
|
2017-07-06 10:34:40 +00:00
|
|
|
u16 head, tail, size;
|
2009-01-24 12:35:28 +00:00
|
|
|
|
|
|
|
/* Send ack to DSP */
|
|
|
|
/* inb(chip->io + HP_RXL); */
|
|
|
|
|
|
|
|
/* Evaluate queued DSP messages */
|
2017-07-06 10:34:40 +00:00
|
|
|
head = readw(chip->DSPQ + JQS_wHead);
|
|
|
|
tail = readw(chip->DSPQ + JQS_wTail);
|
|
|
|
size = readw(chip->DSPQ + JQS_wSize);
|
|
|
|
if (head > size || tail > size)
|
|
|
|
goto out;
|
|
|
|
while (head != tail) {
|
|
|
|
snd_msnd_eval_dsp_msg(chip, readw(pwDSPQData + 2 * head));
|
|
|
|
if (++head > size)
|
|
|
|
head = 0;
|
|
|
|
writew(head, chip->DSPQ + JQS_wHead);
|
2009-01-24 12:35:28 +00:00
|
|
|
}
|
2017-07-06 10:34:40 +00:00
|
|
|
out:
|
2009-01-24 12:35:28 +00:00
|
|
|
/* Send ack to DSP */
|
|
|
|
inb(chip->io + HP_RXL);
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int snd_msnd_reset_dsp(long io, unsigned char *info)
|
|
|
|
{
|
|
|
|
int timeout = 100;
|
|
|
|
|
|
|
|
outb(HPDSPRESET_ON, io + HP_DSPR);
|
|
|
|
msleep(1);
|
|
|
|
#ifndef MSND_CLASSIC
|
|
|
|
if (info)
|
|
|
|
*info = inb(io + HP_INFO);
|
|
|
|
#endif
|
|
|
|
outb(HPDSPRESET_OFF, io + HP_DSPR);
|
|
|
|
msleep(1);
|
|
|
|
while (timeout-- > 0) {
|
|
|
|
if (inb(io + HP_CVR) == HP_CVR_DEF)
|
|
|
|
return 0;
|
|
|
|
msleep(1);
|
|
|
|
}
|
|
|
|
snd_printk(KERN_ERR LOGNAME ": Cannot reset DSP\n");
|
|
|
|
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2012-12-06 17:35:21 +00:00
|
|
|
static int snd_msnd_probe(struct snd_card *card)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
struct snd_msnd *chip = card->private_data;
|
|
|
|
unsigned char info;
|
|
|
|
#ifndef MSND_CLASSIC
|
|
|
|
char *xv, *rev = NULL;
|
|
|
|
char *pin = "TB Pinnacle", *fiji = "TB Fiji";
|
|
|
|
char *pinfiji = "TB Pinnacle/Fiji";
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!request_region(chip->io, DSP_NUMIO, "probing")) {
|
|
|
|
snd_printk(KERN_ERR LOGNAME ": I/O port conflict\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (snd_msnd_reset_dsp(chip->io, &info) < 0) {
|
|
|
|
release_region(chip->io, DSP_NUMIO);
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef MSND_CLASSIC
|
|
|
|
strcpy(card->shortname, "Classic/Tahiti/Monterey");
|
|
|
|
strcpy(card->longname, "Turtle Beach Multisound");
|
|
|
|
printk(KERN_INFO LOGNAME ": %s, "
|
|
|
|
"I/O 0x%lx-0x%lx, IRQ %d, memory mapped to 0x%lX-0x%lX\n",
|
|
|
|
card->shortname,
|
|
|
|
chip->io, chip->io + DSP_NUMIO - 1,
|
|
|
|
chip->irq,
|
|
|
|
chip->base, chip->base + 0x7fff);
|
|
|
|
#else
|
|
|
|
switch (info >> 4) {
|
|
|
|
case 0xf:
|
|
|
|
xv = "<= 1.15";
|
|
|
|
break;
|
|
|
|
case 0x1:
|
|
|
|
xv = "1.18/1.2";
|
|
|
|
break;
|
|
|
|
case 0x2:
|
|
|
|
xv = "1.3";
|
|
|
|
break;
|
|
|
|
case 0x3:
|
|
|
|
xv = "1.4";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
xv = "unknown";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (info & 0x7) {
|
|
|
|
case 0x0:
|
|
|
|
rev = "I";
|
|
|
|
strcpy(card->shortname, pin);
|
|
|
|
break;
|
|
|
|
case 0x1:
|
|
|
|
rev = "F";
|
|
|
|
strcpy(card->shortname, pin);
|
|
|
|
break;
|
|
|
|
case 0x2:
|
|
|
|
rev = "G";
|
|
|
|
strcpy(card->shortname, pin);
|
|
|
|
break;
|
|
|
|
case 0x3:
|
|
|
|
rev = "H";
|
|
|
|
strcpy(card->shortname, pin);
|
|
|
|
break;
|
|
|
|
case 0x4:
|
|
|
|
rev = "E";
|
|
|
|
strcpy(card->shortname, fiji);
|
|
|
|
break;
|
|
|
|
case 0x5:
|
|
|
|
rev = "C";
|
|
|
|
strcpy(card->shortname, fiji);
|
|
|
|
break;
|
|
|
|
case 0x6:
|
|
|
|
rev = "D";
|
|
|
|
strcpy(card->shortname, fiji);
|
|
|
|
break;
|
|
|
|
case 0x7:
|
|
|
|
rev = "A-B (Fiji) or A-E (Pinnacle)";
|
|
|
|
strcpy(card->shortname, pinfiji);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
strcpy(card->longname, "Turtle Beach Multisound Pinnacle");
|
|
|
|
printk(KERN_INFO LOGNAME ": %s revision %s, Xilinx version %s, "
|
|
|
|
"I/O 0x%lx-0x%lx, IRQ %d, memory mapped to 0x%lX-0x%lX\n",
|
|
|
|
card->shortname,
|
|
|
|
rev, xv,
|
|
|
|
chip->io, chip->io + DSP_NUMIO - 1,
|
|
|
|
chip->irq,
|
|
|
|
chip->base, chip->base + 0x7fff);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
release_region(chip->io, DSP_NUMIO);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int snd_msnd_init_sma(struct snd_msnd *chip)
|
|
|
|
{
|
|
|
|
static int initted;
|
|
|
|
u16 mastVolLeft, mastVolRight;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
#ifdef MSND_CLASSIC
|
|
|
|
outb(chip->memid, chip->io + HP_MEMM);
|
|
|
|
#endif
|
|
|
|
outb(HPBLKSEL_0, chip->io + HP_BLKS);
|
|
|
|
/* Motorola 56k shared memory base */
|
|
|
|
chip->SMA = chip->mappedbase + SMA_STRUCT_START;
|
|
|
|
|
|
|
|
if (initted) {
|
|
|
|
mastVolLeft = readw(chip->SMA + SMA_wCurrMastVolLeft);
|
|
|
|
mastVolRight = readw(chip->SMA + SMA_wCurrMastVolRight);
|
|
|
|
} else
|
|
|
|
mastVolLeft = mastVolRight = 0;
|
|
|
|
memset_io(chip->mappedbase, 0, 0x8000);
|
|
|
|
|
|
|
|
/* Critical section: bank 1 access */
|
|
|
|
spin_lock_irqsave(&chip->lock, flags);
|
|
|
|
outb(HPBLKSEL_1, chip->io + HP_BLKS);
|
|
|
|
memset_io(chip->mappedbase, 0, 0x8000);
|
|
|
|
outb(HPBLKSEL_0, chip->io + HP_BLKS);
|
|
|
|
spin_unlock_irqrestore(&chip->lock, flags);
|
|
|
|
|
|
|
|
/* Digital audio play queue */
|
|
|
|
chip->DAPQ = chip->mappedbase + DAPQ_OFFSET;
|
|
|
|
snd_msnd_init_queue(chip->DAPQ, DAPQ_DATA_BUFF, DAPQ_BUFF_SIZE);
|
|
|
|
|
|
|
|
/* Digital audio record queue */
|
|
|
|
chip->DARQ = chip->mappedbase + DARQ_OFFSET;
|
|
|
|
snd_msnd_init_queue(chip->DARQ, DARQ_DATA_BUFF, DARQ_BUFF_SIZE);
|
|
|
|
|
|
|
|
/* MIDI out queue */
|
|
|
|
chip->MODQ = chip->mappedbase + MODQ_OFFSET;
|
|
|
|
snd_msnd_init_queue(chip->MODQ, MODQ_DATA_BUFF, MODQ_BUFF_SIZE);
|
|
|
|
|
|
|
|
/* MIDI in queue */
|
|
|
|
chip->MIDQ = chip->mappedbase + MIDQ_OFFSET;
|
|
|
|
snd_msnd_init_queue(chip->MIDQ, MIDQ_DATA_BUFF, MIDQ_BUFF_SIZE);
|
|
|
|
|
|
|
|
/* DSP -> host message queue */
|
|
|
|
chip->DSPQ = chip->mappedbase + DSPQ_OFFSET;
|
|
|
|
snd_msnd_init_queue(chip->DSPQ, DSPQ_DATA_BUFF, DSPQ_BUFF_SIZE);
|
|
|
|
|
|
|
|
/* Setup some DSP values */
|
|
|
|
#ifndef MSND_CLASSIC
|
|
|
|
writew(1, chip->SMA + SMA_wCurrPlayFormat);
|
|
|
|
writew(chip->play_sample_size, chip->SMA + SMA_wCurrPlaySampleSize);
|
|
|
|
writew(chip->play_channels, chip->SMA + SMA_wCurrPlayChannels);
|
|
|
|
writew(chip->play_sample_rate, chip->SMA + SMA_wCurrPlaySampleRate);
|
|
|
|
#endif
|
|
|
|
writew(chip->play_sample_rate, chip->SMA + SMA_wCalFreqAtoD);
|
|
|
|
writew(mastVolLeft, chip->SMA + SMA_wCurrMastVolLeft);
|
|
|
|
writew(mastVolRight, chip->SMA + SMA_wCurrMastVolRight);
|
|
|
|
#ifndef MSND_CLASSIC
|
|
|
|
writel(0x00010000, chip->SMA + SMA_dwCurrPlayPitch);
|
|
|
|
writel(0x00000001, chip->SMA + SMA_dwCurrPlayRate);
|
|
|
|
#endif
|
|
|
|
writew(0x303, chip->SMA + SMA_wCurrInputTagBits);
|
|
|
|
|
|
|
|
initted = 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int upload_dsp_code(struct snd_card *card)
|
|
|
|
{
|
|
|
|
struct snd_msnd *chip = card->private_data;
|
|
|
|
const struct firmware *init_fw = NULL, *perm_fw = NULL;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
outb(HPBLKSEL_0, chip->io + HP_BLKS);
|
|
|
|
|
|
|
|
err = request_firmware(&init_fw, INITCODEFILE, card->dev);
|
|
|
|
if (err < 0) {
|
|
|
|
printk(KERN_ERR LOGNAME ": Error loading " INITCODEFILE);
|
|
|
|
goto cleanup1;
|
|
|
|
}
|
|
|
|
err = request_firmware(&perm_fw, PERMCODEFILE, card->dev);
|
|
|
|
if (err < 0) {
|
|
|
|
printk(KERN_ERR LOGNAME ": Error loading " PERMCODEFILE);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy_toio(chip->mappedbase, perm_fw->data, perm_fw->size);
|
|
|
|
if (snd_msnd_upload_host(chip, init_fw->data, init_fw->size) < 0) {
|
|
|
|
printk(KERN_WARNING LOGNAME ": Error uploading to DSP\n");
|
|
|
|
err = -ENODEV;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
printk(KERN_INFO LOGNAME ": DSP firmware uploaded\n");
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
release_firmware(perm_fw);
|
|
|
|
cleanup1:
|
|
|
|
release_firmware(init_fw);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef MSND_CLASSIC
|
|
|
|
static void reset_proteus(struct snd_msnd *chip)
|
|
|
|
{
|
|
|
|
outb(HPPRORESET_ON, chip->io + HP_PROR);
|
|
|
|
msleep(TIME_PRO_RESET);
|
|
|
|
outb(HPPRORESET_OFF, chip->io + HP_PROR);
|
|
|
|
msleep(TIME_PRO_RESET_DONE);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int snd_msnd_initialize(struct snd_card *card)
|
|
|
|
{
|
|
|
|
struct snd_msnd *chip = card->private_data;
|
|
|
|
int err, timeout;
|
|
|
|
|
|
|
|
#ifdef MSND_CLASSIC
|
|
|
|
outb(HPWAITSTATE_0, chip->io + HP_WAIT);
|
|
|
|
outb(HPBITMODE_16, chip->io + HP_BITM);
|
|
|
|
|
|
|
|
reset_proteus(chip);
|
|
|
|
#endif
|
|
|
|
err = snd_msnd_init_sma(chip);
|
|
|
|
if (err < 0) {
|
|
|
|
printk(KERN_WARNING LOGNAME ": Cannot initialize SMA\n");
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = snd_msnd_reset_dsp(chip->io, NULL);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = upload_dsp_code(card);
|
|
|
|
if (err < 0) {
|
|
|
|
printk(KERN_WARNING LOGNAME ": Cannot upload DSP code\n");
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
timeout = 200;
|
|
|
|
|
|
|
|
while (readw(chip->mappedbase)) {
|
|
|
|
msleep(1);
|
|
|
|
if (!timeout--) {
|
|
|
|
snd_printd(KERN_ERR LOGNAME ": DSP reset timeout\n");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
snd_msndmix_setup(chip);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int snd_msnd_dsp_full_reset(struct snd_card *card)
|
|
|
|
{
|
|
|
|
struct snd_msnd *chip = card->private_data;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
if (test_bit(F_RESETTING, &chip->flags) || ++chip->nresets > 10)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
set_bit(F_RESETTING, &chip->flags);
|
|
|
|
snd_msnd_dsp_halt(chip, NULL); /* Unconditionally halt */
|
|
|
|
|
|
|
|
rv = snd_msnd_initialize(card);
|
|
|
|
if (rv)
|
|
|
|
printk(KERN_WARNING LOGNAME ": DSP reset failed\n");
|
|
|
|
snd_msndmix_force_recsrc(chip, 0);
|
|
|
|
clear_bit(F_RESETTING, &chip->flags);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int snd_msnd_dev_free(struct snd_device *device)
|
|
|
|
{
|
|
|
|
snd_printdd("snd_msnd_chip_free()\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int snd_msnd_send_dsp_cmd_chk(struct snd_msnd *chip, u8 cmd)
|
|
|
|
{
|
|
|
|
if (snd_msnd_send_dsp_cmd(chip, cmd) == 0)
|
|
|
|
return 0;
|
|
|
|
snd_msnd_dsp_full_reset(chip->card);
|
|
|
|
return snd_msnd_send_dsp_cmd(chip, cmd);
|
|
|
|
}
|
|
|
|
|
2012-12-06 17:35:21 +00:00
|
|
|
static int snd_msnd_calibrate_adc(struct snd_msnd *chip, u16 srate)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
snd_printdd("snd_msnd_calibrate_adc(%i)\n", srate);
|
|
|
|
writew(srate, chip->SMA + SMA_wCalFreqAtoD);
|
|
|
|
if (chip->calibrate_signal == 0)
|
|
|
|
writew(readw(chip->SMA + SMA_wCurrHostStatusFlags)
|
|
|
|
| 0x0001, chip->SMA + SMA_wCurrHostStatusFlags);
|
|
|
|
else
|
|
|
|
writew(readw(chip->SMA + SMA_wCurrHostStatusFlags)
|
|
|
|
& ~0x0001, chip->SMA + SMA_wCurrHostStatusFlags);
|
|
|
|
if (snd_msnd_send_word(chip, 0, 0, HDEXAR_CAL_A_TO_D) == 0 &&
|
|
|
|
snd_msnd_send_dsp_cmd_chk(chip, HDEX_AUX_REQ) == 0) {
|
|
|
|
schedule_timeout_interruptible(msecs_to_jiffies(333));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
printk(KERN_WARNING LOGNAME ": ADC calibration failed\n");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ALSA callback function, called when attempting to open the MIDI device.
|
|
|
|
*/
|
|
|
|
static int snd_msnd_mpu401_open(struct snd_mpu401 *mpu)
|
|
|
|
{
|
|
|
|
snd_msnd_enable_irq(mpu->private_data);
|
|
|
|
snd_msnd_send_dsp_cmd(mpu->private_data, HDEX_MIDI_IN_START);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void snd_msnd_mpu401_close(struct snd_mpu401 *mpu)
|
|
|
|
{
|
|
|
|
snd_msnd_send_dsp_cmd(mpu->private_data, HDEX_MIDI_IN_STOP);
|
|
|
|
snd_msnd_disable_irq(mpu->private_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static long mpu_io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
|
|
|
|
static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
|
|
|
|
|
2012-12-06 17:35:21 +00:00
|
|
|
static int snd_msnd_attach(struct snd_card *card)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
struct snd_msnd *chip = card->private_data;
|
|
|
|
int err;
|
|
|
|
static struct snd_device_ops ops = {
|
|
|
|
.dev_free = snd_msnd_dev_free,
|
|
|
|
};
|
|
|
|
|
|
|
|
err = request_irq(chip->irq, snd_msnd_interrupt, 0, card->shortname,
|
|
|
|
chip);
|
|
|
|
if (err < 0) {
|
|
|
|
printk(KERN_ERR LOGNAME ": Couldn't grab IRQ %d\n", chip->irq);
|
|
|
|
return err;
|
|
|
|
}
|
2010-07-29 10:45:24 +00:00
|
|
|
if (request_region(chip->io, DSP_NUMIO, card->shortname) == NULL) {
|
|
|
|
free_irq(chip->irq, chip);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
2009-01-24 12:35:28 +00:00
|
|
|
|
|
|
|
if (!request_mem_region(chip->base, BUFFSIZE, card->shortname)) {
|
|
|
|
printk(KERN_ERR LOGNAME
|
|
|
|
": unable to grab memory region 0x%lx-0x%lx\n",
|
|
|
|
chip->base, chip->base + BUFFSIZE - 1);
|
|
|
|
release_region(chip->io, DSP_NUMIO);
|
|
|
|
free_irq(chip->irq, chip);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
chip->mappedbase = ioremap_nocache(chip->base, 0x8000);
|
|
|
|
if (!chip->mappedbase) {
|
|
|
|
printk(KERN_ERR LOGNAME
|
|
|
|
": unable to map memory region 0x%lx-0x%lx\n",
|
|
|
|
chip->base, chip->base + BUFFSIZE - 1);
|
|
|
|
err = -EIO;
|
|
|
|
goto err_release_region;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = snd_msnd_dsp_full_reset(card);
|
|
|
|
if (err < 0)
|
|
|
|
goto err_release_region;
|
|
|
|
|
|
|
|
/* Register device */
|
|
|
|
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
|
|
|
|
if (err < 0)
|
|
|
|
goto err_release_region;
|
|
|
|
|
2015-01-02 11:24:40 +00:00
|
|
|
err = snd_msnd_pcm(card, 0);
|
2009-01-24 12:35:28 +00:00
|
|
|
if (err < 0) {
|
|
|
|
printk(KERN_ERR LOGNAME ": error creating new PCM device\n");
|
|
|
|
goto err_release_region;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = snd_msndmix_new(card);
|
|
|
|
if (err < 0) {
|
|
|
|
printk(KERN_ERR LOGNAME ": error creating new Mixer device\n");
|
|
|
|
goto err_release_region;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (mpu_io[0] != SNDRV_AUTO_PORT) {
|
|
|
|
struct snd_mpu401 *mpu;
|
|
|
|
|
|
|
|
err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
|
|
|
|
mpu_io[0],
|
|
|
|
MPU401_MODE_INPUT |
|
|
|
|
MPU401_MODE_OUTPUT,
|
ALSA: mpu401: clean up interrupt specification
The semantics of snd_mpu401_uart_new()'s interrupt parameters are
somewhat counterintuitive: To prevent the function from allocating its
own interrupt, either the irq number must be invalid, or the irq_flags
parameter must be zero. At the same time, the irq parameter being
invalid specifies that the mpu401 code has to work without an interrupt
allocated by the caller. This implies that, if there is an interrupt
and it is allocated by the caller, the irq parameter must be set to
a valid-looking number which then isn't actually used.
With the removal of IRQF_DISABLED, zero becomes a valid irq_flags value,
which forces us to handle the parameters differently.
This patch introduces a new flag MPU401_INFO_IRQ_HOOK for when the
device interrupt is handled by the caller, and makes the allocation of
the interrupt to depend only on the irq parameter. As suggested by
Takashi, the irq_flags parameter was dropped because, when used, it had
the constant value IRQF_DISABLED.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2011-09-13 09:24:41 +00:00
|
|
|
mpu_irq[0],
|
2009-01-24 12:35:28 +00:00
|
|
|
&chip->rmidi);
|
|
|
|
if (err < 0) {
|
|
|
|
printk(KERN_ERR LOGNAME
|
|
|
|
": error creating new Midi device\n");
|
|
|
|
goto err_release_region;
|
|
|
|
}
|
|
|
|
mpu = chip->rmidi->private_data;
|
|
|
|
|
|
|
|
mpu->open_input = snd_msnd_mpu401_open;
|
|
|
|
mpu->close_input = snd_msnd_mpu401_close;
|
|
|
|
mpu->private_data = chip;
|
|
|
|
}
|
|
|
|
|
|
|
|
disable_irq(chip->irq);
|
|
|
|
snd_msnd_calibrate_adc(chip, chip->play_sample_rate);
|
|
|
|
snd_msndmix_force_recsrc(chip, 0);
|
|
|
|
|
|
|
|
err = snd_card_register(card);
|
|
|
|
if (err < 0)
|
|
|
|
goto err_release_region;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_release_region:
|
2015-01-03 21:55:54 +00:00
|
|
|
iounmap(chip->mappedbase);
|
2009-01-24 12:35:28 +00:00
|
|
|
release_mem_region(chip->base, BUFFSIZE);
|
|
|
|
release_region(chip->io, DSP_NUMIO);
|
|
|
|
free_irq(chip->irq, chip);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-12-06 17:35:21 +00:00
|
|
|
static void snd_msnd_unload(struct snd_card *card)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
struct snd_msnd *chip = card->private_data;
|
|
|
|
|
|
|
|
iounmap(chip->mappedbase);
|
|
|
|
release_mem_region(chip->base, BUFFSIZE);
|
|
|
|
release_region(chip->io, DSP_NUMIO);
|
|
|
|
free_irq(chip->irq, chip);
|
|
|
|
snd_card_free(card);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef MSND_CLASSIC
|
|
|
|
|
|
|
|
/* Pinnacle/Fiji Logical Device Configuration */
|
|
|
|
|
2012-12-06 17:35:21 +00:00
|
|
|
static int snd_msnd_write_cfg(int cfg, int reg, int value)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
outb(reg, cfg);
|
|
|
|
outb(value, cfg + 1);
|
|
|
|
if (value != inb(cfg + 1)) {
|
|
|
|
printk(KERN_ERR LOGNAME ": snd_msnd_write_cfg: I/O error\n");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-06 17:35:21 +00:00
|
|
|
static int snd_msnd_write_cfg_io0(int cfg, int num, u16 io)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
|
|
|
|
return -EIO;
|
|
|
|
if (snd_msnd_write_cfg(cfg, IREG_IO0_BASEHI, HIBYTE(io)))
|
|
|
|
return -EIO;
|
|
|
|
if (snd_msnd_write_cfg(cfg, IREG_IO0_BASELO, LOBYTE(io)))
|
|
|
|
return -EIO;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-06 17:35:21 +00:00
|
|
|
static int snd_msnd_write_cfg_io1(int cfg, int num, u16 io)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
|
|
|
|
return -EIO;
|
|
|
|
if (snd_msnd_write_cfg(cfg, IREG_IO1_BASEHI, HIBYTE(io)))
|
|
|
|
return -EIO;
|
|
|
|
if (snd_msnd_write_cfg(cfg, IREG_IO1_BASELO, LOBYTE(io)))
|
|
|
|
return -EIO;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-06 17:35:21 +00:00
|
|
|
static int snd_msnd_write_cfg_irq(int cfg, int num, u16 irq)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
|
|
|
|
return -EIO;
|
|
|
|
if (snd_msnd_write_cfg(cfg, IREG_IRQ_NUMBER, LOBYTE(irq)))
|
|
|
|
return -EIO;
|
|
|
|
if (snd_msnd_write_cfg(cfg, IREG_IRQ_TYPE, IRQTYPE_EDGE))
|
|
|
|
return -EIO;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-06 17:35:21 +00:00
|
|
|
static int snd_msnd_write_cfg_mem(int cfg, int num, int mem)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
u16 wmem;
|
|
|
|
|
|
|
|
mem >>= 8;
|
|
|
|
wmem = (u16)(mem & 0xfff);
|
|
|
|
if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
|
|
|
|
return -EIO;
|
|
|
|
if (snd_msnd_write_cfg(cfg, IREG_MEMBASEHI, HIBYTE(wmem)))
|
|
|
|
return -EIO;
|
|
|
|
if (snd_msnd_write_cfg(cfg, IREG_MEMBASELO, LOBYTE(wmem)))
|
|
|
|
return -EIO;
|
|
|
|
if (wmem && snd_msnd_write_cfg(cfg, IREG_MEMCONTROL,
|
|
|
|
MEMTYPE_HIADDR | MEMTYPE_16BIT))
|
|
|
|
return -EIO;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-06 17:35:21 +00:00
|
|
|
static int snd_msnd_activate_logical(int cfg, int num)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
|
|
|
|
return -EIO;
|
|
|
|
if (snd_msnd_write_cfg(cfg, IREG_ACTIVATE, LD_ACTIVATE))
|
|
|
|
return -EIO;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-06 17:35:21 +00:00
|
|
|
static int snd_msnd_write_cfg_logical(int cfg, int num, u16 io0,
|
|
|
|
u16 io1, u16 irq, int mem)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
if (snd_msnd_write_cfg(cfg, IREG_LOGDEVICE, num))
|
|
|
|
return -EIO;
|
|
|
|
if (snd_msnd_write_cfg_io0(cfg, num, io0))
|
|
|
|
return -EIO;
|
|
|
|
if (snd_msnd_write_cfg_io1(cfg, num, io1))
|
|
|
|
return -EIO;
|
|
|
|
if (snd_msnd_write_cfg_irq(cfg, num, irq))
|
|
|
|
return -EIO;
|
|
|
|
if (snd_msnd_write_cfg_mem(cfg, num, mem))
|
|
|
|
return -EIO;
|
|
|
|
if (snd_msnd_activate_logical(cfg, num))
|
|
|
|
return -EIO;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-12-06 17:35:21 +00:00
|
|
|
static int snd_msnd_pinnacle_cfg_reset(int cfg)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Reset devices if told to */
|
|
|
|
printk(KERN_INFO LOGNAME ": Resetting all devices\n");
|
|
|
|
for (i = 0; i < 4; ++i)
|
|
|
|
if (snd_msnd_write_cfg_logical(cfg, i, 0, 0, 0, 0))
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
|
|
|
|
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
|
|
|
|
|
|
|
|
module_param_array(index, int, NULL, S_IRUGO);
|
|
|
|
MODULE_PARM_DESC(index, "Index value for msnd_pinnacle soundcard.");
|
|
|
|
module_param_array(id, charp, NULL, S_IRUGO);
|
|
|
|
MODULE_PARM_DESC(id, "ID string for msnd_pinnacle soundcard.");
|
|
|
|
|
|
|
|
static long io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
|
|
|
|
static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
|
|
|
|
static long mem[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
|
|
|
|
|
2010-09-08 07:58:12 +00:00
|
|
|
#ifndef MSND_CLASSIC
|
2009-01-24 12:35:28 +00:00
|
|
|
static long cfg[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
|
|
|
|
|
|
|
|
/* Extra Peripheral Configuration (Default: Disable) */
|
|
|
|
static long ide_io0[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
|
|
|
|
static long ide_io1[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
|
|
|
|
static int ide_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
|
|
|
|
|
|
|
|
static long joystick_io[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
|
|
|
|
/* If we have the digital daugherboard... */
|
|
|
|
static int digital[SNDRV_CARDS];
|
|
|
|
|
|
|
|
/* Extra Peripheral Configuration */
|
|
|
|
static int reset[SNDRV_CARDS];
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int write_ndelay[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = 1 };
|
|
|
|
|
|
|
|
static int calibrate_signal;
|
|
|
|
|
|
|
|
#ifdef CONFIG_PNP
|
2011-12-15 03:19:36 +00:00
|
|
|
static bool isapnp[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
|
2009-01-24 12:35:28 +00:00
|
|
|
module_param_array(isapnp, bool, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard.");
|
2009-02-04 17:28:42 +00:00
|
|
|
#define has_isapnp(x) isapnp[x]
|
|
|
|
#else
|
|
|
|
#define has_isapnp(x) 0
|
2009-01-24 12:35:28 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Karsten Wiese <annabellesgarden@yahoo.de>");
|
|
|
|
MODULE_DESCRIPTION("Turtle Beach " LONGNAME " Linux Driver");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_FIRMWARE(INITCODEFILE);
|
|
|
|
MODULE_FIRMWARE(PERMCODEFILE);
|
|
|
|
|
2017-04-04 15:54:30 +00:00
|
|
|
module_param_hw_array(io, long, ioport, NULL, S_IRUGO);
|
2009-01-24 12:35:28 +00:00
|
|
|
MODULE_PARM_DESC(io, "IO port #");
|
2017-04-04 15:54:30 +00:00
|
|
|
module_param_hw_array(irq, int, irq, NULL, S_IRUGO);
|
|
|
|
module_param_hw_array(mem, long, iomem, NULL, S_IRUGO);
|
2009-01-24 12:35:28 +00:00
|
|
|
module_param_array(write_ndelay, int, NULL, S_IRUGO);
|
|
|
|
module_param(calibrate_signal, int, S_IRUGO);
|
|
|
|
#ifndef MSND_CLASSIC
|
|
|
|
module_param_array(digital, int, NULL, S_IRUGO);
|
2017-04-04 15:54:30 +00:00
|
|
|
module_param_hw_array(cfg, long, ioport, NULL, S_IRUGO);
|
2009-01-24 12:35:28 +00:00
|
|
|
module_param_array(reset, int, 0, S_IRUGO);
|
2017-04-04 15:54:30 +00:00
|
|
|
module_param_hw_array(mpu_io, long, ioport, NULL, S_IRUGO);
|
|
|
|
module_param_hw_array(mpu_irq, int, irq, NULL, S_IRUGO);
|
|
|
|
module_param_hw_array(ide_io0, long, ioport, NULL, S_IRUGO);
|
|
|
|
module_param_hw_array(ide_io1, long, ioport, NULL, S_IRUGO);
|
|
|
|
module_param_hw_array(ide_irq, int, irq, NULL, S_IRUGO);
|
|
|
|
module_param_hw_array(joystick_io, long, ioport, NULL, S_IRUGO);
|
2009-01-24 12:35:28 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2012-12-06 17:35:21 +00:00
|
|
|
static int snd_msnd_isa_match(struct device *pdev, unsigned int i)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
if (io[i] == SNDRV_AUTO_PORT)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (irq[i] == SNDRV_AUTO_PORT || mem[i] == SNDRV_AUTO_PORT) {
|
|
|
|
printk(KERN_WARNING LOGNAME ": io, irq and mem must be set\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef MSND_CLASSIC
|
|
|
|
if (!(io[i] == 0x290 ||
|
|
|
|
io[i] == 0x260 ||
|
|
|
|
io[i] == 0x250 ||
|
|
|
|
io[i] == 0x240 ||
|
|
|
|
io[i] == 0x230 ||
|
|
|
|
io[i] == 0x220 ||
|
|
|
|
io[i] == 0x210 ||
|
|
|
|
io[i] == 0x3e0)) {
|
|
|
|
printk(KERN_ERR LOGNAME ": \"io\" - DSP I/O base must be set "
|
|
|
|
" to 0x210, 0x220, 0x230, 0x240, 0x250, 0x260, 0x290, "
|
|
|
|
"or 0x3E0\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (io[i] < 0x100 || io[i] > 0x3e0 || (io[i] % 0x10) != 0) {
|
|
|
|
printk(KERN_ERR LOGNAME
|
|
|
|
": \"io\" - DSP I/O base must within the range 0x100 "
|
|
|
|
"to 0x3E0 and must be evenly divisible by 0x10\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* MSND_CLASSIC */
|
|
|
|
|
|
|
|
if (!(irq[i] == 5 ||
|
|
|
|
irq[i] == 7 ||
|
|
|
|
irq[i] == 9 ||
|
|
|
|
irq[i] == 10 ||
|
|
|
|
irq[i] == 11 ||
|
|
|
|
irq[i] == 12)) {
|
|
|
|
printk(KERN_ERR LOGNAME
|
|
|
|
": \"irq\" - must be set to 5, 7, 9, 10, 11 or 12\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(mem[i] == 0xb0000 ||
|
|
|
|
mem[i] == 0xc8000 ||
|
|
|
|
mem[i] == 0xd0000 ||
|
|
|
|
mem[i] == 0xd8000 ||
|
|
|
|
mem[i] == 0xe0000 ||
|
|
|
|
mem[i] == 0xe8000)) {
|
|
|
|
printk(KERN_ERR LOGNAME ": \"mem\" - must be set to "
|
|
|
|
"0xb0000, 0xc8000, 0xd0000, 0xd8000, 0xe0000 or "
|
|
|
|
"0xe8000\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef MSND_CLASSIC
|
|
|
|
if (cfg[i] == SNDRV_AUTO_PORT) {
|
|
|
|
printk(KERN_INFO LOGNAME ": Assuming PnP mode\n");
|
|
|
|
} else if (cfg[i] != 0x250 && cfg[i] != 0x260 && cfg[i] != 0x270) {
|
|
|
|
printk(KERN_INFO LOGNAME
|
|
|
|
": Config port must be 0x250, 0x260 or 0x270 "
|
|
|
|
"(or unspecified for PnP mode)\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* MSND_CLASSIC */
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-12-06 17:35:21 +00:00
|
|
|
static int snd_msnd_isa_probe(struct device *pdev, unsigned int idx)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
struct snd_card *card;
|
|
|
|
struct snd_msnd *chip;
|
|
|
|
|
2010-09-08 07:58:12 +00:00
|
|
|
if (has_isapnp(idx)
|
|
|
|
#ifndef MSND_CLASSIC
|
|
|
|
|| cfg[idx] == SNDRV_AUTO_PORT
|
|
|
|
#endif
|
|
|
|
) {
|
2009-01-24 12:35:28 +00:00
|
|
|
printk(KERN_INFO LOGNAME ": Assuming PnP mode\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
2014-01-29 12:03:56 +00:00
|
|
|
err = snd_card_new(pdev, index[idx], id[idx], THIS_MODULE,
|
|
|
|
sizeof(struct snd_msnd), &card);
|
2009-01-24 12:35:28 +00:00
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
chip = card->private_data;
|
|
|
|
chip->card = card;
|
|
|
|
|
|
|
|
#ifdef MSND_CLASSIC
|
|
|
|
switch (irq[idx]) {
|
|
|
|
case 5:
|
|
|
|
chip->irqid = HPIRQ_5; break;
|
|
|
|
case 7:
|
|
|
|
chip->irqid = HPIRQ_7; break;
|
|
|
|
case 9:
|
|
|
|
chip->irqid = HPIRQ_9; break;
|
|
|
|
case 10:
|
|
|
|
chip->irqid = HPIRQ_10; break;
|
|
|
|
case 11:
|
|
|
|
chip->irqid = HPIRQ_11; break;
|
|
|
|
case 12:
|
|
|
|
chip->irqid = HPIRQ_12; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (mem[idx]) {
|
|
|
|
case 0xb0000:
|
|
|
|
chip->memid = HPMEM_B000; break;
|
|
|
|
case 0xc8000:
|
|
|
|
chip->memid = HPMEM_C800; break;
|
|
|
|
case 0xd0000:
|
|
|
|
chip->memid = HPMEM_D000; break;
|
|
|
|
case 0xd8000:
|
|
|
|
chip->memid = HPMEM_D800; break;
|
|
|
|
case 0xe0000:
|
|
|
|
chip->memid = HPMEM_E000; break;
|
|
|
|
case 0xe8000:
|
|
|
|
chip->memid = HPMEM_E800; break;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
printk(KERN_INFO LOGNAME ": Non-PnP mode: configuring at port 0x%lx\n",
|
|
|
|
cfg[idx]);
|
|
|
|
|
|
|
|
if (!request_region(cfg[idx], 2, "Pinnacle/Fiji Config")) {
|
|
|
|
printk(KERN_ERR LOGNAME ": Config port 0x%lx conflict\n",
|
|
|
|
cfg[idx]);
|
|
|
|
snd_card_free(card);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
if (reset[idx])
|
|
|
|
if (snd_msnd_pinnacle_cfg_reset(cfg[idx])) {
|
|
|
|
err = -EIO;
|
|
|
|
goto cfg_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* DSP */
|
|
|
|
err = snd_msnd_write_cfg_logical(cfg[idx], 0,
|
|
|
|
io[idx], 0,
|
|
|
|
irq[idx], mem[idx]);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
goto cfg_error;
|
|
|
|
|
|
|
|
/* The following are Pinnacle specific */
|
|
|
|
|
|
|
|
/* MPU */
|
|
|
|
if (mpu_io[idx] != SNDRV_AUTO_PORT
|
|
|
|
&& mpu_irq[idx] != SNDRV_AUTO_IRQ) {
|
|
|
|
printk(KERN_INFO LOGNAME
|
|
|
|
": Configuring MPU to I/O 0x%lx IRQ %d\n",
|
|
|
|
mpu_io[idx], mpu_irq[idx]);
|
|
|
|
err = snd_msnd_write_cfg_logical(cfg[idx], 1,
|
|
|
|
mpu_io[idx], 0,
|
|
|
|
mpu_irq[idx], 0);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
goto cfg_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* IDE */
|
|
|
|
if (ide_io0[idx] != SNDRV_AUTO_PORT
|
|
|
|
&& ide_io1[idx] != SNDRV_AUTO_PORT
|
|
|
|
&& ide_irq[idx] != SNDRV_AUTO_IRQ) {
|
|
|
|
printk(KERN_INFO LOGNAME
|
|
|
|
": Configuring IDE to I/O 0x%lx, 0x%lx IRQ %d\n",
|
|
|
|
ide_io0[idx], ide_io1[idx], ide_irq[idx]);
|
|
|
|
err = snd_msnd_write_cfg_logical(cfg[idx], 2,
|
|
|
|
ide_io0[idx], ide_io1[idx],
|
|
|
|
ide_irq[idx], 0);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
goto cfg_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Joystick */
|
|
|
|
if (joystick_io[idx] != SNDRV_AUTO_PORT) {
|
|
|
|
printk(KERN_INFO LOGNAME
|
|
|
|
": Configuring joystick to I/O 0x%lx\n",
|
|
|
|
joystick_io[idx]);
|
|
|
|
err = snd_msnd_write_cfg_logical(cfg[idx], 3,
|
|
|
|
joystick_io[idx], 0,
|
|
|
|
0, 0);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
goto cfg_error;
|
|
|
|
}
|
|
|
|
release_region(cfg[idx], 2);
|
|
|
|
|
|
|
|
#endif /* MSND_CLASSIC */
|
|
|
|
|
|
|
|
set_default_audio_parameters(chip);
|
|
|
|
#ifdef MSND_CLASSIC
|
|
|
|
chip->type = msndClassic;
|
|
|
|
#else
|
|
|
|
chip->type = msndPinnacle;
|
|
|
|
#endif
|
|
|
|
chip->io = io[idx];
|
|
|
|
chip->irq = irq[idx];
|
|
|
|
chip->base = mem[idx];
|
|
|
|
|
|
|
|
chip->calibrate_signal = calibrate_signal ? 1 : 0;
|
|
|
|
chip->recsrc = 0;
|
|
|
|
chip->dspq_data_buff = DSPQ_DATA_BUFF;
|
|
|
|
chip->dspq_buff_size = DSPQ_BUFF_SIZE;
|
|
|
|
if (write_ndelay[idx])
|
|
|
|
clear_bit(F_DISABLE_WRITE_NDELAY, &chip->flags);
|
|
|
|
else
|
|
|
|
set_bit(F_DISABLE_WRITE_NDELAY, &chip->flags);
|
|
|
|
#ifndef MSND_CLASSIC
|
|
|
|
if (digital[idx])
|
|
|
|
set_bit(F_HAVEDIGITAL, &chip->flags);
|
|
|
|
#endif
|
|
|
|
spin_lock_init(&chip->lock);
|
|
|
|
err = snd_msnd_probe(card);
|
|
|
|
if (err < 0) {
|
|
|
|
printk(KERN_ERR LOGNAME ": Probe failed\n");
|
|
|
|
snd_card_free(card);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = snd_msnd_attach(card);
|
|
|
|
if (err < 0) {
|
|
|
|
printk(KERN_ERR LOGNAME ": Attach failed\n");
|
|
|
|
snd_card_free(card);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
dev_set_drvdata(pdev, card);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
#ifndef MSND_CLASSIC
|
|
|
|
cfg_error:
|
|
|
|
release_region(cfg[idx], 2);
|
|
|
|
snd_card_free(card);
|
|
|
|
return err;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2012-12-06 17:35:21 +00:00
|
|
|
static int snd_msnd_isa_remove(struct device *pdev, unsigned int dev)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
snd_msnd_unload(dev_get_drvdata(pdev));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct isa_driver snd_msnd_driver = {
|
|
|
|
.match = snd_msnd_isa_match,
|
|
|
|
.probe = snd_msnd_isa_probe,
|
2012-12-06 17:35:21 +00:00
|
|
|
.remove = snd_msnd_isa_remove,
|
2009-01-24 12:35:28 +00:00
|
|
|
/* FIXME: suspend, resume */
|
|
|
|
.driver = {
|
|
|
|
.name = DEV_NAME
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef CONFIG_PNP
|
2012-12-06 17:35:21 +00:00
|
|
|
static int snd_msnd_pnp_detect(struct pnp_card_link *pcard,
|
|
|
|
const struct pnp_card_device_id *pid)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
static int idx;
|
|
|
|
struct pnp_dev *pnp_dev;
|
|
|
|
struct pnp_dev *mpu_dev;
|
|
|
|
struct snd_card *card;
|
|
|
|
struct snd_msnd *chip;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
for ( ; idx < SNDRV_CARDS; idx++) {
|
2009-02-04 17:28:42 +00:00
|
|
|
if (has_isapnp(idx))
|
2009-01-24 12:35:28 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (idx >= SNDRV_CARDS)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check that we still have room for another sound card ...
|
|
|
|
*/
|
|
|
|
pnp_dev = pnp_request_card_device(pcard, pid->devs[0].id, NULL);
|
|
|
|
if (!pnp_dev)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
mpu_dev = pnp_request_card_device(pcard, pid->devs[1].id, NULL);
|
|
|
|
if (!mpu_dev)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (!pnp_is_active(pnp_dev) && pnp_activate_dev(pnp_dev) < 0) {
|
|
|
|
printk(KERN_INFO "msnd_pinnacle: device is inactive\n");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pnp_is_active(mpu_dev) && pnp_activate_dev(mpu_dev) < 0) {
|
|
|
|
printk(KERN_INFO "msnd_pinnacle: MPU device is inactive\n");
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a new ALSA sound card entry, in anticipation
|
|
|
|
* of detecting our hardware ...
|
|
|
|
*/
|
2014-01-29 12:03:56 +00:00
|
|
|
ret = snd_card_new(&pcard->card->dev,
|
|
|
|
index[idx], id[idx], THIS_MODULE,
|
|
|
|
sizeof(struct snd_msnd), &card);
|
2009-01-24 12:35:28 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
chip = card->private_data;
|
|
|
|
chip->card = card;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the correct parameters off the ISA PnP bus ...
|
|
|
|
*/
|
|
|
|
io[idx] = pnp_port_start(pnp_dev, 0);
|
|
|
|
irq[idx] = pnp_irq(pnp_dev, 0);
|
|
|
|
mem[idx] = pnp_mem_start(pnp_dev, 0);
|
|
|
|
mpu_io[idx] = pnp_port_start(mpu_dev, 0);
|
|
|
|
mpu_irq[idx] = pnp_irq(mpu_dev, 0);
|
|
|
|
|
|
|
|
set_default_audio_parameters(chip);
|
|
|
|
#ifdef MSND_CLASSIC
|
|
|
|
chip->type = msndClassic;
|
|
|
|
#else
|
|
|
|
chip->type = msndPinnacle;
|
|
|
|
#endif
|
|
|
|
chip->io = io[idx];
|
|
|
|
chip->irq = irq[idx];
|
|
|
|
chip->base = mem[idx];
|
|
|
|
|
|
|
|
chip->calibrate_signal = calibrate_signal ? 1 : 0;
|
|
|
|
chip->recsrc = 0;
|
|
|
|
chip->dspq_data_buff = DSPQ_DATA_BUFF;
|
|
|
|
chip->dspq_buff_size = DSPQ_BUFF_SIZE;
|
|
|
|
if (write_ndelay[idx])
|
|
|
|
clear_bit(F_DISABLE_WRITE_NDELAY, &chip->flags);
|
|
|
|
else
|
|
|
|
set_bit(F_DISABLE_WRITE_NDELAY, &chip->flags);
|
|
|
|
#ifndef MSND_CLASSIC
|
|
|
|
if (digital[idx])
|
|
|
|
set_bit(F_HAVEDIGITAL, &chip->flags);
|
|
|
|
#endif
|
|
|
|
spin_lock_init(&chip->lock);
|
|
|
|
ret = snd_msnd_probe(card);
|
|
|
|
if (ret < 0) {
|
|
|
|
printk(KERN_ERR LOGNAME ": Probe failed\n");
|
|
|
|
goto _release_card;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = snd_msnd_attach(card);
|
|
|
|
if (ret < 0) {
|
|
|
|
printk(KERN_ERR LOGNAME ": Attach failed\n");
|
|
|
|
goto _release_card;
|
|
|
|
}
|
|
|
|
|
|
|
|
pnp_set_card_drvdata(pcard, card);
|
|
|
|
++idx;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
_release_card:
|
|
|
|
snd_card_free(card);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-12-06 17:35:21 +00:00
|
|
|
static void snd_msnd_pnp_remove(struct pnp_card_link *pcard)
|
2009-01-24 12:35:28 +00:00
|
|
|
{
|
|
|
|
snd_msnd_unload(pnp_get_card_drvdata(pcard));
|
|
|
|
pnp_set_card_drvdata(pcard, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int isa_registered;
|
|
|
|
static int pnp_registered;
|
|
|
|
|
2017-08-17 10:06:26 +00:00
|
|
|
static const struct pnp_card_device_id msnd_pnpids[] = {
|
2009-01-24 12:35:28 +00:00
|
|
|
/* Pinnacle PnP */
|
|
|
|
{ .id = "BVJ0440", .devs = { { "TBS0000" }, { "TBS0001" } } },
|
|
|
|
{ .id = "" } /* end */
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(pnp_card, msnd_pnpids);
|
|
|
|
|
|
|
|
static struct pnp_card_driver msnd_pnpc_driver = {
|
|
|
|
.flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
|
|
|
|
.name = "msnd_pinnacle",
|
|
|
|
.id_table = msnd_pnpids,
|
|
|
|
.probe = snd_msnd_pnp_detect,
|
2012-12-06 17:35:21 +00:00
|
|
|
.remove = snd_msnd_pnp_remove,
|
2009-01-24 12:35:28 +00:00
|
|
|
};
|
|
|
|
#endif /* CONFIG_PNP */
|
|
|
|
|
|
|
|
static int __init snd_msnd_init(void)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = isa_register_driver(&snd_msnd_driver, SNDRV_CARDS);
|
|
|
|
#ifdef CONFIG_PNP
|
|
|
|
if (!err)
|
|
|
|
isa_registered = 1;
|
|
|
|
|
|
|
|
err = pnp_register_card_driver(&msnd_pnpc_driver);
|
|
|
|
if (!err)
|
|
|
|
pnp_registered = 1;
|
|
|
|
|
|
|
|
if (isa_registered)
|
|
|
|
err = 0;
|
|
|
|
#endif
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit snd_msnd_exit(void)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_PNP
|
|
|
|
if (pnp_registered)
|
|
|
|
pnp_unregister_card_driver(&msnd_pnpc_driver);
|
|
|
|
if (isa_registered)
|
|
|
|
#endif
|
|
|
|
isa_unregister_driver(&snd_msnd_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(snd_msnd_init);
|
|
|
|
module_exit(snd_msnd_exit);
|
|
|
|
|