android_kernel_xiaomi_sm8350/sound/oss/emu10k1/main.c
David Howells 7d12e780e0 IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.

The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around.  On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).

Where appropriate, an arch may override the generic storage facility and do
something different with the variable.  On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.

Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions.  Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller.  A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.

I've build this code with allyesconfig for x86_64 and i386.  I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.

This will affect all archs.  Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:

	struct pt_regs *old_regs = set_irq_regs(regs);

And put the old one back at the end:

	set_irq_regs(old_regs);

Don't pass regs through to generic_handle_irq() or __do_IRQ().

In timer_interrupt(), this sort of change will be necessary:

	-	update_process_times(user_mode(regs));
	-	profile_tick(CPU_PROFILING, regs);
	+	update_process_times(user_mode(get_irq_regs()));
	+	profile_tick(CPU_PROFILING);

I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().

Some notes on the interrupt handling in the drivers:

 (*) input_dev() is now gone entirely.  The regs pointer is no longer stored in
     the input_dev struct.

 (*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking.  It does
     something different depending on whether it's been supplied with a regs
     pointer or not.

 (*) Various IRQ handler function pointers have been moved to type
     irq_handler_t.

Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 15:10:12 +01:00

1477 lines
41 KiB
C

/*
**********************************************************************
* main.c - Creative EMU10K1 audio driver
* Copyright 1999, 2000 Creative Labs, Inc.
*
**********************************************************************
*
* Date Author Summary of changes
* ---- ------ ------------------
* October 20, 1999 Bertrand Lee base code release
* November 2, 1999 Alan Cox cleaned up stuff
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
* USA.
*
**********************************************************************
*
* Supported devices:
* /dev/dsp: Standard /dev/dsp device, OSS-compatible
* /dev/dsp1: Routes to rear speakers only
* /dev/mixer: Standard /dev/mixer device, OSS-compatible
* /dev/midi: Raw MIDI UART device, mostly OSS-compatible
* /dev/sequencer: Sequencer Interface (requires sound.o)
*
* Revision history:
* 0.1 beta Initial release
* 0.2 Lowered initial mixer vol. Improved on stuttering wave playback. Added MIDI UART support.
* 0.3 Fixed mixer routing bug, added APS, joystick support.
* 0.4 Added rear-channel, SPDIF support.
* 0.5 Source cleanup, SMP fixes, multiopen support, 64 bit arch fixes,
* moved bh's to tasklets, moved to the new PCI driver initialization style.
* 0.6 Make use of pci_alloc_consistent, improve compatibility layer for 2.2 kernels,
* code reorganization and cleanup.
* 0.7 Support for the Emu-APS. Bug fixes for voice cache setup, mmaped sound + poll().
* Support for setting external TRAM size.
* 0.8 Make use of the kernel ac97 interface. Support for a dsp patch manager.
* 0.9 Re-enables rear speakers volume controls
* 0.10 Initializes rear speaker volume.
* Dynamic patch storage allocation.
* New private ioctls to change control gpr values.
* Enable volume control interrupts.
* By default enable dsp routes to digital out.
* 0.11 Fixed fx / 4 problem.
* 0.12 Implemented mmaped for recording.
* Fixed bug: not unreserving mmaped buffer pages.
* IRQ handler cleanup.
* 0.13 Fixed problem with dsp1
* Simplified dsp patch writing (inside the driver)
* Fixed several bugs found by the Stanford tools
* 0.14 New control gpr to oss mixer mapping feature (Chris Purnell)
* Added AC3 Passthrough Support (Juha Yrjola)
* Added Support for 5.1 cards (digital out and the third analog out)
* 0.15 Added Sequencer Support (Daniel Mack)
* Support for multichannel pcm playback (Eduard Hasenleithner)
* 0.16 Mixer improvements, added old treble/bass support (Daniel Bertrand)
* Small code format cleanup.
* Deadlock bug fix for emu10k1_volxxx_irqhandler().
* 0.17 Fix for mixer SOUND_MIXER_INFO ioctl.
* Fix for HIGHMEM machines (emu10k1 can only do 31 bit bus master)
* midi poll initial implementation.
* Small mixer fixes/cleanups.
* Improved support for 5.1 cards.
* 0.18 Fix for possible leak in pci_alloc_consistent()
* Cleaned up poll() functions (audio and midi). Don't start input.
* Restrict DMA pages used to 512Mib range.
* New AC97_BOOST mixer ioctl.
* 0.19a Added Support for Audigy Cards
* Real fix for kernel with highmem support (cast dma_handle to u32).
* Fix recording buffering parameters calculation.
* Use unsigned long for variables in bit ops.
* 0.20a Fixed recording startup
* Fixed timer rate setting (it's a 16-bit register)
* 0.21 Converted code to use pci_name() instead of accessing slot_name
* directly (Eugene Teo)
*********************************************************************/
/* These are only included once per module */
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/dma-mapping.h>
#include "hwaccess.h"
#include "8010.h"
#include "efxmgr.h"
#include "cardwo.h"
#include "cardwi.h"
#include "cardmo.h"
#include "cardmi.h"
#include "recmgr.h"
#include "ecard.h"
#ifdef EMU10K1_SEQUENCER
#define MIDI_SYNTH_NAME "EMU10K1 MIDI"
#define MIDI_SYNTH_CAPS SYNTH_CAP_INPUT
#include "../sound_config.h"
#include "../midi_synth.h"
/* this should be in dev_table.h */
#define SNDCARD_EMU10K1 46
#endif
/* the emu10k1 _seems_ to only supports 29 bit (512MiB) bit bus master */
#define EMU10K1_DMA_MASK DMA_29BIT_MASK /* DMA buffer mask for pci_alloc_consist */
#ifndef PCI_VENDOR_ID_CREATIVE
#define PCI_VENDOR_ID_CREATIVE 0x1102
#endif
#ifndef PCI_DEVICE_ID_CREATIVE_EMU10K1
#define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002
#endif
#ifndef PCI_DEVICE_ID_CREATIVE_AUDIGY
#define PCI_DEVICE_ID_CREATIVE_AUDIGY 0x0004
#endif
#define EMU_APS_SUBID 0x40011102
enum {
EMU10K1 = 0,
AUDIGY,
};
static char *card_names[] __devinitdata = {
"EMU10K1",
"Audigy",
};
static struct pci_device_id emu10k1_pci_tbl[] = {
{PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_EMU10K1,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, EMU10K1},
{PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_AUDIGY,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, AUDIGY},
{0,}
};
MODULE_DEVICE_TABLE(pci, emu10k1_pci_tbl);
/* Global var instantiation */
LIST_HEAD(emu10k1_devs);
extern struct file_operations emu10k1_audio_fops;
extern struct file_operations emu10k1_mixer_fops;
extern struct file_operations emu10k1_midi_fops;
#ifdef EMU10K1_SEQUENCER
static struct midi_operations emu10k1_midi_operations;
#endif
extern irqreturn_t emu10k1_interrupt(int, void *);
static int __devinit emu10k1_audio_init(struct emu10k1_card *card)
{
/* Assign default playback voice parameters */
if (card->is_audigy)
card->mchannel_fx = 0;
else
card->mchannel_fx = 8;
if (card->is_audigy) {
/* mono voice */
card->waveout.send_dcba[SEND_MONO] = 0xffffffff;
card->waveout.send_hgfe[SEND_MONO] = 0x0000ffff;
/* stereo voice */
/* left */
card->waveout.send_dcba[SEND_LEFT] = 0x00ff00ff;
card->waveout.send_hgfe[SEND_LEFT] = 0x00007f7f;
/* right */
card->waveout.send_dcba[SEND_RIGHT] = 0xff00ff00;
card->waveout.send_hgfe[SEND_RIGHT] = 0x00007f7f;
card->waveout.send_routing[ROUTE_PCM] = 0x03020100; // Regular pcm
card->waveout.send_routing2[ROUTE_PCM] = 0x07060504;
card->waveout.send_routing[ROUTE_PT] = 0x3f3f3d3c; // Passthrough
card->waveout.send_routing2[ROUTE_PT] = 0x3f3f3f3f;
card->waveout.send_routing[ROUTE_PCM1] = 0x03020100; // Spare
card->waveout.send_routing2[ROUTE_PCM1] = 0x07060404;
} else {
/* mono voice */
card->waveout.send_dcba[SEND_MONO] = 0x0000ffff;
/* stereo voice */
/* left */
card->waveout.send_dcba[SEND_LEFT] = 0x000000ff;
/* right */
card->waveout.send_dcba[SEND_RIGHT] = 0x0000ff00;
card->waveout.send_routing[ROUTE_PCM] = 0x3210; // pcm
card->waveout.send_routing[ROUTE_PT] = 0x3210; // passthrough
card->waveout.send_routing[ROUTE_PCM1] = 0x7654; // /dev/dsp1
}
/* Assign default recording parameters */
/* FIXME */
if (card->is_aps)
card->wavein.recsrc = WAVERECORD_FX;
else
card->wavein.recsrc = WAVERECORD_AC97;
card->wavein.fxwc = 0x0003;
return 0;
}
static void emu10k1_audio_cleanup(struct emu10k1_card *card)
{
}
static int __devinit emu10k1_register_devices(struct emu10k1_card *card)
{
card->audio_dev = register_sound_dsp(&emu10k1_audio_fops, -1);
if (card->audio_dev < 0) {
printk(KERN_ERR "emu10k1: cannot register first audio device!\n");
goto err_dev;
}
card->audio_dev1 = register_sound_dsp(&emu10k1_audio_fops, -1);
if (card->audio_dev1 < 0) {
printk(KERN_ERR "emu10k1: cannot register second audio device!\n");
goto err_dev1;
}
card->ac97->dev_mixer = register_sound_mixer(&emu10k1_mixer_fops, -1);
if (card->ac97->dev_mixer < 0) {
printk(KERN_ERR "emu10k1: cannot register mixer device\n");
goto err_mixer;
}
card->midi_dev = register_sound_midi(&emu10k1_midi_fops, -1);
if (card->midi_dev < 0) {
printk(KERN_ERR "emu10k1: cannot register midi device!\n");
goto err_midi;
}
#ifdef EMU10K1_SEQUENCER
card->seq_dev = sound_alloc_mididev();
if (card->seq_dev == -1)
printk(KERN_WARNING "emu10k1: unable to register sequencer device!");
else {
std_midi_synth.midi_dev = card->seq_dev;
midi_devs[card->seq_dev] =
(struct midi_operations *)
kmalloc(sizeof(struct midi_operations), GFP_KERNEL);
if (midi_devs[card->seq_dev] == NULL) {
printk(KERN_ERR "emu10k1: unable to allocate memory!");
sound_unload_mididev(card->seq_dev);
card->seq_dev = -1;
/* return without error */
} else {
memcpy((char *)midi_devs[card->seq_dev],
(char *)&emu10k1_midi_operations,
sizeof(struct midi_operations));
midi_devs[card->seq_dev]->devc = card;
sequencer_init();
card->seq_mididev = NULL;
}
}
#endif
return 0;
err_midi:
unregister_sound_mixer(card->ac97->dev_mixer);
err_mixer:
unregister_sound_dsp(card->audio_dev);
err_dev1:
unregister_sound_dsp(card->audio_dev);
err_dev:
return -ENODEV;
}
static void emu10k1_unregister_devices(struct emu10k1_card *card)
{
#ifdef EMU10K1_SEQUENCER
if (card->seq_dev > -1) {
kfree(midi_devs[card->seq_dev]);
midi_devs[card->seq_dev] = NULL;
sound_unload_mididev(card->seq_dev);
card->seq_dev = -1;
}
#endif
unregister_sound_midi(card->midi_dev);
unregister_sound_mixer(card->ac97->dev_mixer);
unregister_sound_dsp(card->audio_dev1);
unregister_sound_dsp(card->audio_dev);
}
static int emu10k1_info_proc (char *page, char **start, off_t off,
int count, int *eof, void *data)
{
struct emu10k1_card *card = data;
int len = 0;
if (card == NULL)
return -ENODEV;
len += sprintf (page + len, "Driver Version : %s\n", DRIVER_VERSION);
len += sprintf (page + len, "Card type : %s\n", card->is_aps ? "Aps" : (card->is_audigy ? "Audigy" : "Emu10k1"));
len += sprintf (page + len, "Revision : %d\n", card->chiprev);
len += sprintf (page + len, "Model : %#06x\n", card->model);
len += sprintf (page + len, "IO : %#06lx-%#06lx\n", card->iobase, card->iobase + card->length - 1);
len += sprintf (page + len, "IRQ : %d\n\n", card->irq);
len += sprintf (page + len, "Registered /dev Entries:\n");
len += sprintf (page + len, "/dev/dsp%d\n", card->audio_dev / 16);
len += sprintf (page + len, "/dev/dsp%d\n", card->audio_dev1 / 16);
len += sprintf (page + len, "/dev/mixer%d\n", card->ac97->dev_mixer / 16);
len += sprintf (page + len, "/dev/midi%d\n", card->midi_dev / 16);
#ifdef EMU10K1_SEQUENCER
len += sprintf (page + len, "/dev/sequencer\n");
#endif
return len;
}
static int __devinit emu10k1_proc_init(struct emu10k1_card *card)
{
char s[48];
if (!proc_mkdir ("driver/emu10k1", NULL)) {
printk(KERN_ERR "emu10k1: unable to create proc directory driver/emu10k1\n");
goto err_out;
}
sprintf(s, "driver/emu10k1/%s", pci_name(card->pci_dev));
if (!proc_mkdir (s, NULL)) {
printk(KERN_ERR "emu10k1: unable to create proc directory %s\n", s);
goto err_emu10k1_proc;
}
sprintf(s, "driver/emu10k1/%s/info", pci_name(card->pci_dev));
if (!create_proc_read_entry (s, 0, NULL, emu10k1_info_proc, card)) {
printk(KERN_ERR "emu10k1: unable to create proc entry %s\n", s);
goto err_dev_proc;
}
if (!card->is_aps) {
sprintf(s, "driver/emu10k1/%s/ac97", pci_name(card->pci_dev));
if (!create_proc_read_entry (s, 0, NULL, ac97_read_proc, card->ac97)) {
printk(KERN_ERR "emu10k1: unable to create proc entry %s\n", s);
goto err_proc_ac97;
}
}
return 0;
err_proc_ac97:
sprintf(s, "driver/emu10k1/%s/info", pci_name(card->pci_dev));
remove_proc_entry(s, NULL);
err_dev_proc:
sprintf(s, "driver/emu10k1/%s", pci_name(card->pci_dev));
remove_proc_entry(s, NULL);
err_emu10k1_proc:
remove_proc_entry("driver/emu10k1", NULL);
err_out:
return -EIO;
}
static void emu10k1_proc_cleanup(struct emu10k1_card *card)
{
char s[48];
if (!card->is_aps) {
sprintf(s, "driver/emu10k1/%s/ac97", pci_name(card->pci_dev));
remove_proc_entry(s, NULL);
}
sprintf(s, "driver/emu10k1/%s/info", pci_name(card->pci_dev));
remove_proc_entry(s, NULL);
sprintf(s, "driver/emu10k1/%s", pci_name(card->pci_dev));
remove_proc_entry(s, NULL);
remove_proc_entry("driver/emu10k1", NULL);
}
static int __devinit emu10k1_mixer_init(struct emu10k1_card *card)
{
struct ac97_codec *codec = ac97_alloc_codec();
if(codec == NULL)
{
printk(KERN_ERR "emu10k1: cannot allocate mixer\n");
return -EIO;
}
card->ac97 = codec;
card->ac97->private_data = card;
if (!card->is_aps) {
card->ac97->id = 0;
card->ac97->codec_read = emu10k1_ac97_read;
card->ac97->codec_write = emu10k1_ac97_write;
if (ac97_probe_codec (card->ac97) == 0) {
printk(KERN_ERR "emu10k1: unable to probe AC97 codec\n");
goto err_out;
}
/* 5.1: Enable the additional AC97 Slots and unmute extra channels on AC97 codec */
if (codec->codec_read(codec, AC97_EXTENDED_ID) & 0x0080){
printk(KERN_INFO "emu10k1: SBLive! 5.1 card detected\n");
sblive_writeptr(card, AC97SLOT, 0, AC97SLOT_CNTR | AC97SLOT_LFE);
codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0);
}
// Force 5bit:
//card->ac97->bit_resolution=5;
/* these will store the original values and never be modified */
card->ac97_supported_mixers = card->ac97->supported_mixers;
card->ac97_stereo_mixers = card->ac97->stereo_mixers;
}
return 0;
err_out:
ac97_release_codec(card->ac97);
return -EIO;
}
static void emu10k1_mixer_cleanup(struct emu10k1_card *card)
{
ac97_release_codec(card->ac97);
}
static int __devinit emu10k1_midi_init(struct emu10k1_card *card)
{
int ret;
card->mpuout = kmalloc(sizeof(struct emu10k1_mpuout), GFP_KERNEL);
if (card->mpuout == NULL) {
printk(KERN_WARNING "emu10k1: Unable to allocate emu10k1_mpuout: out of memory\n");
ret = -ENOMEM;
goto err_out1;
}
memset(card->mpuout, 0, sizeof(struct emu10k1_mpuout));
card->mpuout->intr = 1;
card->mpuout->status = FLAGS_AVAILABLE;
card->mpuout->state = CARDMIDIOUT_STATE_DEFAULT;
tasklet_init(&card->mpuout->tasklet, emu10k1_mpuout_bh, (unsigned long) card);
spin_lock_init(&card->mpuout->lock);
card->mpuin = kmalloc(sizeof(struct emu10k1_mpuin), GFP_KERNEL);
if (card->mpuin == NULL) {
printk(KERN_WARNING "emu10k1: Unable to allocate emu10k1_mpuin: out of memory\n");
ret = -ENOMEM;
goto err_out2;
}
memset(card->mpuin, 0, sizeof(struct emu10k1_mpuin));
card->mpuin->status = FLAGS_AVAILABLE;
tasklet_init(&card->mpuin->tasklet, emu10k1_mpuin_bh, (unsigned long) card->mpuin);
spin_lock_init(&card->mpuin->lock);
/* Reset the MPU port */
if (emu10k1_mpu_reset(card) < 0) {
ERROR();
ret = -EIO;
goto err_out3;
}
return 0;
err_out3:
kfree(card->mpuin);
err_out2:
kfree(card->mpuout);
err_out1:
return ret;
}
static void emu10k1_midi_cleanup(struct emu10k1_card *card)
{
tasklet_kill(&card->mpuout->tasklet);
kfree(card->mpuout);
tasklet_kill(&card->mpuin->tasklet);
kfree(card->mpuin);
}
static void __devinit voice_init(struct emu10k1_card *card)
{
int i;
for (i = 0; i < NUM_G; i++)
card->voicetable[i] = VOICE_USAGE_FREE;
}
static void __devinit timer_init(struct emu10k1_card *card)
{
INIT_LIST_HEAD(&card->timers);
card->timer_delay = TIMER_STOPPED;
spin_lock_init(&card->timer_lock);
}
static void __devinit addxmgr_init(struct emu10k1_card *card)
{
u32 count;
for (count = 0; count < MAXPAGES; count++)
card->emupagetable[count] = 0;
/* Mark first page as used */
/* This page is reserved by the driver */
card->emupagetable[0] = 0x8001;
card->emupagetable[1] = MAXPAGES - 1;
}
static void fx_cleanup(struct patch_manager *mgr)
{
int i;
for(i = 0; i < mgr->current_pages; i++)
free_page((unsigned long) mgr->patch[i]);
}
static int __devinit fx_init(struct emu10k1_card *card)
{
struct patch_manager *mgr = &card->mgr;
struct dsp_patch *patch;
struct dsp_rpatch *rpatch;
s32 left, right;
int i;
u32 pc = 0;
u32 patch_n=0;
struct emu_efx_info_t emu_efx_info[2]=
{{ 20, 10, 0x400, 0x100, 0x20 },
{ 24, 12, 0x600, 0x400, 0x60 },
};
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
mgr->ctrl_gpr[i][0] = -1;
mgr->ctrl_gpr[i][1] = -1;
}
if (card->is_audigy)
mgr->current_pages = (2 + PATCHES_PER_PAGE - 1) / PATCHES_PER_PAGE;
else
/* !! The number below must equal the number of patches, currently 11 !! */
mgr->current_pages = (11 + PATCHES_PER_PAGE - 1) / PATCHES_PER_PAGE;
for (i = 0; i < mgr->current_pages; i++) {
mgr->patch[i] = (void *)__get_free_page(GFP_KERNEL);
if (mgr->patch[i] == NULL) {
mgr->current_pages = i;
fx_cleanup(mgr);
return -ENOMEM;
}
memset(mgr->patch[i], 0, PAGE_SIZE);
}
if (card->is_audigy) {
for (i = 0; i < 1024; i++)
OP(0xf, 0x0c0, 0x0c0, 0x0cf, 0x0c0);
for (i = 0; i < 512 ; i++)
sblive_writeptr(card, A_GPR_BASE+i,0,0);
pc=0;
//Pcm input volume
OP(0, 0x402, 0x0c0, 0x406, 0x000);
OP(0, 0x403, 0x0c0, 0x407, 0x001);
//CD-Digital input Volume
OP(0, 0x404, 0x0c0, 0x40d, 0x42);
OP(0, 0x405, 0x0c0, 0x40f, 0x43);
// CD + PCM
OP(6, 0x400, 0x0c0, 0x402, 0x404);
OP(6, 0x401, 0x0c0, 0x403, 0x405);
// Front Output + Master Volume
OP(0, 0x68, 0x0c0, 0x408, 0x400);
OP(0, 0x69, 0x0c0, 0x409, 0x401);
// Add-in analog inputs for other speakers
OP(6, 0x400, 0x40, 0x400, 0xc0);
OP(6, 0x401, 0x41, 0x401, 0xc0);
// Digital Front + Master Volume
OP(0, 0x60, 0x0c0, 0x408, 0x400);
OP(0, 0x61, 0x0c0, 0x409, 0x401);
// Rear Output + Rear Volume
OP(0, 0x06e, 0x0c0, 0x419, 0x400);
OP(0, 0x06f, 0x0c0, 0x41a, 0x401);
// Digital Rear Output + Rear Volume
OP(0, 0x066, 0x0c0, 0x419, 0x400);
OP(0, 0x067, 0x0c0, 0x41a, 0x401);
// Audigy Drive, Headphone out
OP(6, 0x64, 0x0c0, 0x0c0, 0x400);
OP(6, 0x65, 0x0c0, 0x0c0, 0x401);
// ac97 Recording
OP(6, 0x76, 0x0c0, 0x0c0, 0x40);
OP(6, 0x77, 0x0c0, 0x0c0, 0x41);
// Center = sub = Left/2 + Right/2
OP(0xe, 0x400, 0x401, 0xcd, 0x400);
// center/sub Volume (master)
OP(0, 0x06a, 0x0c0, 0x408, 0x400);
OP(0, 0x06b, 0x0c0, 0x409, 0x400);
// Digital center/sub Volume (master)
OP(0, 0x062, 0x0c0, 0x408, 0x400);
OP(0, 0x063, 0x0c0, 0x409, 0x400);
ROUTING_PATCH_START(rpatch, "Routing");
ROUTING_PATCH_END(rpatch);
/* delimiter patch */
patch = PATCH(mgr, patch_n);
patch->code_size = 0;
sblive_writeptr(card, 0x53, 0, 0);
} else {
for (i = 0; i < 512 ; i++)
OP(6, 0x40, 0x40, 0x40, 0x40);
for (i = 0; i < 256; i++)
sblive_writeptr_tag(card, 0,
FXGPREGBASE + i, 0,
TANKMEMADDRREGBASE + i, 0,
TAGLIST_END);
pc = 0;
//first free GPR = 0x11b
/* FX volume correction and Volume control*/
INPUT_PATCH_START(patch, "Pcm L vol", 0x0, 0);
GET_OUTPUT_GPR(patch, 0x100, 0x0);
GET_CONTROL_GPR(patch, 0x106, "Vol", 0, 0x7fffffff);
GET_DYNAMIC_GPR(patch, 0x112);
OP(4, 0x112, 0x40, PCM_IN_L, 0x44); //*4
OP(0, 0x100, 0x040, 0x112, 0x106); //*vol
INPUT_PATCH_END(patch);
INPUT_PATCH_START(patch, "Pcm R vol", 0x1, 0);
GET_OUTPUT_GPR(patch, 0x101, 0x1);
GET_CONTROL_GPR(patch, 0x107, "Vol", 0, 0x7fffffff);
GET_DYNAMIC_GPR(patch, 0x112);
OP(4, 0x112, 0x40, PCM_IN_R, 0x44);
OP(0, 0x101, 0x040, 0x112, 0x107);
INPUT_PATCH_END(patch);
// CD-Digital In Volume control
INPUT_PATCH_START(patch, "CD-Digital Vol L", 0x12, 0);
GET_OUTPUT_GPR(patch, 0x10c, 0x12);
GET_CONTROL_GPR(patch, 0x10d, "Vol", 0, 0x7fffffff);
OP(0, 0x10c, 0x040, SPDIF_CD_L, 0x10d);
INPUT_PATCH_END(patch);
INPUT_PATCH_START(patch, "CD-Digital Vol R", 0x13, 0);
GET_OUTPUT_GPR(patch, 0x10e, 0x13);
GET_CONTROL_GPR(patch, 0x10f, "Vol", 0, 0x7fffffff);
OP(0, 0x10e, 0x040, SPDIF_CD_R, 0x10f);
INPUT_PATCH_END(patch);
//Volume Correction for Multi-channel Inputs
INPUT_PATCH_START(patch, "Multi-Channel Gain", 0x08, 0);
patch->input=patch->output=0x3F00;
GET_OUTPUT_GPR(patch, 0x113, MULTI_FRONT_L);
GET_OUTPUT_GPR(patch, 0x114, MULTI_FRONT_R);
GET_OUTPUT_GPR(patch, 0x115, MULTI_REAR_L);
GET_OUTPUT_GPR(patch, 0x116, MULTI_REAR_R);
GET_OUTPUT_GPR(patch, 0x117, MULTI_CENTER);
GET_OUTPUT_GPR(patch, 0x118, MULTI_LFE);
OP(4, 0x113, 0x40, MULTI_FRONT_L, 0x44);
OP(4, 0x114, 0x40, MULTI_FRONT_R, 0x44);
OP(4, 0x115, 0x40, MULTI_REAR_L, 0x44);
OP(4, 0x116, 0x40, MULTI_REAR_R, 0x44);
OP(4, 0x117, 0x40, MULTI_CENTER, 0x44);
OP(4, 0x118, 0x40, MULTI_LFE, 0x44);
INPUT_PATCH_END(patch);
//Routing patch start
ROUTING_PATCH_START(rpatch, "Routing");
GET_INPUT_GPR(rpatch, 0x100, 0x0);
GET_INPUT_GPR(rpatch, 0x101, 0x1);
GET_INPUT_GPR(rpatch, 0x10c, 0x12);
GET_INPUT_GPR(rpatch, 0x10e, 0x13);
GET_INPUT_GPR(rpatch, 0x113, MULTI_FRONT_L);
GET_INPUT_GPR(rpatch, 0x114, MULTI_FRONT_R);
GET_INPUT_GPR(rpatch, 0x115, MULTI_REAR_L);
GET_INPUT_GPR(rpatch, 0x116, MULTI_REAR_R);
GET_INPUT_GPR(rpatch, 0x117, MULTI_CENTER);
GET_INPUT_GPR(rpatch, 0x118, MULTI_LFE);
GET_DYNAMIC_GPR(rpatch, 0x102);
GET_DYNAMIC_GPR(rpatch, 0x103);
GET_OUTPUT_GPR(rpatch, 0x104, 0x8);
GET_OUTPUT_GPR(rpatch, 0x105, 0x9);
GET_OUTPUT_GPR(rpatch, 0x10a, 0x2);
GET_OUTPUT_GPR(rpatch, 0x10b, 0x3);
/* input buffer */
OP(6, 0x102, AC97_IN_L, 0x40, 0x40);
OP(6, 0x103, AC97_IN_R, 0x40, 0x40);
/* Digital In + PCM + MULTI_FRONT-> AC97 out (front speakers)*/
OP(6, AC97_FRONT_L, 0x100, 0x10c, 0x113);
CONNECT(MULTI_FRONT_L, AC97_FRONT_L);
CONNECT(PCM_IN_L, AC97_FRONT_L);
CONNECT(SPDIF_CD_L, AC97_FRONT_L);
OP(6, AC97_FRONT_R, 0x101, 0x10e, 0x114);
CONNECT(MULTI_FRONT_R, AC97_FRONT_R);
CONNECT(PCM_IN_R, AC97_FRONT_R);
CONNECT(SPDIF_CD_R, AC97_FRONT_R);
/* Digital In + PCM + AC97 In + PCM1 + MULTI_REAR --> Rear Channel */
OP(6, 0x104, PCM1_IN_L, 0x100, 0x115);
OP(6, 0x104, 0x104, 0x10c, 0x102);
CONNECT(MULTI_REAR_L, ANALOG_REAR_L);
CONNECT(AC97_IN_L, ANALOG_REAR_L);
CONNECT(PCM_IN_L, ANALOG_REAR_L);
CONNECT(SPDIF_CD_L, ANALOG_REAR_L);
CONNECT(PCM1_IN_L, ANALOG_REAR_L);
OP(6, 0x105, PCM1_IN_R, 0x101, 0x116);
OP(6, 0x105, 0x105, 0x10e, 0x103);
CONNECT(MULTI_REAR_R, ANALOG_REAR_R);
CONNECT(AC97_IN_R, ANALOG_REAR_R);
CONNECT(PCM_IN_R, ANALOG_REAR_R);
CONNECT(SPDIF_CD_R, ANALOG_REAR_R);
CONNECT(PCM1_IN_R, ANALOG_REAR_R);
/* Digital In + PCM + AC97 In + MULTI_FRONT --> Digital out */
OP(6, 0x10b, 0x100, 0x102, 0x10c);
OP(6, 0x10b, 0x10b, 0x113, 0x40);
CONNECT(MULTI_FRONT_L, DIGITAL_OUT_L);
CONNECT(PCM_IN_L, DIGITAL_OUT_L);
CONNECT(AC97_IN_L, DIGITAL_OUT_L);
CONNECT(SPDIF_CD_L, DIGITAL_OUT_L);
OP(6, 0x10a, 0x101, 0x103, 0x10e);
OP(6, 0x10b, 0x10b, 0x114, 0x40);
CONNECT(MULTI_FRONT_R, DIGITAL_OUT_R);
CONNECT(PCM_IN_R, DIGITAL_OUT_R);
CONNECT(AC97_IN_R, DIGITAL_OUT_R);
CONNECT(SPDIF_CD_R, DIGITAL_OUT_R);
/* AC97 In --> ADC Recording Buffer */
OP(6, ADC_REC_L, 0x102, 0x40, 0x40);
CONNECT(AC97_IN_L, ADC_REC_L);
OP(6, ADC_REC_R, 0x103, 0x40, 0x40);
CONNECT(AC97_IN_R, ADC_REC_R);
/* fx12:Analog-Center */
OP(6, ANALOG_CENTER, 0x117, 0x40, 0x40);
CONNECT(MULTI_CENTER, ANALOG_CENTER);
/* fx11:Analog-LFE */
OP(6, ANALOG_LFE, 0x118, 0x40, 0x40);
CONNECT(MULTI_LFE, ANALOG_LFE);
/* fx12:Digital-Center */
OP(6, DIGITAL_CENTER, 0x117, 0x40, 0x40);
CONNECT(MULTI_CENTER, DIGITAL_CENTER);
/* fx11:Analog-LFE */
OP(6, DIGITAL_LFE, 0x118, 0x40, 0x40);
CONNECT(MULTI_LFE, DIGITAL_LFE);
ROUTING_PATCH_END(rpatch);
// Rear volume control
OUTPUT_PATCH_START(patch, "Vol Rear L", 0x8, 0);
GET_INPUT_GPR(patch, 0x104, 0x8);
GET_CONTROL_GPR(patch, 0x119, "Vol", 0, 0x7fffffff);
OP(0, ANALOG_REAR_L, 0x040, 0x104, 0x119);
OUTPUT_PATCH_END(patch);
OUTPUT_PATCH_START(patch, "Vol Rear R", 0x9, 0);
GET_INPUT_GPR(patch, 0x105, 0x9);
GET_CONTROL_GPR(patch, 0x11a, "Vol", 0, 0x7fffffff);
OP(0, ANALOG_REAR_R, 0x040, 0x105, 0x11a);
OUTPUT_PATCH_END(patch);
//Master volume control on front-digital
OUTPUT_PATCH_START(patch, "Vol Master L", 0x2, 1);
GET_INPUT_GPR(patch, 0x10a, 0x2);
GET_CONTROL_GPR(patch, 0x108, "Vol", 0, 0x7fffffff);
OP(0, DIGITAL_OUT_L, 0x040, 0x10a, 0x108);
OUTPUT_PATCH_END(patch);
OUTPUT_PATCH_START(patch, "Vol Master R", 0x3, 1);
GET_INPUT_GPR(patch, 0x10b, 0x3);
GET_CONTROL_GPR(patch, 0x109, "Vol", 0, 0x7fffffff);
OP(0, DIGITAL_OUT_R, 0x040, 0x10b, 0x109);
OUTPUT_PATCH_END(patch);
/* delimiter patch */
patch = PATCH(mgr, patch_n);
patch->code_size = 0;
sblive_writeptr(card, DBG, 0, 0);
}
spin_lock_init(&mgr->lock);
// Set up Volume controls, try to keep this the same for both Audigy and Live
//Master volume
mgr->ctrl_gpr[SOUND_MIXER_VOLUME][0] = 8;
mgr->ctrl_gpr[SOUND_MIXER_VOLUME][1] = 9;
left = card->ac97->mixer_state[SOUND_MIXER_VOLUME] & 0xff;
right = (card->ac97->mixer_state[SOUND_MIXER_VOLUME] >> 8) & 0xff;
emu10k1_set_volume_gpr(card, 8, left, 1 << card->ac97->bit_resolution);
emu10k1_set_volume_gpr(card, 9, right, 1 << card->ac97->bit_resolution);
//Rear volume
mgr->ctrl_gpr[ SOUND_MIXER_OGAIN ][0] = 0x19;
mgr->ctrl_gpr[ SOUND_MIXER_OGAIN ][1] = 0x1a;
left = right = 67;
card->ac97->mixer_state[SOUND_MIXER_OGAIN] = (right << 8) | left;
card->ac97->supported_mixers |= SOUND_MASK_OGAIN;
card->ac97->stereo_mixers |= SOUND_MASK_OGAIN;
emu10k1_set_volume_gpr(card, 0x19, left, VOL_5BIT);
emu10k1_set_volume_gpr(card, 0x1a, right, VOL_5BIT);
//PCM Volume
mgr->ctrl_gpr[SOUND_MIXER_PCM][0] = 6;
mgr->ctrl_gpr[SOUND_MIXER_PCM][1] = 7;
left = card->ac97->mixer_state[SOUND_MIXER_PCM] & 0xff;
right = (card->ac97->mixer_state[SOUND_MIXER_PCM] >> 8) & 0xff;
emu10k1_set_volume_gpr(card, 6, left, VOL_5BIT);
emu10k1_set_volume_gpr(card, 7, right, VOL_5BIT);
//CD-Digital Volume
mgr->ctrl_gpr[SOUND_MIXER_DIGITAL1][0] = 0xd;
mgr->ctrl_gpr[SOUND_MIXER_DIGITAL1][1] = 0xf;
left = right = 67;
card->ac97->mixer_state[SOUND_MIXER_DIGITAL1] = (right << 8) | left;
card->ac97->supported_mixers |= SOUND_MASK_DIGITAL1;
card->ac97->stereo_mixers |= SOUND_MASK_DIGITAL1;
emu10k1_set_volume_gpr(card, 0xd, left, VOL_5BIT);
emu10k1_set_volume_gpr(card, 0xf, right, VOL_5BIT);
//hard wire the ac97's pcm, pcm volume is done above using dsp code.
if (card->is_audigy)
//for Audigy, we mute it and use the philips 6 channel DAC instead
emu10k1_ac97_write(card->ac97, 0x18, 0x8000);
else
//For the Live we hardwire it to full volume
emu10k1_ac97_write(card->ac97, 0x18, 0x0);
//remove it from the ac97_codec's control
card->ac97_supported_mixers &= ~SOUND_MASK_PCM;
card->ac97_stereo_mixers &= ~SOUND_MASK_PCM;
//set Igain to 0dB by default, maybe consider hardwiring it here.
emu10k1_ac97_write(card->ac97, AC97_RECORD_GAIN, 0x0000);
card->ac97->mixer_state[SOUND_MIXER_IGAIN] = 0x101;
return 0;
}
static int __devinit hw_init(struct emu10k1_card *card)
{
int nCh;
u32 pagecount; /* tmp */
int ret;
/* Disable audio and lock cache */
emu10k1_writefn0(card, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE);
/* Reset recording buffers */
sblive_writeptr_tag(card, 0,
MICBS, ADCBS_BUFSIZE_NONE,
MICBA, 0,
FXBS, ADCBS_BUFSIZE_NONE,
FXBA, 0,
ADCBS, ADCBS_BUFSIZE_NONE,
ADCBA, 0,
TAGLIST_END);
/* Disable channel interrupt */
emu10k1_writefn0(card, INTE, 0);
sblive_writeptr_tag(card, 0,
CLIEL, 0,
CLIEH, 0,
SOLEL, 0,
SOLEH, 0,
TAGLIST_END);
if (card->is_audigy) {
sblive_writeptr_tag(card,0,
0x5e,0xf00,
0x5f,0x3,
TAGLIST_END);
}
/* Init envelope engine */
for (nCh = 0; nCh < NUM_G; nCh++) {
sblive_writeptr_tag(card, nCh,
DCYSUSV, 0,
IP, 0,
VTFT, 0xffff,
CVCF, 0xffff,
PTRX, 0,
//CPF, 0,
CCR, 0,
PSST, 0,
DSL, 0x10,
CCCA, 0,
Z1, 0,
Z2, 0,
FXRT, 0xd01c0000,
ATKHLDM, 0,
DCYSUSM, 0,
IFATN, 0xffff,
PEFE, 0,
FMMOD, 0,
TREMFRQ, 24, /* 1 Hz */
FM2FRQ2, 24, /* 1 Hz */
TEMPENV, 0,
/*** These are last so OFF prevents writing ***/
LFOVAL2, 0,
LFOVAL1, 0,
ATKHLDV, 0,
ENVVOL, 0,
ENVVAL, 0,
TAGLIST_END);
sblive_writeptr(card, CPF, nCh, 0);
/*
Audigy FXRT initialization
reversed eng'd, may not be accurate.
*/
if (card->is_audigy) {
sblive_writeptr_tag(card,nCh,
0x4c,0x0,
0x4d,0x0,
0x4e,0x0,
0x4f,0x0,
A_FXRT1, 0x3f3f3f3f,
A_FXRT2, 0x3f3f3f3f,
A_SENDAMOUNTS, 0,
TAGLIST_END);
}
}
/*
** Init to 0x02109204 :
** Clock accuracy = 0 (1000ppm)
** Sample Rate = 2 (48kHz)
** Audio Channel = 1 (Left of 2)
** Source Number = 0 (Unspecified)
** Generation Status = 1 (Original for Cat Code 12)
** Cat Code = 12 (Digital Signal Mixer)
** Mode = 0 (Mode 0)
** Emphasis = 0 (None)
** CP = 1 (Copyright unasserted)
** AN = 0 (Digital audio)
** P = 0 (Consumer)
*/
sblive_writeptr_tag(card, 0,
/* SPDIF0 */
SPCS0, (SPCS_CLKACCY_1000PPM | 0x002000000 |
SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT),
/* SPDIF1 */
SPCS1, (SPCS_CLKACCY_1000PPM | 0x002000000 |
SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT),
/* SPDIF2 & SPDIF3 */
SPCS2, (SPCS_CLKACCY_1000PPM | 0x002000000 |
SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT),
TAGLIST_END);
if (card->is_audigy && (card->chiprev == 4)) {
/* Hacks for Alice3 to work independent of haP16V driver */
u32 tmp;
//Setup SRCMulti_I2S SamplingRate
tmp = sblive_readptr(card, A_SPDIF_SAMPLERATE, 0);
tmp &= 0xfffff1ff;
tmp |= (0x2<<9);
sblive_writeptr(card, A_SPDIF_SAMPLERATE, 0, tmp);
/* Setup SRCSel (Enable Spdif,I2S SRCMulti) */
emu10k1_writefn0(card, 0x20, 0x600000);
emu10k1_writefn0(card, 0x24, 0x14);
/* Setup SRCMulti Input Audio Enable */
emu10k1_writefn0(card, 0x20, 0x6E0000);
emu10k1_writefn0(card, 0x24, 0xFF00FF00);
}
ret = fx_init(card); /* initialize effects engine */
if (ret < 0)
return ret;
card->tankmem.size = 0;
card->virtualpagetable.size = MAXPAGES * sizeof(u32);
card->virtualpagetable.addr = pci_alloc_consistent(card->pci_dev, card->virtualpagetable.size, &card->virtualpagetable.dma_handle);
if (card->virtualpagetable.addr == NULL) {
ERROR();
ret = -ENOMEM;
goto err0;
}
card->silentpage.size = EMUPAGESIZE;
card->silentpage.addr = pci_alloc_consistent(card->pci_dev, card->silentpage.size, &card->silentpage.dma_handle);
if (card->silentpage.addr == NULL) {
ERROR();
ret = -ENOMEM;
goto err1;
}
for (pagecount = 0; pagecount < MAXPAGES; pagecount++)
((u32 *) card->virtualpagetable.addr)[pagecount] = cpu_to_le32(((u32) card->silentpage.dma_handle * 2) | pagecount);
/* Init page table & tank memory base register */
sblive_writeptr_tag(card, 0,
PTB, (u32) card->virtualpagetable.dma_handle,
TCB, 0,
TCBS, 0,
TAGLIST_END);
for (nCh = 0; nCh < NUM_G; nCh++) {
sblive_writeptr_tag(card, nCh,
MAPA, MAP_PTI_MASK | ((u32) card->silentpage.dma_handle * 2),
MAPB, MAP_PTI_MASK | ((u32) card->silentpage.dma_handle * 2),
TAGLIST_END);
}
/* Hokay, now enable the AUD bit */
/* Enable Audio = 1 */
/* Mute Disable Audio = 0 */
/* Lock Tank Memory = 1 */
/* Lock Sound Memory = 0 */
/* Auto Mute = 1 */
if (card->is_audigy) {
if (card->chiprev == 4)
emu10k1_writefn0(card, HCFG, HCFG_AUDIOENABLE | HCFG_AC3ENABLE_CDSPDIF | HCFG_AC3ENABLE_GPSPDIF | HCFG_AUTOMUTE | HCFG_JOYENABLE);
else
emu10k1_writefn0(card, HCFG, HCFG_AUDIOENABLE | HCFG_AUTOMUTE | HCFG_JOYENABLE);
} else {
if (card->model == 0x20 || card->model == 0xc400 ||
(card->model == 0x21 && card->chiprev < 6))
emu10k1_writefn0(card, HCFG, HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE);
else
emu10k1_writefn0(card, HCFG, HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE);
}
/* Enable Vol_Ctrl irqs */
emu10k1_irq_enable(card, INTE_VOLINCRENABLE | INTE_VOLDECRENABLE | INTE_MUTEENABLE | INTE_FXDSPENABLE);
if (card->is_audigy && (card->chiprev == 4)) {
/* Unmute Analog now. Set GPO6 to 1 for Apollo.
* This has to be done after init ALice3 I2SOut beyond 48KHz.
* So, sequence is important. */
u32 tmp = emu10k1_readfn0(card, A_IOCFG);
tmp |= 0x0040;
emu10k1_writefn0(card, A_IOCFG, tmp);
}
/* FIXME: TOSLink detection */
card->has_toslink = 0;
/* Initialize digital passthrough variables */
card->pt.pos_gpr = card->pt.intr_gpr = card->pt.enable_gpr = -1;
card->pt.selected = 0;
card->pt.state = PT_STATE_INACTIVE;
card->pt.spcs_to_use = 0x01;
card->pt.patch_name = "AC3pass";
card->pt.intr_gpr_name = "count";
card->pt.enable_gpr_name = "enable";
card->pt.pos_gpr_name = "ptr";
spin_lock_init(&card->pt.lock);
init_waitqueue_head(&card->pt.wait);
/* tmp = sblive_readfn0(card, HCFG);
if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) {
sblive_writefn0(card, HCFG, tmp | 0x800);
udelay(512);
if (tmp != (sblive_readfn0(card, HCFG) & ~0x800)) {
card->has_toslink = 1;
sblive_writefn0(card, HCFG, tmp);
}
}
*/
return 0;
err1:
pci_free_consistent(card->pci_dev, card->virtualpagetable.size, card->virtualpagetable.addr, card->virtualpagetable.dma_handle);
err0:
fx_cleanup(&card->mgr);
return ret;
}
static int __devinit emu10k1_init(struct emu10k1_card *card)
{
/* Init Card */
if (hw_init(card) < 0)
return -1;
voice_init(card);
timer_init(card);
addxmgr_init(card);
DPD(2, " hw control register -> %#x\n", emu10k1_readfn0(card, HCFG));
return 0;
}
static void emu10k1_cleanup(struct emu10k1_card *card)
{
int ch;
emu10k1_writefn0(card, INTE, 0);
/** Shutdown the chip **/
for (ch = 0; ch < NUM_G; ch++)
sblive_writeptr(card, DCYSUSV, ch, 0);
for (ch = 0; ch < NUM_G; ch++) {
sblive_writeptr_tag(card, ch,
VTFT, 0,
CVCF, 0,
PTRX, 0,
//CPF, 0,
TAGLIST_END);
sblive_writeptr(card, CPF, ch, 0);
}
/* Disable audio and lock cache */
emu10k1_writefn0(card, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE);
sblive_writeptr_tag(card, 0,
PTB, 0,
/* Reset recording buffers */
MICBS, ADCBS_BUFSIZE_NONE,
MICBA, 0,
FXBS, ADCBS_BUFSIZE_NONE,
FXBA, 0,
FXWC, 0,
ADCBS, ADCBS_BUFSIZE_NONE,
ADCBA, 0,
TCBS, 0,
TCB, 0,
DBG, 0x8000,
/* Disable channel interrupt */
CLIEL, 0,
CLIEH, 0,
SOLEL, 0,
SOLEH, 0,
TAGLIST_END);
if (card->is_audigy)
sblive_writeptr(card, 0, A_DBG, A_DBG_SINGLE_STEP);
pci_free_consistent(card->pci_dev, card->virtualpagetable.size, card->virtualpagetable.addr, card->virtualpagetable.dma_handle);
pci_free_consistent(card->pci_dev, card->silentpage.size, card->silentpage.addr, card->silentpage.dma_handle);
if(card->tankmem.size != 0)
pci_free_consistent(card->pci_dev, card->tankmem.size, card->tankmem.addr, card->tankmem.dma_handle);
/* release patch storage memory */
fx_cleanup(&card->mgr);
}
/* Driver initialization routine */
static int __devinit emu10k1_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
{
struct emu10k1_card *card;
u32 subsysvid;
int ret;
if (pci_set_dma_mask(pci_dev, EMU10K1_DMA_MASK)) {
printk(KERN_ERR "emu10k1: architecture does not support 29bit PCI busmaster DMA\n");
return -ENODEV;
}
if (pci_enable_device(pci_dev))
return -EIO;
pci_set_master(pci_dev);
if ((card = kmalloc(sizeof(struct emu10k1_card), GFP_KERNEL)) == NULL) {
printk(KERN_ERR "emu10k1: out of memory\n");
return -ENOMEM;
}
memset(card, 0, sizeof(struct emu10k1_card));
card->iobase = pci_resource_start(pci_dev, 0);
card->length = pci_resource_len(pci_dev, 0);
if (request_region(card->iobase, card->length, card_names[pci_id->driver_data]) == NULL) {
printk(KERN_ERR "emu10k1: IO space in use\n");
ret = -EBUSY;
goto err_region;
}
pci_set_drvdata(pci_dev, card);
card->irq = pci_dev->irq;
card->pci_dev = pci_dev;
/* Reserve IRQ Line */
if (request_irq(card->irq, emu10k1_interrupt, IRQF_SHARED, card_names[pci_id->driver_data], card)) {
printk(KERN_ERR "emu10k1: IRQ in use\n");
ret = -EBUSY;
goto err_irq;
}
pci_read_config_byte(pci_dev, PCI_REVISION_ID, &card->chiprev);
pci_read_config_word(pci_dev, PCI_SUBSYSTEM_ID, &card->model);
printk(KERN_INFO "emu10k1: %s rev %d model %#04x found, IO at %#04lx-%#04lx, IRQ %d\n",
card_names[pci_id->driver_data], card->chiprev, card->model, card->iobase,
card->iobase + card->length - 1, card->irq);
if (pci_id->device == PCI_DEVICE_ID_CREATIVE_AUDIGY)
card->is_audigy = 1;
pci_read_config_dword(pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &subsysvid);
card->is_aps = (subsysvid == EMU_APS_SUBID);
spin_lock_init(&card->lock);
mutex_init(&card->open_sem);
card->open_mode = 0;
init_waitqueue_head(&card->open_wait);
ret = emu10k1_audio_init(card);
if (ret < 0) {
printk(KERN_ERR "emu10k1: cannot initialize audio devices\n");
goto err_audio;
}
ret = emu10k1_mixer_init(card);
if (ret < 0) {
printk(KERN_ERR "emu10k1: cannot initialize AC97 codec\n");
goto err_mixer;
}
ret = emu10k1_midi_init(card);
if (ret < 0) {
printk(KERN_ERR "emu10k1: cannot register midi device\n");
goto err_midi;
}
ret = emu10k1_init(card);
if (ret < 0) {
printk(KERN_ERR "emu10k1: cannot initialize device\n");
goto err_emu10k1_init;
}
if (card->is_aps)
emu10k1_ecard_init(card);
ret = emu10k1_register_devices(card);
if (ret < 0)
goto err_register;
/* proc entries must be created after registering devices, as
* emu10k1_info_proc prints card->audio_dev &co. */
ret = emu10k1_proc_init(card);
if (ret < 0) {
printk(KERN_ERR "emu10k1: cannot initialize proc directory\n");
goto err_proc;
}
list_add(&card->list, &emu10k1_devs);
return 0;
err_proc:
emu10k1_unregister_devices(card);
err_register:
emu10k1_cleanup(card);
err_emu10k1_init:
emu10k1_midi_cleanup(card);
err_midi:
emu10k1_mixer_cleanup(card);
err_mixer:
emu10k1_audio_cleanup(card);
err_audio:
free_irq(card->irq, card);
err_irq:
release_region(card->iobase, card->length);
pci_set_drvdata(pci_dev, NULL);
err_region:
kfree(card);
return ret;
}
static void __devexit emu10k1_remove(struct pci_dev *pci_dev)
{
struct emu10k1_card *card = pci_get_drvdata(pci_dev);
list_del(&card->list);
emu10k1_unregister_devices(card);
emu10k1_cleanup(card);
emu10k1_midi_cleanup(card);
emu10k1_mixer_cleanup(card);
emu10k1_proc_cleanup(card);
emu10k1_audio_cleanup(card);
free_irq(card->irq, card);
release_region(card->iobase, card->length);
kfree(card);
pci_set_drvdata(pci_dev, NULL);
}
MODULE_AUTHOR("Bertrand Lee, Cai Ying. (Email to: emu10k1-devel@lists.sourceforge.net)");
MODULE_DESCRIPTION("Creative EMU10K1 PCI Audio Driver v" DRIVER_VERSION "\nCopyright (C) 1999 Creative Technology Ltd.");
MODULE_LICENSE("GPL");
static struct pci_driver emu10k1_pci_driver = {
.name = "emu10k1",
.id_table = emu10k1_pci_tbl,
.probe = emu10k1_probe,
.remove = __devexit_p(emu10k1_remove),
};
static int __init emu10k1_init_module(void)
{
printk(KERN_INFO "Creative EMU10K1 PCI Audio Driver, version " DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n");
return pci_register_driver(&emu10k1_pci_driver);
}
static void __exit emu10k1_cleanup_module(void)
{
pci_unregister_driver(&emu10k1_pci_driver);
return;
}
module_init(emu10k1_init_module);
module_exit(emu10k1_cleanup_module);
#ifdef EMU10K1_SEQUENCER
/* in midi.c */
extern int emu10k1_seq_midi_open(int dev, int mode,
void (*input)(int dev, unsigned char midi_byte),
void (*output)(int dev));
extern void emu10k1_seq_midi_close(int dev);
extern int emu10k1_seq_midi_out(int dev, unsigned char midi_byte);
extern int emu10k1_seq_midi_start_read(int dev);
extern int emu10k1_seq_midi_end_read(int dev);
extern void emu10k1_seq_midi_kick(int dev);
extern int emu10k1_seq_midi_buffer_status(int dev);
static struct midi_operations emu10k1_midi_operations =
{
THIS_MODULE,
{"EMU10K1 MIDI", 0, 0, SNDCARD_EMU10K1},
&std_midi_synth,
{0},
emu10k1_seq_midi_open,
emu10k1_seq_midi_close,
NULL,
emu10k1_seq_midi_out,
emu10k1_seq_midi_start_read,
emu10k1_seq_midi_end_read,
emu10k1_seq_midi_kick,
NULL,
emu10k1_seq_midi_buffer_status,
NULL
};
#endif