mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 01:08:03 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
15
sound/pci/emu10k1/Makefile
Normal file
15
sound/pci/emu10k1/Makefile
Normal file
|
@ -0,0 +1,15 @@
|
|||
#
|
||||
# Makefile for ALSA
|
||||
# Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
|
||||
#
|
||||
|
||||
snd-emu10k1-objs := emu10k1.o emu10k1_main.o \
|
||||
irq.o memory.o voice.o emumpu401.o emupcm.o io.o \
|
||||
emuproc.o emumixer.o emufx.o timer.o p16v.o
|
||||
snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o
|
||||
snd-emu10k1x-objs := emu10k1x.o
|
||||
|
||||
# Toplevel Module Dependency
|
||||
obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o
|
||||
obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-emu10k1-synth.o
|
||||
obj-$(CONFIG_SND_EMU10K1X) += snd-emu10k1x.o
|
289
sound/pci/emu10k1/emu10k1.c
Normal file
289
sound/pci/emu10k1/emu10k1.c
Normal file
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* The driver for the EMU10K1 (SB Live!) based soundcards
|
||||
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
|
||||
*
|
||||
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
|
||||
* Added support for Audigy 2 Value.
|
||||
*
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/module.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/emu10k1.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
|
||||
MODULE_DESCRIPTION("EMU10K1");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_SUPPORTED_DEVICE("{{Creative Labs,SB Live!/PCI512/E-mu APS},"
|
||||
"{Creative Labs,SB Audigy}}");
|
||||
|
||||
#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
|
||||
#define ENABLE_SYNTH
|
||||
#include <sound/emu10k1_synth.h>
|
||||
#endif
|
||||
|
||||
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
|
||||
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
|
||||
static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
|
||||
static int extin[SNDRV_CARDS];
|
||||
static int extout[SNDRV_CARDS];
|
||||
static int seq_ports[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 4};
|
||||
static int max_synth_voices[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 64};
|
||||
static int max_buffer_size[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 128};
|
||||
static bool enable_ir[SNDRV_CARDS];
|
||||
static uint subsystem[SNDRV_CARDS]; /* Force card subsystem model */
|
||||
static uint delay_pcm_irq[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
|
||||
|
||||
module_param_array(index, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(index, "Index value for the EMU10K1 soundcard.");
|
||||
module_param_array(id, charp, NULL, 0444);
|
||||
MODULE_PARM_DESC(id, "ID string for the EMU10K1 soundcard.");
|
||||
module_param_array(enable, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(enable, "Enable the EMU10K1 soundcard.");
|
||||
module_param_array(extin, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(extin, "Available external inputs for FX8010. Zero=default.");
|
||||
module_param_array(extout, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(extout, "Available external outputs for FX8010. Zero=default.");
|
||||
module_param_array(seq_ports, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(seq_ports, "Allocated sequencer ports for internal synthesizer.");
|
||||
module_param_array(max_synth_voices, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(max_synth_voices, "Maximum number of voices for WaveTable.");
|
||||
module_param_array(max_buffer_size, int, NULL, 0444);
|
||||
MODULE_PARM_DESC(max_buffer_size, "Maximum sample buffer size in MB.");
|
||||
module_param_array(enable_ir, bool, NULL, 0444);
|
||||
MODULE_PARM_DESC(enable_ir, "Enable IR.");
|
||||
module_param_array(subsystem, uint, NULL, 0444);
|
||||
MODULE_PARM_DESC(subsystem, "Force card subsystem model.");
|
||||
module_param_array(delay_pcm_irq, uint, NULL, 0444);
|
||||
MODULE_PARM_DESC(delay_pcm_irq, "Delay PCM interrupt by specified number of samples (default 0).");
|
||||
/*
|
||||
* Class 0401: 1102:0008 (rev 00) Subsystem: 1102:1001 -> Audigy2 Value Model:SB0400
|
||||
*/
|
||||
static const struct pci_device_id snd_emu10k1_ids[] = {
|
||||
{ PCI_VDEVICE(CREATIVE, 0x0002), 0 }, /* EMU10K1 */
|
||||
{ PCI_VDEVICE(CREATIVE, 0x0004), 1 }, /* Audigy */
|
||||
{ PCI_VDEVICE(CREATIVE, 0x0008), 1 }, /* Audigy 2 Value SB0400 */
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
/*
|
||||
* Audigy 2 Value notes:
|
||||
* A_IOCFG Input (GPIO)
|
||||
* 0x400 = Front analog jack plugged in. (Green socket)
|
||||
* 0x1000 = Read analog jack plugged in. (Black socket)
|
||||
* 0x2000 = Center/LFE analog jack plugged in. (Orange socket)
|
||||
* A_IOCFG Output (GPIO)
|
||||
* 0x60 = Sound out of front Left.
|
||||
* Win sets it to 0xXX61
|
||||
*/
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, snd_emu10k1_ids);
|
||||
|
||||
static int snd_card_emu10k1_probe(struct pci_dev *pci,
|
||||
const struct pci_device_id *pci_id)
|
||||
{
|
||||
static int dev;
|
||||
struct snd_card *card;
|
||||
struct snd_emu10k1 *emu;
|
||||
#ifdef ENABLE_SYNTH
|
||||
struct snd_seq_device *wave = NULL;
|
||||
#endif
|
||||
int err;
|
||||
|
||||
if (dev >= SNDRV_CARDS)
|
||||
return -ENODEV;
|
||||
if (!enable[dev]) {
|
||||
dev++;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
|
||||
0, &card);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (max_buffer_size[dev] < 32)
|
||||
max_buffer_size[dev] = 32;
|
||||
else if (max_buffer_size[dev] > 1024)
|
||||
max_buffer_size[dev] = 1024;
|
||||
if ((err = snd_emu10k1_create(card, pci, extin[dev], extout[dev],
|
||||
(long)max_buffer_size[dev] * 1024 * 1024,
|
||||
enable_ir[dev], subsystem[dev],
|
||||
&emu)) < 0)
|
||||
goto error;
|
||||
card->private_data = emu;
|
||||
emu->delay_pcm_irq = delay_pcm_irq[dev] & 0x1f;
|
||||
if ((err = snd_emu10k1_pcm(emu, 0, NULL)) < 0)
|
||||
goto error;
|
||||
if ((err = snd_emu10k1_pcm_mic(emu, 1, NULL)) < 0)
|
||||
goto error;
|
||||
if ((err = snd_emu10k1_pcm_efx(emu, 2, NULL)) < 0)
|
||||
goto error;
|
||||
/* This stores the periods table. */
|
||||
if (emu->card_capabilities->ca0151_chip) { /* P16V */
|
||||
if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci),
|
||||
1024, &emu->p16v_buffer)) < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((err = snd_emu10k1_mixer(emu, 0, 3)) < 0)
|
||||
goto error;
|
||||
|
||||
if ((err = snd_emu10k1_timer(emu, 0)) < 0)
|
||||
goto error;
|
||||
|
||||
if ((err = snd_emu10k1_pcm_multi(emu, 3, NULL)) < 0)
|
||||
goto error;
|
||||
if (emu->card_capabilities->ca0151_chip) { /* P16V */
|
||||
if ((err = snd_p16v_pcm(emu, 4, NULL)) < 0)
|
||||
goto error;
|
||||
}
|
||||
if (emu->audigy) {
|
||||
if ((err = snd_emu10k1_audigy_midi(emu)) < 0)
|
||||
goto error;
|
||||
} else {
|
||||
if ((err = snd_emu10k1_midi(emu)) < 0)
|
||||
goto error;
|
||||
}
|
||||
if ((err = snd_emu10k1_fx8010_new(emu, 0, NULL)) < 0)
|
||||
goto error;
|
||||
#ifdef ENABLE_SYNTH
|
||||
if (snd_seq_device_new(card, 1, SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH,
|
||||
sizeof(struct snd_emu10k1_synth_arg), &wave) < 0 ||
|
||||
wave == NULL) {
|
||||
dev_warn(emu->card->dev,
|
||||
"can't initialize Emu10k1 wavetable synth\n");
|
||||
} else {
|
||||
struct snd_emu10k1_synth_arg *arg;
|
||||
arg = SNDRV_SEQ_DEVICE_ARGPTR(wave);
|
||||
strcpy(wave->name, "Emu-10k1 Synth");
|
||||
arg->hwptr = emu;
|
||||
arg->index = 1;
|
||||
arg->seq_ports = seq_ports[dev];
|
||||
arg->max_voices = max_synth_voices[dev];
|
||||
}
|
||||
#endif
|
||||
|
||||
strlcpy(card->driver, emu->card_capabilities->driver,
|
||||
sizeof(card->driver));
|
||||
strlcpy(card->shortname, emu->card_capabilities->name,
|
||||
sizeof(card->shortname));
|
||||
snprintf(card->longname, sizeof(card->longname),
|
||||
"%s (rev.%d, serial:0x%x) at 0x%lx, irq %i",
|
||||
card->shortname, emu->revision, emu->serial, emu->port, emu->irq);
|
||||
|
||||
if ((err = snd_card_register(card)) < 0)
|
||||
goto error;
|
||||
|
||||
pci_set_drvdata(pci, card);
|
||||
dev++;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void snd_card_emu10k1_remove(struct pci_dev *pci)
|
||||
{
|
||||
snd_card_free(pci_get_drvdata(pci));
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int snd_emu10k1_suspend(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
struct snd_card *card = dev_get_drvdata(dev);
|
||||
struct snd_emu10k1 *emu = card->private_data;
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
||||
|
||||
emu->suspend = 1;
|
||||
|
||||
snd_pcm_suspend_all(emu->pcm);
|
||||
snd_pcm_suspend_all(emu->pcm_mic);
|
||||
snd_pcm_suspend_all(emu->pcm_efx);
|
||||
snd_pcm_suspend_all(emu->pcm_multi);
|
||||
snd_pcm_suspend_all(emu->pcm_p16v);
|
||||
|
||||
snd_ac97_suspend(emu->ac97);
|
||||
|
||||
snd_emu10k1_efx_suspend(emu);
|
||||
snd_emu10k1_suspend_regs(emu);
|
||||
if (emu->card_capabilities->ca0151_chip)
|
||||
snd_p16v_suspend(emu);
|
||||
|
||||
snd_emu10k1_done(emu);
|
||||
|
||||
pci_disable_device(pci);
|
||||
pci_save_state(pci);
|
||||
pci_set_power_state(pci, PCI_D3hot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_emu10k1_resume(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pci = to_pci_dev(dev);
|
||||
struct snd_card *card = dev_get_drvdata(dev);
|
||||
struct snd_emu10k1 *emu = card->private_data;
|
||||
|
||||
pci_set_power_state(pci, PCI_D0);
|
||||
pci_restore_state(pci);
|
||||
if (pci_enable_device(pci) < 0) {
|
||||
dev_err(dev, "pci_enable_device failed, disabling device\n");
|
||||
snd_card_disconnect(card);
|
||||
return -EIO;
|
||||
}
|
||||
pci_set_master(pci);
|
||||
|
||||
snd_emu10k1_resume_init(emu);
|
||||
snd_emu10k1_efx_resume(emu);
|
||||
snd_ac97_resume(emu->ac97);
|
||||
snd_emu10k1_resume_regs(emu);
|
||||
|
||||
if (emu->card_capabilities->ca0151_chip)
|
||||
snd_p16v_resume(emu);
|
||||
|
||||
emu->suspend = 0;
|
||||
|
||||
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(snd_emu10k1_pm, snd_emu10k1_suspend, snd_emu10k1_resume);
|
||||
#define SND_EMU10K1_PM_OPS &snd_emu10k1_pm
|
||||
#else
|
||||
#define SND_EMU10K1_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static struct pci_driver emu10k1_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = snd_emu10k1_ids,
|
||||
.probe = snd_card_emu10k1_probe,
|
||||
.remove = snd_card_emu10k1_remove,
|
||||
.driver = {
|
||||
.pm = SND_EMU10K1_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
module_pci_driver(emu10k1_driver);
|
550
sound/pci/emu10k1/emu10k1_callback.c
Normal file
550
sound/pci/emu10k1/emu10k1_callback.c
Normal file
|
@ -0,0 +1,550 @@
|
|||
/*
|
||||
* synth callback routines for Emu10k1
|
||||
*
|
||||
* Copyright (C) 2000 Takashi Iwai <tiwai@suse.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.
|
||||
*
|
||||
* 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/export.h>
|
||||
#include "emu10k1_synth_local.h"
|
||||
#include <sound/asoundef.h>
|
||||
|
||||
/* voice status */
|
||||
enum {
|
||||
V_FREE=0, V_OFF, V_RELEASED, V_PLAYING, V_END
|
||||
};
|
||||
|
||||
/* Keeps track of what we are finding */
|
||||
struct best_voice {
|
||||
unsigned int time;
|
||||
int voice;
|
||||
};
|
||||
|
||||
/*
|
||||
* prototypes
|
||||
*/
|
||||
static void lookup_voices(struct snd_emux *emux, struct snd_emu10k1 *hw,
|
||||
struct best_voice *best, int active_only);
|
||||
static struct snd_emux_voice *get_voice(struct snd_emux *emux,
|
||||
struct snd_emux_port *port);
|
||||
static int start_voice(struct snd_emux_voice *vp);
|
||||
static void trigger_voice(struct snd_emux_voice *vp);
|
||||
static void release_voice(struct snd_emux_voice *vp);
|
||||
static void update_voice(struct snd_emux_voice *vp, int update);
|
||||
static void terminate_voice(struct snd_emux_voice *vp);
|
||||
static void free_voice(struct snd_emux_voice *vp);
|
||||
static void set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
|
||||
static void set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
|
||||
static void set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp);
|
||||
|
||||
/*
|
||||
* Ensure a value is between two points
|
||||
* macro evaluates its args more than once, so changed to upper-case.
|
||||
*/
|
||||
#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
|
||||
#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
|
||||
|
||||
|
||||
/*
|
||||
* set up operators
|
||||
*/
|
||||
static struct snd_emux_operators emu10k1_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.get_voice = get_voice,
|
||||
.prepare = start_voice,
|
||||
.trigger = trigger_voice,
|
||||
.release = release_voice,
|
||||
.update = update_voice,
|
||||
.terminate = terminate_voice,
|
||||
.free_voice = free_voice,
|
||||
.sample_new = snd_emu10k1_sample_new,
|
||||
.sample_free = snd_emu10k1_sample_free,
|
||||
};
|
||||
|
||||
void
|
||||
snd_emu10k1_ops_setup(struct snd_emux *emux)
|
||||
{
|
||||
emux->ops = emu10k1_ops;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* get more voice for pcm
|
||||
*
|
||||
* terminate most inactive voice and give it as a pcm voice.
|
||||
*
|
||||
* voice_lock is already held.
|
||||
*/
|
||||
int
|
||||
snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw)
|
||||
{
|
||||
struct snd_emux *emu;
|
||||
struct snd_emux_voice *vp;
|
||||
struct best_voice best[V_END];
|
||||
int i;
|
||||
|
||||
emu = hw->synth;
|
||||
|
||||
lookup_voices(emu, hw, best, 1); /* no OFF voices */
|
||||
for (i = 0; i < V_END; i++) {
|
||||
if (best[i].voice >= 0) {
|
||||
int ch;
|
||||
vp = &emu->voices[best[i].voice];
|
||||
if ((ch = vp->ch) < 0) {
|
||||
/*
|
||||
dev_warn(emu->card->dev,
|
||||
"synth_get_voice: ch < 0 (%d) ??", i);
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
vp->emu->num_voices--;
|
||||
vp->ch = -1;
|
||||
vp->state = SNDRV_EMUX_ST_OFF;
|
||||
return ch;
|
||||
}
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* turn off the voice (not terminated)
|
||||
*/
|
||||
static void
|
||||
release_voice(struct snd_emux_voice *vp)
|
||||
{
|
||||
int dcysusv;
|
||||
struct snd_emu10k1 *hw;
|
||||
|
||||
hw = vp->hw;
|
||||
dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;
|
||||
snd_emu10k1_ptr_write(hw, DCYSUSM, vp->ch, dcysusv);
|
||||
dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease | DCYSUSV_CHANNELENABLE_MASK;
|
||||
snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, dcysusv);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* terminate the voice
|
||||
*/
|
||||
static void
|
||||
terminate_voice(struct snd_emux_voice *vp)
|
||||
{
|
||||
struct snd_emu10k1 *hw;
|
||||
|
||||
if (snd_BUG_ON(!vp))
|
||||
return;
|
||||
hw = vp->hw;
|
||||
snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
|
||||
if (vp->block) {
|
||||
struct snd_emu10k1_memblk *emem;
|
||||
emem = (struct snd_emu10k1_memblk *)vp->block;
|
||||
if (emem->map_locked > 0)
|
||||
emem->map_locked--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* release the voice to system
|
||||
*/
|
||||
static void
|
||||
free_voice(struct snd_emux_voice *vp)
|
||||
{
|
||||
struct snd_emu10k1 *hw;
|
||||
|
||||
hw = vp->hw;
|
||||
/* FIXME: emu10k1_synth is broken. */
|
||||
/* This can get called with hw == 0 */
|
||||
/* Problem apparent on plug, unplug then plug */
|
||||
/* on the Audigy 2 ZS Notebook. */
|
||||
if (hw && (vp->ch >= 0)) {
|
||||
snd_emu10k1_ptr_write(hw, IFATN, vp->ch, 0xff00);
|
||||
snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0x807f | DCYSUSV_CHANNELENABLE_MASK);
|
||||
// snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, 0);
|
||||
snd_emu10k1_ptr_write(hw, VTFT, vp->ch, 0xffff);
|
||||
snd_emu10k1_ptr_write(hw, CVCF, vp->ch, 0xffff);
|
||||
snd_emu10k1_voice_free(hw, &hw->voices[vp->ch]);
|
||||
vp->emu->num_voices--;
|
||||
vp->ch = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* update registers
|
||||
*/
|
||||
static void
|
||||
update_voice(struct snd_emux_voice *vp, int update)
|
||||
{
|
||||
struct snd_emu10k1 *hw;
|
||||
|
||||
hw = vp->hw;
|
||||
if (update & SNDRV_EMUX_UPDATE_VOLUME)
|
||||
snd_emu10k1_ptr_write(hw, IFATN_ATTENUATION, vp->ch, vp->avol);
|
||||
if (update & SNDRV_EMUX_UPDATE_PITCH)
|
||||
snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);
|
||||
if (update & SNDRV_EMUX_UPDATE_PAN) {
|
||||
snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_A, vp->ch, vp->apan);
|
||||
snd_emu10k1_ptr_write(hw, PTRX_FXSENDAMOUNT_B, vp->ch, vp->aaux);
|
||||
}
|
||||
if (update & SNDRV_EMUX_UPDATE_FMMOD)
|
||||
set_fmmod(hw, vp);
|
||||
if (update & SNDRV_EMUX_UPDATE_TREMFREQ)
|
||||
snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
|
||||
if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)
|
||||
set_fm2frq2(hw, vp);
|
||||
if (update & SNDRV_EMUX_UPDATE_Q)
|
||||
set_filterQ(hw, vp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* look up voice table - get the best voice in order of preference
|
||||
*/
|
||||
/* spinlock held! */
|
||||
static void
|
||||
lookup_voices(struct snd_emux *emu, struct snd_emu10k1 *hw,
|
||||
struct best_voice *best, int active_only)
|
||||
{
|
||||
struct snd_emux_voice *vp;
|
||||
struct best_voice *bp;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < V_END; i++) {
|
||||
best[i].time = (unsigned int)-1; /* XXX MAX_?INT really */
|
||||
best[i].voice = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Go through them all and get a best one to use.
|
||||
* NOTE: could also look at volume and pick the quietest one.
|
||||
*/
|
||||
for (i = 0; i < emu->max_voices; i++) {
|
||||
int state, val;
|
||||
|
||||
vp = &emu->voices[i];
|
||||
state = vp->state;
|
||||
if (state == SNDRV_EMUX_ST_OFF) {
|
||||
if (vp->ch < 0) {
|
||||
if (active_only)
|
||||
continue;
|
||||
bp = best + V_FREE;
|
||||
} else
|
||||
bp = best + V_OFF;
|
||||
}
|
||||
else if (state == SNDRV_EMUX_ST_RELEASED ||
|
||||
state == SNDRV_EMUX_ST_PENDING) {
|
||||
bp = best + V_RELEASED;
|
||||
#if 1
|
||||
val = snd_emu10k1_ptr_read(hw, CVCF_CURRENTVOL, vp->ch);
|
||||
if (! val)
|
||||
bp = best + V_OFF;
|
||||
#endif
|
||||
}
|
||||
else if (state == SNDRV_EMUX_ST_STANDBY)
|
||||
continue;
|
||||
else if (state & SNDRV_EMUX_ST_ON)
|
||||
bp = best + V_PLAYING;
|
||||
else
|
||||
continue;
|
||||
|
||||
/* check if sample is finished playing (non-looping only) */
|
||||
if (bp != best + V_OFF && bp != best + V_FREE &&
|
||||
(vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {
|
||||
val = snd_emu10k1_ptr_read(hw, CCCA_CURRADDR, vp->ch);
|
||||
if (val >= vp->reg.loopstart)
|
||||
bp = best + V_OFF;
|
||||
}
|
||||
|
||||
if (vp->time < bp->time) {
|
||||
bp->time = vp->time;
|
||||
bp->voice = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* get an empty voice
|
||||
*
|
||||
* emu->voice_lock is already held.
|
||||
*/
|
||||
static struct snd_emux_voice *
|
||||
get_voice(struct snd_emux *emu, struct snd_emux_port *port)
|
||||
{
|
||||
struct snd_emu10k1 *hw;
|
||||
struct snd_emux_voice *vp;
|
||||
struct best_voice best[V_END];
|
||||
int i;
|
||||
|
||||
hw = emu->hw;
|
||||
|
||||
lookup_voices(emu, hw, best, 0);
|
||||
for (i = 0; i < V_END; i++) {
|
||||
if (best[i].voice >= 0) {
|
||||
vp = &emu->voices[best[i].voice];
|
||||
if (vp->ch < 0) {
|
||||
/* allocate a voice */
|
||||
struct snd_emu10k1_voice *hwvoice;
|
||||
if (snd_emu10k1_voice_alloc(hw, EMU10K1_SYNTH, 1, &hwvoice) < 0 || hwvoice == NULL)
|
||||
continue;
|
||||
vp->ch = hwvoice->number;
|
||||
emu->num_voices++;
|
||||
}
|
||||
return vp;
|
||||
}
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* prepare envelopes and LFOs
|
||||
*/
|
||||
static int
|
||||
start_voice(struct snd_emux_voice *vp)
|
||||
{
|
||||
unsigned int temp;
|
||||
int ch;
|
||||
unsigned int addr, mapped_offset;
|
||||
struct snd_midi_channel *chan;
|
||||
struct snd_emu10k1 *hw;
|
||||
struct snd_emu10k1_memblk *emem;
|
||||
|
||||
hw = vp->hw;
|
||||
ch = vp->ch;
|
||||
if (snd_BUG_ON(ch < 0))
|
||||
return -EINVAL;
|
||||
chan = vp->chan;
|
||||
|
||||
emem = (struct snd_emu10k1_memblk *)vp->block;
|
||||
if (emem == NULL)
|
||||
return -EINVAL;
|
||||
emem->map_locked++;
|
||||
if (snd_emu10k1_memblk_map(hw, emem) < 0) {
|
||||
/* dev_err(hw->card->devK, "emu: cannot map!\n"); */
|
||||
return -ENOMEM;
|
||||
}
|
||||
mapped_offset = snd_emu10k1_memblk_offset(emem) >> 1;
|
||||
vp->reg.start += mapped_offset;
|
||||
vp->reg.end += mapped_offset;
|
||||
vp->reg.loopstart += mapped_offset;
|
||||
vp->reg.loopend += mapped_offset;
|
||||
|
||||
/* set channel routing */
|
||||
/* A = left(0), B = right(1), C = reverb(c), D = chorus(d) */
|
||||
if (hw->audigy) {
|
||||
temp = FXBUS_MIDI_LEFT | (FXBUS_MIDI_RIGHT << 8) |
|
||||
(FXBUS_MIDI_REVERB << 16) | (FXBUS_MIDI_CHORUS << 24);
|
||||
snd_emu10k1_ptr_write(hw, A_FXRT1, ch, temp);
|
||||
} else {
|
||||
temp = (FXBUS_MIDI_LEFT << 16) | (FXBUS_MIDI_RIGHT << 20) |
|
||||
(FXBUS_MIDI_REVERB << 24) | (FXBUS_MIDI_CHORUS << 28);
|
||||
snd_emu10k1_ptr_write(hw, FXRT, ch, temp);
|
||||
}
|
||||
|
||||
/* channel to be silent and idle */
|
||||
snd_emu10k1_ptr_write(hw, DCYSUSV, ch, 0x0000);
|
||||
snd_emu10k1_ptr_write(hw, VTFT, ch, 0x0000FFFF);
|
||||
snd_emu10k1_ptr_write(hw, CVCF, ch, 0x0000FFFF);
|
||||
snd_emu10k1_ptr_write(hw, PTRX, ch, 0);
|
||||
snd_emu10k1_ptr_write(hw, CPF, ch, 0);
|
||||
|
||||
/* set pitch offset */
|
||||
snd_emu10k1_ptr_write(hw, IP, vp->ch, vp->apitch);
|
||||
|
||||
/* set envelope parameters */
|
||||
snd_emu10k1_ptr_write(hw, ENVVAL, ch, vp->reg.parm.moddelay);
|
||||
snd_emu10k1_ptr_write(hw, ATKHLDM, ch, vp->reg.parm.modatkhld);
|
||||
snd_emu10k1_ptr_write(hw, DCYSUSM, ch, vp->reg.parm.moddcysus);
|
||||
snd_emu10k1_ptr_write(hw, ENVVOL, ch, vp->reg.parm.voldelay);
|
||||
snd_emu10k1_ptr_write(hw, ATKHLDV, ch, vp->reg.parm.volatkhld);
|
||||
/* decay/sustain parameter for volume envelope is used
|
||||
for triggerg the voice */
|
||||
|
||||
/* cutoff and volume */
|
||||
temp = (unsigned int)vp->acutoff << 8 | (unsigned char)vp->avol;
|
||||
snd_emu10k1_ptr_write(hw, IFATN, vp->ch, temp);
|
||||
|
||||
/* modulation envelope heights */
|
||||
snd_emu10k1_ptr_write(hw, PEFE, ch, vp->reg.parm.pefe);
|
||||
|
||||
/* lfo1/2 delay */
|
||||
snd_emu10k1_ptr_write(hw, LFOVAL1, ch, vp->reg.parm.lfo1delay);
|
||||
snd_emu10k1_ptr_write(hw, LFOVAL2, ch, vp->reg.parm.lfo2delay);
|
||||
|
||||
/* lfo1 pitch & cutoff shift */
|
||||
set_fmmod(hw, vp);
|
||||
/* lfo1 volume & freq */
|
||||
snd_emu10k1_ptr_write(hw, TREMFRQ, vp->ch, vp->reg.parm.tremfrq);
|
||||
/* lfo2 pitch & freq */
|
||||
set_fm2frq2(hw, vp);
|
||||
|
||||
/* reverb and loop start (reverb 8bit, MSB) */
|
||||
temp = vp->reg.parm.reverb;
|
||||
temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;
|
||||
LIMITMAX(temp, 255);
|
||||
addr = vp->reg.loopstart;
|
||||
snd_emu10k1_ptr_write(hw, PSST, vp->ch, (temp << 24) | addr);
|
||||
|
||||
/* chorus & loop end (chorus 8bit, MSB) */
|
||||
addr = vp->reg.loopend;
|
||||
temp = vp->reg.parm.chorus;
|
||||
temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;
|
||||
LIMITMAX(temp, 255);
|
||||
temp = (temp <<24) | addr;
|
||||
snd_emu10k1_ptr_write(hw, DSL, ch, temp);
|
||||
|
||||
/* clear filter delay memory */
|
||||
snd_emu10k1_ptr_write(hw, Z1, ch, 0);
|
||||
snd_emu10k1_ptr_write(hw, Z2, ch, 0);
|
||||
|
||||
/* invalidate maps */
|
||||
temp = (hw->silent_page.addr << hw->address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
|
||||
snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
|
||||
snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
|
||||
#if 0
|
||||
/* cache */
|
||||
{
|
||||
unsigned int val, sample;
|
||||
val = 32;
|
||||
if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
|
||||
sample = 0x80808080;
|
||||
else {
|
||||
sample = 0;
|
||||
val *= 2;
|
||||
}
|
||||
|
||||
/* cache */
|
||||
snd_emu10k1_ptr_write(hw, CCR, ch, 0x1c << 16);
|
||||
snd_emu10k1_ptr_write(hw, CDE, ch, sample);
|
||||
snd_emu10k1_ptr_write(hw, CDF, ch, sample);
|
||||
|
||||
/* invalidate maps */
|
||||
temp = ((unsigned int)hw->silent_page.addr << hw_address_mode) | (hw->address_mode ? MAP_PTI_MASK1 : MAP_PTI_MASK0);
|
||||
snd_emu10k1_ptr_write(hw, MAPA, ch, temp);
|
||||
snd_emu10k1_ptr_write(hw, MAPB, ch, temp);
|
||||
|
||||
/* fill cache */
|
||||
val -= 4;
|
||||
val <<= 25;
|
||||
val |= 0x1c << 16;
|
||||
snd_emu10k1_ptr_write(hw, CCR, ch, val);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Q & current address (Q 4bit value, MSB) */
|
||||
addr = vp->reg.start;
|
||||
temp = vp->reg.parm.filterQ;
|
||||
temp = (temp<<28) | addr;
|
||||
if (vp->apitch < 0xe400)
|
||||
temp |= CCCA_INTERPROM_0;
|
||||
else {
|
||||
unsigned int shift = (vp->apitch - 0xe000) >> 10;
|
||||
temp |= shift << 25;
|
||||
}
|
||||
if (vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_8BITS)
|
||||
temp |= CCCA_8BITSELECT;
|
||||
snd_emu10k1_ptr_write(hw, CCCA, ch, temp);
|
||||
|
||||
/* reset volume */
|
||||
temp = (unsigned int)vp->vtarget << 16;
|
||||
snd_emu10k1_ptr_write(hw, VTFT, ch, temp | vp->ftarget);
|
||||
snd_emu10k1_ptr_write(hw, CVCF, ch, temp | 0xff00);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start envelope
|
||||
*/
|
||||
static void
|
||||
trigger_voice(struct snd_emux_voice *vp)
|
||||
{
|
||||
unsigned int temp, ptarget;
|
||||
struct snd_emu10k1 *hw;
|
||||
struct snd_emu10k1_memblk *emem;
|
||||
|
||||
hw = vp->hw;
|
||||
|
||||
emem = (struct snd_emu10k1_memblk *)vp->block;
|
||||
if (! emem || emem->mapped_page < 0)
|
||||
return; /* not mapped */
|
||||
|
||||
#if 0
|
||||
ptarget = (unsigned int)vp->ptarget << 16;
|
||||
#else
|
||||
ptarget = IP_TO_CP(vp->apitch);
|
||||
#endif
|
||||
/* set pitch target and pan (volume) */
|
||||
temp = ptarget | (vp->apan << 8) | vp->aaux;
|
||||
snd_emu10k1_ptr_write(hw, PTRX, vp->ch, temp);
|
||||
|
||||
/* pitch target */
|
||||
snd_emu10k1_ptr_write(hw, CPF, vp->ch, ptarget);
|
||||
|
||||
/* trigger voice */
|
||||
snd_emu10k1_ptr_write(hw, DCYSUSV, vp->ch, vp->reg.parm.voldcysus|DCYSUSV_CHANNELENABLE_MASK);
|
||||
}
|
||||
|
||||
#define MOD_SENSE 18
|
||||
|
||||
/* set lfo1 modulation height and cutoff */
|
||||
static void
|
||||
set_fmmod(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
|
||||
{
|
||||
unsigned short fmmod;
|
||||
short pitch;
|
||||
unsigned char cutoff;
|
||||
int modulation;
|
||||
|
||||
pitch = (char)(vp->reg.parm.fmmod>>8);
|
||||
cutoff = (vp->reg.parm.fmmod & 0xff);
|
||||
modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
|
||||
pitch += (MOD_SENSE * modulation) / 1200;
|
||||
LIMITVALUE(pitch, -128, 127);
|
||||
fmmod = ((unsigned char)pitch<<8) | cutoff;
|
||||
snd_emu10k1_ptr_write(hw, FMMOD, vp->ch, fmmod);
|
||||
}
|
||||
|
||||
/* set lfo2 pitch & frequency */
|
||||
static void
|
||||
set_fm2frq2(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
|
||||
{
|
||||
unsigned short fm2frq2;
|
||||
short pitch;
|
||||
unsigned char freq;
|
||||
int modulation;
|
||||
|
||||
pitch = (char)(vp->reg.parm.fm2frq2>>8);
|
||||
freq = vp->reg.parm.fm2frq2 & 0xff;
|
||||
modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
|
||||
pitch += (MOD_SENSE * modulation) / 1200;
|
||||
LIMITVALUE(pitch, -128, 127);
|
||||
fm2frq2 = ((unsigned char)pitch<<8) | freq;
|
||||
snd_emu10k1_ptr_write(hw, FM2FRQ2, vp->ch, fm2frq2);
|
||||
}
|
||||
|
||||
/* set filterQ */
|
||||
static void
|
||||
set_filterQ(struct snd_emu10k1 *hw, struct snd_emux_voice *vp)
|
||||
{
|
||||
unsigned int val;
|
||||
val = snd_emu10k1_ptr_read(hw, CCCA, vp->ch) & ~CCCA_RESONANCE;
|
||||
val |= (vp->reg.parm.filterQ << 28);
|
||||
snd_emu10k1_ptr_write(hw, CCCA, vp->ch, val);
|
||||
}
|
2154
sound/pci/emu10k1/emu10k1_main.c
Normal file
2154
sound/pci/emu10k1/emu10k1_main.c
Normal file
File diff suppressed because it is too large
Load diff
231
sound/pci/emu10k1/emu10k1_patch.c
Normal file
231
sound/pci/emu10k1/emu10k1_patch.c
Normal file
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* Patch transfer callback for Emu10k1
|
||||
*
|
||||
* Copyright (C) 2000 Takashi iwai <tiwai@suse.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.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
/*
|
||||
* All the code for loading in a patch. There is very little that is
|
||||
* chip specific here. Just the actual writing to the board.
|
||||
*/
|
||||
|
||||
#include "emu10k1_synth_local.h"
|
||||
|
||||
/*
|
||||
*/
|
||||
#define BLANK_LOOP_START 4
|
||||
#define BLANK_LOOP_END 8
|
||||
#define BLANK_LOOP_SIZE 12
|
||||
#define BLANK_HEAD_SIZE 32
|
||||
|
||||
/*
|
||||
* allocate a sample block and copy data from userspace
|
||||
*/
|
||||
int
|
||||
snd_emu10k1_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
|
||||
struct snd_util_memhdr *hdr,
|
||||
const void __user *data, long count)
|
||||
{
|
||||
int offset;
|
||||
int truesize, size, loopsize, blocksize;
|
||||
int loopend, sampleend;
|
||||
unsigned int start_addr;
|
||||
struct snd_emu10k1 *emu;
|
||||
|
||||
emu = rec->hw;
|
||||
if (snd_BUG_ON(!sp || !hdr))
|
||||
return -EINVAL;
|
||||
|
||||
if (sp->v.size == 0) {
|
||||
dev_dbg(emu->card->dev,
|
||||
"emu: rom font for sample %d\n", sp->v.sample);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* recalculate address offset */
|
||||
sp->v.end -= sp->v.start;
|
||||
sp->v.loopstart -= sp->v.start;
|
||||
sp->v.loopend -= sp->v.start;
|
||||
sp->v.start = 0;
|
||||
|
||||
/* some samples have invalid data. the addresses are corrected in voice info */
|
||||
sampleend = sp->v.end;
|
||||
if (sampleend > sp->v.size)
|
||||
sampleend = sp->v.size;
|
||||
loopend = sp->v.loopend;
|
||||
if (loopend > sampleend)
|
||||
loopend = sampleend;
|
||||
|
||||
/* be sure loop points start < end */
|
||||
if (sp->v.loopstart >= sp->v.loopend) {
|
||||
int tmp = sp->v.loopstart;
|
||||
sp->v.loopstart = sp->v.loopend;
|
||||
sp->v.loopend = tmp;
|
||||
}
|
||||
|
||||
/* compute true data size to be loaded */
|
||||
truesize = sp->v.size + BLANK_HEAD_SIZE;
|
||||
loopsize = 0;
|
||||
#if 0 /* not supported */
|
||||
if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
|
||||
loopsize = sp->v.loopend - sp->v.loopstart;
|
||||
truesize += loopsize;
|
||||
#endif
|
||||
if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
|
||||
truesize += BLANK_LOOP_SIZE;
|
||||
|
||||
/* try to allocate a memory block */
|
||||
blocksize = truesize;
|
||||
if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
|
||||
blocksize *= 2;
|
||||
sp->block = snd_emu10k1_synth_alloc(emu, blocksize);
|
||||
if (sp->block == NULL) {
|
||||
dev_dbg(emu->card->dev,
|
||||
"synth malloc failed (size=%d)\n", blocksize);
|
||||
/* not ENOMEM (for compatibility with OSS) */
|
||||
return -ENOSPC;
|
||||
}
|
||||
/* set the total size */
|
||||
sp->v.truesize = blocksize;
|
||||
|
||||
/* write blank samples at head */
|
||||
offset = 0;
|
||||
size = BLANK_HEAD_SIZE;
|
||||
if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
|
||||
size *= 2;
|
||||
if (offset + size > blocksize)
|
||||
return -EINVAL;
|
||||
snd_emu10k1_synth_bzero(emu, sp->block, offset, size);
|
||||
offset += size;
|
||||
|
||||
/* copy start->loopend */
|
||||
size = loopend;
|
||||
if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
|
||||
size *= 2;
|
||||
if (offset + size > blocksize)
|
||||
return -EINVAL;
|
||||
if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
|
||||
snd_emu10k1_synth_free(emu, sp->block);
|
||||
sp->block = NULL;
|
||||
return -EFAULT;
|
||||
}
|
||||
offset += size;
|
||||
data += size;
|
||||
|
||||
#if 0 /* not supported yet */
|
||||
/* handle reverse (or bidirectional) loop */
|
||||
if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)) {
|
||||
/* copy loop in reverse */
|
||||
if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
|
||||
int woffset;
|
||||
unsigned short *wblock = (unsigned short*)block;
|
||||
woffset = offset / 2;
|
||||
if (offset + loopsize * 2 > blocksize)
|
||||
return -EINVAL;
|
||||
for (i = 0; i < loopsize; i++)
|
||||
wblock[woffset + i] = wblock[woffset - i -1];
|
||||
offset += loopsize * 2;
|
||||
} else {
|
||||
if (offset + loopsize > blocksize)
|
||||
return -EINVAL;
|
||||
for (i = 0; i < loopsize; i++)
|
||||
block[offset + i] = block[offset - i -1];
|
||||
offset += loopsize;
|
||||
}
|
||||
|
||||
/* modify loop pointers */
|
||||
if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
|
||||
sp->v.loopend += loopsize;
|
||||
} else {
|
||||
sp->v.loopstart += loopsize;
|
||||
sp->v.loopend += loopsize;
|
||||
}
|
||||
/* add sample pointer */
|
||||
sp->v.end += loopsize;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* loopend -> sample end */
|
||||
size = sp->v.size - loopend;
|
||||
if (size < 0)
|
||||
return -EINVAL;
|
||||
if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
|
||||
size *= 2;
|
||||
if (snd_emu10k1_synth_copy_from_user(emu, sp->block, offset, data, size)) {
|
||||
snd_emu10k1_synth_free(emu, sp->block);
|
||||
sp->block = NULL;
|
||||
return -EFAULT;
|
||||
}
|
||||
offset += size;
|
||||
|
||||
/* clear rest of samples (if any) */
|
||||
if (offset < blocksize)
|
||||
snd_emu10k1_synth_bzero(emu, sp->block, offset, blocksize - offset);
|
||||
|
||||
if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
|
||||
/* if no blank loop is attached in the sample, add it */
|
||||
if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
|
||||
sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
|
||||
sp->v.loopend = sp->v.end + BLANK_LOOP_END;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 /* not supported yet */
|
||||
if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_UNSIGNED) {
|
||||
/* unsigned -> signed */
|
||||
if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS)) {
|
||||
unsigned short *wblock = (unsigned short*)block;
|
||||
for (i = 0; i < truesize; i++)
|
||||
wblock[i] ^= 0x8000;
|
||||
} else {
|
||||
for (i = 0; i < truesize; i++)
|
||||
block[i] ^= 0x80;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* recalculate offset */
|
||||
start_addr = BLANK_HEAD_SIZE * 2;
|
||||
if (! (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS))
|
||||
start_addr >>= 1;
|
||||
sp->v.start += start_addr;
|
||||
sp->v.end += start_addr;
|
||||
sp->v.loopstart += start_addr;
|
||||
sp->v.loopend += start_addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* free a sample block
|
||||
*/
|
||||
int
|
||||
snd_emu10k1_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
|
||||
struct snd_util_memhdr *hdr)
|
||||
{
|
||||
struct snd_emu10k1 *emu;
|
||||
|
||||
emu = rec->hw;
|
||||
if (snd_BUG_ON(!sp || !hdr))
|
||||
return -EINVAL;
|
||||
|
||||
if (sp->block) {
|
||||
snd_emu10k1_synth_free(emu, sp->block);
|
||||
sp->block = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
124
sound/pci/emu10k1/emu10k1_synth.c
Normal file
124
sound/pci/emu10k1/emu10k1_synth.c
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright (C) 2000 Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* Routines for control of EMU10K1 WaveTable synth
|
||||
*
|
||||
* 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 "emu10k1_synth_local.h"
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
MODULE_AUTHOR("Takashi Iwai");
|
||||
MODULE_DESCRIPTION("Routines for control of EMU10K1 WaveTable synth");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* create a new hardware dependent device for Emu10k1
|
||||
*/
|
||||
static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev)
|
||||
{
|
||||
struct snd_emux *emux;
|
||||
struct snd_emu10k1 *hw;
|
||||
struct snd_emu10k1_synth_arg *arg;
|
||||
unsigned long flags;
|
||||
|
||||
arg = SNDRV_SEQ_DEVICE_ARGPTR(dev);
|
||||
if (arg == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (arg->seq_ports <= 0)
|
||||
return 0; /* nothing */
|
||||
if (arg->max_voices < 1)
|
||||
arg->max_voices = 1;
|
||||
else if (arg->max_voices > 64)
|
||||
arg->max_voices = 64;
|
||||
|
||||
if (snd_emux_new(&emux) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
snd_emu10k1_ops_setup(emux);
|
||||
hw = arg->hwptr;
|
||||
emux->hw = hw;
|
||||
emux->max_voices = arg->max_voices;
|
||||
emux->num_ports = arg->seq_ports;
|
||||
emux->pitch_shift = -501;
|
||||
emux->memhdr = hw->memhdr;
|
||||
/* maximum two ports */
|
||||
emux->midi_ports = arg->seq_ports < 2 ? arg->seq_ports : 2;
|
||||
/* audigy has two external midis */
|
||||
emux->midi_devidx = hw->audigy ? 2 : 1;
|
||||
emux->linear_panning = 0;
|
||||
emux->hwdep_idx = 2; /* FIXED */
|
||||
|
||||
if (snd_emux_register(emux, dev->card, arg->index, "Emu10k1") < 0) {
|
||||
snd_emux_free(emux);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&hw->voice_lock, flags);
|
||||
hw->synth = emux;
|
||||
hw->get_synth_voice = snd_emu10k1_synth_get_voice;
|
||||
spin_unlock_irqrestore(&hw->voice_lock, flags);
|
||||
|
||||
dev->driver_data = emux;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev)
|
||||
{
|
||||
struct snd_emux *emux;
|
||||
struct snd_emu10k1 *hw;
|
||||
unsigned long flags;
|
||||
|
||||
if (dev->driver_data == NULL)
|
||||
return 0; /* not registered actually */
|
||||
|
||||
emux = dev->driver_data;
|
||||
|
||||
hw = emux->hw;
|
||||
spin_lock_irqsave(&hw->voice_lock, flags);
|
||||
hw->synth = NULL;
|
||||
hw->get_synth_voice = NULL;
|
||||
spin_unlock_irqrestore(&hw->voice_lock, flags);
|
||||
|
||||
snd_emux_free(emux);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* INIT part
|
||||
*/
|
||||
|
||||
static int __init alsa_emu10k1_synth_init(void)
|
||||
{
|
||||
|
||||
static struct snd_seq_dev_ops ops = {
|
||||
snd_emu10k1_synth_new_device,
|
||||
snd_emu10k1_synth_delete_device,
|
||||
};
|
||||
return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, &ops,
|
||||
sizeof(struct snd_emu10k1_synth_arg));
|
||||
}
|
||||
|
||||
static void __exit alsa_emu10k1_synth_exit(void)
|
||||
{
|
||||
snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH);
|
||||
}
|
||||
|
||||
module_init(alsa_emu10k1_synth_init)
|
||||
module_exit(alsa_emu10k1_synth_exit)
|
42
sound/pci/emu10k1/emu10k1_synth_local.h
Normal file
42
sound/pci/emu10k1/emu10k1_synth_local.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
#ifndef __EMU10K1_SYNTH_LOCAL_H
|
||||
#define __EMU10K1_SYNTH_LOCAL_H
|
||||
/*
|
||||
* Local defininitons for Emu10k1 wavetable
|
||||
*
|
||||
* Copyright (C) 2000 Takashi Iwai <tiwai@suse.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.
|
||||
*
|
||||
* 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/time.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/emu10k1_synth.h>
|
||||
|
||||
/* emu10k1_patch.c */
|
||||
int snd_emu10k1_sample_new(struct snd_emux *private_data,
|
||||
struct snd_sf_sample *sp,
|
||||
struct snd_util_memhdr *hdr,
|
||||
const void __user *_data, long count);
|
||||
int snd_emu10k1_sample_free(struct snd_emux *private_data,
|
||||
struct snd_sf_sample *sp,
|
||||
struct snd_util_memhdr *hdr);
|
||||
int snd_emu10k1_memhdr_init(struct snd_emux *emu);
|
||||
|
||||
/* emu10k1_callback.c */
|
||||
void snd_emu10k1_ops_setup(struct snd_emux *emu);
|
||||
int snd_emu10k1_synth_get_voice(struct snd_emu10k1 *hw);
|
||||
|
||||
|
||||
#endif /* __EMU10K1_SYNTH_LOCAL_H */
|
1651
sound/pci/emu10k1/emu10k1x.c
Normal file
1651
sound/pci/emu10k1/emu10k1x.c
Normal file
File diff suppressed because it is too large
Load diff
2775
sound/pci/emu10k1/emufx.c
Normal file
2775
sound/pci/emu10k1/emufx.c
Normal file
File diff suppressed because it is too large
Load diff
2145
sound/pci/emu10k1/emumixer.c
Normal file
2145
sound/pci/emu10k1/emumixer.c
Normal file
File diff suppressed because it is too large
Load diff
399
sound/pci/emu10k1/emumpu401.c
Normal file
399
sound/pci/emu10k1/emumpu401.c
Normal file
|
@ -0,0 +1,399 @@
|
|||
/*
|
||||
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
|
||||
* Routines for control of EMU10K1 MPU-401 in UART mode
|
||||
*
|
||||
*
|
||||
* 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/time.h>
|
||||
#include <linux/init.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/emu10k1.h>
|
||||
|
||||
#define EMU10K1_MIDI_MODE_INPUT (1<<0)
|
||||
#define EMU10K1_MIDI_MODE_OUTPUT (1<<1)
|
||||
|
||||
static inline unsigned char mpu401_read(struct snd_emu10k1 *emu,
|
||||
struct snd_emu10k1_midi *mpu, int idx)
|
||||
{
|
||||
if (emu->audigy)
|
||||
return (unsigned char)snd_emu10k1_ptr_read(emu, mpu->port + idx, 0);
|
||||
else
|
||||
return inb(emu->port + mpu->port + idx);
|
||||
}
|
||||
|
||||
static inline void mpu401_write(struct snd_emu10k1 *emu,
|
||||
struct snd_emu10k1_midi *mpu, int data, int idx)
|
||||
{
|
||||
if (emu->audigy)
|
||||
snd_emu10k1_ptr_write(emu, mpu->port + idx, 0, data);
|
||||
else
|
||||
outb(data, emu->port + mpu->port + idx);
|
||||
}
|
||||
|
||||
#define mpu401_write_data(emu, mpu, data) mpu401_write(emu, mpu, data, 0)
|
||||
#define mpu401_write_cmd(emu, mpu, data) mpu401_write(emu, mpu, data, 1)
|
||||
#define mpu401_read_data(emu, mpu) mpu401_read(emu, mpu, 0)
|
||||
#define mpu401_read_stat(emu, mpu) mpu401_read(emu, mpu, 1)
|
||||
|
||||
#define mpu401_input_avail(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x80))
|
||||
#define mpu401_output_ready(emu,mpu) (!(mpu401_read_stat(emu,mpu) & 0x40))
|
||||
|
||||
#define MPU401_RESET 0xff
|
||||
#define MPU401_ENTER_UART 0x3f
|
||||
#define MPU401_ACK 0xfe
|
||||
|
||||
static void mpu401_clear_rx(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *mpu)
|
||||
{
|
||||
int timeout = 100000;
|
||||
for (; timeout > 0 && mpu401_input_avail(emu, mpu); timeout--)
|
||||
mpu401_read_data(emu, mpu);
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
if (timeout <= 0)
|
||||
dev_err(emu->card->dev,
|
||||
"cmd: clear rx timeout (status = 0x%x)\n",
|
||||
mpu401_read_stat(emu, mpu));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
static void do_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, unsigned int status)
|
||||
{
|
||||
unsigned char byte;
|
||||
|
||||
if (midi->rmidi == NULL) {
|
||||
snd_emu10k1_intr_disable(emu, midi->tx_enable | midi->rx_enable);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock(&midi->input_lock);
|
||||
if ((status & midi->ipr_rx) && mpu401_input_avail(emu, midi)) {
|
||||
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
|
||||
mpu401_clear_rx(emu, midi);
|
||||
} else {
|
||||
byte = mpu401_read_data(emu, midi);
|
||||
if (midi->substream_input)
|
||||
snd_rawmidi_receive(midi->substream_input, &byte, 1);
|
||||
}
|
||||
}
|
||||
spin_unlock(&midi->input_lock);
|
||||
|
||||
spin_lock(&midi->output_lock);
|
||||
if ((status & midi->ipr_tx) && mpu401_output_ready(emu, midi)) {
|
||||
if (midi->substream_output &&
|
||||
snd_rawmidi_transmit(midi->substream_output, &byte, 1) == 1) {
|
||||
mpu401_write_data(emu, midi, byte);
|
||||
} else {
|
||||
snd_emu10k1_intr_disable(emu, midi->tx_enable);
|
||||
}
|
||||
}
|
||||
spin_unlock(&midi->output_lock);
|
||||
}
|
||||
|
||||
static void snd_emu10k1_midi_interrupt(struct snd_emu10k1 *emu, unsigned int status)
|
||||
{
|
||||
do_emu10k1_midi_interrupt(emu, &emu->midi, status);
|
||||
}
|
||||
|
||||
static void snd_emu10k1_midi_interrupt2(struct snd_emu10k1 *emu, unsigned int status)
|
||||
{
|
||||
do_emu10k1_midi_interrupt(emu, &emu->midi2, status);
|
||||
}
|
||||
|
||||
static int snd_emu10k1_midi_cmd(struct snd_emu10k1 * emu, struct snd_emu10k1_midi *midi, unsigned char cmd, int ack)
|
||||
{
|
||||
unsigned long flags;
|
||||
int timeout, ok;
|
||||
|
||||
spin_lock_irqsave(&midi->input_lock, flags);
|
||||
mpu401_write_data(emu, midi, 0x00);
|
||||
/* mpu401_clear_rx(emu, midi); */
|
||||
|
||||
mpu401_write_cmd(emu, midi, cmd);
|
||||
if (ack) {
|
||||
ok = 0;
|
||||
timeout = 10000;
|
||||
while (!ok && timeout-- > 0) {
|
||||
if (mpu401_input_avail(emu, midi)) {
|
||||
if (mpu401_read_data(emu, midi) == MPU401_ACK)
|
||||
ok = 1;
|
||||
}
|
||||
}
|
||||
if (!ok && mpu401_read_data(emu, midi) == MPU401_ACK)
|
||||
ok = 1;
|
||||
} else {
|
||||
ok = 1;
|
||||
}
|
||||
spin_unlock_irqrestore(&midi->input_lock, flags);
|
||||
if (!ok) {
|
||||
dev_err(emu->card->dev,
|
||||
"midi_cmd: 0x%x failed at 0x%lx (status = 0x%x, data = 0x%x)!!!\n",
|
||||
cmd, emu->port,
|
||||
mpu401_read_stat(emu, midi),
|
||||
mpu401_read_data(emu, midi));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_emu10k1_midi_input_open(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_emu10k1 *emu;
|
||||
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
|
||||
unsigned long flags;
|
||||
|
||||
emu = midi->emu;
|
||||
if (snd_BUG_ON(!emu))
|
||||
return -ENXIO;
|
||||
spin_lock_irqsave(&midi->open_lock, flags);
|
||||
midi->midi_mode |= EMU10K1_MIDI_MODE_INPUT;
|
||||
midi->substream_input = substream;
|
||||
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
|
||||
spin_unlock_irqrestore(&midi->open_lock, flags);
|
||||
if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
|
||||
goto error_out;
|
||||
if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
|
||||
goto error_out;
|
||||
} else {
|
||||
spin_unlock_irqrestore(&midi->open_lock, flags);
|
||||
}
|
||||
return 0;
|
||||
|
||||
error_out:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int snd_emu10k1_midi_output_open(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_emu10k1 *emu;
|
||||
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
|
||||
unsigned long flags;
|
||||
|
||||
emu = midi->emu;
|
||||
if (snd_BUG_ON(!emu))
|
||||
return -ENXIO;
|
||||
spin_lock_irqsave(&midi->open_lock, flags);
|
||||
midi->midi_mode |= EMU10K1_MIDI_MODE_OUTPUT;
|
||||
midi->substream_output = substream;
|
||||
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
|
||||
spin_unlock_irqrestore(&midi->open_lock, flags);
|
||||
if (snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 1))
|
||||
goto error_out;
|
||||
if (snd_emu10k1_midi_cmd(emu, midi, MPU401_ENTER_UART, 1))
|
||||
goto error_out;
|
||||
} else {
|
||||
spin_unlock_irqrestore(&midi->open_lock, flags);
|
||||
}
|
||||
return 0;
|
||||
|
||||
error_out:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int snd_emu10k1_midi_input_close(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_emu10k1 *emu;
|
||||
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
|
||||
unsigned long flags;
|
||||
int err = 0;
|
||||
|
||||
emu = midi->emu;
|
||||
if (snd_BUG_ON(!emu))
|
||||
return -ENXIO;
|
||||
spin_lock_irqsave(&midi->open_lock, flags);
|
||||
snd_emu10k1_intr_disable(emu, midi->rx_enable);
|
||||
midi->midi_mode &= ~EMU10K1_MIDI_MODE_INPUT;
|
||||
midi->substream_input = NULL;
|
||||
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT)) {
|
||||
spin_unlock_irqrestore(&midi->open_lock, flags);
|
||||
err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&midi->open_lock, flags);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int snd_emu10k1_midi_output_close(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_emu10k1 *emu;
|
||||
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
|
||||
unsigned long flags;
|
||||
int err = 0;
|
||||
|
||||
emu = midi->emu;
|
||||
if (snd_BUG_ON(!emu))
|
||||
return -ENXIO;
|
||||
spin_lock_irqsave(&midi->open_lock, flags);
|
||||
snd_emu10k1_intr_disable(emu, midi->tx_enable);
|
||||
midi->midi_mode &= ~EMU10K1_MIDI_MODE_OUTPUT;
|
||||
midi->substream_output = NULL;
|
||||
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_INPUT)) {
|
||||
spin_unlock_irqrestore(&midi->open_lock, flags);
|
||||
err = snd_emu10k1_midi_cmd(emu, midi, MPU401_RESET, 0);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&midi->open_lock, flags);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void snd_emu10k1_midi_input_trigger(struct snd_rawmidi_substream *substream, int up)
|
||||
{
|
||||
struct snd_emu10k1 *emu;
|
||||
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
|
||||
emu = midi->emu;
|
||||
if (snd_BUG_ON(!emu))
|
||||
return;
|
||||
|
||||
if (up)
|
||||
snd_emu10k1_intr_enable(emu, midi->rx_enable);
|
||||
else
|
||||
snd_emu10k1_intr_disable(emu, midi->rx_enable);
|
||||
}
|
||||
|
||||
static void snd_emu10k1_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
|
||||
{
|
||||
struct snd_emu10k1 *emu;
|
||||
struct snd_emu10k1_midi *midi = (struct snd_emu10k1_midi *)substream->rmidi->private_data;
|
||||
unsigned long flags;
|
||||
|
||||
emu = midi->emu;
|
||||
if (snd_BUG_ON(!emu))
|
||||
return;
|
||||
|
||||
if (up) {
|
||||
int max = 4;
|
||||
unsigned char byte;
|
||||
|
||||
/* try to send some amount of bytes here before interrupts */
|
||||
spin_lock_irqsave(&midi->output_lock, flags);
|
||||
while (max > 0) {
|
||||
if (mpu401_output_ready(emu, midi)) {
|
||||
if (!(midi->midi_mode & EMU10K1_MIDI_MODE_OUTPUT) ||
|
||||
snd_rawmidi_transmit(substream, &byte, 1) != 1) {
|
||||
/* no more data */
|
||||
spin_unlock_irqrestore(&midi->output_lock, flags);
|
||||
return;
|
||||
}
|
||||
mpu401_write_data(emu, midi, byte);
|
||||
max--;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&midi->output_lock, flags);
|
||||
snd_emu10k1_intr_enable(emu, midi->tx_enable);
|
||||
} else {
|
||||
snd_emu10k1_intr_disable(emu, midi->tx_enable);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
|
||||
static struct snd_rawmidi_ops snd_emu10k1_midi_output =
|
||||
{
|
||||
.open = snd_emu10k1_midi_output_open,
|
||||
.close = snd_emu10k1_midi_output_close,
|
||||
.trigger = snd_emu10k1_midi_output_trigger,
|
||||
};
|
||||
|
||||
static struct snd_rawmidi_ops snd_emu10k1_midi_input =
|
||||
{
|
||||
.open = snd_emu10k1_midi_input_open,
|
||||
.close = snd_emu10k1_midi_input_close,
|
||||
.trigger = snd_emu10k1_midi_input_trigger,
|
||||
};
|
||||
|
||||
static void snd_emu10k1_midi_free(struct snd_rawmidi *rmidi)
|
||||
{
|
||||
struct snd_emu10k1_midi *midi = rmidi->private_data;
|
||||
midi->interrupt = NULL;
|
||||
midi->rmidi = NULL;
|
||||
}
|
||||
|
||||
static int emu10k1_midi_init(struct snd_emu10k1 *emu, struct snd_emu10k1_midi *midi, int device, char *name)
|
||||
{
|
||||
struct snd_rawmidi *rmidi;
|
||||
int err;
|
||||
|
||||
if ((err = snd_rawmidi_new(emu->card, name, device, 1, 1, &rmidi)) < 0)
|
||||
return err;
|
||||
midi->emu = emu;
|
||||
spin_lock_init(&midi->open_lock);
|
||||
spin_lock_init(&midi->input_lock);
|
||||
spin_lock_init(&midi->output_lock);
|
||||
strcpy(rmidi->name, name);
|
||||
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_emu10k1_midi_output);
|
||||
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_emu10k1_midi_input);
|
||||
rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
|
||||
SNDRV_RAWMIDI_INFO_INPUT |
|
||||
SNDRV_RAWMIDI_INFO_DUPLEX;
|
||||
rmidi->private_data = midi;
|
||||
rmidi->private_free = snd_emu10k1_midi_free;
|
||||
midi->rmidi = rmidi;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_emu10k1_midi(struct snd_emu10k1 *emu)
|
||||
{
|
||||
struct snd_emu10k1_midi *midi = &emu->midi;
|
||||
int err;
|
||||
|
||||
if ((err = emu10k1_midi_init(emu, midi, 0, "EMU10K1 MPU-401 (UART)")) < 0)
|
||||
return err;
|
||||
|
||||
midi->tx_enable = INTE_MIDITXENABLE;
|
||||
midi->rx_enable = INTE_MIDIRXENABLE;
|
||||
midi->port = MUDATA;
|
||||
midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
|
||||
midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
|
||||
midi->interrupt = snd_emu10k1_midi_interrupt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_emu10k1_audigy_midi(struct snd_emu10k1 *emu)
|
||||
{
|
||||
struct snd_emu10k1_midi *midi;
|
||||
int err;
|
||||
|
||||
midi = &emu->midi;
|
||||
if ((err = emu10k1_midi_init(emu, midi, 0, "Audigy MPU-401 (UART)")) < 0)
|
||||
return err;
|
||||
|
||||
midi->tx_enable = INTE_MIDITXENABLE;
|
||||
midi->rx_enable = INTE_MIDIRXENABLE;
|
||||
midi->port = A_MUDATA1;
|
||||
midi->ipr_tx = IPR_MIDITRANSBUFEMPTY;
|
||||
midi->ipr_rx = IPR_MIDIRECVBUFEMPTY;
|
||||
midi->interrupt = snd_emu10k1_midi_interrupt;
|
||||
|
||||
midi = &emu->midi2;
|
||||
if ((err = emu10k1_midi_init(emu, midi, 1, "Audigy MPU-401 #2")) < 0)
|
||||
return err;
|
||||
|
||||
midi->tx_enable = INTE_A_MIDITXENABLE2;
|
||||
midi->rx_enable = INTE_A_MIDIRXENABLE2;
|
||||
midi->port = A_MUDATA2;
|
||||
midi->ipr_tx = IPR_A_MIDITRANSBUFEMPTY2;
|
||||
midi->ipr_rx = IPR_A_MIDIRECVBUFEMPTY2;
|
||||
midi->interrupt = snd_emu10k1_midi_interrupt2;
|
||||
return 0;
|
||||
}
|
1883
sound/pci/emu10k1/emupcm.c
Normal file
1883
sound/pci/emu10k1/emupcm.c
Normal file
File diff suppressed because it is too large
Load diff
659
sound/pci/emu10k1/emuproc.c
Normal file
659
sound/pci/emu10k1/emuproc.c
Normal file
|
@ -0,0 +1,659 @@
|
|||
/*
|
||||
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
|
||||
* Creative Labs, Inc.
|
||||
* Routines for control of EMU10K1 chips / proc interface routines
|
||||
*
|
||||
* Copyright (c) by James Courtier-Dutton <James@superbug.co.uk>
|
||||
* Added EMU 1010 support.
|
||||
*
|
||||
* BUGS:
|
||||
* --
|
||||
*
|
||||
* TODO:
|
||||
* --
|
||||
*
|
||||
* 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/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/emu10k1.h>
|
||||
#include "p16v.h"
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu,
|
||||
struct snd_info_buffer *buffer,
|
||||
char *title,
|
||||
int status_reg,
|
||||
int rate_reg)
|
||||
{
|
||||
static char *clkaccy[4] = { "1000ppm", "50ppm", "variable", "unknown" };
|
||||
static int samplerate[16] = { 44100, 1, 48000, 32000, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||
static char *channel[16] = { "unspec", "left", "right", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15" };
|
||||
static char *emphasis[8] = { "none", "50/15 usec 2 channel", "2", "3", "4", "5", "6", "7" };
|
||||
unsigned int status, rate = 0;
|
||||
|
||||
status = snd_emu10k1_ptr_read(emu, status_reg, 0);
|
||||
|
||||
snd_iprintf(buffer, "\n%s\n", title);
|
||||
|
||||
if (status != 0xffffffff) {
|
||||
snd_iprintf(buffer, "Professional Mode : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no");
|
||||
snd_iprintf(buffer, "Not Audio Data : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no");
|
||||
snd_iprintf(buffer, "Copyright : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no");
|
||||
snd_iprintf(buffer, "Emphasis : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]);
|
||||
snd_iprintf(buffer, "Mode : %i\n", (status & SPCS_MODEMASK) >> 6);
|
||||
snd_iprintf(buffer, "Category Code : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8);
|
||||
snd_iprintf(buffer, "Generation Status : %s\n", status & SPCS_GENERATIONSTATUS ? "original" : "copy");
|
||||
snd_iprintf(buffer, "Source Mask : %i\n", (status & SPCS_SOURCENUMMASK) >> 16);
|
||||
snd_iprintf(buffer, "Channel Number : %s\n", channel[(status & SPCS_CHANNELNUMMASK) >> 20]);
|
||||
snd_iprintf(buffer, "Sample Rate : %iHz\n", samplerate[(status & SPCS_SAMPLERATEMASK) >> 24]);
|
||||
snd_iprintf(buffer, "Clock Accuracy : %s\n", clkaccy[(status & SPCS_CLKACCYMASK) >> 28]);
|
||||
|
||||
if (rate_reg > 0) {
|
||||
rate = snd_emu10k1_ptr_read(emu, rate_reg, 0);
|
||||
snd_iprintf(buffer, "S/PDIF Valid : %s\n", rate & SRCS_SPDIFVALID ? "on" : "off");
|
||||
snd_iprintf(buffer, "S/PDIF Locked : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off");
|
||||
snd_iprintf(buffer, "Rate Locked : %s\n", rate & SRCS_RATELOCKED ? "on" : "off");
|
||||
/* From ((Rate * 48000 ) / 262144); */
|
||||
snd_iprintf(buffer, "Estimated Sample Rate : %d\n", ((rate & 0xFFFFF ) * 375) >> 11);
|
||||
}
|
||||
} else {
|
||||
snd_iprintf(buffer, "No signal detected.\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void snd_emu10k1_proc_read(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
/* FIXME - output names are in emufx.c too */
|
||||
static char *creative_outs[32] = {
|
||||
/* 00 */ "AC97 Left",
|
||||
/* 01 */ "AC97 Right",
|
||||
/* 02 */ "Optical IEC958 Left",
|
||||
/* 03 */ "Optical IEC958 Right",
|
||||
/* 04 */ "Center",
|
||||
/* 05 */ "LFE",
|
||||
/* 06 */ "Headphone Left",
|
||||
/* 07 */ "Headphone Right",
|
||||
/* 08 */ "Surround Left",
|
||||
/* 09 */ "Surround Right",
|
||||
/* 10 */ "PCM Capture Left",
|
||||
/* 11 */ "PCM Capture Right",
|
||||
/* 12 */ "MIC Capture",
|
||||
/* 13 */ "AC97 Surround Left",
|
||||
/* 14 */ "AC97 Surround Right",
|
||||
/* 15 */ "???",
|
||||
/* 16 */ "???",
|
||||
/* 17 */ "Analog Center",
|
||||
/* 18 */ "Analog LFE",
|
||||
/* 19 */ "???",
|
||||
/* 20 */ "???",
|
||||
/* 21 */ "???",
|
||||
/* 22 */ "???",
|
||||
/* 23 */ "???",
|
||||
/* 24 */ "???",
|
||||
/* 25 */ "???",
|
||||
/* 26 */ "???",
|
||||
/* 27 */ "???",
|
||||
/* 28 */ "???",
|
||||
/* 29 */ "???",
|
||||
/* 30 */ "???",
|
||||
/* 31 */ "???"
|
||||
};
|
||||
|
||||
static char *audigy_outs[64] = {
|
||||
/* 00 */ "Digital Front Left",
|
||||
/* 01 */ "Digital Front Right",
|
||||
/* 02 */ "Digital Center",
|
||||
/* 03 */ "Digital LEF",
|
||||
/* 04 */ "Headphone Left",
|
||||
/* 05 */ "Headphone Right",
|
||||
/* 06 */ "Digital Rear Left",
|
||||
/* 07 */ "Digital Rear Right",
|
||||
/* 08 */ "Front Left",
|
||||
/* 09 */ "Front Right",
|
||||
/* 10 */ "Center",
|
||||
/* 11 */ "LFE",
|
||||
/* 12 */ "???",
|
||||
/* 13 */ "???",
|
||||
/* 14 */ "Rear Left",
|
||||
/* 15 */ "Rear Right",
|
||||
/* 16 */ "AC97 Front Left",
|
||||
/* 17 */ "AC97 Front Right",
|
||||
/* 18 */ "ADC Caputre Left",
|
||||
/* 19 */ "ADC Capture Right",
|
||||
/* 20 */ "???",
|
||||
/* 21 */ "???",
|
||||
/* 22 */ "???",
|
||||
/* 23 */ "???",
|
||||
/* 24 */ "???",
|
||||
/* 25 */ "???",
|
||||
/* 26 */ "???",
|
||||
/* 27 */ "???",
|
||||
/* 28 */ "???",
|
||||
/* 29 */ "???",
|
||||
/* 30 */ "???",
|
||||
/* 31 */ "???",
|
||||
/* 32 */ "FXBUS2_0",
|
||||
/* 33 */ "FXBUS2_1",
|
||||
/* 34 */ "FXBUS2_2",
|
||||
/* 35 */ "FXBUS2_3",
|
||||
/* 36 */ "FXBUS2_4",
|
||||
/* 37 */ "FXBUS2_5",
|
||||
/* 38 */ "FXBUS2_6",
|
||||
/* 39 */ "FXBUS2_7",
|
||||
/* 40 */ "FXBUS2_8",
|
||||
/* 41 */ "FXBUS2_9",
|
||||
/* 42 */ "FXBUS2_10",
|
||||
/* 43 */ "FXBUS2_11",
|
||||
/* 44 */ "FXBUS2_12",
|
||||
/* 45 */ "FXBUS2_13",
|
||||
/* 46 */ "FXBUS2_14",
|
||||
/* 47 */ "FXBUS2_15",
|
||||
/* 48 */ "FXBUS2_16",
|
||||
/* 49 */ "FXBUS2_17",
|
||||
/* 50 */ "FXBUS2_18",
|
||||
/* 51 */ "FXBUS2_19",
|
||||
/* 52 */ "FXBUS2_20",
|
||||
/* 53 */ "FXBUS2_21",
|
||||
/* 54 */ "FXBUS2_22",
|
||||
/* 55 */ "FXBUS2_23",
|
||||
/* 56 */ "FXBUS2_24",
|
||||
/* 57 */ "FXBUS2_25",
|
||||
/* 58 */ "FXBUS2_26",
|
||||
/* 59 */ "FXBUS2_27",
|
||||
/* 60 */ "FXBUS2_28",
|
||||
/* 61 */ "FXBUS2_29",
|
||||
/* 62 */ "FXBUS2_30",
|
||||
/* 63 */ "FXBUS2_31"
|
||||
};
|
||||
|
||||
struct snd_emu10k1 *emu = entry->private_data;
|
||||
unsigned int val, val1;
|
||||
int nefx = emu->audigy ? 64 : 32;
|
||||
char **outputs = emu->audigy ? audigy_outs : creative_outs;
|
||||
int idx;
|
||||
|
||||
snd_iprintf(buffer, "EMU10K1\n\n");
|
||||
snd_iprintf(buffer, "Card : %s\n",
|
||||
emu->audigy ? "Audigy" : (emu->card_capabilities->ecard ? "EMU APS" : "Creative"));
|
||||
snd_iprintf(buffer, "Internal TRAM (words) : 0x%x\n", emu->fx8010.itram_size);
|
||||
snd_iprintf(buffer, "External TRAM (words) : 0x%x\n", (int)emu->fx8010.etram_pages.bytes / 2);
|
||||
snd_iprintf(buffer, "\n");
|
||||
snd_iprintf(buffer, "Effect Send Routing :\n");
|
||||
for (idx = 0; idx < NUM_G; idx++) {
|
||||
val = emu->audigy ?
|
||||
snd_emu10k1_ptr_read(emu, A_FXRT1, idx) :
|
||||
snd_emu10k1_ptr_read(emu, FXRT, idx);
|
||||
val1 = emu->audigy ?
|
||||
snd_emu10k1_ptr_read(emu, A_FXRT2, idx) :
|
||||
0;
|
||||
if (emu->audigy) {
|
||||
snd_iprintf(buffer, "Ch%i: A=%i, B=%i, C=%i, D=%i, ",
|
||||
idx,
|
||||
val & 0x3f,
|
||||
(val >> 8) & 0x3f,
|
||||
(val >> 16) & 0x3f,
|
||||
(val >> 24) & 0x3f);
|
||||
snd_iprintf(buffer, "E=%i, F=%i, G=%i, H=%i\n",
|
||||
val1 & 0x3f,
|
||||
(val1 >> 8) & 0x3f,
|
||||
(val1 >> 16) & 0x3f,
|
||||
(val1 >> 24) & 0x3f);
|
||||
} else {
|
||||
snd_iprintf(buffer, "Ch%i: A=%i, B=%i, C=%i, D=%i\n",
|
||||
idx,
|
||||
(val >> 16) & 0x0f,
|
||||
(val >> 20) & 0x0f,
|
||||
(val >> 24) & 0x0f,
|
||||
(val >> 28) & 0x0f);
|
||||
}
|
||||
}
|
||||
snd_iprintf(buffer, "\nCaptured FX Outputs :\n");
|
||||
for (idx = 0; idx < nefx; idx++) {
|
||||
if (emu->efx_voices_mask[idx/32] & (1 << (idx%32)))
|
||||
snd_iprintf(buffer, " Output %02i [%s]\n", idx, outputs[idx]);
|
||||
}
|
||||
snd_iprintf(buffer, "\nAll FX Outputs :\n");
|
||||
for (idx = 0; idx < (emu->audigy ? 64 : 32); idx++)
|
||||
snd_iprintf(buffer, " Output %02i [%s]\n", idx, outputs[idx]);
|
||||
}
|
||||
|
||||
static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct snd_emu10k1 *emu = entry->private_data;
|
||||
u32 value;
|
||||
u32 value2;
|
||||
u32 rate;
|
||||
|
||||
if (emu->card_capabilities->emu_model) {
|
||||
snd_emu1010_fpga_read(emu, 0x38, &value);
|
||||
if ((value & 0x1) == 0) {
|
||||
snd_emu1010_fpga_read(emu, 0x2a, &value);
|
||||
snd_emu1010_fpga_read(emu, 0x2b, &value2);
|
||||
rate = 0x1770000 / (((value << 5) | value2)+1);
|
||||
snd_iprintf(buffer, "ADAT Locked : %u\n", rate);
|
||||
} else {
|
||||
snd_iprintf(buffer, "ADAT Unlocked\n");
|
||||
}
|
||||
snd_emu1010_fpga_read(emu, 0x20, &value);
|
||||
if ((value & 0x4) == 0) {
|
||||
snd_emu1010_fpga_read(emu, 0x28, &value);
|
||||
snd_emu1010_fpga_read(emu, 0x29, &value2);
|
||||
rate = 0x1770000 / (((value << 5) | value2)+1);
|
||||
snd_iprintf(buffer, "SPDIF Locked : %d\n", rate);
|
||||
} else {
|
||||
snd_iprintf(buffer, "SPDIF Unlocked\n");
|
||||
}
|
||||
} else {
|
||||
snd_emu10k1_proc_spdif_status(emu, buffer, "CD-ROM S/PDIF In", CDCS, CDSRCS);
|
||||
snd_emu10k1_proc_spdif_status(emu, buffer, "Optical or Coax S/PDIF In", GPSCS, GPSRCS);
|
||||
}
|
||||
#if 0
|
||||
val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0);
|
||||
snd_iprintf(buffer, "\nZoomed Video\n");
|
||||
snd_iprintf(buffer, "Rate Locked : %s\n", val & SRCS_RATELOCKED ? "on" : "off");
|
||||
snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", val & SRCS_ESTSAMPLERATE);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void snd_emu10k1_proc_rates_read(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
static int samplerate[8] = { 44100, 48000, 96000, 192000, 4, 5, 6, 7 };
|
||||
struct snd_emu10k1 *emu = entry->private_data;
|
||||
unsigned int val, tmp, n;
|
||||
val = snd_emu10k1_ptr20_read(emu, CAPTURE_RATE_STATUS, 0);
|
||||
tmp = (val >> 16) & 0x8;
|
||||
for (n = 0; n < 4; n++) {
|
||||
tmp = val >> (16 + (n*4));
|
||||
if (tmp & 0x8) snd_iprintf(buffer, "Channel %d: Rate=%d\n", n, samplerate[tmp & 0x7]);
|
||||
else snd_iprintf(buffer, "Channel %d: No input\n", n);
|
||||
}
|
||||
}
|
||||
|
||||
static void snd_emu10k1_proc_acode_read(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
u32 pc;
|
||||
struct snd_emu10k1 *emu = entry->private_data;
|
||||
|
||||
snd_iprintf(buffer, "FX8010 Instruction List '%s'\n", emu->fx8010.name);
|
||||
snd_iprintf(buffer, " Code dump :\n");
|
||||
for (pc = 0; pc < (emu->audigy ? 1024 : 512); pc++) {
|
||||
u32 low, high;
|
||||
|
||||
low = snd_emu10k1_efx_read(emu, pc * 2);
|
||||
high = snd_emu10k1_efx_read(emu, pc * 2 + 1);
|
||||
if (emu->audigy)
|
||||
snd_iprintf(buffer, " OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n",
|
||||
(high >> 24) & 0x0f,
|
||||
(high >> 12) & 0x7ff,
|
||||
(high >> 0) & 0x7ff,
|
||||
(low >> 12) & 0x7ff,
|
||||
(low >> 0) & 0x7ff,
|
||||
pc,
|
||||
high, low);
|
||||
else
|
||||
snd_iprintf(buffer, " OP(0x%02x, 0x%03x, 0x%03x, 0x%03x, 0x%03x) /* 0x%04x: 0x%08x%08x */\n",
|
||||
(high >> 20) & 0x0f,
|
||||
(high >> 10) & 0x3ff,
|
||||
(high >> 0) & 0x3ff,
|
||||
(low >> 10) & 0x3ff,
|
||||
(low >> 0) & 0x3ff,
|
||||
pc,
|
||||
high, low);
|
||||
}
|
||||
}
|
||||
|
||||
#define TOTAL_SIZE_GPR (0x100*4)
|
||||
#define A_TOTAL_SIZE_GPR (0x200*4)
|
||||
#define TOTAL_SIZE_TANKMEM_DATA (0xa0*4)
|
||||
#define TOTAL_SIZE_TANKMEM_ADDR (0xa0*4)
|
||||
#define A_TOTAL_SIZE_TANKMEM_DATA (0x100*4)
|
||||
#define A_TOTAL_SIZE_TANKMEM_ADDR (0x100*4)
|
||||
#define TOTAL_SIZE_CODE (0x200*8)
|
||||
#define A_TOTAL_SIZE_CODE (0x400*8)
|
||||
|
||||
static ssize_t snd_emu10k1_fx8010_read(struct snd_info_entry *entry,
|
||||
void *file_private_data,
|
||||
struct file *file, char __user *buf,
|
||||
size_t count, loff_t pos)
|
||||
{
|
||||
struct snd_emu10k1 *emu = entry->private_data;
|
||||
unsigned int offset;
|
||||
int tram_addr = 0;
|
||||
unsigned int *tmp;
|
||||
long res;
|
||||
unsigned int idx;
|
||||
|
||||
if (!strcmp(entry->name, "fx8010_tram_addr")) {
|
||||
offset = TANKMEMADDRREGBASE;
|
||||
tram_addr = 1;
|
||||
} else if (!strcmp(entry->name, "fx8010_tram_data")) {
|
||||
offset = TANKMEMDATAREGBASE;
|
||||
} else if (!strcmp(entry->name, "fx8010_code")) {
|
||||
offset = emu->audigy ? A_MICROCODEBASE : MICROCODEBASE;
|
||||
} else {
|
||||
offset = emu->audigy ? A_FXGPREGBASE : FXGPREGBASE;
|
||||
}
|
||||
|
||||
tmp = kmalloc(count + 8, GFP_KERNEL);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
for (idx = 0; idx < ((pos & 3) + count + 3) >> 2; idx++) {
|
||||
unsigned int val;
|
||||
val = snd_emu10k1_ptr_read(emu, offset + idx + (pos >> 2), 0);
|
||||
if (tram_addr && emu->audigy) {
|
||||
val >>= 11;
|
||||
val |= snd_emu10k1_ptr_read(emu, 0x100 + idx + (pos >> 2), 0) << 20;
|
||||
}
|
||||
tmp[idx] = val;
|
||||
}
|
||||
if (copy_to_user(buf, ((char *)tmp) + (pos & 3), count))
|
||||
res = -EFAULT;
|
||||
else
|
||||
res = count;
|
||||
kfree(tmp);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void snd_emu10k1_proc_voices_read(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct snd_emu10k1 *emu = entry->private_data;
|
||||
struct snd_emu10k1_voice *voice;
|
||||
int idx;
|
||||
|
||||
snd_iprintf(buffer, "ch\tuse\tpcm\tefx\tsynth\tmidi\n");
|
||||
for (idx = 0; idx < NUM_G; idx++) {
|
||||
voice = &emu->voices[idx];
|
||||
snd_iprintf(buffer, "%i\t%i\t%i\t%i\t%i\t%i\n",
|
||||
idx,
|
||||
voice->use,
|
||||
voice->pcm,
|
||||
voice->efx,
|
||||
voice->synth,
|
||||
voice->midi);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct snd_emu10k1 *emu = entry->private_data;
|
||||
u32 value;
|
||||
int i;
|
||||
snd_iprintf(buffer, "EMU1010 Registers:\n\n");
|
||||
|
||||
for(i = 0; i < 0x40; i+=1) {
|
||||
snd_emu1010_fpga_read(emu, i, &value);
|
||||
snd_iprintf(buffer, "%02X: %08X, %02X\n", i, value, (value >> 8) & 0x7f);
|
||||
}
|
||||
}
|
||||
|
||||
static void snd_emu_proc_io_reg_read(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct snd_emu10k1 *emu = entry->private_data;
|
||||
unsigned long value;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
snd_iprintf(buffer, "IO Registers:\n\n");
|
||||
for(i = 0; i < 0x40; i+=4) {
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
value = inl(emu->port + i);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
snd_iprintf(buffer, "%02X: %08lX\n", i, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void snd_emu_proc_io_reg_write(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
struct snd_emu10k1 *emu = entry->private_data;
|
||||
unsigned long flags;
|
||||
char line[64];
|
||||
u32 reg, val;
|
||||
while (!snd_info_get_line(buffer, line, sizeof(line))) {
|
||||
if (sscanf(line, "%x %x", ®, &val) != 2)
|
||||
continue;
|
||||
if (reg < 0x40 && val <= 0xffffffff) {
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
outl(val, emu->port + (reg & 0xfffffffc));
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int snd_ptr_read(struct snd_emu10k1 * emu,
|
||||
unsigned int iobase,
|
||||
unsigned int reg,
|
||||
unsigned int chn)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int regptr, val;
|
||||
|
||||
regptr = (reg << 16) | chn;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
outl(regptr, emu->port + iobase + PTR);
|
||||
val = inl(emu->port + iobase + DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
return val;
|
||||
}
|
||||
|
||||
static void snd_ptr_write(struct snd_emu10k1 *emu,
|
||||
unsigned int iobase,
|
||||
unsigned int reg,
|
||||
unsigned int chn,
|
||||
unsigned int data)
|
||||
{
|
||||
unsigned int regptr;
|
||||
unsigned long flags;
|
||||
|
||||
regptr = (reg << 16) | chn;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
outl(regptr, emu->port + iobase + PTR);
|
||||
outl(data, emu->port + iobase + DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
|
||||
static void snd_emu_proc_ptr_reg_read(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer, int iobase, int offset, int length, int voices)
|
||||
{
|
||||
struct snd_emu10k1 *emu = entry->private_data;
|
||||
unsigned long value;
|
||||
int i,j;
|
||||
if (offset+length > 0xa0) {
|
||||
snd_iprintf(buffer, "Input values out of range\n");
|
||||
return;
|
||||
}
|
||||
snd_iprintf(buffer, "Registers 0x%x\n", iobase);
|
||||
for(i = offset; i < offset+length; i++) {
|
||||
snd_iprintf(buffer, "%02X: ",i);
|
||||
for (j = 0; j < voices; j++) {
|
||||
if(iobase == 0)
|
||||
value = snd_ptr_read(emu, 0, i, j);
|
||||
else
|
||||
value = snd_ptr_read(emu, 0x20, i, j);
|
||||
snd_iprintf(buffer, "%08lX ", value);
|
||||
}
|
||||
snd_iprintf(buffer, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void snd_emu_proc_ptr_reg_write(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer, int iobase)
|
||||
{
|
||||
struct snd_emu10k1 *emu = entry->private_data;
|
||||
char line[64];
|
||||
unsigned int reg, channel_id , val;
|
||||
while (!snd_info_get_line(buffer, line, sizeof(line))) {
|
||||
if (sscanf(line, "%x %x %x", ®, &channel_id, &val) != 3)
|
||||
continue;
|
||||
if (reg < 0xa0 && val <= 0xffffffff && channel_id <= 3)
|
||||
snd_ptr_write(emu, iobase, reg, channel_id, val);
|
||||
}
|
||||
}
|
||||
|
||||
static void snd_emu_proc_ptr_reg_write00(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
snd_emu_proc_ptr_reg_write(entry, buffer, 0);
|
||||
}
|
||||
|
||||
static void snd_emu_proc_ptr_reg_write20(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
snd_emu_proc_ptr_reg_write(entry, buffer, 0x20);
|
||||
}
|
||||
|
||||
|
||||
static void snd_emu_proc_ptr_reg_read00a(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
snd_emu_proc_ptr_reg_read(entry, buffer, 0, 0, 0x40, 64);
|
||||
}
|
||||
|
||||
static void snd_emu_proc_ptr_reg_read00b(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
snd_emu_proc_ptr_reg_read(entry, buffer, 0, 0x40, 0x40, 64);
|
||||
}
|
||||
|
||||
static void snd_emu_proc_ptr_reg_read20a(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
snd_emu_proc_ptr_reg_read(entry, buffer, 0x20, 0, 0x40, 4);
|
||||
}
|
||||
|
||||
static void snd_emu_proc_ptr_reg_read20b(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
snd_emu_proc_ptr_reg_read(entry, buffer, 0x20, 0x40, 0x40, 4);
|
||||
}
|
||||
|
||||
static void snd_emu_proc_ptr_reg_read20c(struct snd_info_entry *entry,
|
||||
struct snd_info_buffer * buffer)
|
||||
{
|
||||
snd_emu_proc_ptr_reg_read(entry, buffer, 0x20, 0x80, 0x20, 4);
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct snd_info_entry_ops snd_emu10k1_proc_ops_fx8010 = {
|
||||
.read = snd_emu10k1_fx8010_read,
|
||||
};
|
||||
|
||||
int snd_emu10k1_proc_init(struct snd_emu10k1 *emu)
|
||||
{
|
||||
struct snd_info_entry *entry;
|
||||
#ifdef CONFIG_SND_DEBUG
|
||||
if (emu->card_capabilities->emu_model) {
|
||||
if (! snd_card_proc_new(emu->card, "emu1010_regs", &entry))
|
||||
snd_info_set_text_ops(entry, emu, snd_emu_proc_emu1010_reg_read);
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "io_regs", &entry)) {
|
||||
snd_info_set_text_ops(entry, emu, snd_emu_proc_io_reg_read);
|
||||
entry->c.text.write = snd_emu_proc_io_reg_write;
|
||||
entry->mode |= S_IWUSR;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "ptr_regs00a", &entry)) {
|
||||
snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00a);
|
||||
entry->c.text.write = snd_emu_proc_ptr_reg_write00;
|
||||
entry->mode |= S_IWUSR;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "ptr_regs00b", &entry)) {
|
||||
snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00b);
|
||||
entry->c.text.write = snd_emu_proc_ptr_reg_write00;
|
||||
entry->mode |= S_IWUSR;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "ptr_regs20a", &entry)) {
|
||||
snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20a);
|
||||
entry->c.text.write = snd_emu_proc_ptr_reg_write20;
|
||||
entry->mode |= S_IWUSR;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "ptr_regs20b", &entry)) {
|
||||
snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20b);
|
||||
entry->c.text.write = snd_emu_proc_ptr_reg_write20;
|
||||
entry->mode |= S_IWUSR;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "ptr_regs20c", &entry)) {
|
||||
snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20c);
|
||||
entry->c.text.write = snd_emu_proc_ptr_reg_write20;
|
||||
entry->mode |= S_IWUSR;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (! snd_card_proc_new(emu->card, "emu10k1", &entry))
|
||||
snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_read);
|
||||
|
||||
if (emu->card_capabilities->emu10k2_chip) {
|
||||
if (! snd_card_proc_new(emu->card, "spdif-in", &entry))
|
||||
snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_spdif_read);
|
||||
}
|
||||
if (emu->card_capabilities->ca0151_chip) {
|
||||
if (! snd_card_proc_new(emu->card, "capture-rates", &entry))
|
||||
snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_rates_read);
|
||||
}
|
||||
|
||||
if (! snd_card_proc_new(emu->card, "voices", &entry))
|
||||
snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_voices_read);
|
||||
|
||||
if (! snd_card_proc_new(emu->card, "fx8010_gpr", &entry)) {
|
||||
entry->content = SNDRV_INFO_CONTENT_DATA;
|
||||
entry->private_data = emu;
|
||||
entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
|
||||
entry->size = emu->audigy ? A_TOTAL_SIZE_GPR : TOTAL_SIZE_GPR;
|
||||
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "fx8010_tram_data", &entry)) {
|
||||
entry->content = SNDRV_INFO_CONTENT_DATA;
|
||||
entry->private_data = emu;
|
||||
entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
|
||||
entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_DATA : TOTAL_SIZE_TANKMEM_DATA ;
|
||||
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "fx8010_tram_addr", &entry)) {
|
||||
entry->content = SNDRV_INFO_CONTENT_DATA;
|
||||
entry->private_data = emu;
|
||||
entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
|
||||
entry->size = emu->audigy ? A_TOTAL_SIZE_TANKMEM_ADDR : TOTAL_SIZE_TANKMEM_ADDR ;
|
||||
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "fx8010_code", &entry)) {
|
||||
entry->content = SNDRV_INFO_CONTENT_DATA;
|
||||
entry->private_data = emu;
|
||||
entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
|
||||
entry->size = emu->audigy ? A_TOTAL_SIZE_CODE : TOTAL_SIZE_CODE;
|
||||
entry->c.ops = &snd_emu10k1_proc_ops_fx8010;
|
||||
}
|
||||
if (! snd_card_proc_new(emu->card, "fx8010_acode", &entry)) {
|
||||
entry->content = SNDRV_INFO_CONTENT_TEXT;
|
||||
entry->private_data = emu;
|
||||
entry->mode = S_IFREG | S_IRUGO /*| S_IWUSR*/;
|
||||
entry->c.text.read = snd_emu10k1_proc_acode_read;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PROC_FS */
|
580
sound/pci/emu10k1/io.c
Normal file
580
sound/pci/emu10k1/io.c
Normal file
|
@ -0,0 +1,580 @@
|
|||
/*
|
||||
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
|
||||
* Creative Labs, Inc.
|
||||
* Routines for control of EMU10K1 chips
|
||||
*
|
||||
* BUGS:
|
||||
* --
|
||||
*
|
||||
* TODO:
|
||||
* --
|
||||
*
|
||||
* 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/time.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/emu10k1.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include "p17v.h"
|
||||
|
||||
unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int regptr, val;
|
||||
unsigned int mask;
|
||||
|
||||
mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
|
||||
regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
|
||||
|
||||
if (reg & 0xff000000) {
|
||||
unsigned char size, offset;
|
||||
|
||||
size = (reg >> 24) & 0x3f;
|
||||
offset = (reg >> 16) & 0x1f;
|
||||
mask = ((1 << size) - 1) << offset;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
outl(regptr, emu->port + PTR);
|
||||
val = inl(emu->port + DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
|
||||
return (val & mask) >> offset;
|
||||
} else {
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
outl(regptr, emu->port + PTR);
|
||||
val = inl(emu->port + DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_emu10k1_ptr_read);
|
||||
|
||||
void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
|
||||
{
|
||||
unsigned int regptr;
|
||||
unsigned long flags;
|
||||
unsigned int mask;
|
||||
|
||||
if (snd_BUG_ON(!emu))
|
||||
return;
|
||||
mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
|
||||
regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
|
||||
|
||||
if (reg & 0xff000000) {
|
||||
unsigned char size, offset;
|
||||
|
||||
size = (reg >> 24) & 0x3f;
|
||||
offset = (reg >> 16) & 0x1f;
|
||||
mask = ((1 << size) - 1) << offset;
|
||||
data = (data << offset) & mask;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
outl(regptr, emu->port + PTR);
|
||||
data |= inl(emu->port + DATA) & ~mask;
|
||||
outl(data, emu->port + DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
} else {
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
outl(regptr, emu->port + PTR);
|
||||
outl(data, emu->port + DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_emu10k1_ptr_write);
|
||||
|
||||
unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,
|
||||
unsigned int reg,
|
||||
unsigned int chn)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int regptr, val;
|
||||
|
||||
regptr = (reg << 16) | chn;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
outl(regptr, emu->port + 0x20 + PTR);
|
||||
val = inl(emu->port + 0x20 + DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
return val;
|
||||
}
|
||||
|
||||
void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
|
||||
unsigned int reg,
|
||||
unsigned int chn,
|
||||
unsigned int data)
|
||||
{
|
||||
unsigned int regptr;
|
||||
unsigned long flags;
|
||||
|
||||
regptr = (reg << 16) | chn;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
outl(regptr, emu->port + 0x20 + PTR);
|
||||
outl(data, emu->port + 0x20 + DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
|
||||
unsigned int data)
|
||||
{
|
||||
unsigned int reset, set;
|
||||
unsigned int reg, tmp;
|
||||
int n, result;
|
||||
int err = 0;
|
||||
|
||||
/* This function is not re-entrant, so protect against it. */
|
||||
spin_lock(&emu->spi_lock);
|
||||
if (emu->card_capabilities->ca0108_chip)
|
||||
reg = 0x3c; /* PTR20, reg 0x3c */
|
||||
else {
|
||||
/* For other chip types the SPI register
|
||||
* is currently unknown. */
|
||||
err = 1;
|
||||
goto spi_write_exit;
|
||||
}
|
||||
if (data > 0xffff) {
|
||||
/* Only 16bit values allowed */
|
||||
err = 1;
|
||||
goto spi_write_exit;
|
||||
}
|
||||
|
||||
tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
|
||||
reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
|
||||
set = reset | 0x10000; /* Set xxx1xxxx */
|
||||
snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
|
||||
tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* write post */
|
||||
snd_emu10k1_ptr20_write(emu, reg, 0, set | data);
|
||||
result = 1;
|
||||
/* Wait for status bit to return to 0 */
|
||||
for (n = 0; n < 100; n++) {
|
||||
udelay(10);
|
||||
tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
|
||||
if (!(tmp & 0x10000)) {
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result) {
|
||||
/* Timed out */
|
||||
err = 1;
|
||||
goto spi_write_exit;
|
||||
}
|
||||
snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
|
||||
tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
|
||||
err = 0;
|
||||
spi_write_exit:
|
||||
spin_unlock(&emu->spi_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* The ADC does not support i2c read, so only write is implemented */
|
||||
int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
|
||||
u32 reg,
|
||||
u32 value)
|
||||
{
|
||||
u32 tmp;
|
||||
int timeout = 0;
|
||||
int status;
|
||||
int retry;
|
||||
int err = 0;
|
||||
|
||||
if ((reg > 0x7f) || (value > 0x1ff)) {
|
||||
dev_err(emu->card->dev, "i2c_write: invalid values.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* This function is not re-entrant, so protect against it. */
|
||||
spin_lock(&emu->i2c_lock);
|
||||
|
||||
tmp = reg << 25 | value << 16;
|
||||
|
||||
/* This controls the I2C connected to the WM8775 ADC Codec */
|
||||
snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp);
|
||||
tmp = snd_emu10k1_ptr20_read(emu, P17V_I2C_1, 0); /* write post */
|
||||
|
||||
for (retry = 0; retry < 10; retry++) {
|
||||
/* Send the data to i2c */
|
||||
tmp = 0;
|
||||
tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
|
||||
snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp);
|
||||
|
||||
/* Wait till the transaction ends */
|
||||
while (1) {
|
||||
mdelay(1);
|
||||
status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0);
|
||||
timeout++;
|
||||
if ((status & I2C_A_ADC_START) == 0)
|
||||
break;
|
||||
|
||||
if (timeout > 1000) {
|
||||
dev_warn(emu->card->dev,
|
||||
"emu10k1:I2C:timeout status=0x%x\n",
|
||||
status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
//Read back and see if the transaction is successful
|
||||
if ((status & I2C_A_ADC_ABORT) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (retry == 10) {
|
||||
dev_err(emu->card->dev, "Writing to ADC failed!\n");
|
||||
dev_err(emu->card->dev, "status=0x%x, reg=%d, value=%d\n",
|
||||
status, reg, value);
|
||||
/* dump_stack(); */
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
spin_unlock(&emu->i2c_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (reg > 0x3f)
|
||||
return 1;
|
||||
reg += 0x40; /* 0x40 upwards are registers. */
|
||||
if (value > 0x3f) /* 0 to 0x3f are values */
|
||||
return 1;
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
outl(reg, emu->port + A_IOCFG);
|
||||
udelay(10);
|
||||
outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
|
||||
udelay(10);
|
||||
outl(value, emu->port + A_IOCFG);
|
||||
udelay(10);
|
||||
outl(value | 0x80 , emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value)
|
||||
{
|
||||
unsigned long flags;
|
||||
if (reg > 0x3f)
|
||||
return 1;
|
||||
reg += 0x40; /* 0x40 upwards are registers. */
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
outl(reg, emu->port + A_IOCFG);
|
||||
udelay(10);
|
||||
outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
|
||||
udelay(10);
|
||||
*value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Each Destination has one and only one Source,
|
||||
* but one Source can feed any number of Destinations simultaneously.
|
||||
*/
|
||||
int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, u32 dst, u32 src)
|
||||
{
|
||||
snd_emu1010_fpga_write(emu, 0x00, ((dst >> 8) & 0x3f) );
|
||||
snd_emu1010_fpga_write(emu, 0x01, (dst & 0x3f) );
|
||||
snd_emu1010_fpga_write(emu, 0x02, ((src >> 8) & 0x3f) );
|
||||
snd_emu1010_fpga_write(emu, 0x03, (src & 0x3f) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int enable;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
enable = inl(emu->port + INTE) | intrenb;
|
||||
outl(enable, emu->port + INTE);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int enable;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
enable = inl(emu->port + INTE) & ~intrenb;
|
||||
outl(enable, emu->port + INTE);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int val;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
/* voice interrupt */
|
||||
if (voicenum >= 32) {
|
||||
outl(CLIEH << 16, emu->port + PTR);
|
||||
val = inl(emu->port + DATA);
|
||||
val |= 1 << (voicenum - 32);
|
||||
} else {
|
||||
outl(CLIEL << 16, emu->port + PTR);
|
||||
val = inl(emu->port + DATA);
|
||||
val |= 1 << voicenum;
|
||||
}
|
||||
outl(val, emu->port + DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int val;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
/* voice interrupt */
|
||||
if (voicenum >= 32) {
|
||||
outl(CLIEH << 16, emu->port + PTR);
|
||||
val = inl(emu->port + DATA);
|
||||
val &= ~(1 << (voicenum - 32));
|
||||
} else {
|
||||
outl(CLIEL << 16, emu->port + PTR);
|
||||
val = inl(emu->port + DATA);
|
||||
val &= ~(1 << voicenum);
|
||||
}
|
||||
outl(val, emu->port + DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
/* voice interrupt */
|
||||
if (voicenum >= 32) {
|
||||
outl(CLIPH << 16, emu->port + PTR);
|
||||
voicenum = 1 << (voicenum - 32);
|
||||
} else {
|
||||
outl(CLIPL << 16, emu->port + PTR);
|
||||
voicenum = 1 << voicenum;
|
||||
}
|
||||
outl(voicenum, emu->port + DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int val;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
/* voice interrupt */
|
||||
if (voicenum >= 32) {
|
||||
outl(HLIEH << 16, emu->port + PTR);
|
||||
val = inl(emu->port + DATA);
|
||||
val |= 1 << (voicenum - 32);
|
||||
} else {
|
||||
outl(HLIEL << 16, emu->port + PTR);
|
||||
val = inl(emu->port + DATA);
|
||||
val |= 1 << voicenum;
|
||||
}
|
||||
outl(val, emu->port + DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int val;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
/* voice interrupt */
|
||||
if (voicenum >= 32) {
|
||||
outl(HLIEH << 16, emu->port + PTR);
|
||||
val = inl(emu->port + DATA);
|
||||
val &= ~(1 << (voicenum - 32));
|
||||
} else {
|
||||
outl(HLIEL << 16, emu->port + PTR);
|
||||
val = inl(emu->port + DATA);
|
||||
val &= ~(1 << voicenum);
|
||||
}
|
||||
outl(val, emu->port + DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
/* voice interrupt */
|
||||
if (voicenum >= 32) {
|
||||
outl(HLIPH << 16, emu->port + PTR);
|
||||
voicenum = 1 << (voicenum - 32);
|
||||
} else {
|
||||
outl(HLIPL << 16, emu->port + PTR);
|
||||
voicenum = 1 << voicenum;
|
||||
}
|
||||
outl(voicenum, emu->port + DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int sol;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
/* voice interrupt */
|
||||
if (voicenum >= 32) {
|
||||
outl(SOLEH << 16, emu->port + PTR);
|
||||
sol = inl(emu->port + DATA);
|
||||
sol |= 1 << (voicenum - 32);
|
||||
} else {
|
||||
outl(SOLEL << 16, emu->port + PTR);
|
||||
sol = inl(emu->port + DATA);
|
||||
sol |= 1 << voicenum;
|
||||
}
|
||||
outl(sol, emu->port + DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int sol;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
/* voice interrupt */
|
||||
if (voicenum >= 32) {
|
||||
outl(SOLEH << 16, emu->port + PTR);
|
||||
sol = inl(emu->port + DATA);
|
||||
sol &= ~(1 << (voicenum - 32));
|
||||
} else {
|
||||
outl(SOLEL << 16, emu->port + PTR);
|
||||
sol = inl(emu->port + DATA);
|
||||
sol &= ~(1 << voicenum);
|
||||
}
|
||||
outl(sol, emu->port + DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
|
||||
{
|
||||
volatile unsigned count;
|
||||
unsigned int newtime = 0, curtime;
|
||||
|
||||
curtime = inl(emu->port + WC) >> 6;
|
||||
while (wait-- > 0) {
|
||||
count = 0;
|
||||
while (count++ < 16384) {
|
||||
newtime = inl(emu->port + WC) >> 6;
|
||||
if (newtime != curtime)
|
||||
break;
|
||||
}
|
||||
if (count > 16384)
|
||||
break;
|
||||
curtime = newtime;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
|
||||
{
|
||||
struct snd_emu10k1 *emu = ac97->private_data;
|
||||
unsigned long flags;
|
||||
unsigned short val;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
outb(reg, emu->port + AC97ADDRESS);
|
||||
val = inw(emu->port + AC97DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
return val;
|
||||
}
|
||||
|
||||
void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
|
||||
{
|
||||
struct snd_emu10k1 *emu = ac97->private_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
outb(reg, emu->port + AC97ADDRESS);
|
||||
outw(data, emu->port + AC97DATA);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* convert rate to pitch
|
||||
*/
|
||||
|
||||
unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
|
||||
{
|
||||
static u32 logMagTable[128] = {
|
||||
0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
|
||||
0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
|
||||
0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
|
||||
0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
|
||||
0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
|
||||
0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
|
||||
0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
|
||||
0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
|
||||
0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
|
||||
0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
|
||||
0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
|
||||
0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
|
||||
0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
|
||||
0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
|
||||
0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
|
||||
0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
|
||||
};
|
||||
static char logSlopeTable[128] = {
|
||||
0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
|
||||
0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
|
||||
0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
|
||||
0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
|
||||
0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
|
||||
0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
|
||||
0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
|
||||
0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
|
||||
0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
|
||||
0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
|
||||
0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
|
||||
0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
|
||||
0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
|
||||
0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
|
||||
0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
|
||||
0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
|
||||
};
|
||||
int i;
|
||||
|
||||
if (rate == 0)
|
||||
return 0; /* Bail out if no leading "1" */
|
||||
rate *= 11185; /* Scale 48000 to 0x20002380 */
|
||||
for (i = 31; i > 0; i--) {
|
||||
if (rate & 0x80000000) { /* Detect leading "1" */
|
||||
return (((unsigned int) (i - 15) << 20) +
|
||||
logMagTable[0x7f & (rate >> 24)] +
|
||||
(0x7f & (rate >> 17)) *
|
||||
logSlopeTable[0x7f & (rate >> 24)]);
|
||||
}
|
||||
rate <<= 1;
|
||||
}
|
||||
|
||||
return 0; /* Should never reach this point */
|
||||
}
|
||||
|
213
sound/pci/emu10k1/irq.c
Normal file
213
sound/pci/emu10k1/irq.c
Normal file
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
|
||||
* Creative Labs, Inc.
|
||||
* Routines for IRQ control of EMU10K1 chips
|
||||
*
|
||||
* BUGS:
|
||||
* --
|
||||
*
|
||||
* TODO:
|
||||
* --
|
||||
*
|
||||
* 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/time.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/emu10k1.h>
|
||||
|
||||
irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct snd_emu10k1 *emu = dev_id;
|
||||
unsigned int status, status2, orig_status, orig_status2;
|
||||
int handled = 0;
|
||||
int timeout = 0;
|
||||
|
||||
while (((status = inl(emu->port + IPR)) != 0) && (timeout < 1000)) {
|
||||
timeout++;
|
||||
orig_status = status;
|
||||
handled = 1;
|
||||
if ((status & 0xffffffff) == 0xffffffff) {
|
||||
dev_info(emu->card->dev,
|
||||
"Suspected sound card removal\n");
|
||||
break;
|
||||
}
|
||||
if (status & IPR_PCIERROR) {
|
||||
dev_err(emu->card->dev, "interrupt: PCI error\n");
|
||||
snd_emu10k1_intr_disable(emu, INTE_PCIERRORENABLE);
|
||||
status &= ~IPR_PCIERROR;
|
||||
}
|
||||
if (status & (IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE)) {
|
||||
if (emu->hwvol_interrupt)
|
||||
emu->hwvol_interrupt(emu, status);
|
||||
else
|
||||
snd_emu10k1_intr_disable(emu, INTE_VOLINCRENABLE|INTE_VOLDECRENABLE|INTE_MUTEENABLE);
|
||||
status &= ~(IPR_VOLINCR|IPR_VOLDECR|IPR_MUTE);
|
||||
}
|
||||
if (status & IPR_CHANNELLOOP) {
|
||||
int voice;
|
||||
int voice_max = status & IPR_CHANNELNUMBERMASK;
|
||||
u32 val;
|
||||
struct snd_emu10k1_voice *pvoice = emu->voices;
|
||||
|
||||
val = snd_emu10k1_ptr_read(emu, CLIPL, 0);
|
||||
for (voice = 0; voice <= voice_max; voice++) {
|
||||
if (voice == 0x20)
|
||||
val = snd_emu10k1_ptr_read(emu, CLIPH, 0);
|
||||
if (val & 1) {
|
||||
if (pvoice->use && pvoice->interrupt != NULL) {
|
||||
pvoice->interrupt(emu, pvoice);
|
||||
snd_emu10k1_voice_intr_ack(emu, voice);
|
||||
} else {
|
||||
snd_emu10k1_voice_intr_disable(emu, voice);
|
||||
}
|
||||
}
|
||||
val >>= 1;
|
||||
pvoice++;
|
||||
}
|
||||
val = snd_emu10k1_ptr_read(emu, HLIPL, 0);
|
||||
for (voice = 0; voice <= voice_max; voice++) {
|
||||
if (voice == 0x20)
|
||||
val = snd_emu10k1_ptr_read(emu, HLIPH, 0);
|
||||
if (val & 1) {
|
||||
if (pvoice->use && pvoice->interrupt != NULL) {
|
||||
pvoice->interrupt(emu, pvoice);
|
||||
snd_emu10k1_voice_half_loop_intr_ack(emu, voice);
|
||||
} else {
|
||||
snd_emu10k1_voice_half_loop_intr_disable(emu, voice);
|
||||
}
|
||||
}
|
||||
val >>= 1;
|
||||
pvoice++;
|
||||
}
|
||||
status &= ~IPR_CHANNELLOOP;
|
||||
}
|
||||
status &= ~IPR_CHANNELNUMBERMASK;
|
||||
if (status & (IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL)) {
|
||||
if (emu->capture_interrupt)
|
||||
emu->capture_interrupt(emu, status);
|
||||
else
|
||||
snd_emu10k1_intr_disable(emu, INTE_ADCBUFENABLE);
|
||||
status &= ~(IPR_ADCBUFFULL|IPR_ADCBUFHALFFULL);
|
||||
}
|
||||
if (status & (IPR_MICBUFFULL|IPR_MICBUFHALFFULL)) {
|
||||
if (emu->capture_mic_interrupt)
|
||||
emu->capture_mic_interrupt(emu, status);
|
||||
else
|
||||
snd_emu10k1_intr_disable(emu, INTE_MICBUFENABLE);
|
||||
status &= ~(IPR_MICBUFFULL|IPR_MICBUFHALFFULL);
|
||||
}
|
||||
if (status & (IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL)) {
|
||||
if (emu->capture_efx_interrupt)
|
||||
emu->capture_efx_interrupt(emu, status);
|
||||
else
|
||||
snd_emu10k1_intr_disable(emu, INTE_EFXBUFENABLE);
|
||||
status &= ~(IPR_EFXBUFFULL|IPR_EFXBUFHALFFULL);
|
||||
}
|
||||
if (status & (IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY)) {
|
||||
if (emu->midi.interrupt)
|
||||
emu->midi.interrupt(emu, status);
|
||||
else
|
||||
snd_emu10k1_intr_disable(emu, INTE_MIDITXENABLE|INTE_MIDIRXENABLE);
|
||||
status &= ~(IPR_MIDITRANSBUFEMPTY|IPR_MIDIRECVBUFEMPTY);
|
||||
}
|
||||
if (status & (IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2)) {
|
||||
if (emu->midi2.interrupt)
|
||||
emu->midi2.interrupt(emu, status);
|
||||
else
|
||||
snd_emu10k1_intr_disable(emu, INTE_A_MIDITXENABLE2|INTE_A_MIDIRXENABLE2);
|
||||
status &= ~(IPR_A_MIDITRANSBUFEMPTY2|IPR_A_MIDIRECVBUFEMPTY2);
|
||||
}
|
||||
if (status & IPR_INTERVALTIMER) {
|
||||
if (emu->timer)
|
||||
snd_timer_interrupt(emu->timer, emu->timer->sticks);
|
||||
else
|
||||
snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
|
||||
status &= ~IPR_INTERVALTIMER;
|
||||
}
|
||||
if (status & (IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE)) {
|
||||
if (emu->spdif_interrupt)
|
||||
emu->spdif_interrupt(emu, status);
|
||||
else
|
||||
snd_emu10k1_intr_disable(emu, INTE_GPSPDIFENABLE|INTE_CDSPDIFENABLE);
|
||||
status &= ~(IPR_GPSPDIFSTATUSCHANGE|IPR_CDROMSTATUSCHANGE);
|
||||
}
|
||||
if (status & IPR_FXDSP) {
|
||||
if (emu->dsp_interrupt)
|
||||
emu->dsp_interrupt(emu);
|
||||
else
|
||||
snd_emu10k1_intr_disable(emu, INTE_FXDSPENABLE);
|
||||
status &= ~IPR_FXDSP;
|
||||
}
|
||||
if (status & IPR_P16V) {
|
||||
while ((status2 = inl(emu->port + IPR2)) != 0) {
|
||||
u32 mask = INTE2_PLAYBACK_CH_0_LOOP; /* Full Loop */
|
||||
struct snd_emu10k1_voice *pvoice = &(emu->p16v_voices[0]);
|
||||
struct snd_emu10k1_voice *cvoice = &(emu->p16v_capture_voice);
|
||||
|
||||
/* dev_dbg(emu->card->dev, "status2=0x%x\n", status2); */
|
||||
orig_status2 = status2;
|
||||
if(status2 & mask) {
|
||||
if(pvoice->use) {
|
||||
snd_pcm_period_elapsed(pvoice->epcm->substream);
|
||||
} else {
|
||||
dev_err(emu->card->dev,
|
||||
"p16v: status: 0x%08x, mask=0x%08x, pvoice=%p, use=%d\n",
|
||||
status2, mask, pvoice,
|
||||
pvoice->use);
|
||||
}
|
||||
}
|
||||
if(status2 & 0x110000) {
|
||||
/* dev_info(emu->card->dev, "capture int found\n"); */
|
||||
if(cvoice->use) {
|
||||
/* dev_info(emu->card->dev, "capture period_elapsed\n"); */
|
||||
snd_pcm_period_elapsed(cvoice->epcm->substream);
|
||||
}
|
||||
}
|
||||
outl(orig_status2, emu->port + IPR2); /* ack all */
|
||||
}
|
||||
status &= ~IPR_P16V;
|
||||
}
|
||||
|
||||
if (status) {
|
||||
unsigned int bits;
|
||||
dev_err(emu->card->dev,
|
||||
"unhandled interrupt: 0x%08x\n", status);
|
||||
//make sure any interrupts we don't handle are disabled:
|
||||
bits = INTE_FXDSPENABLE |
|
||||
INTE_PCIERRORENABLE |
|
||||
INTE_VOLINCRENABLE |
|
||||
INTE_VOLDECRENABLE |
|
||||
INTE_MUTEENABLE |
|
||||
INTE_MICBUFENABLE |
|
||||
INTE_ADCBUFENABLE |
|
||||
INTE_EFXBUFENABLE |
|
||||
INTE_GPSPDIFENABLE |
|
||||
INTE_CDSPDIFENABLE |
|
||||
INTE_INTERVALTIMERENB |
|
||||
INTE_MIDITXENABLE |
|
||||
INTE_MIDIRXENABLE;
|
||||
if (emu->audigy)
|
||||
bits |= INTE_A_MIDITXENABLE2 | INTE_A_MIDIRXENABLE2;
|
||||
snd_emu10k1_intr_disable(emu, bits);
|
||||
}
|
||||
outl(orig_status, emu->port + IPR); /* ack all */
|
||||
}
|
||||
if (timeout == 1000)
|
||||
dev_info(emu->card->dev, "emu10k1 irq routine failure\n");
|
||||
|
||||
return IRQ_RETVAL(handled);
|
||||
}
|
581
sound/pci/emu10k1/memory.c
Normal file
581
sound/pci/emu10k1/memory.c
Normal file
|
@ -0,0 +1,581 @@
|
|||
/*
|
||||
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
|
||||
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
|
||||
*
|
||||
* EMU10K1 memory page allocation (PTB area)
|
||||
*
|
||||
*
|
||||
* 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/pci.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/export.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/emu10k1.h>
|
||||
|
||||
/* page arguments of these two macros are Emu page (4096 bytes), not like
|
||||
* aligned pages in others
|
||||
*/
|
||||
#define __set_ptb_entry(emu,page,addr) \
|
||||
(((u32 *)(emu)->ptb_pages.area)[page] = cpu_to_le32(((addr) << (emu->address_mode)) | (page)))
|
||||
|
||||
#define UNIT_PAGES (PAGE_SIZE / EMUPAGESIZE)
|
||||
#define MAX_ALIGN_PAGES0 (MAXPAGES0 / UNIT_PAGES)
|
||||
#define MAX_ALIGN_PAGES1 (MAXPAGES1 / UNIT_PAGES)
|
||||
/* get aligned page from offset address */
|
||||
#define get_aligned_page(offset) ((offset) >> PAGE_SHIFT)
|
||||
/* get offset address from aligned page */
|
||||
#define aligned_page_offset(page) ((page) << PAGE_SHIFT)
|
||||
|
||||
#if PAGE_SIZE == 4096
|
||||
/* page size == EMUPAGESIZE */
|
||||
/* fill PTB entrie(s) corresponding to page with addr */
|
||||
#define set_ptb_entry(emu,page,addr) __set_ptb_entry(emu,page,addr)
|
||||
/* fill PTB entrie(s) corresponding to page with silence pointer */
|
||||
#define set_silent_ptb(emu,page) __set_ptb_entry(emu,page,emu->silent_page.addr)
|
||||
#else
|
||||
/* fill PTB entries -- we need to fill UNIT_PAGES entries */
|
||||
static inline void set_ptb_entry(struct snd_emu10k1 *emu, int page, dma_addr_t addr)
|
||||
{
|
||||
int i;
|
||||
page *= UNIT_PAGES;
|
||||
for (i = 0; i < UNIT_PAGES; i++, page++) {
|
||||
__set_ptb_entry(emu, page, addr);
|
||||
addr += EMUPAGESIZE;
|
||||
}
|
||||
}
|
||||
static inline void set_silent_ptb(struct snd_emu10k1 *emu, int page)
|
||||
{
|
||||
int i;
|
||||
page *= UNIT_PAGES;
|
||||
for (i = 0; i < UNIT_PAGES; i++, page++)
|
||||
/* do not increment ptr */
|
||||
__set_ptb_entry(emu, page, emu->silent_page.addr);
|
||||
}
|
||||
#endif /* PAGE_SIZE */
|
||||
|
||||
|
||||
/*
|
||||
*/
|
||||
static int synth_alloc_pages(struct snd_emu10k1 *hw, struct snd_emu10k1_memblk *blk);
|
||||
static int synth_free_pages(struct snd_emu10k1 *hw, struct snd_emu10k1_memblk *blk);
|
||||
|
||||
#define get_emu10k1_memblk(l,member) list_entry(l, struct snd_emu10k1_memblk, member)
|
||||
|
||||
|
||||
/* initialize emu10k1 part */
|
||||
static void emu10k1_memblk_init(struct snd_emu10k1_memblk *blk)
|
||||
{
|
||||
blk->mapped_page = -1;
|
||||
INIT_LIST_HEAD(&blk->mapped_link);
|
||||
INIT_LIST_HEAD(&blk->mapped_order_link);
|
||||
blk->map_locked = 0;
|
||||
|
||||
blk->first_page = get_aligned_page(blk->mem.offset);
|
||||
blk->last_page = get_aligned_page(blk->mem.offset + blk->mem.size - 1);
|
||||
blk->pages = blk->last_page - blk->first_page + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* search empty region on PTB with the given size
|
||||
*
|
||||
* if an empty region is found, return the page and store the next mapped block
|
||||
* in nextp
|
||||
* if not found, return a negative error code.
|
||||
*/
|
||||
static int search_empty_map_area(struct snd_emu10k1 *emu, int npages, struct list_head **nextp)
|
||||
{
|
||||
int page = 0, found_page = -ENOMEM;
|
||||
int max_size = npages;
|
||||
int size;
|
||||
struct list_head *candidate = &emu->mapped_link_head;
|
||||
struct list_head *pos;
|
||||
|
||||
list_for_each (pos, &emu->mapped_link_head) {
|
||||
struct snd_emu10k1_memblk *blk = get_emu10k1_memblk(pos, mapped_link);
|
||||
if (blk->mapped_page < 0)
|
||||
continue;
|
||||
size = blk->mapped_page - page;
|
||||
if (size == npages) {
|
||||
*nextp = pos;
|
||||
return page;
|
||||
}
|
||||
else if (size > max_size) {
|
||||
/* we look for the maximum empty hole */
|
||||
max_size = size;
|
||||
candidate = pos;
|
||||
found_page = page;
|
||||
}
|
||||
page = blk->mapped_page + blk->pages;
|
||||
}
|
||||
size = (emu->address_mode ? MAX_ALIGN_PAGES1 : MAX_ALIGN_PAGES0) - page;
|
||||
if (size >= max_size) {
|
||||
*nextp = pos;
|
||||
return page;
|
||||
}
|
||||
*nextp = candidate;
|
||||
return found_page;
|
||||
}
|
||||
|
||||
/*
|
||||
* map a memory block onto emu10k1's PTB
|
||||
*
|
||||
* call with memblk_lock held
|
||||
*/
|
||||
static int map_memblk(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
|
||||
{
|
||||
int page, pg;
|
||||
struct list_head *next;
|
||||
|
||||
page = search_empty_map_area(emu, blk->pages, &next);
|
||||
if (page < 0) /* not found */
|
||||
return page;
|
||||
/* insert this block in the proper position of mapped list */
|
||||
list_add_tail(&blk->mapped_link, next);
|
||||
/* append this as a newest block in order list */
|
||||
list_add_tail(&blk->mapped_order_link, &emu->mapped_order_link_head);
|
||||
blk->mapped_page = page;
|
||||
/* fill PTB */
|
||||
for (pg = blk->first_page; pg <= blk->last_page; pg++) {
|
||||
set_ptb_entry(emu, page, emu->page_addr_table[pg]);
|
||||
page++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* unmap the block
|
||||
* return the size of resultant empty pages
|
||||
*
|
||||
* call with memblk_lock held
|
||||
*/
|
||||
static int unmap_memblk(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
|
||||
{
|
||||
int start_page, end_page, mpage, pg;
|
||||
struct list_head *p;
|
||||
struct snd_emu10k1_memblk *q;
|
||||
|
||||
/* calculate the expected size of empty region */
|
||||
if ((p = blk->mapped_link.prev) != &emu->mapped_link_head) {
|
||||
q = get_emu10k1_memblk(p, mapped_link);
|
||||
start_page = q->mapped_page + q->pages;
|
||||
} else
|
||||
start_page = 0;
|
||||
if ((p = blk->mapped_link.next) != &emu->mapped_link_head) {
|
||||
q = get_emu10k1_memblk(p, mapped_link);
|
||||
end_page = q->mapped_page;
|
||||
} else
|
||||
end_page = (emu->address_mode ? MAX_ALIGN_PAGES1 : MAX_ALIGN_PAGES0);
|
||||
|
||||
/* remove links */
|
||||
list_del(&blk->mapped_link);
|
||||
list_del(&blk->mapped_order_link);
|
||||
/* clear PTB */
|
||||
mpage = blk->mapped_page;
|
||||
for (pg = blk->first_page; pg <= blk->last_page; pg++) {
|
||||
set_silent_ptb(emu, mpage);
|
||||
mpage++;
|
||||
}
|
||||
blk->mapped_page = -1;
|
||||
return end_page - start_page; /* return the new empty size */
|
||||
}
|
||||
|
||||
/*
|
||||
* search empty pages with the given size, and create a memory block
|
||||
*
|
||||
* unlike synth_alloc the memory block is aligned to the page start
|
||||
*/
|
||||
static struct snd_emu10k1_memblk *
|
||||
search_empty(struct snd_emu10k1 *emu, int size)
|
||||
{
|
||||
struct list_head *p;
|
||||
struct snd_emu10k1_memblk *blk;
|
||||
int page, psize;
|
||||
|
||||
psize = get_aligned_page(size + PAGE_SIZE -1);
|
||||
page = 0;
|
||||
list_for_each(p, &emu->memhdr->block) {
|
||||
blk = get_emu10k1_memblk(p, mem.list);
|
||||
if (page + psize <= blk->first_page)
|
||||
goto __found_pages;
|
||||
page = blk->last_page + 1;
|
||||
}
|
||||
if (page + psize > emu->max_cache_pages)
|
||||
return NULL;
|
||||
|
||||
__found_pages:
|
||||
/* create a new memory block */
|
||||
blk = (struct snd_emu10k1_memblk *)__snd_util_memblk_new(emu->memhdr, psize << PAGE_SHIFT, p->prev);
|
||||
if (blk == NULL)
|
||||
return NULL;
|
||||
blk->mem.offset = aligned_page_offset(page); /* set aligned offset */
|
||||
emu10k1_memblk_init(blk);
|
||||
return blk;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* check if the given pointer is valid for pages
|
||||
*/
|
||||
static int is_valid_page(struct snd_emu10k1 *emu, dma_addr_t addr)
|
||||
{
|
||||
if (addr & ~emu->dma_mask) {
|
||||
dev_err(emu->card->dev,
|
||||
"max memory size is 0x%lx (addr = 0x%lx)!!\n",
|
||||
emu->dma_mask, (unsigned long)addr);
|
||||
return 0;
|
||||
}
|
||||
if (addr & (EMUPAGESIZE-1)) {
|
||||
dev_err(emu->card->dev, "page is not aligned\n");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* map the given memory block on PTB.
|
||||
* if the block is already mapped, update the link order.
|
||||
* if no empty pages are found, tries to release unused memory blocks
|
||||
* and retry the mapping.
|
||||
*/
|
||||
int snd_emu10k1_memblk_map(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
|
||||
{
|
||||
int err;
|
||||
int size;
|
||||
struct list_head *p, *nextp;
|
||||
struct snd_emu10k1_memblk *deleted;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&emu->memblk_lock, flags);
|
||||
if (blk->mapped_page >= 0) {
|
||||
/* update order link */
|
||||
list_move_tail(&blk->mapped_order_link,
|
||||
&emu->mapped_order_link_head);
|
||||
spin_unlock_irqrestore(&emu->memblk_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
if ((err = map_memblk(emu, blk)) < 0) {
|
||||
/* no enough page - try to unmap some blocks */
|
||||
/* starting from the oldest block */
|
||||
p = emu->mapped_order_link_head.next;
|
||||
for (; p != &emu->mapped_order_link_head; p = nextp) {
|
||||
nextp = p->next;
|
||||
deleted = get_emu10k1_memblk(p, mapped_order_link);
|
||||
if (deleted->map_locked)
|
||||
continue;
|
||||
size = unmap_memblk(emu, deleted);
|
||||
if (size >= blk->pages) {
|
||||
/* ok the empty region is enough large */
|
||||
err = map_memblk(emu, blk);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&emu->memblk_lock, flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_emu10k1_memblk_map);
|
||||
|
||||
/*
|
||||
* page allocation for DMA
|
||||
*/
|
||||
struct snd_util_memblk *
|
||||
snd_emu10k1_alloc_pages(struct snd_emu10k1 *emu, struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_util_memhdr *hdr;
|
||||
struct snd_emu10k1_memblk *blk;
|
||||
int page, err, idx;
|
||||
|
||||
if (snd_BUG_ON(!emu))
|
||||
return NULL;
|
||||
if (snd_BUG_ON(runtime->dma_bytes <= 0 ||
|
||||
runtime->dma_bytes >= (emu->address_mode ? MAXPAGES1 : MAXPAGES0) * EMUPAGESIZE))
|
||||
return NULL;
|
||||
hdr = emu->memhdr;
|
||||
if (snd_BUG_ON(!hdr))
|
||||
return NULL;
|
||||
|
||||
idx = runtime->period_size >= runtime->buffer_size ?
|
||||
(emu->delay_pcm_irq * 2) : 0;
|
||||
mutex_lock(&hdr->block_mutex);
|
||||
blk = search_empty(emu, runtime->dma_bytes + idx);
|
||||
if (blk == NULL) {
|
||||
mutex_unlock(&hdr->block_mutex);
|
||||
return NULL;
|
||||
}
|
||||
/* fill buffer addresses but pointers are not stored so that
|
||||
* snd_free_pci_page() is not called in in synth_free()
|
||||
*/
|
||||
idx = 0;
|
||||
for (page = blk->first_page; page <= blk->last_page; page++, idx++) {
|
||||
unsigned long ofs = idx << PAGE_SHIFT;
|
||||
dma_addr_t addr;
|
||||
if (ofs >= runtime->dma_bytes)
|
||||
addr = emu->silent_page.addr;
|
||||
else
|
||||
addr = snd_pcm_sgbuf_get_addr(substream, ofs);
|
||||
if (! is_valid_page(emu, addr)) {
|
||||
dev_err(emu->card->dev,
|
||||
"emu: failure page = %d\n", idx);
|
||||
mutex_unlock(&hdr->block_mutex);
|
||||
return NULL;
|
||||
}
|
||||
emu->page_addr_table[page] = addr;
|
||||
emu->page_ptr_table[page] = NULL;
|
||||
}
|
||||
|
||||
/* set PTB entries */
|
||||
blk->map_locked = 1; /* do not unmap this block! */
|
||||
err = snd_emu10k1_memblk_map(emu, blk);
|
||||
if (err < 0) {
|
||||
__snd_util_mem_free(hdr, (struct snd_util_memblk *)blk);
|
||||
mutex_unlock(&hdr->block_mutex);
|
||||
return NULL;
|
||||
}
|
||||
mutex_unlock(&hdr->block_mutex);
|
||||
return (struct snd_util_memblk *)blk;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* release DMA buffer from page table
|
||||
*/
|
||||
int snd_emu10k1_free_pages(struct snd_emu10k1 *emu, struct snd_util_memblk *blk)
|
||||
{
|
||||
if (snd_BUG_ON(!emu || !blk))
|
||||
return -EINVAL;
|
||||
return snd_emu10k1_synth_free(emu, blk);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* memory allocation using multiple pages (for synth)
|
||||
* Unlike the DMA allocation above, non-contiguous pages are assined.
|
||||
*/
|
||||
|
||||
/*
|
||||
* allocate a synth sample area
|
||||
*/
|
||||
struct snd_util_memblk *
|
||||
snd_emu10k1_synth_alloc(struct snd_emu10k1 *hw, unsigned int size)
|
||||
{
|
||||
struct snd_emu10k1_memblk *blk;
|
||||
struct snd_util_memhdr *hdr = hw->memhdr;
|
||||
|
||||
mutex_lock(&hdr->block_mutex);
|
||||
blk = (struct snd_emu10k1_memblk *)__snd_util_mem_alloc(hdr, size);
|
||||
if (blk == NULL) {
|
||||
mutex_unlock(&hdr->block_mutex);
|
||||
return NULL;
|
||||
}
|
||||
if (synth_alloc_pages(hw, blk)) {
|
||||
__snd_util_mem_free(hdr, (struct snd_util_memblk *)blk);
|
||||
mutex_unlock(&hdr->block_mutex);
|
||||
return NULL;
|
||||
}
|
||||
snd_emu10k1_memblk_map(hw, blk);
|
||||
mutex_unlock(&hdr->block_mutex);
|
||||
return (struct snd_util_memblk *)blk;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_emu10k1_synth_alloc);
|
||||
|
||||
/*
|
||||
* free a synth sample area
|
||||
*/
|
||||
int
|
||||
snd_emu10k1_synth_free(struct snd_emu10k1 *emu, struct snd_util_memblk *memblk)
|
||||
{
|
||||
struct snd_util_memhdr *hdr = emu->memhdr;
|
||||
struct snd_emu10k1_memblk *blk = (struct snd_emu10k1_memblk *)memblk;
|
||||
unsigned long flags;
|
||||
|
||||
mutex_lock(&hdr->block_mutex);
|
||||
spin_lock_irqsave(&emu->memblk_lock, flags);
|
||||
if (blk->mapped_page >= 0)
|
||||
unmap_memblk(emu, blk);
|
||||
spin_unlock_irqrestore(&emu->memblk_lock, flags);
|
||||
synth_free_pages(emu, blk);
|
||||
__snd_util_mem_free(hdr, memblk);
|
||||
mutex_unlock(&hdr->block_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_emu10k1_synth_free);
|
||||
|
||||
/* check new allocation range */
|
||||
static void get_single_page_range(struct snd_util_memhdr *hdr,
|
||||
struct snd_emu10k1_memblk *blk,
|
||||
int *first_page_ret, int *last_page_ret)
|
||||
{
|
||||
struct list_head *p;
|
||||
struct snd_emu10k1_memblk *q;
|
||||
int first_page, last_page;
|
||||
first_page = blk->first_page;
|
||||
if ((p = blk->mem.list.prev) != &hdr->block) {
|
||||
q = get_emu10k1_memblk(p, mem.list);
|
||||
if (q->last_page == first_page)
|
||||
first_page++; /* first page was already allocated */
|
||||
}
|
||||
last_page = blk->last_page;
|
||||
if ((p = blk->mem.list.next) != &hdr->block) {
|
||||
q = get_emu10k1_memblk(p, mem.list);
|
||||
if (q->first_page == last_page)
|
||||
last_page--; /* last page was already allocated */
|
||||
}
|
||||
*first_page_ret = first_page;
|
||||
*last_page_ret = last_page;
|
||||
}
|
||||
|
||||
/* release allocated pages */
|
||||
static void __synth_free_pages(struct snd_emu10k1 *emu, int first_page,
|
||||
int last_page)
|
||||
{
|
||||
int page;
|
||||
|
||||
for (page = first_page; page <= last_page; page++) {
|
||||
free_page((unsigned long)emu->page_ptr_table[page]);
|
||||
emu->page_addr_table[page] = 0;
|
||||
emu->page_ptr_table[page] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate kernel pages
|
||||
*/
|
||||
static int synth_alloc_pages(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
|
||||
{
|
||||
int page, first_page, last_page;
|
||||
|
||||
emu10k1_memblk_init(blk);
|
||||
get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
|
||||
/* allocate kernel pages */
|
||||
for (page = first_page; page <= last_page; page++) {
|
||||
/* first try to allocate from <4GB zone */
|
||||
struct page *p = alloc_page(GFP_KERNEL | GFP_DMA32 |
|
||||
__GFP_NOWARN);
|
||||
if (!p || (page_to_pfn(p) & ~(emu->dma_mask >> PAGE_SHIFT))) {
|
||||
if (p)
|
||||
__free_page(p);
|
||||
/* try to allocate from <16MB zone */
|
||||
p = alloc_page(GFP_ATOMIC | GFP_DMA |
|
||||
__GFP_NORETRY | /* no OOM-killer */
|
||||
__GFP_NOWARN);
|
||||
}
|
||||
if (!p) {
|
||||
__synth_free_pages(emu, first_page, page - 1);
|
||||
return -ENOMEM;
|
||||
}
|
||||
emu->page_addr_table[page] = page_to_phys(p);
|
||||
emu->page_ptr_table[page] = page_address(p);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* free pages
|
||||
*/
|
||||
static int synth_free_pages(struct snd_emu10k1 *emu, struct snd_emu10k1_memblk *blk)
|
||||
{
|
||||
int first_page, last_page;
|
||||
|
||||
get_single_page_range(emu->memhdr, blk, &first_page, &last_page);
|
||||
__synth_free_pages(emu, first_page, last_page);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* calculate buffer pointer from offset address */
|
||||
static inline void *offset_ptr(struct snd_emu10k1 *emu, int page, int offset)
|
||||
{
|
||||
char *ptr;
|
||||
if (snd_BUG_ON(page < 0 || page >= emu->max_cache_pages))
|
||||
return NULL;
|
||||
ptr = emu->page_ptr_table[page];
|
||||
if (! ptr) {
|
||||
dev_err(emu->card->dev,
|
||||
"access to NULL ptr: page = %d\n", page);
|
||||
return NULL;
|
||||
}
|
||||
ptr += offset & (PAGE_SIZE - 1);
|
||||
return (void*)ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* bzero(blk + offset, size)
|
||||
*/
|
||||
int snd_emu10k1_synth_bzero(struct snd_emu10k1 *emu, struct snd_util_memblk *blk,
|
||||
int offset, int size)
|
||||
{
|
||||
int page, nextofs, end_offset, temp, temp1;
|
||||
void *ptr;
|
||||
struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk;
|
||||
|
||||
offset += blk->offset & (PAGE_SIZE - 1);
|
||||
end_offset = offset + size;
|
||||
page = get_aligned_page(offset);
|
||||
do {
|
||||
nextofs = aligned_page_offset(page + 1);
|
||||
temp = nextofs - offset;
|
||||
temp1 = end_offset - offset;
|
||||
if (temp1 < temp)
|
||||
temp = temp1;
|
||||
ptr = offset_ptr(emu, page + p->first_page, offset);
|
||||
if (ptr)
|
||||
memset(ptr, 0, temp);
|
||||
offset = nextofs;
|
||||
page++;
|
||||
} while (offset < end_offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_emu10k1_synth_bzero);
|
||||
|
||||
/*
|
||||
* copy_from_user(blk + offset, data, size)
|
||||
*/
|
||||
int snd_emu10k1_synth_copy_from_user(struct snd_emu10k1 *emu, struct snd_util_memblk *blk,
|
||||
int offset, const char __user *data, int size)
|
||||
{
|
||||
int page, nextofs, end_offset, temp, temp1;
|
||||
void *ptr;
|
||||
struct snd_emu10k1_memblk *p = (struct snd_emu10k1_memblk *)blk;
|
||||
|
||||
offset += blk->offset & (PAGE_SIZE - 1);
|
||||
end_offset = offset + size;
|
||||
page = get_aligned_page(offset);
|
||||
do {
|
||||
nextofs = aligned_page_offset(page + 1);
|
||||
temp = nextofs - offset;
|
||||
temp1 = end_offset - offset;
|
||||
if (temp1 < temp)
|
||||
temp = temp1;
|
||||
ptr = offset_ptr(emu, page + p->first_page, offset);
|
||||
if (ptr && copy_from_user(ptr, data, temp))
|
||||
return -EFAULT;
|
||||
offset = nextofs;
|
||||
data += temp;
|
||||
page++;
|
||||
} while (offset < end_offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_emu10k1_synth_copy_from_user);
|
937
sound/pci/emu10k1/p16v.c
Normal file
937
sound/pci/emu10k1/p16v.c
Normal file
|
@ -0,0 +1,937 @@
|
|||
/*
|
||||
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
|
||||
* Driver p16v chips
|
||||
* Version: 0.25
|
||||
*
|
||||
* FEATURES currently supported:
|
||||
* Output fixed at S32_LE, 2 channel to hw:0,0
|
||||
* Rates: 44.1, 48, 96, 192.
|
||||
*
|
||||
* Changelog:
|
||||
* 0.8
|
||||
* Use separate card based buffer for periods table.
|
||||
* 0.9
|
||||
* Use 2 channel output streams instead of 8 channel.
|
||||
* (8 channel output streams might be good for ASIO type output)
|
||||
* Corrected speaker output, so Front -> Front etc.
|
||||
* 0.10
|
||||
* Fixed missed interrupts.
|
||||
* 0.11
|
||||
* Add Sound card model number and names.
|
||||
* Add Analog volume controls.
|
||||
* 0.12
|
||||
* Corrected playback interrupts. Now interrupt per period, instead of half period.
|
||||
* 0.13
|
||||
* Use single trigger for multichannel.
|
||||
* 0.14
|
||||
* Mic capture now works at fixed: S32_LE, 96000Hz, Stereo.
|
||||
* 0.15
|
||||
* Force buffer_size / period_size == INTEGER.
|
||||
* 0.16
|
||||
* Update p16v.c to work with changed alsa api.
|
||||
* 0.17
|
||||
* Update p16v.c to work with changed alsa api. Removed boot_devs.
|
||||
* 0.18
|
||||
* Merging with snd-emu10k1 driver.
|
||||
* 0.19
|
||||
* One stereo channel at 24bit now works.
|
||||
* 0.20
|
||||
* Added better register defines.
|
||||
* 0.21
|
||||
* Integrated with snd-emu10k1 driver.
|
||||
* 0.22
|
||||
* Removed #if 0 ... #endif
|
||||
* 0.23
|
||||
* Implement different capture rates.
|
||||
* 0.24
|
||||
* Implement different capture source channels.
|
||||
* e.g. When HD Capture source is set to SPDIF,
|
||||
* setting HD Capture channel to 0 captures from CDROM digital input.
|
||||
* setting HD Capture channel to 1 captures from SPDIF in.
|
||||
* 0.25
|
||||
* Include capture buffer sizes.
|
||||
*
|
||||
* BUGS:
|
||||
* Some stability problems when unloading the snd-p16v kernel module.
|
||||
* --
|
||||
*
|
||||
* TODO:
|
||||
* SPDIF out.
|
||||
* Find out how to change capture sample rates. E.g. To record SPDIF at 48000Hz.
|
||||
* Currently capture fixed at 48000Hz.
|
||||
*
|
||||
* --
|
||||
* GENERAL INFO:
|
||||
* Model: SB0240
|
||||
* P16V Chip: CA0151-DBS
|
||||
* Audigy 2 Chip: CA0102-IAT
|
||||
* AC97 Codec: STAC 9721
|
||||
* ADC: Philips 1361T (Stereo 24bit)
|
||||
* DAC: CS4382-K (8-channel, 24bit, 192Khz)
|
||||
*
|
||||
* This code was initially based on code from ALSA's emu10k1x.c which is:
|
||||
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
|
||||
*
|
||||
* 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/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/ac97_codec.h>
|
||||
#include <sound/info.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <sound/emu10k1.h>
|
||||
#include "p16v.h"
|
||||
|
||||
#define SET_CHANNEL 0 /* Testing channel outputs 0=Front, 1=Center/LFE, 2=Unknown, 3=Rear */
|
||||
#define PCM_FRONT_CHANNEL 0
|
||||
#define PCM_REAR_CHANNEL 1
|
||||
#define PCM_CENTER_LFE_CHANNEL 2
|
||||
#define PCM_SIDE_CHANNEL 3
|
||||
#define CONTROL_FRONT_CHANNEL 0
|
||||
#define CONTROL_REAR_CHANNEL 3
|
||||
#define CONTROL_CENTER_LFE_CHANNEL 1
|
||||
#define CONTROL_SIDE_CHANNEL 2
|
||||
|
||||
/* Card IDs:
|
||||
* Class 0401: 1102:0004 (rev 04) Subsystem: 1102:2002 -> Audigy2 ZS 7.1 Model:SB0350
|
||||
* Class 0401: 1102:0004 (rev 04) Subsystem: 1102:1007 -> Audigy2 6.1 Model:SB0240
|
||||
* Class 0401: 1102:0004 (rev 04) Subsystem: 1102:1002 -> Audigy2 Platinum Model:SB msb0240230009266
|
||||
* Class 0401: 1102:0004 (rev 04) Subsystem: 1102:2007 -> Audigy4 Pro Model:SB0380 M1SB0380472001901E
|
||||
*
|
||||
*/
|
||||
|
||||
/* hardware definition */
|
||||
static struct snd_pcm_hardware snd_p16v_playback_hw = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_RESUME |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_SYNC_START,
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE, /* Only supports 24-bit samples padded to 32 bits. */
|
||||
.rates = SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100,
|
||||
.rate_min = 44100,
|
||||
.rate_max = 192000,
|
||||
.channels_min = 8,
|
||||
.channels_max = 8,
|
||||
.buffer_bytes_max = ((65536 - 64) * 8),
|
||||
.period_bytes_min = 64,
|
||||
.period_bytes_max = (65536 - 64),
|
||||
.periods_min = 2,
|
||||
.periods_max = 8,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hardware snd_p16v_capture_hw = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
||||
SNDRV_PCM_INFO_RESUME |
|
||||
SNDRV_PCM_INFO_MMAP_VALID),
|
||||
.formats = SNDRV_PCM_FMTBIT_S32_LE,
|
||||
.rates = SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100,
|
||||
.rate_min = 44100,
|
||||
.rate_max = 192000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
.buffer_bytes_max = (65536 - 64),
|
||||
.period_bytes_min = 64,
|
||||
.period_bytes_max = (65536 - 128) >> 1, /* size has to be N*64 bytes */
|
||||
.periods_min = 2,
|
||||
.periods_max = 2,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static void snd_p16v_pcm_free_substream(struct snd_pcm_runtime *runtime)
|
||||
{
|
||||
struct snd_emu10k1_pcm *epcm = runtime->private_data;
|
||||
|
||||
if (epcm) {
|
||||
/* dev_dbg(emu->card->dev, "epcm free: %p\n", epcm); */
|
||||
kfree(epcm);
|
||||
}
|
||||
}
|
||||
|
||||
/* open_playback callback */
|
||||
static int snd_p16v_pcm_open_playback_channel(struct snd_pcm_substream *substream, int channel_id)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
|
||||
struct snd_emu10k1_voice *channel = &(emu->p16v_voices[channel_id]);
|
||||
struct snd_emu10k1_pcm *epcm;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int err;
|
||||
|
||||
epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
|
||||
/* dev_dbg(emu->card->dev, "epcm kcalloc: %p\n", epcm); */
|
||||
|
||||
if (epcm == NULL)
|
||||
return -ENOMEM;
|
||||
epcm->emu = emu;
|
||||
epcm->substream = substream;
|
||||
/*
|
||||
dev_dbg(emu->card->dev, "epcm device=%d, channel_id=%d\n",
|
||||
substream->pcm->device, channel_id);
|
||||
*/
|
||||
runtime->private_data = epcm;
|
||||
runtime->private_free = snd_p16v_pcm_free_substream;
|
||||
|
||||
runtime->hw = snd_p16v_playback_hw;
|
||||
|
||||
channel->emu = emu;
|
||||
channel->number = channel_id;
|
||||
|
||||
channel->use=1;
|
||||
#if 0 /* debug */
|
||||
dev_dbg(emu->card->dev,
|
||||
"p16v: open channel_id=%d, channel=%p, use=0x%x\n",
|
||||
channel_id, channel, channel->use);
|
||||
dev_dbg(emu->card->dev, "open:channel_id=%d, chip=%p, channel=%p\n",
|
||||
channel_id, chip, channel);
|
||||
#endif /* debug */
|
||||
/* channel->interrupt = snd_p16v_pcm_channel_interrupt; */
|
||||
channel->epcm = epcm;
|
||||
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
|
||||
return err;
|
||||
|
||||
runtime->sync.id32[0] = substream->pcm->card->number;
|
||||
runtime->sync.id32[1] = 'P';
|
||||
runtime->sync.id32[2] = 16;
|
||||
runtime->sync.id32[3] = 'V';
|
||||
|
||||
return 0;
|
||||
}
|
||||
/* open_capture callback */
|
||||
static int snd_p16v_pcm_open_capture_channel(struct snd_pcm_substream *substream, int channel_id)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
|
||||
struct snd_emu10k1_voice *channel = &(emu->p16v_capture_voice);
|
||||
struct snd_emu10k1_pcm *epcm;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int err;
|
||||
|
||||
epcm = kzalloc(sizeof(*epcm), GFP_KERNEL);
|
||||
/* dev_dbg(emu->card->dev, "epcm kcalloc: %p\n", epcm); */
|
||||
|
||||
if (epcm == NULL)
|
||||
return -ENOMEM;
|
||||
epcm->emu = emu;
|
||||
epcm->substream = substream;
|
||||
/*
|
||||
dev_dbg(emu->card->dev, "epcm device=%d, channel_id=%d\n",
|
||||
substream->pcm->device, channel_id);
|
||||
*/
|
||||
runtime->private_data = epcm;
|
||||
runtime->private_free = snd_p16v_pcm_free_substream;
|
||||
|
||||
runtime->hw = snd_p16v_capture_hw;
|
||||
|
||||
channel->emu = emu;
|
||||
channel->number = channel_id;
|
||||
|
||||
channel->use=1;
|
||||
#if 0 /* debug */
|
||||
dev_dbg(emu->card->dev,
|
||||
"p16v: open channel_id=%d, channel=%p, use=0x%x\n",
|
||||
channel_id, channel, channel->use);
|
||||
dev_dbg(emu->card->dev, "open:channel_id=%d, chip=%p, channel=%p\n",
|
||||
channel_id, chip, channel);
|
||||
#endif /* debug */
|
||||
/* channel->interrupt = snd_p16v_pcm_channel_interrupt; */
|
||||
channel->epcm = epcm;
|
||||
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* close callback */
|
||||
static int snd_p16v_pcm_close_playback(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
|
||||
//struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
//struct snd_emu10k1_pcm *epcm = runtime->private_data;
|
||||
emu->p16v_voices[substream->pcm->device - emu->p16v_device_offset].use = 0;
|
||||
/* FIXME: maybe zero others */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* close callback */
|
||||
static int snd_p16v_pcm_close_capture(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
|
||||
//struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
//struct snd_emu10k1_pcm *epcm = runtime->private_data;
|
||||
emu->p16v_capture_voice.use = 0;
|
||||
/* FIXME: maybe zero others */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_p16v_pcm_open_playback_front(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_p16v_pcm_open_playback_channel(substream, PCM_FRONT_CHANNEL);
|
||||
}
|
||||
|
||||
static int snd_p16v_pcm_open_capture(struct snd_pcm_substream *substream)
|
||||
{
|
||||
// Only using channel 0 for now, but the card has 2 channels.
|
||||
return snd_p16v_pcm_open_capture_channel(substream, 0);
|
||||
}
|
||||
|
||||
/* hw_params callback */
|
||||
static int snd_p16v_pcm_hw_params_playback(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
int result;
|
||||
result = snd_pcm_lib_malloc_pages(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
return result;
|
||||
}
|
||||
|
||||
/* hw_params callback */
|
||||
static int snd_p16v_pcm_hw_params_capture(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *hw_params)
|
||||
{
|
||||
int result;
|
||||
result = snd_pcm_lib_malloc_pages(substream,
|
||||
params_buffer_bytes(hw_params));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* hw_free callback */
|
||||
static int snd_p16v_pcm_hw_free_playback(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int result;
|
||||
result = snd_pcm_lib_free_pages(substream);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* hw_free callback */
|
||||
static int snd_p16v_pcm_hw_free_capture(struct snd_pcm_substream *substream)
|
||||
{
|
||||
int result;
|
||||
result = snd_pcm_lib_free_pages(substream);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* prepare playback callback */
|
||||
static int snd_p16v_pcm_prepare_playback(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int channel = substream->pcm->device - emu->p16v_device_offset;
|
||||
u32 *table_base = (u32 *)(emu->p16v_buffer.area+(8*16*channel));
|
||||
u32 period_size_bytes = frames_to_bytes(runtime, runtime->period_size);
|
||||
int i;
|
||||
u32 tmp;
|
||||
|
||||
#if 0 /* debug */
|
||||
dev_dbg(emu->card->dev,
|
||||
"prepare:channel_number=%d, rate=%d, "
|
||||
"format=0x%x, channels=%d, buffer_size=%ld, "
|
||||
"period_size=%ld, periods=%u, frames_to_bytes=%d\n",
|
||||
channel, runtime->rate, runtime->format, runtime->channels,
|
||||
runtime->buffer_size, runtime->period_size,
|
||||
runtime->periods, frames_to_bytes(runtime, 1));
|
||||
dev_dbg(emu->card->dev,
|
||||
"dma_addr=%x, dma_area=%p, table_base=%p\n",
|
||||
runtime->dma_addr, runtime->dma_area, table_base);
|
||||
dev_dbg(emu->card->dev,
|
||||
"dma_addr=%x, dma_area=%p, dma_bytes(size)=%x\n",
|
||||
emu->p16v_buffer.addr, emu->p16v_buffer.area,
|
||||
emu->p16v_buffer.bytes);
|
||||
#endif /* debug */
|
||||
tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
|
||||
switch (runtime->rate) {
|
||||
case 44100:
|
||||
snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x8080);
|
||||
break;
|
||||
case 96000:
|
||||
snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x4040);
|
||||
break;
|
||||
case 192000:
|
||||
snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x2020);
|
||||
break;
|
||||
case 48000:
|
||||
default:
|
||||
snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0xe0e0) | 0x0000);
|
||||
break;
|
||||
}
|
||||
/* FIXME: Check emu->buffer.size before actually writing to it. */
|
||||
for(i = 0; i < runtime->periods; i++) {
|
||||
table_base[i*2]=runtime->dma_addr+(i*period_size_bytes);
|
||||
table_base[(i*2)+1]=period_size_bytes<<16;
|
||||
}
|
||||
|
||||
snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_ADDR, channel, emu->p16v_buffer.addr+(8*16*channel));
|
||||
snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_SIZE, channel, (runtime->periods - 1) << 19);
|
||||
snd_emu10k1_ptr20_write(emu, PLAYBACK_LIST_PTR, channel, 0);
|
||||
snd_emu10k1_ptr20_write(emu, PLAYBACK_DMA_ADDR, channel, runtime->dma_addr);
|
||||
//snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, frames_to_bytes(runtime, runtime->period_size)<<16); // buffer size in bytes
|
||||
snd_emu10k1_ptr20_write(emu, PLAYBACK_PERIOD_SIZE, channel, 0); // buffer size in bytes
|
||||
snd_emu10k1_ptr20_write(emu, PLAYBACK_POINTER, channel, 0);
|
||||
snd_emu10k1_ptr20_write(emu, 0x07, channel, 0x0);
|
||||
snd_emu10k1_ptr20_write(emu, 0x08, channel, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* prepare capture callback */
|
||||
static int snd_p16v_pcm_prepare_capture(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int channel = substream->pcm->device - emu->p16v_device_offset;
|
||||
u32 tmp;
|
||||
|
||||
/*
|
||||
dev_dbg(emu->card->dev, "prepare capture:channel_number=%d, rate=%d, "
|
||||
"format=0x%x, channels=%d, buffer_size=%ld, period_size=%ld, "
|
||||
"frames_to_bytes=%d\n",
|
||||
channel, runtime->rate, runtime->format, runtime->channels,
|
||||
runtime->buffer_size, runtime->period_size,
|
||||
frames_to_bytes(runtime, 1));
|
||||
*/
|
||||
tmp = snd_emu10k1_ptr_read(emu, A_SPDIF_SAMPLERATE, channel);
|
||||
switch (runtime->rate) {
|
||||
case 44100:
|
||||
snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0800);
|
||||
break;
|
||||
case 96000:
|
||||
snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0400);
|
||||
break;
|
||||
case 192000:
|
||||
snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0200);
|
||||
break;
|
||||
case 48000:
|
||||
default:
|
||||
snd_emu10k1_ptr_write(emu, A_SPDIF_SAMPLERATE, channel, (tmp & ~0x0e00) | 0x0000);
|
||||
break;
|
||||
}
|
||||
/* FIXME: Check emu->buffer.size before actually writing to it. */
|
||||
snd_emu10k1_ptr20_write(emu, 0x13, channel, 0);
|
||||
snd_emu10k1_ptr20_write(emu, CAPTURE_DMA_ADDR, channel, runtime->dma_addr);
|
||||
snd_emu10k1_ptr20_write(emu, CAPTURE_BUFFER_SIZE, channel, frames_to_bytes(runtime, runtime->buffer_size) << 16); // buffer size in bytes
|
||||
snd_emu10k1_ptr20_write(emu, CAPTURE_POINTER, channel, 0);
|
||||
//snd_emu10k1_ptr20_write(emu, CAPTURE_SOURCE, 0x0, 0x333300e4); /* Select MIC or Line in */
|
||||
//snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) | (0x110000<<channel));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_p16v_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int enable;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
enable = inl(emu->port + INTE2) | intrenb;
|
||||
outl(enable, emu->port + INTE2);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
static void snd_p16v_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int disable;
|
||||
|
||||
spin_lock_irqsave(&emu->emu_lock, flags);
|
||||
disable = inl(emu->port + INTE2) & (~intrenb);
|
||||
outl(disable, emu->port + INTE2);
|
||||
spin_unlock_irqrestore(&emu->emu_lock, flags);
|
||||
}
|
||||
|
||||
/* trigger_playback callback */
|
||||
static int snd_p16v_pcm_trigger_playback(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime;
|
||||
struct snd_emu10k1_pcm *epcm;
|
||||
int channel;
|
||||
int result = 0;
|
||||
struct snd_pcm_substream *s;
|
||||
u32 basic = 0;
|
||||
u32 inte = 0;
|
||||
int running = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
running=1;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
default:
|
||||
running = 0;
|
||||
break;
|
||||
}
|
||||
snd_pcm_group_for_each_entry(s, substream) {
|
||||
if (snd_pcm_substream_chip(s) != emu ||
|
||||
s->stream != SNDRV_PCM_STREAM_PLAYBACK)
|
||||
continue;
|
||||
runtime = s->runtime;
|
||||
epcm = runtime->private_data;
|
||||
channel = substream->pcm->device-emu->p16v_device_offset;
|
||||
/* dev_dbg(emu->card->dev, "p16v channel=%d\n", channel); */
|
||||
epcm->running = running;
|
||||
basic |= (0x1<<channel);
|
||||
inte |= (INTE2_PLAYBACK_CH_0_LOOP<<channel);
|
||||
snd_pcm_trigger_done(s, substream);
|
||||
}
|
||||
/* dev_dbg(emu->card->dev, "basic=0x%x, inte=0x%x\n", basic, inte); */
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
snd_p16v_intr_enable(emu, inte);
|
||||
snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0)| (basic));
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & ~(basic));
|
||||
snd_p16v_intr_disable(emu, inte);
|
||||
break;
|
||||
default:
|
||||
result = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* trigger_capture callback */
|
||||
static int snd_p16v_pcm_trigger_capture(struct snd_pcm_substream *substream,
|
||||
int cmd)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_emu10k1_pcm *epcm = runtime->private_data;
|
||||
int channel = 0;
|
||||
int result = 0;
|
||||
u32 inte = INTE2_CAPTURE_CH_0_LOOP | INTE2_CAPTURE_CH_0_HALF_LOOP;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
snd_p16v_intr_enable(emu, inte);
|
||||
snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0)|(0x100<<channel));
|
||||
epcm->running = 1;
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & ~(0x100<<channel));
|
||||
snd_p16v_intr_disable(emu, inte);
|
||||
//snd_emu10k1_ptr20_write(emu, EXTENDED_INT_MASK, 0, snd_emu10k1_ptr20_read(emu, EXTENDED_INT_MASK, 0) & ~(0x110000<<channel));
|
||||
epcm->running = 0;
|
||||
break;
|
||||
default:
|
||||
result = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* pointer_playback callback */
|
||||
static snd_pcm_uframes_t
|
||||
snd_p16v_pcm_pointer_playback(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_emu10k1_pcm *epcm = runtime->private_data;
|
||||
snd_pcm_uframes_t ptr, ptr1, ptr2,ptr3,ptr4 = 0;
|
||||
int channel = substream->pcm->device - emu->p16v_device_offset;
|
||||
if (!epcm->running)
|
||||
return 0;
|
||||
|
||||
ptr3 = snd_emu10k1_ptr20_read(emu, PLAYBACK_LIST_PTR, channel);
|
||||
ptr1 = snd_emu10k1_ptr20_read(emu, PLAYBACK_POINTER, channel);
|
||||
ptr4 = snd_emu10k1_ptr20_read(emu, PLAYBACK_LIST_PTR, channel);
|
||||
if (ptr3 != ptr4) ptr1 = snd_emu10k1_ptr20_read(emu, PLAYBACK_POINTER, channel);
|
||||
ptr2 = bytes_to_frames(runtime, ptr1);
|
||||
ptr2+= (ptr4 >> 3) * runtime->period_size;
|
||||
ptr=ptr2;
|
||||
if (ptr >= runtime->buffer_size)
|
||||
ptr -= runtime->buffer_size;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* pointer_capture callback */
|
||||
static snd_pcm_uframes_t
|
||||
snd_p16v_pcm_pointer_capture(struct snd_pcm_substream *substream)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_emu10k1_pcm *epcm = runtime->private_data;
|
||||
snd_pcm_uframes_t ptr, ptr1, ptr2 = 0;
|
||||
int channel = 0;
|
||||
|
||||
if (!epcm->running)
|
||||
return 0;
|
||||
|
||||
ptr1 = snd_emu10k1_ptr20_read(emu, CAPTURE_POINTER, channel);
|
||||
ptr2 = bytes_to_frames(runtime, ptr1);
|
||||
ptr=ptr2;
|
||||
if (ptr >= runtime->buffer_size) {
|
||||
ptr -= runtime->buffer_size;
|
||||
dev_warn(emu->card->dev, "buffer capture limited!\n");
|
||||
}
|
||||
/*
|
||||
dev_dbg(emu->card->dev, "ptr1 = 0x%lx, ptr2=0x%lx, ptr=0x%lx, "
|
||||
"buffer_size = 0x%x, period_size = 0x%x, bits=%d, rate=%d\n",
|
||||
ptr1, ptr2, ptr, (int)runtime->buffer_size,
|
||||
(int)runtime->period_size, (int)runtime->frame_bits,
|
||||
(int)runtime->rate);
|
||||
*/
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/* operators */
|
||||
static struct snd_pcm_ops snd_p16v_playback_front_ops = {
|
||||
.open = snd_p16v_pcm_open_playback_front,
|
||||
.close = snd_p16v_pcm_close_playback,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_p16v_pcm_hw_params_playback,
|
||||
.hw_free = snd_p16v_pcm_hw_free_playback,
|
||||
.prepare = snd_p16v_pcm_prepare_playback,
|
||||
.trigger = snd_p16v_pcm_trigger_playback,
|
||||
.pointer = snd_p16v_pcm_pointer_playback,
|
||||
};
|
||||
|
||||
static struct snd_pcm_ops snd_p16v_capture_ops = {
|
||||
.open = snd_p16v_pcm_open_capture,
|
||||
.close = snd_p16v_pcm_close_capture,
|
||||
.ioctl = snd_pcm_lib_ioctl,
|
||||
.hw_params = snd_p16v_pcm_hw_params_capture,
|
||||
.hw_free = snd_p16v_pcm_hw_free_capture,
|
||||
.prepare = snd_p16v_pcm_prepare_capture,
|
||||
.trigger = snd_p16v_pcm_trigger_capture,
|
||||
.pointer = snd_p16v_pcm_pointer_capture,
|
||||
};
|
||||
|
||||
|
||||
int snd_p16v_free(struct snd_emu10k1 *chip)
|
||||
{
|
||||
// release the data
|
||||
if (chip->p16v_buffer.area) {
|
||||
snd_dma_free_pages(&chip->p16v_buffer);
|
||||
/*
|
||||
dev_dbg(chip->card->dev, "period lables free: %p\n",
|
||||
&chip->p16v_buffer);
|
||||
*/
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_p16v_pcm(struct snd_emu10k1 *emu, int device, struct snd_pcm **rpcm)
|
||||
{
|
||||
struct snd_pcm *pcm;
|
||||
struct snd_pcm_substream *substream;
|
||||
int err;
|
||||
int capture=1;
|
||||
|
||||
/* dev_dbg(emu->card->dev, "snd_p16v_pcm called. device=%d\n", device); */
|
||||
emu->p16v_device_offset = device;
|
||||
if (rpcm)
|
||||
*rpcm = NULL;
|
||||
|
||||
if ((err = snd_pcm_new(emu->card, "p16v", device, 1, capture, &pcm)) < 0)
|
||||
return err;
|
||||
|
||||
pcm->private_data = emu;
|
||||
// Single playback 8 channel device.
|
||||
// Single capture 2 channel device.
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_p16v_playback_front_ops);
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_p16v_capture_ops);
|
||||
|
||||
pcm->info_flags = 0;
|
||||
pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
|
||||
strcpy(pcm->name, "p16v");
|
||||
emu->pcm_p16v = pcm;
|
||||
|
||||
for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
|
||||
substream;
|
||||
substream = substream->next) {
|
||||
if ((err = snd_pcm_lib_preallocate_pages(substream,
|
||||
SNDRV_DMA_TYPE_DEV,
|
||||
snd_dma_pci_data(emu->pci),
|
||||
((65536 - 64) * 8), ((65536 - 64) * 8))) < 0)
|
||||
return err;
|
||||
/*
|
||||
dev_dbg(emu->card->dev,
|
||||
"preallocate playback substream: err=%d\n", err);
|
||||
*/
|
||||
}
|
||||
|
||||
for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
|
||||
substream;
|
||||
substream = substream->next) {
|
||||
if ((err = snd_pcm_lib_preallocate_pages(substream,
|
||||
SNDRV_DMA_TYPE_DEV,
|
||||
snd_dma_pci_data(emu->pci),
|
||||
65536 - 64, 65536 - 64)) < 0)
|
||||
return err;
|
||||
/*
|
||||
dev_dbg(emu->card->dev,
|
||||
"preallocate capture substream: err=%d\n", err);
|
||||
*/
|
||||
}
|
||||
|
||||
if (rpcm)
|
||||
*rpcm = pcm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_p16v_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 = 255;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_p16v_volume_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
||||
int high_low = (kcontrol->private_value >> 8) & 0xff;
|
||||
int reg = kcontrol->private_value & 0xff;
|
||||
u32 value;
|
||||
|
||||
value = snd_emu10k1_ptr20_read(emu, reg, high_low);
|
||||
if (high_low) {
|
||||
ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
|
||||
ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
|
||||
} else {
|
||||
ucontrol->value.integer.value[0] = 0xff - ((value >> 8) & 0xff); /* Left */
|
||||
ucontrol->value.integer.value[1] = 0xff - ((value >> 0) & 0xff); /* Right */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_p16v_volume_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
||||
int high_low = (kcontrol->private_value >> 8) & 0xff;
|
||||
int reg = kcontrol->private_value & 0xff;
|
||||
u32 value, oval;
|
||||
|
||||
oval = value = snd_emu10k1_ptr20_read(emu, reg, 0);
|
||||
if (high_low == 1) {
|
||||
value &= 0xffff;
|
||||
value |= ((0xff - ucontrol->value.integer.value[0]) << 24) |
|
||||
((0xff - ucontrol->value.integer.value[1]) << 16);
|
||||
} else {
|
||||
value &= 0xffff0000;
|
||||
value |= ((0xff - ucontrol->value.integer.value[0]) << 8) |
|
||||
((0xff - ucontrol->value.integer.value[1]) );
|
||||
}
|
||||
if (value != oval) {
|
||||
snd_emu10k1_ptr20_write(emu, reg, 0, value);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_p16v_capture_source_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
static char *texts[8] = {
|
||||
"SPDIF", "I2S", "SRC48", "SRCMulti_SPDIF", "SRCMulti_I2S",
|
||||
"CDIF", "FX", "AC97"
|
||||
};
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.enumerated.items = 8;
|
||||
if (uinfo->value.enumerated.item > 7)
|
||||
uinfo->value.enumerated.item = 7;
|
||||
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_p16v_capture_source_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
ucontrol->value.enumerated.item[0] = emu->p16v_capture_source;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_p16v_capture_source_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int val;
|
||||
int change = 0;
|
||||
u32 mask;
|
||||
u32 source;
|
||||
|
||||
val = ucontrol->value.enumerated.item[0] ;
|
||||
if (val > 7)
|
||||
return -EINVAL;
|
||||
change = (emu->p16v_capture_source != val);
|
||||
if (change) {
|
||||
emu->p16v_capture_source = val;
|
||||
source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
|
||||
mask = snd_emu10k1_ptr20_read(emu, BASIC_INTERRUPT, 0) & 0xffff;
|
||||
snd_emu10k1_ptr20_write(emu, BASIC_INTERRUPT, 0, source | mask);
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
||||
static int snd_p16v_capture_channel_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
static char *texts[4] = { "0", "1", "2", "3", };
|
||||
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
||||
uinfo->count = 1;
|
||||
uinfo->value.enumerated.items = 4;
|
||||
if (uinfo->value.enumerated.item > 3)
|
||||
uinfo->value.enumerated.item = 3;
|
||||
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_p16v_capture_channel_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
||||
|
||||
ucontrol->value.enumerated.item[0] = emu->p16v_capture_channel;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_p16v_capture_channel_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol);
|
||||
unsigned int val;
|
||||
int change = 0;
|
||||
u32 tmp;
|
||||
|
||||
val = ucontrol->value.enumerated.item[0] ;
|
||||
if (val > 3)
|
||||
return -EINVAL;
|
||||
change = (emu->p16v_capture_channel != val);
|
||||
if (change) {
|
||||
emu->p16v_capture_channel = val;
|
||||
tmp = snd_emu10k1_ptr20_read(emu, CAPTURE_P16V_SOURCE, 0) & 0xfffc;
|
||||
snd_emu10k1_ptr20_write(emu, CAPTURE_P16V_SOURCE, 0, tmp | val);
|
||||
}
|
||||
return change;
|
||||
}
|
||||
static const DECLARE_TLV_DB_SCALE(snd_p16v_db_scale1, -5175, 25, 1);
|
||||
|
||||
#define P16V_VOL(xname,xreg,xhl) { \
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
|
||||
.info = snd_p16v_volume_info, \
|
||||
.get = snd_p16v_volume_get, \
|
||||
.put = snd_p16v_volume_put, \
|
||||
.tlv = { .p = snd_p16v_db_scale1 }, \
|
||||
.private_value = ((xreg) | ((xhl) << 8)) \
|
||||
}
|
||||
|
||||
static struct snd_kcontrol_new p16v_mixer_controls[] = {
|
||||
P16V_VOL("HD Analog Front Playback Volume", PLAYBACK_VOLUME_MIXER9, 0),
|
||||
P16V_VOL("HD Analog Rear Playback Volume", PLAYBACK_VOLUME_MIXER10, 1),
|
||||
P16V_VOL("HD Analog Center/LFE Playback Volume", PLAYBACK_VOLUME_MIXER9, 1),
|
||||
P16V_VOL("HD Analog Side Playback Volume", PLAYBACK_VOLUME_MIXER10, 0),
|
||||
P16V_VOL("HD SPDIF Front Playback Volume", PLAYBACK_VOLUME_MIXER7, 0),
|
||||
P16V_VOL("HD SPDIF Rear Playback Volume", PLAYBACK_VOLUME_MIXER8, 1),
|
||||
P16V_VOL("HD SPDIF Center/LFE Playback Volume", PLAYBACK_VOLUME_MIXER7, 1),
|
||||
P16V_VOL("HD SPDIF Side Playback Volume", PLAYBACK_VOLUME_MIXER8, 0),
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "HD source Capture",
|
||||
.info = snd_p16v_capture_source_info,
|
||||
.get = snd_p16v_capture_source_get,
|
||||
.put = snd_p16v_capture_source_put
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
||||
.name = "HD channel Capture",
|
||||
.info = snd_p16v_capture_channel_info,
|
||||
.get = snd_p16v_capture_channel_get,
|
||||
.put = snd_p16v_capture_channel_put
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
int snd_p16v_mixer(struct snd_emu10k1 *emu)
|
||||
{
|
||||
int i, err;
|
||||
struct snd_card *card = emu->card;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(p16v_mixer_controls); i++) {
|
||||
if ((err = snd_ctl_add(card, snd_ctl_new1(&p16v_mixer_controls[i],
|
||||
emu))) < 0)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
#define NUM_CHS 1 /* up to 4, but only first channel is used */
|
||||
|
||||
int snd_p16v_alloc_pm_buffer(struct snd_emu10k1 *emu)
|
||||
{
|
||||
emu->p16v_saved = vmalloc(NUM_CHS * 4 * 0x80);
|
||||
if (! emu->p16v_saved)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void snd_p16v_free_pm_buffer(struct snd_emu10k1 *emu)
|
||||
{
|
||||
vfree(emu->p16v_saved);
|
||||
}
|
||||
|
||||
void snd_p16v_suspend(struct snd_emu10k1 *emu)
|
||||
{
|
||||
int i, ch;
|
||||
unsigned int *val;
|
||||
|
||||
val = emu->p16v_saved;
|
||||
for (ch = 0; ch < NUM_CHS; ch++)
|
||||
for (i = 0; i < 0x80; i++, val++)
|
||||
*val = snd_emu10k1_ptr20_read(emu, i, ch);
|
||||
}
|
||||
|
||||
void snd_p16v_resume(struct snd_emu10k1 *emu)
|
||||
{
|
||||
int i, ch;
|
||||
unsigned int *val;
|
||||
|
||||
val = emu->p16v_saved;
|
||||
for (ch = 0; ch < NUM_CHS; ch++)
|
||||
for (i = 0; i < 0x80; i++, val++)
|
||||
snd_emu10k1_ptr20_write(emu, i, ch, *val);
|
||||
}
|
||||
#endif
|
299
sound/pci/emu10k1/p16v.h
Normal file
299
sound/pci/emu10k1/p16v.h
Normal file
|
@ -0,0 +1,299 @@
|
|||
/*
|
||||
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
|
||||
* Driver p16v chips
|
||||
* Version: 0.21
|
||||
*
|
||||
* FEATURES currently supported:
|
||||
* Output fixed at S32_LE, 2 channel to hw:0,0
|
||||
* Rates: 44.1, 48, 96, 192.
|
||||
*
|
||||
* Changelog:
|
||||
* 0.8
|
||||
* Use separate card based buffer for periods table.
|
||||
* 0.9
|
||||
* Use 2 channel output streams instead of 8 channel.
|
||||
* (8 channel output streams might be good for ASIO type output)
|
||||
* Corrected speaker output, so Front -> Front etc.
|
||||
* 0.10
|
||||
* Fixed missed interrupts.
|
||||
* 0.11
|
||||
* Add Sound card model number and names.
|
||||
* Add Analog volume controls.
|
||||
* 0.12
|
||||
* Corrected playback interrupts. Now interrupt per period, instead of half period.
|
||||
* 0.13
|
||||
* Use single trigger for multichannel.
|
||||
* 0.14
|
||||
* Mic capture now works at fixed: S32_LE, 96000Hz, Stereo.
|
||||
* 0.15
|
||||
* Force buffer_size / period_size == INTEGER.
|
||||
* 0.16
|
||||
* Update p16v.c to work with changed alsa api.
|
||||
* 0.17
|
||||
* Update p16v.c to work with changed alsa api. Removed boot_devs.
|
||||
* 0.18
|
||||
* Merging with snd-emu10k1 driver.
|
||||
* 0.19
|
||||
* One stereo channel at 24bit now works.
|
||||
* 0.20
|
||||
* Added better register defines.
|
||||
* 0.21
|
||||
* Split from p16v.c
|
||||
*
|
||||
*
|
||||
* BUGS:
|
||||
* Some stability problems when unloading the snd-p16v kernel module.
|
||||
* --
|
||||
*
|
||||
* TODO:
|
||||
* SPDIF out.
|
||||
* Find out how to change capture sample rates. E.g. To record SPDIF at 48000Hz.
|
||||
* Currently capture fixed at 48000Hz.
|
||||
*
|
||||
* --
|
||||
* GENERAL INFO:
|
||||
* Model: SB0240
|
||||
* P16V Chip: CA0151-DBS
|
||||
* Audigy 2 Chip: CA0102-IAT
|
||||
* AC97 Codec: STAC 9721
|
||||
* ADC: Philips 1361T (Stereo 24bit)
|
||||
* DAC: CS4382-K (8-channel, 24bit, 192Khz)
|
||||
*
|
||||
* This code was initially based on code from ALSA's emu10k1x.c which is:
|
||||
* Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
/********************************************************************************************************/
|
||||
/* Audigy2 P16V pointer-offset register set, accessed through the PTR2 and DATA2 registers */
|
||||
/********************************************************************************************************/
|
||||
|
||||
/* The sample rate of the SPDIF outputs is set by modifying a register in the EMU10K2 PTR register A_SPDIF_SAMPLERATE.
|
||||
* The sample rate is also controlled by the same registers that control the rate of the EMU10K2 sample rate converters.
|
||||
*/
|
||||
|
||||
/* Initially all registers from 0x00 to 0x3f have zero contents. */
|
||||
#define PLAYBACK_LIST_ADDR 0x00 /* Base DMA address of a list of pointers to each period/size */
|
||||
/* One list entry: 4 bytes for DMA address,
|
||||
* 4 bytes for period_size << 16.
|
||||
* One list entry is 8 bytes long.
|
||||
* One list entry for each period in the buffer.
|
||||
*/
|
||||
#define PLAYBACK_LIST_SIZE 0x01 /* Size of list in bytes << 16. E.g. 8 periods -> 0x00380000 */
|
||||
#define PLAYBACK_LIST_PTR 0x02 /* Pointer to the current period being played */
|
||||
#define PLAYBACK_UNKNOWN3 0x03 /* Not used */
|
||||
#define PLAYBACK_DMA_ADDR 0x04 /* Playback DMA address */
|
||||
#define PLAYBACK_PERIOD_SIZE 0x05 /* Playback period size. win2000 uses 0x04000000 */
|
||||
#define PLAYBACK_POINTER 0x06 /* Playback period pointer. Used with PLAYBACK_LIST_PTR to determine buffer position currently in DAC */
|
||||
#define PLAYBACK_FIFO_END_ADDRESS 0x07 /* Playback FIFO end address */
|
||||
#define PLAYBACK_FIFO_POINTER 0x08 /* Playback FIFO pointer and number of valid sound samples in cache */
|
||||
#define PLAYBACK_UNKNOWN9 0x09 /* Not used */
|
||||
#define CAPTURE_DMA_ADDR 0x10 /* Capture DMA address */
|
||||
#define CAPTURE_BUFFER_SIZE 0x11 /* Capture buffer size */
|
||||
#define CAPTURE_POINTER 0x12 /* Capture buffer pointer. Sample currently in ADC */
|
||||
#define CAPTURE_FIFO_POINTER 0x13 /* Capture FIFO pointer and number of valid sound samples in cache */
|
||||
#define CAPTURE_P16V_VOLUME1 0x14 /* Low: Capture volume 0xXXXX3030 */
|
||||
#define CAPTURE_P16V_VOLUME2 0x15 /* High:Has no effect on capture volume */
|
||||
#define CAPTURE_P16V_SOURCE 0x16 /* P16V source select. Set to 0x0700E4E5 for AC97 CAPTURE */
|
||||
/* [0:1] Capture input 0 channel select. 0 = Capture output 0.
|
||||
* 1 = Capture output 1.
|
||||
* 2 = Capture output 2.
|
||||
* 3 = Capture output 3.
|
||||
* [3:2] Capture input 1 channel select. 0 = Capture output 0.
|
||||
* 1 = Capture output 1.
|
||||
* 2 = Capture output 2.
|
||||
* 3 = Capture output 3.
|
||||
* [5:4] Capture input 2 channel select. 0 = Capture output 0.
|
||||
* 1 = Capture output 1.
|
||||
* 2 = Capture output 2.
|
||||
* 3 = Capture output 3.
|
||||
* [7:6] Capture input 3 channel select. 0 = Capture output 0.
|
||||
* 1 = Capture output 1.
|
||||
* 2 = Capture output 2.
|
||||
* 3 = Capture output 3.
|
||||
* [9:8] Playback input 0 channel select. 0 = Play output 0.
|
||||
* 1 = Play output 1.
|
||||
* 2 = Play output 2.
|
||||
* 3 = Play output 3.
|
||||
* [11:10] Playback input 1 channel select. 0 = Play output 0.
|
||||
* 1 = Play output 1.
|
||||
* 2 = Play output 2.
|
||||
* 3 = Play output 3.
|
||||
* [13:12] Playback input 2 channel select. 0 = Play output 0.
|
||||
* 1 = Play output 1.
|
||||
* 2 = Play output 2.
|
||||
* 3 = Play output 3.
|
||||
* [15:14] Playback input 3 channel select. 0 = Play output 0.
|
||||
* 1 = Play output 1.
|
||||
* 2 = Play output 2.
|
||||
* 3 = Play output 3.
|
||||
* [19:16] Playback mixer output enable. 1 bit per channel.
|
||||
* [23:20] Capture mixer output enable. 1 bit per channel.
|
||||
* [26:24] FX engine channel capture 0 = 0x60-0x67.
|
||||
* 1 = 0x68-0x6f.
|
||||
* 2 = 0x70-0x77.
|
||||
* 3 = 0x78-0x7f.
|
||||
* 4 = 0x80-0x87.
|
||||
* 5 = 0x88-0x8f.
|
||||
* 6 = 0x90-0x97.
|
||||
* 7 = 0x98-0x9f.
|
||||
* [31:27] Not used.
|
||||
*/
|
||||
|
||||
/* 0x1 = capture on.
|
||||
* 0x100 = capture off.
|
||||
* 0x200 = capture off.
|
||||
* 0x1000 = capture off.
|
||||
*/
|
||||
#define CAPTURE_RATE_STATUS 0x17 /* Capture sample rate. Read only */
|
||||
/* [15:0] Not used.
|
||||
* [18:16] Channel 0 Detected sample rate. 0 - 44.1khz
|
||||
* 1 - 48 khz
|
||||
* 2 - 96 khz
|
||||
* 3 - 192 khz
|
||||
* 7 - undefined rate.
|
||||
* [19] Channel 0. 1 - Valid, 0 - Not Valid.
|
||||
* [22:20] Channel 1 Detected sample rate.
|
||||
* [23] Channel 1. 1 - Valid, 0 - Not Valid.
|
||||
* [26:24] Channel 2 Detected sample rate.
|
||||
* [27] Channel 2. 1 - Valid, 0 - Not Valid.
|
||||
* [30:28] Channel 3 Detected sample rate.
|
||||
* [31] Channel 3. 1 - Valid, 0 - Not Valid.
|
||||
*/
|
||||
/* 0x18 - 0x1f unused */
|
||||
#define PLAYBACK_LAST_SAMPLE 0x20 /* The sample currently being played. Read only */
|
||||
/* 0x21 - 0x3f unused */
|
||||
#define BASIC_INTERRUPT 0x40 /* Used by both playback and capture interrupt handler */
|
||||
/* Playback (0x1<<channel_id) Don't touch high 16bits. */
|
||||
/* Capture (0x100<<channel_id). not tested */
|
||||
/* Start Playback [3:0] (one bit per channel)
|
||||
* Start Capture [11:8] (one bit per channel)
|
||||
* Record source select for channel 0 [18:16]
|
||||
* Record source select for channel 1 [22:20]
|
||||
* Record source select for channel 2 [26:24]
|
||||
* Record source select for channel 3 [30:28]
|
||||
* 0 - SPDIF channel.
|
||||
* 1 - I2S channel.
|
||||
* 2 - SRC48 channel.
|
||||
* 3 - SRCMulti_SPDIF channel.
|
||||
* 4 - SRCMulti_I2S channel.
|
||||
* 5 - SPDIF channel.
|
||||
* 6 - fxengine capture.
|
||||
* 7 - AC97 capture.
|
||||
*/
|
||||
/* Default 41110000.
|
||||
* Writing 0xffffffff hangs the PC.
|
||||
* Writing 0xffff0000 -> 77770000 so it must be some sort of route.
|
||||
* bit 0x1 starts DMA playback on channel_id 0
|
||||
*/
|
||||
/* 0x41,42 take values from 0 - 0xffffffff, but have no effect on playback */
|
||||
/* 0x43,0x48 do not remember settings */
|
||||
/* 0x41-45 unused */
|
||||
#define WATERMARK 0x46 /* Test bit to indicate cache level usage */
|
||||
/* Values it can have while playing on channel 0.
|
||||
* 0000f000, 0000f004, 0000f008, 0000f00c.
|
||||
* Readonly.
|
||||
*/
|
||||
/* 0x47-0x4f unused */
|
||||
/* 0x50-0x5f Capture cache data */
|
||||
#define SRCSel 0x60 /* SRCSel. Default 0x4. Bypass P16V 0x14 */
|
||||
/* [0] 0 = 10K2 audio, 1 = SRC48 mixer output.
|
||||
* [2] 0 = 10K2 audio, 1 = SRCMulti SPDIF mixer output.
|
||||
* [4] 0 = 10K2 audio, 1 = SRCMulti I2S mixer output.
|
||||
*/
|
||||
/* SRC48 converts samples rates 44.1, 48, 96, 192 to 48 khz. */
|
||||
/* SRCMulti converts 48khz samples rates to 44.1, 48, 96, 192 to 48. */
|
||||
/* SRC48 and SRCMULTI sample rate select and output select. */
|
||||
/* 0xffffffff -> 0xC0000015
|
||||
* 0xXXXXXXX4 = Enable Front Left/Right
|
||||
* Enable PCMs
|
||||
*/
|
||||
|
||||
/* 0x61 -> 0x6c are Volume controls */
|
||||
#define PLAYBACK_VOLUME_MIXER1 0x61 /* SRC48 Low to mixer input volume control. */
|
||||
#define PLAYBACK_VOLUME_MIXER2 0x62 /* SRC48 High to mixer input volume control. */
|
||||
#define PLAYBACK_VOLUME_MIXER3 0x63 /* SRCMULTI SPDIF Low to mixer input volume control. */
|
||||
#define PLAYBACK_VOLUME_MIXER4 0x64 /* SRCMULTI SPDIF High to mixer input volume control. */
|
||||
#define PLAYBACK_VOLUME_MIXER5 0x65 /* SRCMULTI I2S Low to mixer input volume control. */
|
||||
#define PLAYBACK_VOLUME_MIXER6 0x66 /* SRCMULTI I2S High to mixer input volume control. */
|
||||
#define PLAYBACK_VOLUME_MIXER7 0x67 /* P16V Low to SRCMULTI SPDIF mixer input volume control. */
|
||||
#define PLAYBACK_VOLUME_MIXER8 0x68 /* P16V High to SRCMULTI SPDIF mixer input volume control. */
|
||||
#define PLAYBACK_VOLUME_MIXER9 0x69 /* P16V Low to SRCMULTI I2S mixer input volume control. */
|
||||
/* 0xXXXX3030 = PCM0 Volume (Front).
|
||||
* 0x3030XXXX = PCM1 Volume (Center)
|
||||
*/
|
||||
#define PLAYBACK_VOLUME_MIXER10 0x6a /* P16V High to SRCMULTI I2S mixer input volume control. */
|
||||
/* 0x3030XXXX = PCM3 Volume (Rear). */
|
||||
#define PLAYBACK_VOLUME_MIXER11 0x6b /* E10K2 Low to SRC48 mixer input volume control. */
|
||||
#define PLAYBACK_VOLUME_MIXER12 0x6c /* E10K2 High to SRC48 mixer input volume control. */
|
||||
|
||||
#define SRC48_ENABLE 0x6d /* SRC48 input audio enable */
|
||||
/* SRC48 converts samples rates 44.1, 48, 96, 192 to 48 khz. */
|
||||
/* [23:16] The corresponding P16V channel to SRC48 enabled if == 1.
|
||||
* [31:24] The corresponding E10K2 channel to SRC48 enabled.
|
||||
*/
|
||||
#define SRCMULTI_ENABLE 0x6e /* SRCMulti input audio enable. Default 0xffffffff */
|
||||
/* SRCMulti converts 48khz samples rates to 44.1, 48, 96, 192 to 48. */
|
||||
/* [7:0] The corresponding P16V channel to SRCMulti_I2S enabled if == 1.
|
||||
* [15:8] The corresponding E10K2 channel to SRCMulti I2S enabled.
|
||||
* [23:16] The corresponding P16V channel to SRCMulti SPDIF enabled.
|
||||
* [31:24] The corresponding E10K2 channel to SRCMulti SPDIF enabled.
|
||||
*/
|
||||
/* Bypass P16V 0xff00ff00
|
||||
* Bitmap. 0 = Off, 1 = On.
|
||||
* P16V playback outputs:
|
||||
* 0xXXXXXXX1 = PCM0 Left. (Front)
|
||||
* 0xXXXXXXX2 = PCM0 Right.
|
||||
* 0xXXXXXXX4 = PCM1 Left. (Center/LFE)
|
||||
* 0xXXXXXXX8 = PCM1 Right.
|
||||
* 0xXXXXXX1X = PCM2 Left. (Unknown)
|
||||
* 0xXXXXXX2X = PCM2 Right.
|
||||
* 0xXXXXXX4X = PCM3 Left. (Rear)
|
||||
* 0xXXXXXX8X = PCM3 Right.
|
||||
*/
|
||||
#define AUDIO_OUT_ENABLE 0x6f /* Default: 000100FF */
|
||||
/* [3:0] Does something, but not documented. Probably capture enable.
|
||||
* [7:4] Playback channels enable. not documented.
|
||||
* [16] AC97 output enable if == 1
|
||||
* [30] 0 = SRCMulti_I2S input from fxengine 0x68-0x6f.
|
||||
* 1 = SRCMulti_I2S input from SRC48 output.
|
||||
* [31] 0 = SRCMulti_SPDIF input from fxengine 0x60-0x67.
|
||||
* 1 = SRCMulti_SPDIF input from SRC48 output.
|
||||
*/
|
||||
/* 0xffffffff -> C00100FF */
|
||||
/* 0 -> Not playback sound, irq still running */
|
||||
/* 0xXXXXXX10 = PCM0 Left/Right On. (Front)
|
||||
* 0xXXXXXX20 = PCM1 Left/Right On. (Center/LFE)
|
||||
* 0xXXXXXX40 = PCM2 Left/Right On. (Unknown)
|
||||
* 0xXXXXXX80 = PCM3 Left/Right On. (Rear)
|
||||
*/
|
||||
#define PLAYBACK_SPDIF_SELECT 0x70 /* Default: 12030F00 */
|
||||
/* 0xffffffff -> 3FF30FFF */
|
||||
/* 0x00000001 pauses stream/irq fail. */
|
||||
/* All other bits do not effect playback */
|
||||
#define PLAYBACK_SPDIF_SRC_SELECT 0x71 /* Default: 0000E4E4 */
|
||||
/* 0xffffffff -> F33FFFFF */
|
||||
/* All bits do not effect playback */
|
||||
#define PLAYBACK_SPDIF_USER_DATA0 0x72 /* SPDIF out user data 0 */
|
||||
#define PLAYBACK_SPDIF_USER_DATA1 0x73 /* SPDIF out user data 1 */
|
||||
/* 0x74-0x75 unknown */
|
||||
#define CAPTURE_SPDIF_CONTROL 0x76 /* SPDIF in control setting */
|
||||
#define CAPTURE_SPDIF_STATUS 0x77 /* SPDIF in status */
|
||||
#define CAPURE_SPDIF_USER_DATA0 0x78 /* SPDIF in user data 0 */
|
||||
#define CAPURE_SPDIF_USER_DATA1 0x79 /* SPDIF in user data 1 */
|
||||
#define CAPURE_SPDIF_USER_DATA2 0x7a /* SPDIF in user data 2 */
|
||||
|
158
sound/pci/emu10k1/p17v.h
Normal file
158
sound/pci/emu10k1/p17v.h
Normal file
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
|
||||
* Driver p17v chips
|
||||
* Version: 0.01
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
/******************************************************************************/
|
||||
/* Audigy2Value Tina (P17V) pointer-offset register set,
|
||||
* accessed through the PTR20 and DATA24 registers */
|
||||
/******************************************************************************/
|
||||
|
||||
/* 00 - 07: Not used */
|
||||
#define P17V_PLAYBACK_FIFO_PTR 0x08 /* Current playback fifo pointer
|
||||
* and number of sound samples in cache.
|
||||
*/
|
||||
/* 09 - 12: Not used */
|
||||
#define P17V_CAPTURE_FIFO_PTR 0x13 /* Current capture fifo pointer
|
||||
* and number of sound samples in cache.
|
||||
*/
|
||||
/* 14 - 17: Not used */
|
||||
#define P17V_PB_CHN_SEL 0x18 /* P17v playback channel select */
|
||||
#define P17V_SE_SLOT_SEL_L 0x19 /* Sound Engine slot select low */
|
||||
#define P17V_SE_SLOT_SEL_H 0x1a /* Sound Engine slot select high */
|
||||
/* 1b - 1f: Not used */
|
||||
/* 20 - 2f: Not used */
|
||||
/* 30 - 3b: Not used */
|
||||
#define P17V_SPI 0x3c /* SPI interface register */
|
||||
#define P17V_I2C_ADDR 0x3d /* I2C Address */
|
||||
#define P17V_I2C_0 0x3e /* I2C Data */
|
||||
#define P17V_I2C_1 0x3f /* I2C Data */
|
||||
/* I2C values */
|
||||
#define I2C_A_ADC_ADD_MASK 0x000000fe /*The address is a 7 bit address */
|
||||
#define I2C_A_ADC_RW_MASK 0x00000001 /*bit mask for R/W */
|
||||
#define I2C_A_ADC_TRANS_MASK 0x00000010 /*Bit mask for I2c address DAC value */
|
||||
#define I2C_A_ADC_ABORT_MASK 0x00000020 /*Bit mask for I2C transaction abort flag */
|
||||
#define I2C_A_ADC_LAST_MASK 0x00000040 /*Bit mask for Last word transaction */
|
||||
#define I2C_A_ADC_BYTE_MASK 0x00000080 /*Bit mask for Byte Mode */
|
||||
|
||||
#define I2C_A_ADC_ADD 0x00000034 /*This is the Device address for ADC */
|
||||
#define I2C_A_ADC_READ 0x00000001 /*To perform a read operation */
|
||||
#define I2C_A_ADC_START 0x00000100 /*Start I2C transaction */
|
||||
#define I2C_A_ADC_ABORT 0x00000200 /*I2C transaction abort */
|
||||
#define I2C_A_ADC_LAST 0x00000400 /*I2C last transaction */
|
||||
#define I2C_A_ADC_BYTE 0x00000800 /*I2C one byte mode */
|
||||
|
||||
#define I2C_D_ADC_REG_MASK 0xfe000000 /*ADC address register */
|
||||
#define I2C_D_ADC_DAT_MASK 0x01ff0000 /*ADC data register */
|
||||
|
||||
#define ADC_TIMEOUT 0x00000007 /*ADC Timeout Clock Disable */
|
||||
#define ADC_IFC_CTRL 0x0000000b /*ADC Interface Control */
|
||||
#define ADC_MASTER 0x0000000c /*ADC Master Mode Control */
|
||||
#define ADC_POWER 0x0000000d /*ADC PowerDown Control */
|
||||
#define ADC_ATTEN_ADCL 0x0000000e /*ADC Attenuation ADCL */
|
||||
#define ADC_ATTEN_ADCR 0x0000000f /*ADC Attenuation ADCR */
|
||||
#define ADC_ALC_CTRL1 0x00000010 /*ADC ALC Control 1 */
|
||||
#define ADC_ALC_CTRL2 0x00000011 /*ADC ALC Control 2 */
|
||||
#define ADC_ALC_CTRL3 0x00000012 /*ADC ALC Control 3 */
|
||||
#define ADC_NOISE_CTRL 0x00000013 /*ADC Noise Gate Control */
|
||||
#define ADC_LIMIT_CTRL 0x00000014 /*ADC Limiter Control */
|
||||
#define ADC_MUX 0x00000015 /*ADC Mux offset */
|
||||
#if 0
|
||||
/* FIXME: Not tested yet. */
|
||||
#define ADC_GAIN_MASK 0x000000ff //Mask for ADC Gain
|
||||
#define ADC_ZERODB 0x000000cf //Value to set ADC to 0dB
|
||||
#define ADC_MUTE_MASK 0x000000c0 //Mask for ADC mute
|
||||
#define ADC_MUTE 0x000000c0 //Value to mute ADC
|
||||
#define ADC_OSR 0x00000008 //Mask for ADC oversample rate select
|
||||
#define ADC_TIMEOUT_DISABLE 0x00000008 //Value and mask to disable Timeout clock
|
||||
#define ADC_HPF_DISABLE 0x00000100 //Value and mask to disable High pass filter
|
||||
#define ADC_TRANWIN_MASK 0x00000070 //Mask for Length of Transient Window
|
||||
#endif
|
||||
|
||||
#define ADC_MUX_MASK 0x0000000f //Mask for ADC Mux
|
||||
#define ADC_MUX_0 0x00000001 //Value to select Unknown at ADC Mux (Not used)
|
||||
#define ADC_MUX_1 0x00000002 //Value to select Unknown at ADC Mux (Not used)
|
||||
#define ADC_MUX_2 0x00000004 //Value to select Mic at ADC Mux
|
||||
#define ADC_MUX_3 0x00000008 //Value to select Line-In at ADC Mux
|
||||
|
||||
#define P17V_START_AUDIO 0x40 /* Start Audio bit */
|
||||
/* 41 - 47: Reserved */
|
||||
#define P17V_START_CAPTURE 0x48 /* Start Capture bit */
|
||||
#define P17V_CAPTURE_FIFO_BASE 0x49 /* Record FIFO base address */
|
||||
#define P17V_CAPTURE_FIFO_SIZE 0x4a /* Record FIFO buffer size */
|
||||
#define P17V_CAPTURE_FIFO_INDEX 0x4b /* Record FIFO capture index */
|
||||
#define P17V_CAPTURE_VOL_H 0x4c /* P17v capture volume control */
|
||||
#define P17V_CAPTURE_VOL_L 0x4d /* P17v capture volume control */
|
||||
/* 4e - 4f: Not used */
|
||||
/* 50 - 5f: Not used */
|
||||
#define P17V_SRCSel 0x60 /* SRC48 and SRCMulti sample rate select
|
||||
* and output select
|
||||
*/
|
||||
#define P17V_MIXER_AC97_10K1_VOL_L 0x61 /* 10K to Mixer_AC97 input volume control */
|
||||
#define P17V_MIXER_AC97_10K1_VOL_H 0x62 /* 10K to Mixer_AC97 input volume control */
|
||||
#define P17V_MIXER_AC97_P17V_VOL_L 0x63 /* P17V to Mixer_AC97 input volume control */
|
||||
#define P17V_MIXER_AC97_P17V_VOL_H 0x64 /* P17V to Mixer_AC97 input volume control */
|
||||
#define P17V_MIXER_AC97_SRP_REC_VOL_L 0x65 /* SRP Record to Mixer_AC97 input volume control */
|
||||
#define P17V_MIXER_AC97_SRP_REC_VOL_H 0x66 /* SRP Record to Mixer_AC97 input volume control */
|
||||
/* 67 - 68: Reserved */
|
||||
#define P17V_MIXER_Spdif_10K1_VOL_L 0x69 /* 10K to Mixer_Spdif input volume control */
|
||||
#define P17V_MIXER_Spdif_10K1_VOL_H 0x6A /* 10K to Mixer_Spdif input volume control */
|
||||
#define P17V_MIXER_Spdif_P17V_VOL_L 0x6B /* P17V to Mixer_Spdif input volume control */
|
||||
#define P17V_MIXER_Spdif_P17V_VOL_H 0x6C /* P17V to Mixer_Spdif input volume control */
|
||||
#define P17V_MIXER_Spdif_SRP_REC_VOL_L 0x6D /* SRP Record to Mixer_Spdif input volume control */
|
||||
#define P17V_MIXER_Spdif_SRP_REC_VOL_H 0x6E /* SRP Record to Mixer_Spdif input volume control */
|
||||
/* 6f - 70: Reserved */
|
||||
#define P17V_MIXER_I2S_10K1_VOL_L 0x71 /* 10K to Mixer_I2S input volume control */
|
||||
#define P17V_MIXER_I2S_10K1_VOL_H 0x72 /* 10K to Mixer_I2S input volume control */
|
||||
#define P17V_MIXER_I2S_P17V_VOL_L 0x73 /* P17V to Mixer_I2S input volume control */
|
||||
#define P17V_MIXER_I2S_P17V_VOL_H 0x74 /* P17V to Mixer_I2S input volume control */
|
||||
#define P17V_MIXER_I2S_SRP_REC_VOL_L 0x75 /* SRP Record to Mixer_I2S input volume control */
|
||||
#define P17V_MIXER_I2S_SRP_REC_VOL_H 0x76 /* SRP Record to Mixer_I2S input volume control */
|
||||
/* 77 - 78: Reserved */
|
||||
#define P17V_MIXER_AC97_ENABLE 0x79 /* Mixer AC97 input audio enable */
|
||||
#define P17V_MIXER_SPDIF_ENABLE 0x7A /* Mixer SPDIF input audio enable */
|
||||
#define P17V_MIXER_I2S_ENABLE 0x7B /* Mixer I2S input audio enable */
|
||||
#define P17V_AUDIO_OUT_ENABLE 0x7C /* Audio out enable */
|
||||
#define P17V_MIXER_ATT 0x7D /* SRP Mixer Attenuation Select */
|
||||
#define P17V_SRP_RECORD_SRR 0x7E /* SRP Record channel source Select */
|
||||
#define P17V_SOFT_RESET_SRP_MIXER 0x7F /* SRP and mixer soft reset */
|
||||
|
||||
#define P17V_AC97_OUT_MASTER_VOL_L 0x80 /* AC97 Output master volume control */
|
||||
#define P17V_AC97_OUT_MASTER_VOL_H 0x81 /* AC97 Output master volume control */
|
||||
#define P17V_SPDIF_OUT_MASTER_VOL_L 0x82 /* SPDIF Output master volume control */
|
||||
#define P17V_SPDIF_OUT_MASTER_VOL_H 0x83 /* SPDIF Output master volume control */
|
||||
#define P17V_I2S_OUT_MASTER_VOL_L 0x84 /* I2S Output master volume control */
|
||||
#define P17V_I2S_OUT_MASTER_VOL_H 0x85 /* I2S Output master volume control */
|
||||
/* 86 - 87: Not used */
|
||||
#define P17V_I2S_CHANNEL_SWAP_PHASE_INVERSE 0x88 /* I2S out mono channel swap
|
||||
* and phase inverse */
|
||||
#define P17V_SPDIF_CHANNEL_SWAP_PHASE_INVERSE 0x89 /* SPDIF out mono channel swap
|
||||
* and phase inverse */
|
||||
/* 8A: Not used */
|
||||
#define P17V_SRP_P17V_ESR 0x8B /* SRP_P17V estimated sample rate and rate lock */
|
||||
#define P17V_SRP_REC_ESR 0x8C /* SRP_REC estimated sample rate and rate lock */
|
||||
#define P17V_SRP_BYPASS 0x8D /* srps channel bypass and srps bypass */
|
||||
/* 8E - 92: Not used */
|
||||
#define P17V_I2S_SRC_SEL 0x93 /* I2SIN mode sel */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
96
sound/pci/emu10k1/timer.c
Normal file
96
sound/pci/emu10k1/timer.c
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) by Lee Revell <rlrevell@joe-job.com>
|
||||
* Clemens Ladisch <clemens@ladisch.de>
|
||||
* Routines for control of EMU10K1 chips
|
||||
*
|
||||
* BUGS:
|
||||
* --
|
||||
*
|
||||
* TODO:
|
||||
* --
|
||||
*
|
||||
* 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/time.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/emu10k1.h>
|
||||
|
||||
static int snd_emu10k1_timer_start(struct snd_timer *timer)
|
||||
{
|
||||
struct snd_emu10k1 *emu;
|
||||
unsigned long flags;
|
||||
unsigned int delay;
|
||||
|
||||
emu = snd_timer_chip(timer);
|
||||
delay = timer->sticks - 1;
|
||||
if (delay < 5 ) /* minimum time is 5 ticks */
|
||||
delay = 5;
|
||||
spin_lock_irqsave(&emu->reg_lock, flags);
|
||||
snd_emu10k1_intr_enable(emu, INTE_INTERVALTIMERENB);
|
||||
outw(delay & TIMER_RATE_MASK, emu->port + TIMER);
|
||||
spin_unlock_irqrestore(&emu->reg_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_emu10k1_timer_stop(struct snd_timer *timer)
|
||||
{
|
||||
struct snd_emu10k1 *emu;
|
||||
unsigned long flags;
|
||||
|
||||
emu = snd_timer_chip(timer);
|
||||
spin_lock_irqsave(&emu->reg_lock, flags);
|
||||
snd_emu10k1_intr_disable(emu, INTE_INTERVALTIMERENB);
|
||||
spin_unlock_irqrestore(&emu->reg_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_emu10k1_timer_precise_resolution(struct snd_timer *timer,
|
||||
unsigned long *num, unsigned long *den)
|
||||
{
|
||||
*num = 1;
|
||||
*den = 48000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_timer_hardware snd_emu10k1_timer_hw = {
|
||||
.flags = SNDRV_TIMER_HW_AUTO,
|
||||
.resolution = 20833, /* 1 sample @ 48KHZ = 20.833...us */
|
||||
.ticks = 1024,
|
||||
.start = snd_emu10k1_timer_start,
|
||||
.stop = snd_emu10k1_timer_stop,
|
||||
.precise_resolution = snd_emu10k1_timer_precise_resolution,
|
||||
};
|
||||
|
||||
int snd_emu10k1_timer(struct snd_emu10k1 *emu, int device)
|
||||
{
|
||||
struct snd_timer *timer = NULL;
|
||||
struct snd_timer_id tid;
|
||||
int err;
|
||||
|
||||
tid.dev_class = SNDRV_TIMER_CLASS_CARD;
|
||||
tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
|
||||
tid.card = emu->card->number;
|
||||
tid.device = device;
|
||||
tid.subdevice = 0;
|
||||
if ((err = snd_timer_new(emu->card, "EMU10K1", &tid, &timer)) >= 0) {
|
||||
strcpy(timer->name, "EMU10K1 timer");
|
||||
timer->private_data = emu;
|
||||
timer->hw = snd_emu10k1_timer_hw;
|
||||
}
|
||||
emu->timer = timer;
|
||||
return err;
|
||||
}
|
32
sound/pci/emu10k1/tina2.h
Normal file
32
sound/pci/emu10k1/tina2.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) by James Courtier-Dutton <James@superbug.demon.co.uk>
|
||||
* Driver tina2 chips
|
||||
* Version: 0.1
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
/********************************************************************************************************/
|
||||
/* Audigy2 Tina2 (notebook) pointer-offset register set, accessed through the PTR2 and DATA2 registers */
|
||||
/********************************************************************************************************/
|
||||
|
||||
#define TINA2_VOLUME 0x71 /* Attenuate playback volume to prevent distortion. */
|
||||
/* The windows driver does not use this register,
|
||||
* so it must use some other attenuation method.
|
||||
* Without this, the output is 12dB too loud,
|
||||
* resulting in distortion.
|
||||
*/
|
||||
|
168
sound/pci/emu10k1/voice.c
Normal file
168
sound/pci/emu10k1/voice.c
Normal file
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
|
||||
* Creative Labs, Inc.
|
||||
* Lee Revell <rlrevell@joe-job.com>
|
||||
* Routines for control of EMU10K1 chips - voice manager
|
||||
*
|
||||
* Rewrote voice allocator for multichannel support - rlrevell 12/2004
|
||||
*
|
||||
* BUGS:
|
||||
* --
|
||||
*
|
||||
* TODO:
|
||||
* --
|
||||
*
|
||||
* 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/time.h>
|
||||
#include <linux/export.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/emu10k1.h>
|
||||
|
||||
/* Previously the voice allocator started at 0 every time. The new voice
|
||||
* allocator uses a round robin scheme. The next free voice is tracked in
|
||||
* the card record and each allocation begins where the last left off. The
|
||||
* hardware requires stereo interleaved voices be aligned to an even/odd
|
||||
* boundary. For multichannel voice allocation we ensure than the block of
|
||||
* voices does not cross the 32 voice boundary. This simplifies the
|
||||
* multichannel support and ensures we can use a single write to the
|
||||
* (set|clear)_loop_stop registers. Otherwise (for example) the voices would
|
||||
* get out of sync when pausing/resuming a stream.
|
||||
* --rlrevell
|
||||
*/
|
||||
|
||||
static int voice_alloc(struct snd_emu10k1 *emu, int type, int number,
|
||||
struct snd_emu10k1_voice **rvoice)
|
||||
{
|
||||
struct snd_emu10k1_voice *voice;
|
||||
int i, j, k, first_voice, last_voice, skip;
|
||||
|
||||
*rvoice = NULL;
|
||||
first_voice = last_voice = 0;
|
||||
for (i = emu->next_free_voice, j = 0; j < NUM_G ; i += number, j += number) {
|
||||
/*
|
||||
dev_dbg(emu->card->dev, "i %d j %d next free %d!\n",
|
||||
i, j, emu->next_free_voice);
|
||||
*/
|
||||
i %= NUM_G;
|
||||
|
||||
/* stereo voices must be even/odd */
|
||||
if ((number == 2) && (i % 2)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
skip = 0;
|
||||
for (k = 0; k < number; k++) {
|
||||
voice = &emu->voices[(i+k) % NUM_G];
|
||||
if (voice->use) {
|
||||
skip = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!skip) {
|
||||
/* dev_dbg(emu->card->dev, "allocated voice %d\n", i); */
|
||||
first_voice = i;
|
||||
last_voice = (i + number) % NUM_G;
|
||||
emu->next_free_voice = last_voice;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (first_voice == last_voice)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < number; i++) {
|
||||
voice = &emu->voices[(first_voice + i) % NUM_G];
|
||||
/*
|
||||
dev_dbg(emu->card->dev, "voice alloc - %i, %i of %i\n",
|
||||
voice->number, idx-first_voice+1, number);
|
||||
*/
|
||||
voice->use = 1;
|
||||
switch (type) {
|
||||
case EMU10K1_PCM:
|
||||
voice->pcm = 1;
|
||||
break;
|
||||
case EMU10K1_SYNTH:
|
||||
voice->synth = 1;
|
||||
break;
|
||||
case EMU10K1_MIDI:
|
||||
voice->midi = 1;
|
||||
break;
|
||||
case EMU10K1_EFX:
|
||||
voice->efx = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*rvoice = &emu->voices[first_voice];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_emu10k1_voice_alloc(struct snd_emu10k1 *emu, int type, int number,
|
||||
struct snd_emu10k1_voice **rvoice)
|
||||
{
|
||||
unsigned long flags;
|
||||
int result;
|
||||
|
||||
if (snd_BUG_ON(!rvoice))
|
||||
return -EINVAL;
|
||||
if (snd_BUG_ON(!number))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&emu->voice_lock, flags);
|
||||
for (;;) {
|
||||
result = voice_alloc(emu, type, number, rvoice);
|
||||
if (result == 0 || type == EMU10K1_SYNTH || type == EMU10K1_MIDI)
|
||||
break;
|
||||
|
||||
/* free a voice from synth */
|
||||
if (emu->get_synth_voice) {
|
||||
result = emu->get_synth_voice(emu);
|
||||
if (result >= 0) {
|
||||
struct snd_emu10k1_voice *pvoice = &emu->voices[result];
|
||||
pvoice->interrupt = NULL;
|
||||
pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
|
||||
pvoice->epcm = NULL;
|
||||
}
|
||||
}
|
||||
if (result < 0)
|
||||
break;
|
||||
}
|
||||
spin_unlock_irqrestore(&emu->voice_lock, flags);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_emu10k1_voice_alloc);
|
||||
|
||||
int snd_emu10k1_voice_free(struct snd_emu10k1 *emu,
|
||||
struct snd_emu10k1_voice *pvoice)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (snd_BUG_ON(!pvoice))
|
||||
return -EINVAL;
|
||||
spin_lock_irqsave(&emu->voice_lock, flags);
|
||||
pvoice->interrupt = NULL;
|
||||
pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = pvoice->efx = 0;
|
||||
pvoice->epcm = NULL;
|
||||
snd_emu10k1_voice_init(emu, pvoice->number);
|
||||
spin_unlock_irqrestore(&emu->voice_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(snd_emu10k1_voice_free);
|
Loading…
Add table
Add a link
Reference in a new issue