diff options
author | ariff <ariff@FreeBSD.org> | 2007-09-12 07:43:43 +0000 |
---|---|---|
committer | ariff <ariff@FreeBSD.org> | 2007-09-12 07:43:43 +0000 |
commit | ea3192c3f4d2f82afb6215fb244df50276cd3558 (patch) | |
tree | dd78d0fa1d7afdfdfb8eab47903493e8bec3c684 | |
parent | a2ad10dc879940266a904337d417adf37a381444 (diff) | |
download | FreeBSD-src-ea3192c3f4d2f82afb6215fb244df50276cd3558.zip FreeBSD-src-ea3192c3f4d2f82afb6215fb244df50276cd3558.tar.gz |
Update snd_emu10kx driver with recent perforce changes (and few
other changes too).
(without any real order)
1. Use device_get_nameunit for mutex naming
2. Add timer for low-latency playback
3. Move most mixer controls from sysctls to mixer(8) controls.
This is a largest part of this patch.
4. Add analog/digital switch (as a temporary sysctl)
5. Get back support for low-bitrate playback (with help of (2))
6. Change locking for exclusive I/O. Writing to non-PTR register
is almost safe and does not need to be ordered with PTR operations.
7. Disable MIDI until we get it to detach properly and fix memory
managment problems.
8. Enable multichannel playback by default. It is as stable as
single-channel mode. Multichannel recording is still an
experimental feature.
9. Multichannel options can be changed by loader tunables.
10. Add a way to disable card from a loader tunable.
11. Add new PCI IDs.
12. Debugger settings are loader tunables now.
14. Remove some unused variables.
15. Mark pcm sub-devices MPSAFE.
16. Partially revert (bus_setup_intr -> snd_setup_intr) since it need
to be done independently
Submitted by: Yuriy Tsibizov (driver maintainer)
Approved by: re (bmah)
-rw-r--r-- | share/man/man4/snd_emu10kx.4 | 167 | ||||
-rw-r--r-- | sys/conf/NOTES | 1 | ||||
-rw-r--r-- | sys/conf/options | 3 | ||||
-rw-r--r-- | sys/dev/sound/pci/emu10kx-midi.c | 10 | ||||
-rw-r--r-- | sys/dev/sound/pci/emu10kx-pcm.c | 467 | ||||
-rw-r--r-- | sys/dev/sound/pci/emu10kx.c | 1285 | ||||
-rw-r--r-- | sys/dev/sound/pci/emu10kx.h | 5 | ||||
-rw-r--r-- | sys/modules/sound/driver/emu10kx/Makefile | 13 |
8 files changed, 1373 insertions, 578 deletions
diff --git a/share/man/man4/snd_emu10kx.4 b/share/man/man4/snd_emu10kx.4 index 9c8ccab..759cd47 100644 --- a/share/man/man4/snd_emu10kx.4 +++ b/share/man/man4/snd_emu10kx.4 @@ -1,5 +1,5 @@ .\" -.\" Copyright (c) 2003,2006 Yuriy Tsibizov, +.\" Copyright (c) 2003-2007 Yuriy Tsibizov .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -23,10 +23,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $Id: snd_emu10kx.4,v 1.19 2006/06/07 11:18:57 chibis Exp $ .\" $FreeBSD$ .\" -.Dd July 15, 2006 +.Dd July 15, 2007 .Dt SND_EMU10KX 4 .Os .Sh NAME @@ -38,10 +37,6 @@ kernel configuration file: .Bd -ragged -offset indent .Cd "device sound" .Cd "device snd_emu10kx" -.Pp -For additional options: -.Cd "options EMU10KX_MULTICHANNEL" -.Cd "options EMU10KX_DEBUG" .Ed .Pp Alternatively, to load the driver as a module at boot time, place the @@ -55,7 +50,7 @@ The .Nm bridge driver allows the generic audio driver .Xr sound 4 -to attach to the Creative sound cards based on EMU10K1, CA0100, CA0101, CA0102 +to attach to Creative sound cards based on the EMU10K1, CA0100, CA0101, CA0102 and CA0108 DSPs. .Pp The @@ -110,26 +105,80 @@ Creative Sound Blaster Audigy LS / ES, identified by as .Qq Li "CA0106-DAT Audigy LS" . .It -All other cards with -DAT chipsets. +All other Creative sound cards with -DAT chipsets. +.It +All Creative X-Fi series sound cards. .El .Sh MULTICHANNEL PLAYBACK -It is possible to build this driver with multichannel playback capabilities. -If you enable the -.Dv EMU10KX_MULTICHANNEL -option in your kernel configuration (or -build it as a module) you will get up to five DSP devices, one for each sound -card output. +By default driver is loaded with multichannel playback capabilities enabled. +If you do not set the +.Dv hint.emu10kx.0.multichannel_disabled +option in your +.Xr loader.conf 5 +configuration file you will get up to +five DSP devices, one for each sound card output. +You can use additional software (like +.Em audio/pulseaudio +from +.Em The Ports Collection +) to do sound stream demultiplexing. Only .Dq FRONT output can play and record sound from external sources (like line or S/PDIF inputs). +.Sh MULTICHANNEL RECORDING +By default multichannel recording capabilities are not enabled when you load +this driver. +If you enable the +.Dv hint.emu10kx.0.multichannel_recording +option in +.Xr loader.conf 5 +you will get one more DSP device that is rate-locked to 48kHz/16bit/mono. +This is actually 48kHz/16bit/32 channels on SB Live! cards and +48kHz/16bit/64channels on Audigy cards, but the current implementation of +sound subsystem does not support such an amount of PCM channels. +This device can not be opened for read, thus confusing many applications. +.Pp +Within multichannel stream first half (0-15 or 0-31) is a copy of all DSP +outputs, second half (15-30 or 32-63) is a copy of some DSP inputs. +On Live! cards the last substream (31) is used as a sync stream and always +set to 0xc0de. +Audigy cards do not need such sync data, because stream always start with +substream 0. +.Ss SB Live! substream map (in byte offsets, each substream is 2 bytes LE) +.Bl -tag -width ".Dv +0x00..+0x1E" +.It Dv Offset +Substream +.It +0x00..+0x1E +PCM streams 0..15 +.It +0x20, +0x22 +Empty +.It +0x24..+0x2A +PCM inputs: front left, front right, rear left, rear right, center, sub +.It +0x2C..+0x3C +DSP inputs 0..8: +.It +0x3E +sync substream (0xc0de) +.El +.Pp +.Ss Audigy substream map (in byte offsets, each substream is 2 bytes LE) +.Bl -tag -width ".Dv +0x00..+0x3E" +.It Dv Offset +Substream +.It +0x00..+0x3E +PCM streams 0..31 +.It +0x40..+0x5E +PCM inputs: front LR, rear LR, center, sub, ... +.It +0x60..+0x7E +DSP inputs 0..16 +.El .Sh OSS MIXER CONTROLS -These are controls available through the standard OSS programming interface. +These are the controls available through the standard OSS programming interface. You can use .Xr mixer 8 to change them. .Pp -On EMU10K1-based cards the OSS mixer directly controls the AC97 codec on card. +On EMU10K1-based cards the OSS mixer directly controls the AC97 codec. On newer cards the OSS mixer controls some parameters of the AC97 codec and some DSP-based mixer controls. .Bl -inset @@ -142,18 +191,28 @@ volume in multichannel mode and all output volume in single channel mode. .It Qq rec mixer control acts very different on EMU10K1 and other cards. On EMU10K1 cards it controls the AC97 codec recording level. -On non-EMU10K1 cards -it controls the amount of AC97 "stereo mix" entering the DSP. -AC97 recording level and AC97 recording source are fixed -on CA0100, CA0101, CA0102 and CA0108 cards. -AC97 recording level are always set to -maximum and recording source is always +On non-EMU10K1 cards it controls the amount of AC97 "stereo mix" entering +the DSP. +AC97 recording level and AC97 recording source are fixed on CA0100, CA0101, +CA0102 and CA0108 cards. +AC97 recording level are always set to maximum and recording source is always .Dq Li "stereo mix" . +.It Qq dig1 +is a CD S/PDIF (on-card) volume control +.It Qq dig2 +is an AudigyDrive S/PDIF (Audigy series) or TOSLink (SB Live! series) volume +control +.It Qq dig3 +is an on-card S/PDIF volume control +.It Qq line2 +is AudigyDrive "Line In 2" volume control +.It Qq line3 +is AudigyDrive "AUX In 2" volume control .El .Pp -Other OSS mixer controls do not work. +Other OSS mixer controls control inputs of AC97 codec. .Sh PRIVATE DEVICE CONTROLS -You can control most of EMU10Kx operation and configuration parameters through +You can control some of EMU10Kx operation and configuration parameters through .Va dev.emu10kx. Ns Aq Ar X sysctls. These @@ -161,19 +220,37 @@ These values are temporary and should not be relied upon. .Sh DRIVER CONFIGURATION -.Ss Kernel Configuration Options -The following kernel configuration options control the -.Nm -driver. -.Bl -tag -width ".Dv EMU10KX_MULTICHANNEL" -.It Dv EMU10KX_MULTICHANNEL -This option enables -.Sx MULTICHANNEL PLAYBACK -for all instances of the -.Nm -driver. -.It Dv EMU10KX_DEBUG -This option enables additional debug messages. +Loader tunables are used to set driver configuration. +Tunables can be set at the +.Xr loader 8 +prompt before booting the kernel or they can be stored in +.Pa /boot/loader.conf . +These tunables can't be changed from a maching +.Xr sysctl 8 +entry after boot, but you can change them using +.Xr kenv 1 +while the driver is not loaded. +.Bl -tag -width indent +.It Va hint.emu10kx. Ns Ao Ar X Ac Ns Va .disabled +Disables loading a driver instance. +.It Va hint.emu10kx. Ns Ao Ar X Ac Ns Va .multichannel_disabled +Disables multichannel playback support, when one card is represented as +several PCM devices. +.It Va hint.emu10kx. Ns Ao Ar X Ac Ns Va .multichannel_recording +Enables experimental multichannel recording support. +.It Va hint.emu10kx. Ns Ao Ar X Ac Ns Va .debug +Set debug output level. +.Bl -tag -width 2n +.It 0 +No additional debug options enabled +.It 1 +Enables all DSP outputs to be connected, even those +that are known to be unused on a particular card. +.It 2 +Additional debug messages about in-driver events will be printed. +.It 2 +Additional debug messages will be printed when memory allocation fails. +.El .El .Sh FILES .Bl -tag -width ".Pa /dev/emu10kx?" -compact @@ -193,25 +270,23 @@ device driver first appeared in The PCM part of the driver is based on the .Xr snd_emu10k1 4 SB Live!\& driver by -.An "Cameron Grant" . +.An "Cameron Grant" Aq cg@freebsd.org . The MIDI interface is based on the .Xr snd_emu10k1 4 MIDI interface code by -.An "Mathew Kanner" . +.An "Mathew Kanner" Aq matk@freebsd.org . The .Nm device driver and this manual page were written by .An Yuriy Tsibizov . .Sh BUGS -8kHz/8bit/mono recording does not work. -8kHz recording was removed from the driver capabilities. .Pp -The driver does not detect lost S/PDIF signal and produces noise when S/PDIF -is not connected and S/PDIF volume is not zero. +The driver does not detect lost S/PDIF signals and produces noise when +S/PDIF is not connected and S/PDIF volume is not zero. .Pp The PCM driver cannot detect the presence of Live!Drive or AudigyDrive -breakout boxes -and tries to use them (and list their connectors in the mixer). +breakout boxes and tries to use them (and list their connectors in the +mixer). .Pp The MIDI driver cannot detect the presence of Live!Drive or AudigyDrive breakout boxes and tries to enable the IR receiver on them anyway. diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 76d74e3..f0c32cd 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -2043,7 +2043,6 @@ device snd_csa device snd_ds1 device snd_emu10k1 device snd_emu10kx -options SND_EMU10KX_MULTICHANNEL device snd_envy24 device snd_envy24ht device snd_es137x diff --git a/sys/conf/options b/sys/conf/options index 9d6959b..43cb606 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -749,8 +749,5 @@ XBOX opt_xbox.h # XFS XFS -# snd_emu10kx sound driver options -SND_EMU10KX_MULTICHANNEL opt_emu10kx.h - # Interrupt filtering INTR_FILTER opt_global.h diff --git a/sys/dev/sound/pci/emu10kx-midi.c b/sys/dev/sound/pci/emu10kx-midi.c index e6339db..736e7c2 100644 --- a/sys/dev/sound/pci/emu10kx-midi.c +++ b/sys/dev/sound/pci/emu10kx-midi.c @@ -46,7 +46,6 @@ #include <dev/sound/midi/mpu401.h> #include "mpufoi_if.h" -#include "opt_emu10kx.h" #include <dev/sound/pci/emu10kx.h> #include "emu10k1-alsa%diked.h" @@ -117,9 +116,8 @@ emu_midi_card_intr(void *p, uint32_t intr_status) if (sc->mpu_intr == NULL) { /* We should read MIDI event to unlock card after * interrupt. XXX - check, why this happens. */ -#ifdef SND_EMU10KX_DEBUG - device_printf(sc->dev, "midi interrupt %08x without interrupt handler, force mread!\n", intr_status); -#endif + if (bootverbose) + device_printf(sc->dev, "midi interrupt %08x without interrupt handler, force mread!\n", intr_status); (void)emu_mread((void *)(NULL), sc, 0); } return (intr_status); /* Acknowledge everything */ @@ -166,7 +164,7 @@ emu_midi_attach(device_t dev) scp->port = midiinfo->port; scp->card = midiinfo->card; - mtx_init(&scp->mtx, "emu10kx_midi", NULL, MTX_DEF); + mtx_init(&scp->mtx, device_get_nameunit(dev), "midi softc", MTX_DEF); if (scp->is_emu10k1) { /* SB Live! - only one MIDI device here */ @@ -192,8 +190,6 @@ emu_midi_attach(device_t dev) ipr_val |= IPR_A_MIDIRECVBUFEMPTY2; } } - if (inte_val == 0) - return (ENXIO); scp->ihandle = emu_intr_register(scp->card, inte_val, ipr_val, &emu_midi_card_intr, scp); /* Init the interface. */ diff --git a/sys/dev/sound/pci/emu10kx-pcm.c b/sys/dev/sound/pci/emu10kx-pcm.c index daf3d5a..99fefd7 100644 --- a/sys/dev/sound/pci/emu10kx-pcm.c +++ b/sys/dev/sound/pci/emu10kx-pcm.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> - * Copyright (c) 2003-2006 Yuriy Tsibizov <yuriy.tsibizov@gfk.ru> + * Copyright (c) 2003-2007 Yuriy Tsibizov <yuriy.tsibizov@gfk.ru> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -45,14 +45,13 @@ #include "mixer_if.h" -#include "opt_emu10kx.h" #include <dev/sound/pci/emu10kx.h> #include "emu10k1-alsa%diked.h" struct emu_pcm_pchinfo { int spd; int fmt; - int blksz; + unsigned int blksz; int run; struct emu_voice *master; struct emu_voice *slave; @@ -65,7 +64,7 @@ struct emu_pcm_pchinfo { struct emu_pcm_rchinfo { int spd; int fmt; - int blksz; + unsigned int blksz; int run; uint32_t idxreg; uint32_t basereg; @@ -77,6 +76,7 @@ struct emu_pcm_rchinfo { struct snd_dbuf *buffer; struct pcm_channel *channel; struct emu_pcm_info *pcm; + int timer; }; /* XXX Hardware playback channels */ @@ -89,7 +89,6 @@ struct emu_pcm_rchinfo { struct emu_pcm_info { struct mtx *lock; device_t dev; /* device information */ - struct snddev_info *devinfo; /* pcm device information */ struct emu_sc_info *card; struct emu_pcm_pchinfo pch[MAX_CHANNELS]; /* hardware channels */ int pnum; /* next free channel number */ @@ -103,6 +102,12 @@ struct emu_pcm_info { int is_emu10k1; struct ac97_info *codec; uint32_t ac97_state[0x7F]; + kobj_class_t ac97_mixerclass; + uint32_t ac97_recdevs; + uint32_t ac97_playdevs; + struct snd_mixer *sm; + int mch_disabled; + unsigned int emu10k1_volcache[2][2]; }; @@ -128,6 +133,14 @@ static struct pcmchan_caps emu_reccaps_efx_audigy = { 48000*64, 48000*64, emu_rfmt_efx, 0 }; +static int emu_rates_live[] = { + 48000*32 +}; + +static int emu_rates_audigy[] = { + 48000*64 +}; + static uint32_t emu_pfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, @@ -150,27 +163,105 @@ static int emu10k2_adcspeed[9] = {48000, 44100, 32000, 24000, 22050, 16000, 1200 static uint32_t emu_pcm_intr(void *pcm, uint32_t stat); -static const struct emu_dspmix_props { - u_int8_t present; -} dspmix [SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_VOLUME] = {1}, - [SOUND_MIXER_PCM] = {1}, +static const struct emu_dspmix_props_k1 { + uint8_t present; + uint8_t recdev; + int8_t input; +} dspmix_k1 [SOUND_MIXER_NRDEVICES] = { + /* no mixer device for ac97 */ /* in0 AC97 */ + [SOUND_MIXER_DIGITAL1] = {1, 1, 1}, /* in1 CD SPDIF */ + /* not connected */ /* in2 (zoom) */ + [SOUND_MIXER_DIGITAL2] = {1, 1, 3}, /* in3 toslink */ + [SOUND_MIXER_LINE2] = {1, 1, 4}, /* in4 Line-In2 */ + [SOUND_MIXER_DIGITAL3] = {1, 1, 5}, /* in5 on-card SPDIF */ + [SOUND_MIXER_LINE3] = {1, 1, 6}, /* in6 AUX2 */ + /* not connected */ /* in7 */ +}; +static const struct emu_dspmix_props_k2 { + uint8_t present; + uint8_t recdev; + int8_t input; +} dspmix_k2 [SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_VOLUME] = {1, 0, (-1)}, + [SOUND_MIXER_PCM] = {1, 0, (-1)}, + + /* no mixer device */ /* in0 AC97 */ + [SOUND_MIXER_DIGITAL1] = {1, 1, 1}, /* in1 CD SPDIF */ + [SOUND_MIXER_DIGITAL2] = {1, 1, 2}, /* in2 COAX SPDIF */ + /* not connected */ /* in3 */ + [SOUND_MIXER_LINE2] = {1, 1, 4}, /* in4 Line-In2 */ + [SOUND_MIXER_DIGITAL3] = {1, 1, 5}, /* in5 on-card SPDIF */ + [SOUND_MIXER_LINE3] = {1, 1, 6}, /* in6 AUX2 */ + /* not connected */ /* in7 */ }; static int emu_dspmixer_init(struct snd_mixer *m) { + struct emu_pcm_info *sc; int i; - int v; + int p, r; + + p = 0; + r = 0; + + sc = mix_getdevinfo(m); + + if (sc->route == RT_FRONT) { + /* create submixer for AC97 codec */ + if ((sc->ac97_mixerclass != NULL) && (sc->codec != NULL)) { + sc->sm = mixer_create(sc->dev, sc->ac97_mixerclass, sc->codec, "ac97"); + if (sc->sm != NULL) { + p = mix_getdevs(sc->sm); + r = mix_getrecdevs(sc->sm); + } + } - v = 0; - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (dspmix[i].present) - v |= 1 << i; + sc->ac97_playdevs = p; + sc->ac97_recdevs = r; } - mix_setdevs(m, v); - mix_setrecdevs(m, 0); + /* This two are always here */ + p |= (1 << SOUND_MIXER_PCM); + p |= (1 << SOUND_MIXER_VOLUME); + + if (sc->route == RT_FRONT) { + if (sc->is_emu10k1) { + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (dspmix_k1[i].present) + p |= (1 << i); + if (dspmix_k1[i].recdev) + r |= (1 << i); + } + } else { + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (dspmix_k2[i].present) + p |= (1 << i); + if (dspmix_k2[i].recdev) + r |= (1 << i); + } + } + } + + mix_setdevs(m, p); + mix_setrecdevs(m, r); + + return (0); +} + +static int +emu_dspmixer_uninit(struct snd_mixer *m) +{ + struct emu_pcm_info *sc; + int err = 0; + + /* drop submixer for AC97 codec */ + sc = mix_getdevinfo(m); + if (sc->sm != NULL) + err = mixer_delete(sc->sm); + if (err) + return (err); + sc->sm = NULL; return (0); } @@ -184,6 +275,30 @@ emu_dspmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned righ switch (dev) { case SOUND_MIXER_VOLUME: switch (sc->route) { + case RT_FRONT: + if (sc->sm != NULL) + mix_set(sc->sm, dev, left, right); + if (sc->mch_disabled) { + /* In emu10k1 case PCM volume does not affect + sound routed to rear & center/sub (it is connected + to AC97 codec). Calculate it manually. */ + /* This really should belong to emu10kx.c */ + if (sc->is_emu10k1) { + sc->emu10k1_volcache[0][0] = left; + left = left * sc->emu10k1_volcache[1][0] / 100; + sc->emu10k1_volcache[0][1] = right; + right = right * sc->emu10k1_volcache[1][1] / 100; + } + + emumix_set_volume(sc->card, M_MASTER_REAR_L, left); + emumix_set_volume(sc->card, M_MASTER_REAR_R, right); + if (!sc->is_emu10k1) { + emumix_set_volume(sc->card, M_MASTER_CENTER, (left+right)/2); + emumix_set_volume(sc->card, M_MASTER_SUBWOOFER, (left+right)/2); + /* XXX side */ + } + } /* mch disabled */ + break; case RT_REAR: emumix_set_volume(sc->card, M_MASTER_REAR_L, left); emumix_set_volume(sc->card, M_MASTER_REAR_R, right); @@ -198,6 +313,27 @@ emu_dspmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned righ break; case SOUND_MIXER_PCM: switch (sc->route) { + case RT_FRONT: + if (sc->sm != NULL) + mix_set(sc->sm, dev, left, right); + if (sc->mch_disabled) { + /* See SOUND_MIXER_VOLUME case */ + if (sc->is_emu10k1) { + sc->emu10k1_volcache[1][0] = left; + left = left * sc->emu10k1_volcache[0][0] / 100; + sc->emu10k1_volcache[1][1] = right; + right = right * sc->emu10k1_volcache[0][1] / 100; + } + emumix_set_volume(sc->card, M_MASTER_REAR_L, left); + emumix_set_volume(sc->card, M_MASTER_REAR_R, right); + + if (!sc->is_emu10k1) { + emumix_set_volume(sc->card, M_MASTER_CENTER, (left+right)/2); + emumix_set_volume(sc->card, M_MASTER_SUBWOOFER, (left+right)/2); + /* XXX side */ + } + } /* mch_disabled */ + break; case RT_REAR: emumix_set_volume(sc->card, M_FX2_REAR_L, left); emumix_set_volume(sc->card, M_FX3_REAR_R, right); @@ -210,26 +346,160 @@ emu_dspmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned righ break; } break; + case SOUND_MIXER_DIGITAL1: /* CD SPDIF, in1 */ + emumix_set_volume(sc->card, M_IN1_FRONT_L, left); + emumix_set_volume(sc->card, M_IN1_FRONT_R, right); + break; + case SOUND_MIXER_DIGITAL2: + if (sc->is_emu10k1) { + /* TOSLink, in3 */ + emumix_set_volume(sc->card, M_IN3_FRONT_L, left); + emumix_set_volume(sc->card, M_IN3_FRONT_R, right); + } else { + /* COAX SPDIF, in2 */ + emumix_set_volume(sc->card, M_IN2_FRONT_L, left); + emumix_set_volume(sc->card, M_IN2_FRONT_R, right); + } + break; + case SOUND_MIXER_LINE2: /* Line-In2, in4 */ + emumix_set_volume(sc->card, M_IN4_FRONT_L, left); + emumix_set_volume(sc->card, M_IN4_FRONT_R, right); + break; + case SOUND_MIXER_DIGITAL3: /* on-card SPDIF, in5 */ + emumix_set_volume(sc->card, M_IN5_FRONT_L, left); + emumix_set_volume(sc->card, M_IN5_FRONT_R, right); + break; + case SOUND_MIXER_LINE3: /* AUX2, in6 */ + emumix_set_volume(sc->card, M_IN6_FRONT_L, left); + emumix_set_volume(sc->card, M_IN6_FRONT_R, right); + break; default: - device_printf(sc->dev, "mixer error: unknown device %d\n", dev); + if (sc->sm != NULL) { + /* XXX emumix_set_volume is not required here */ + emumix_set_volume(sc->card, M_IN0_FRONT_L, 100); + emumix_set_volume(sc->card, M_IN0_FRONT_R, 100); + mix_set(sc->sm, dev, left, right); + } else + device_printf(sc->dev, "mixer error: unknown device %d\n", dev); } return (0); } static int -emu_dspmixer_setrecsrc(struct snd_mixer *m __unused, u_int32_t src __unused) +emu_dspmixer_setrecsrc(struct snd_mixer *m, u_int32_t src) { - return (0); + struct emu_pcm_info *sc; + int i; + u_int32_t recmask; + int input[8]; + + sc = mix_getdevinfo(m); + recmask = 0; + for (i=0; i < 8; i++) + input[i]=0; + + if (sc->sm != NULL) + if ((src & sc->ac97_recdevs) !=0) + if (mix_setrecsrc(sc->sm, src & sc->ac97_recdevs) == 0) { + recmask |= (src & sc->ac97_recdevs); + /* Recording from AC97 codec. + Enable AC97 route to rec on DSP */ + input[0] = 1; + } + if (sc->is_emu10k1) { + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (dspmix_k1[i].recdev) + if ((src & (1 << i)) == ((uint32_t)1 << i)) { + recmask |= (1 << i); + /* enable device i */ + input[dspmix_k1[i].input] = 1; + } + } + } else { + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (dspmix_k2[i].recdev) + if ((src & (1 << i)) == ((uint32_t)1 << i)) { + recmask |= (1 << i); + /* enable device i */ + input[dspmix_k2[i].input] = 1; + } + } + } + emumix_set_volume(sc->card, M_IN0_REC_L, input[0] == 1 ? 100 : 0); + emumix_set_volume(sc->card, M_IN0_REC_R, input[0] == 1 ? 100 : 0); + + emumix_set_volume(sc->card, M_IN1_REC_L, input[1] == 1 ? 100 : 0); + emumix_set_volume(sc->card, M_IN1_REC_R, input[1] == 1 ? 100 : 0); + + if (!sc->is_emu10k1) { + emumix_set_volume(sc->card, M_IN2_REC_L, input[2] == 1 ? 100 : 0); + emumix_set_volume(sc->card, M_IN2_REC_R, input[2] == 1 ? 100 : 0); + } + + if (sc->is_emu10k1) { + emumix_set_volume(sc->card, M_IN3_REC_L, input[3] == 1 ? 100 : 0); + emumix_set_volume(sc->card, M_IN3_REC_R, input[3] == 1 ? 100 : 0); + } + + emumix_set_volume(sc->card, M_IN4_REC_L, input[4] == 1 ? 100 : 0); + emumix_set_volume(sc->card, M_IN4_REC_R, input[4] == 1 ? 100 : 0); + + emumix_set_volume(sc->card, M_IN5_REC_L, input[5] == 1 ? 100 : 0); + emumix_set_volume(sc->card, M_IN5_REC_R, input[5] == 1 ? 100 : 0); + + emumix_set_volume(sc->card, M_IN6_REC_L, input[6] == 1 ? 100 : 0); + emumix_set_volume(sc->card, M_IN6_REC_R, input[6] == 1 ? 100 : 0); + + /* XXX check for K1/k2 differences? */ + if ((src & (1 << SOUND_MIXER_PCM)) == (1 << SOUND_MIXER_PCM)) { + emumix_set_volume(sc->card, M_FX0_REC_L, emumix_get_volume(sc->card, M_FX0_FRONT_L)); + emumix_set_volume(sc->card, M_FX1_REC_R, emumix_get_volume(sc->card, M_FX1_FRONT_R)); + } else { + emumix_set_volume(sc->card, M_FX0_REC_L, 0); + emumix_set_volume(sc->card, M_FX1_REC_R, 0); + } + + return (recmask); } static kobj_method_t emudspmixer_methods[] = { KOBJMETHOD(mixer_init, emu_dspmixer_init), + KOBJMETHOD(mixer_uninit, emu_dspmixer_uninit), KOBJMETHOD(mixer_set, emu_dspmixer_set), KOBJMETHOD(mixer_setrecsrc, emu_dspmixer_setrecsrc), { 0, 0 } }; MIXER_DECLARE(emudspmixer); +static int +emu_efxmixer_init(struct snd_mixer *m) +{ + mix_setdevs(m, SOUND_MASK_VOLUME); + mix_setrecdevs(m, SOUND_MASK_MONITOR); + return (0); +} + +static int +emu_efxmixer_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) +{ + if (left + right == 200) return (0); + return (0); +} + +static int +emu_efxmixer_setrecsrc(struct snd_mixer *m __unused, u_int32_t src __unused) +{ + return (SOUND_MASK_MONITOR); +} + +static kobj_method_t emuefxmixer_methods[] = { + KOBJMETHOD(mixer_init, emu_efxmixer_init), + KOBJMETHOD(mixer_set, emu_efxmixer_set), + KOBJMETHOD(mixer_setrecsrc, emu_efxmixer_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(emuefxmixer); + /* * AC97 emulation code for Audigy and later cards. * Some parts of AC97 codec are not used by hardware, but can be used @@ -250,7 +520,7 @@ MIXER_DECLARE(emudspmixer); #define BIT6_TO255(x) (255-(x)*255/(0x3f)) #define V100_TOBIT6(x) (0x3f*(100-x)/100) #define V100_TOBIT4(x) (0x0f*(100-x)/100) -#define AC97ENCODE(x_muted,x_left,x_right) (((x_muted&1)<<15) | ((x_left&0x3f)<<8) | (x_right&0x3f)) +#define AC97ENCODE(x_muted, x_left, x_right) (((x_muted & 1)<<15) | ((x_left & 0x3f)<<8) | (x_right & 0x3f)) static int emu_ac97_read_emulation(struct emu_pcm_info *sc, int regno) @@ -325,7 +595,9 @@ emu_ac97_write_emulation(struct emu_pcm_info *sc, int regno, uint32_t data) case AC97_REG_RECSEL: /* * PCM recording source is set to "stereo mix" (labeled "vol" - * in mixer) XXX !I can't remember why! + * in mixer). There is no 'playback' from AC97 codec - + * if you want to hear anything from AC97 you have to _record_ + * it. Keep things simple and record "stereo mix". */ data = 0x0505; break; @@ -414,7 +686,6 @@ emu_k1_recval(int speed) val = 0; while ((val < 7) && (speed < emu10k1_adcspeed[val])) val++; - if (val == 6) val=5; /* XXX 8kHz does not work */ return (val); } @@ -426,7 +697,6 @@ emu_k2_recval(int speed) val = 0; while ((val < 8) && (speed < emu10k2_adcspeed[val])) val++; - if (val == 7) val=6; /* XXX 8kHz does not work */ return (val); } @@ -504,7 +774,7 @@ emupchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize) ch->blksz = blocksize; emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getbps(ch->buffer)); snd_mtxunlock(sc->lock); - return (blocksize); + return (ch->blksz); } static int @@ -606,6 +876,7 @@ emurchan_init(kobj_t obj __unused, void *devinfo, struct snd_dbuf *b, struct pcm if (sndbuf_alloc(ch->buffer, emu_gettag(sc->card), 0, sc->bufsz) != 0) return (NULL); else { + ch->timer = emu_timer_create(sc->card); emu_wrptr(sc->card, 0, ch->basereg, sndbuf_getbufaddr(ch->buffer)); emu_wrptr(sc->card, 0, ch->sizereg, 0); /* off */ return (ch); @@ -613,6 +884,16 @@ emurchan_init(kobj_t obj __unused, void *devinfo, struct snd_dbuf *b, struct pcm } static int +emurchan_free(kobj_t obj __unused, void *c_devinfo) +{ + struct emu_pcm_rchinfo *ch = c_devinfo; + struct emu_pcm_info *sc = ch->pcm; + + emu_timer_clear(sc->card, ch->timer); + return (0); +} + +static int emurchan_setformat(kobj_t obj __unused, void *c_devinfo, uint32_t format) { struct emu_pcm_rchinfo *ch = c_devinfo; @@ -639,12 +920,20 @@ static int emurchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize) { struct emu_pcm_rchinfo *ch = c_devinfo; + struct emu_pcm_info *sc = ch->pcm; ch->blksz = blocksize; - /* If blocksize is less than half of buffer size we will not get - interrupt in time and channel will die due to interrupt timeout */ - if(ch->blksz < (ch->pcm->bufsz / 2)) - ch->blksz = ch->pcm->bufsz / 2; + /* + * If blocksize is less than half of buffer size we will not get + * BUFHALFFULL interrupt in time and channel will need to generate + * (and use) timer interrupts. Otherwise channel will be marked dead. + */ + if (ch->blksz < (ch->pcm->bufsz / 2)) { + emu_timer_set(sc->card, ch->timer, ch->blksz / sndbuf_getbps(ch->buffer)); + emu_timer_enable(sc->card, ch->timer, 1); + } else { + emu_timer_enable(sc->card, ch->timer, 0); + } return (ch->blksz); } @@ -732,6 +1021,7 @@ emurchan_getcaps(kobj_t obj __unused, void *c_devinfo __unused) static kobj_method_t emurchan_methods[] = { KOBJMETHOD(channel_init, emurchan_init), + KOBJMETHOD(channel_free, emurchan_free), KOBJMETHOD(channel_setformat, emurchan_setformat), KOBJMETHOD(channel_setspeed, emurchan_setspeed), KOBJMETHOD(channel_setblocksize, emurchan_setblocksize), @@ -763,7 +1053,7 @@ emufxrchan_init(kobj_t obj __unused, void *devinfo, struct snd_dbuf *b, struct p ch->buffer = b; ch->pcm = sc; ch->channel = c; - ch->blksz = sc->bufsz; + ch->blksz = sc->bufsz / 2; if (sndbuf_alloc(ch->buffer, emu_gettag(sc->card), 0, sc->bufsz) != 0) return (NULL); @@ -778,7 +1068,7 @@ static int emufxrchan_setformat(kobj_t obj __unused, void *c_devinfo __unused, uint32_t format) { if (format == AFMT_S16_LE) return (0); - return (-1); + return (EINVAL); } static int @@ -796,9 +1086,13 @@ emufxrchan_setblocksize(kobj_t obj __unused, void *c_devinfo, uint32_t blocksize struct emu_pcm_rchinfo *ch = c_devinfo; ch->blksz = blocksize; - /* If blocksize is less than half of buffer size we will not get - interrupt in time and channel will die due to interrupt timeout */ - if(ch->blksz < (ch->pcm->bufsz / 2)) + /* + * XXX If blocksize is less than half of buffer size we will not get + * interrupt in time and channel will die due to interrupt timeout. + * This should not happen with FX rchan, because it will fill buffer + * very fast (64K buffer is 0.021seconds on Audigy). + */ + if (ch->blksz < (ch->pcm->bufsz / 2)) ch->blksz = ch->pcm->bufsz / 2; return (ch->blksz); } @@ -839,13 +1133,13 @@ emufxrchan_trigger(kobj_t obj __unused, void *c_devinfo, int go) ch->run = 1; emu_wrptr(sc->card, 0, ch->sizereg, sz); ch->ihandle = emu_intr_register(sc->card, ch->irqmask, ch->iprmask, &emu_pcm_intr, sc); - /* - SB Live! is limited to 32 mono channels. Audigy - has 64 mono channels, each of them is selected from - one of two A_FXWC[1|2] registers. + /* + * SB Live! is limited to 32 mono channels. Audigy + * has 64 mono channels. Channels are enabled + * by setting a bit in A_FXWC[1|2] registers. */ /* XXX there is no way to demultiplex this streams for now */ - if(sc->is_emu10k1) { + if (sc->is_emu10k1) { emu_wrptr(sc->card, 0, FXWC, 0xffffffff); } else { emu_wrptr(sc->card, 0, A_FXWC1, 0xffffffff); @@ -856,7 +1150,7 @@ emufxrchan_trigger(kobj_t obj __unused, void *c_devinfo, int go) /* FALLTHROUGH */ case PCMTRIG_ABORT: ch->run = 0; - if(sc->is_emu10k1) { + if (sc->is_emu10k1) { emu_wrptr(sc->card, 0, FXWC, 0x0); } else { emu_wrptr(sc->card, 0, A_FXWC1, 0x0); @@ -895,12 +1189,26 @@ emufxrchan_getcaps(kobj_t obj __unused, void *c_devinfo) struct emu_pcm_rchinfo *ch = c_devinfo; struct emu_pcm_info *sc = ch->pcm; - if(sc->is_emu10k1) + if (sc->is_emu10k1) return (&emu_reccaps_efx_live); return (&emu_reccaps_efx_audigy); } +static int +emufxrchan_getrates(kobj_t obj __unused, void *c_devinfo, int **rates) +{ + struct emu_pcm_rchinfo *ch = c_devinfo; + struct emu_pcm_info *sc = ch->pcm; + + if (sc->is_emu10k1) + *rates = emu_rates_live; + else + *rates = emu_rates_audigy; + + return 1; +} + static kobj_method_t emufxrchan_methods[] = { KOBJMETHOD(channel_init, emufxrchan_init), KOBJMETHOD(channel_setformat, emufxrchan_setformat), @@ -909,6 +1217,7 @@ static kobj_method_t emufxrchan_methods[] = { KOBJMETHOD(channel_trigger, emufxrchan_trigger), KOBJMETHOD(channel_getptr, emufxrchan_getptr), KOBJMETHOD(channel_getcaps, emufxrchan_getcaps), + KOBJMETHOD(channel_getrates, emufxrchan_getrates), {0, 0} }; CHANNEL_DECLARE(emufxrchan); @@ -923,29 +1232,51 @@ emu_pcm_intr(void *pcm, uint32_t stat) ack = 0; + snd_mtxlock(sc->lock); + if (stat & IPR_INTERVALTIMER) { ack |= IPR_INTERVALTIMER; for (i = 0; i < MAX_CHANNELS; i++) if (sc->pch[i].channel) { - if (sc->pch[i].run == 1) + if (sc->pch[i].run == 1) { + snd_mtxunlock(sc->lock); chn_intr(sc->pch[i].channel); - else + snd_mtxlock(sc->lock); + } else emu_timer_enable(sc->card, sc->pch[i].timer, 0); } + /* ADC may install timer to get low-latency interrupts */ + if ((sc->rch_adc.channel) && (sc->rch_adc.run)) { + snd_mtxunlock(sc->lock); + chn_intr(sc->rch_adc.channel); + snd_mtxlock(sc->lock); + } + /* + * EFX does not use timer, because it will fill + * buffer at least 32x times faster than ADC. + */ } if (stat & (IPR_ADCBUFFULL | IPR_ADCBUFHALFFULL)) { ack |= stat & (IPR_ADCBUFFULL | IPR_ADCBUFHALFFULL); - if (sc->rch_adc.channel) + if (sc->rch_adc.channel) { + snd_mtxunlock(sc->lock); chn_intr(sc->rch_adc.channel); + snd_mtxlock(sc->lock); + } } if (stat & (IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL)) { ack |= stat & (IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL); - if (sc->rch_efx.channel) + if (sc->rch_efx.channel) { + snd_mtxunlock(sc->lock); chn_intr(sc->rch_efx.channel); + snd_mtxlock(sc->lock); + } } + snd_mtxunlock(sc->lock); + return (ack); } @@ -1009,7 +1340,7 @@ emu_pcm_attach(device_t dev) unsigned int i; char status[SND_STATUSLEN]; uint32_t inte, ipr; - uintptr_t route, r, is_emu10k1; + uintptr_t route, r, ivar; sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); sc->card = (struct emu_sc_info *)(device_get_softc(device_get_parent(dev))); @@ -1019,11 +1350,14 @@ emu_pcm_attach(device_t dev) return (ENXIO); } - sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_emu10kx softc"); + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_emu10kx pcm softc"); sc->dev = dev; - r = BUS_READ_IVAR(device_get_parent(dev), dev, EMU_VAR_ISEMU10K1, &is_emu10k1); - sc->is_emu10k1 = is_emu10k1 ? 1 : 0; + r = BUS_READ_IVAR(device_get_parent(dev), dev, EMU_VAR_ISEMU10K1, &ivar); + sc->is_emu10k1 = ivar ? 1 : 0; + + r = BUS_READ_IVAR(device_get_parent(dev), dev, EMU_VAR_MCH_DISABLED, &ivar); + sc->mch_disabled = ivar ? 1 : 0; sc->codec = NULL; @@ -1041,6 +1375,10 @@ emu_pcm_attach(device_t dev) sc->rt_mono.amounts_right[i] = 0x00; } + sc->emu10k1_volcache[0][0] = 75; + sc->emu10k1_volcache[1][0] = 75; + sc->emu10k1_volcache[0][1] = 75; + sc->emu10k1_volcache[1][1] = 75; r = BUS_READ_IVAR(device_get_parent(dev), dev, EMU_VAR_ROUTE, &route); sc->route = route; switch (route) { @@ -1053,16 +1391,13 @@ emu_pcm_attach(device_t dev) sc->codec = AC97_CREATE(dev, sc, emu_ac97); else sc->codec = AC97_CREATE(dev, sc, emu_eac97); - if (sc->codec == NULL) { - if (mixer_init(dev, &emudspmixer_class, sc)) { - device_printf(dev, "failed to initialize DSP mixer\n"); - goto bad; - } - } else - if (mixer_init(dev, ac97_getmixerclass(), sc->codec) == -1) { - device_printf(dev, "can't initialize AC97 mixer!\n"); - goto bad; - } + sc->ac97_mixerclass = NULL; + if (sc->codec != NULL) + sc->ac97_mixerclass = ac97_getmixerclass(); + if (mixer_init(dev, &emudspmixer_class, sc)) { + device_printf(dev, "failed to initialize DSP mixer\n"); + goto bad; + } break; case RT_REAR: sc->rt.amounts_left[2] = 0xff; @@ -1101,7 +1436,10 @@ emu_pcm_attach(device_t dev) } break; case RT_MCHRECORD: - /* XXX add mixer here */ + if (mixer_init(dev, &emuefxmixer_class, sc)) { + device_printf(dev, "failed to initialize EFX mixer\n"); + goto bad; + } break; default: device_printf(dev, "invalid default route\n"); @@ -1109,7 +1447,7 @@ emu_pcm_attach(device_t dev) } inte = INTE_INTERVALTIMERENB; - ipr = IPR_INTERVALTIMER; /* Used by playback */ + ipr = IPR_INTERVALTIMER; /* Used by playback & ADC */ sc->ihandle = emu_intr_register(sc->card, inte, ipr, &emu_pcm_intr, sc); if (emu_pcm_init(sc) == -1) { @@ -1117,6 +1455,12 @@ emu_pcm_attach(device_t dev) goto bad; } + /* + * We don't register interrupt handler with snd_setup_intr + * in pcm device. Mark pcm device as MPSAFE manually. + */ + pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); + /* XXX we should better get number of available channels from parent */ if (pcm_register(dev, sc, (route == RT_FRONT) ? MAX_CHANNELS : 1, (route == RT_FRONT) ? 1 : 0)) { device_printf(dev, "can't register PCM channels!\n"); @@ -1172,7 +1516,6 @@ static device_method_t emu_pcm_methods[] = { DEVMETHOD(device_probe, emu_pcm_probe), DEVMETHOD(device_attach, emu_pcm_attach), DEVMETHOD(device_detach, emu_pcm_detach), - {0, 0} }; diff --git a/sys/dev/sound/pci/emu10kx.c b/sys/dev/sound/pci/emu10kx.c index 42e9bbd..35af8aa 100644 --- a/sys/dev/sound/pci/emu10kx.c +++ b/sys/dev/sound/pci/emu10kx.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> - * Copyright (c) 2003-2006 Yuriy Tsibizov <yuriy.tsibizov@gfk.ru> + * Copyright (c) 2003-2007 Yuriy Tsibizov <yuriy.tsibizov@gfk.ru> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,6 +38,7 @@ #include <sys/lock.h> #include <sys/mutex.h> #include <sys/sysctl.h> +#include <sys/kdb.h> #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> @@ -48,7 +49,6 @@ #include <dev/sound/pcm/sound.h> #include <dev/sound/pcm/ac97.h> -#include "opt_emu10kx.h" #include <dev/sound/pci/emu10kx.h> /* hw flags */ @@ -104,6 +104,9 @@ #define COND_SATURATION DSP_CONST(0x6) #define COND_NEQ_ZERO DSP_CONST(0x8) +#define DSP_ACCUM DSP_CONST(0x16) +#define DSP_CCR DSP_CONST(0x17) + /* Live! Inputs */ #define IN_AC97_L 0x00 #define IN_AC97_R 0x01 @@ -226,7 +229,9 @@ #define C_SIDE_R 9 #define NUM_CACHES 10 -#define NUM_DUMMIES 64 +#define CDSPDIFMUTE 0 +#define ANALOGMUTE 1 +#define NUM_MUTE 2 #define EMU_MAX_GPR 512 #define EMU_MAX_IRQ_CONSUMERS 32 @@ -243,6 +248,8 @@ struct emu_voice { struct emu_voice *slave; uint32_t sa; uint32_t ea; + uint32_t routing[8]; + uint32_t amounts[8]; }; struct emu_memblk { @@ -260,6 +267,7 @@ struct emu_mem { bus_addr_t silent_page_addr; bus_addr_t ptb_pages_addr; bus_dma_tag_t dmat; + struct emu_sc_info *card; SLIST_HEAD(, emu_memblk) blocks; }; @@ -310,6 +318,7 @@ struct emu_sc_info { struct emu_intr_handler ihandler[EMU_MAX_IRQ_CONSUMERS]; /* Card HW configuration */ + unsigned int mode; /* analog / digital */ unsigned int mchannel_fx; unsigned int dsp_zero; unsigned int code_base; @@ -324,10 +333,11 @@ struct emu_sc_info { unsigned int address_mask; uint32_t is_emu10k1:1, is_emu10k2, is_ca0102, is_ca0108:1, has_ac97:1, has_51:1, has_71:1, - enable_ir:1, enable_debug:1, + enable_ir:1, broken_digital:1, is_cardbus:1; - unsigned int num_inputs; + signed int mch_disabled, mch_rec, dbg_level; + signed int num_inputs; unsigned int num_outputs; unsigned int num_fxbuses; unsigned int routing_code_start; @@ -345,7 +355,8 @@ struct emu_sc_info { int mixer_gpr[NUM_MIXERS]; int mixer_volcache[NUM_MIXERS]; int cache_gpr[NUM_CACHES]; - int dummy_gpr[NUM_DUMMIES]; + int dummy_gpr; + int mute_gpr[NUM_MUTE]; struct sysctl_ctx_list *ctx; struct sysctl_oid *root; }; @@ -397,6 +408,35 @@ static int emu_pci_attach(device_t dev); static int emu_pci_detach(device_t dev); static int emu_modevent(module_t mod __unused, int cmd, void *data __unused); +#ifdef SND_EMU10KX_DEBUG + +#define EMU_MTX_DEBUG() do { \ + if (mtx_owned(&sc->rw)) { \ + printf("RW owned in %s line %d for %s\n", __func__, \ + __LINE__ , device_get_nameunit(sc->dev)); \ + printf("rw lock owned: %d\n", mtx_owned(&sc->rw)); \ + printf("rw lock: value %x thread %x\n", \ + ((&sc->rw)->mtx_lock & ~MTX_FLAGMASK), \ + (uintptr_t)curthread); \ + printf("rw lock: recursed %d\n", mtx_recursed(&sc->rw));\ + db_show_mtx(&sc->rw); \ + } \ + } while (0) +#else +#define EMU_MTX_DEBUG() do { \ + } while (0) +#endif + +#define EMU_RWLOCK() do { \ + EMU_MTX_DEBUG(); \ + mtx_lock(&(sc->rw)); \ + } while (0) + +#define EMU_RWUNLOCK() do { \ + mtx_unlock(&(sc->rw)); \ + EMU_MTX_DEBUG(); \ + } while (0) + /* Supported cards */ struct emu_hwinfo { uint16_t vendor; @@ -432,6 +472,7 @@ static struct emu_hwinfo emu_cards[] = { /* 0x8061..0x???? 5.1 EMU10K1 cards */ {0x1102, 0x0002, 0x1102, 0x8061, "SB????", "SBLive! Player 5.1", HAS_AC97 | HAS_51 | IS_EMU10K1}, + {0x1102, 0x0002, 0x1102, 0x8062, "CT4830", "SBLive! 1024", HAS_AC97 | HAS_51 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x8064, "SB????", "SBLive! 5.1", HAS_AC97 | HAS_51 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x8065, "SB0220", "SBLive! 5.1 Digital", HAS_AC97 | HAS_51 | IS_EMU10K1}, {0x1102, 0x0002, 0x1102, 0x8066, "CT4780", "SBLive! 5.1 Digital", HAS_AC97 | HAS_51 | IS_EMU10K1}, @@ -522,8 +563,10 @@ emu_getcard(device_t dev) } if (0x0000 == emu_cards[i].subdevice) { thiscard = i; - /* don't break, we can get more specific card - * later in the list */ + /* + * don't break, we can get more specific card + * later in the list. + */ } } } @@ -593,10 +636,12 @@ emu_rdptr(struct emu_sc_info *sc, unsigned int chn, unsigned int reg) uint32_t ptr, val, mask, size, offset; ptr = ((reg << 16) & sc->address_mask) | (chn & PTR_CHANNELNUM_MASK); - mtx_lock(&sc->rw); + + EMU_RWLOCK(); emu_wr_nolock(sc, PTR, ptr, 4); val = emu_rd_nolock(sc, DATA, 4); - mtx_unlock(&sc->rw); + EMU_RWUNLOCK(); + /* * XXX Some register numbers has data size and offset encoded in * it to get only part of 32bit register. This use is not described @@ -616,8 +661,10 @@ void emu_wrptr(struct emu_sc_info *sc, unsigned int chn, unsigned int reg, uint32_t data) { uint32_t ptr, mask, size, offset; + ptr = ((reg << 16) & sc->address_mask) | (chn & PTR_CHANNELNUM_MASK); - mtx_lock(&sc->rw); + + EMU_RWLOCK(); emu_wr_nolock(sc, PTR, ptr, 4); /* * XXX Another kind of magic encoding in register number. This can @@ -633,7 +680,7 @@ emu_wrptr(struct emu_sc_info *sc, unsigned int chn, unsigned int reg, uint32_t d data |= emu_rd_nolock(sc, DATA, 4) & ~mask; } emu_wr_nolock(sc, DATA, data, 4); - mtx_unlock(&sc->rw); + EMU_RWUNLOCK(); } /* * PTR2 / DATA2 interface. Access to P16v is made @@ -645,10 +692,13 @@ emu_rd_p16vptr(struct emu_sc_info *sc, uint16_t chn, uint16_t reg) { uint32_t val; - mtx_lock(&sc->rw); + /* XXX separate lock? */ + EMU_RWLOCK(); emu_wr_nolock(sc, PTR2, (reg << 16) | chn, 4); val = emu_rd_nolock(sc, DATA2, 4); - mtx_unlock(&sc->rw); + + EMU_RWUNLOCK(); + return (val); } @@ -656,10 +706,10 @@ void emu_wr_p16vptr(struct emu_sc_info *sc, uint16_t chn, uint16_t reg, uint32_t data) { - mtx_lock(&sc->rw); + EMU_RWLOCK(); emu_wr_nolock(sc, PTR2, (reg << 16) | chn, 4); emu_wr_nolock(sc, DATA2, data, 4); - mtx_unlock(&sc->rw); + EMU_RWUNLOCK(); } /* * XXX CardBus interface. Not tested on any real hardware. @@ -669,26 +719,29 @@ emu_wr_cbptr(struct emu_sc_info *sc, uint32_t data) { uint32_t val; - /* 0x38 is IPE3 (CD S/PDIF interrupt pending register) on CA0102 Seems + /* + * 0x38 is IPE3 (CD S/PDIF interrupt pending register) on CA0102. Seems * to be some reg/value accessible kind of config register on CardBus - * CA0108, with value(?) in top 16 bit, address(?) in low 16 */ - mtx_lock(&sc->rw); + * CA0108, with value(?) in top 16 bit, address(?) in low 16 + */ + val = emu_rd_nolock(sc, 0x38, 4); emu_wr_nolock(sc, 0x38, data, 4); val = emu_rd_nolock(sc, 0x38, 4); - mtx_unlock(&sc->rw); + } /* * Direct hardware register access + * Assume that it is never used to access PTR-based registers and can run unlocked. */ void emu_wr(struct emu_sc_info *sc, unsigned int regno, uint32_t data, unsigned int size) { + KASSERT(regno != PTR, ("emu_wr: attempt to write to PTR")); + KASSERT(regno != PTR2, ("emu_wr: attempt to write to PTR2")); - mtx_lock(&sc->rw); emu_wr_nolock(sc, regno, data, size); - mtx_unlock(&sc->rw); } uint32_t @@ -696,9 +749,10 @@ emu_rd(struct emu_sc_info *sc, unsigned int regno, unsigned int size) { uint32_t rd; - mtx_lock(&sc->rw); + KASSERT(regno != DATA, ("emu_rd: attempt to read DATA")); + KASSERT(regno != DATA2, ("emu_rd: attempt to read DATA2")); + rd = emu_rd_nolock(sc, regno, size); - mtx_unlock(&sc->rw); return (rd); } @@ -711,7 +765,6 @@ emu_enable_ir(struct emu_sc_info *sc) { uint32_t iocfg; - mtx_lock(&sc->rw); if (sc->is_emu10k2 || sc->is_ca0102) { iocfg = emu_rd_nolock(sc, A_IOCFG, 2); emu_wr_nolock(sc, A_IOCFG, iocfg | A_IOCFG_GPOUT2, 2); @@ -734,7 +787,6 @@ emu_enable_ir(struct emu_sc_info *sc) device_printf(sc->dev, "SB Live! IR MIDI events enabled.\n"); sc->enable_ir = 1; } - mtx_unlock(&sc->rw); } @@ -747,12 +799,16 @@ emu_timer_create(struct emu_sc_info *sc) int i, timer; timer = -1; + + mtx_lock(&sc->lock); for (i = 0; i < EMU_MAX_IRQ_CONSUMERS; i++) if (sc->timer[i] == 0) { sc->timer[i] = -1; /* disable it */ timer = i; + mtx_unlock(&sc->lock); return (timer); } + mtx_unlock(&sc->lock); return (-1); } @@ -762,18 +818,22 @@ emu_timer_set(struct emu_sc_info *sc, int timer, int delay) { int i; - if(timer < 0) + if (timer < 0) return (-1); RANGE(delay, 16, 1024); RANGE(timer, 0, EMU_MAX_IRQ_CONSUMERS-1); + mtx_lock(&sc->lock); sc->timer[timer] = delay; for (i = 0; i < EMU_MAX_IRQ_CONSUMERS; i++) if (sc->timerinterval > sc->timer[i]) sc->timerinterval = sc->timer[i]; + /* XXX */ emu_wr(sc, TIMER, sc->timerinterval & 0x03ff, 2); + mtx_unlock(&sc->lock); + return (timer); } @@ -784,7 +844,7 @@ emu_timer_enable(struct emu_sc_info *sc, int timer, int go) int ena_int; int i; - if(timer < 0) + if (timer < 0) return (-1); RANGE(timer, 0, EMU_MAX_IRQ_CONSUMERS-1); @@ -822,7 +882,7 @@ emu_timer_enable(struct emu_sc_info *sc, int timer, int go) int emu_timer_clear(struct emu_sc_info *sc, int timer) { - if(timer < 0) + if (timer < 0) return (-1); RANGE(timer, 0, EMU_MAX_IRQ_CONSUMERS-1); @@ -857,15 +917,15 @@ emu_intr_register(struct emu_sc_info *sc, uint32_t inte_mask, uint32_t intr_mask x |= inte_mask; emu_wr(sc, INTE, x, 4); mtx_unlock(&sc->lock); -#ifdef SND_EMU10KX_DEBUG - device_printf(sc->dev, "ihandle %d registered\n", i); -#endif + if (sc->dbg_level > 1) + device_printf(sc->dev, "ihandle %d registered\n", i); + return (i); } mtx_unlock(&sc->lock); -#ifdef SND_EMU10KX_DEBUG - device_printf(sc->dev, "ihandle not registered\n"); -#endif + if (sc->dbg_level > 1) + device_printf(sc->dev, "ihandle not registered\n"); + return (-1); } @@ -921,10 +981,10 @@ emu_intr(void *p) (sc->ihandler[i].intr_mask) & stat); } } -#ifdef SND_EMU10KX_DEBUG - if(stat & (~ack)) - device_printf(sc->dev, "Unhandled interrupt: %08x\n", stat & (~ack)); -#endif + if (sc->dbg_level > 1) + if (stat & (~ack)) + device_printf(sc->dev, "Unhandled interrupt: %08x\n", stat & (~ack)); + } if ((sc->is_ca0102) || (sc->is_ca0108)) @@ -934,7 +994,9 @@ emu_intr(void *p) if (stat == 0) break; emu_wr(sc, IPR2, stat, 4); - device_printf(sc->dev, "IPR2: %08x\n", stat); + if (sc->dbg_level > 1) + device_printf(sc->dev, "IPR2: %08x\n", stat); + break; /* to avoid infinite loop. shoud be removed * after completion of P16V interface. */ } @@ -946,7 +1008,9 @@ emu_intr(void *p) if (stat == 0) break; emu_wr(sc, IPR3, stat, 4); - device_printf(sc->dev, "IPR3: %08x\n", stat); + if (sc->dbg_level > 1) + device_printf(sc->dev, "IPR3: %08x\n", stat); + break; /* to avoid infinite loop. should be removed * after completion of S/PDIF interface */ } @@ -982,12 +1046,19 @@ emu_malloc(struct emu_mem *mem, uint32_t sz, bus_addr_t * addr) { void *dmabuf; bus_dmamap_t map; + int error; *addr = 0; - if (bus_dmamem_alloc(mem->dmat, &dmabuf, BUS_DMA_NOWAIT, &map)) + if ((error = bus_dmamem_alloc(mem->dmat, &dmabuf, BUS_DMA_NOWAIT, &map))) { + if (mem->card->dbg_level > 2) + device_printf(mem->card->dev, "emu_malloc: failed to alloc DMA map: %d\n", error); return (NULL); - if (bus_dmamap_load(mem->dmat, map, dmabuf, sz, emu_setmap, addr, 0) || !*addr) + } + if ((error = bus_dmamap_load(mem->dmat, map, dmabuf, sz, emu_setmap, addr, 0)) || !*addr) { + if (mem->card->dbg_level > 2) + device_printf(mem->card->dev, "emu_malloc: failed to load DMA memory: %d\n", error); return (NULL); + } return (dmabuf); } @@ -1007,8 +1078,11 @@ emu_memalloc(struct emu_mem *mem, uint32_t sz, bus_addr_t * addr, const char *ow blksz = sz / EMUPAGESIZE; if (sz > (blksz * EMUPAGESIZE)) blksz++; - if (blksz > EMU_MAX_BUFSZ / EMUPAGESIZE) + if (blksz > EMU_MAX_BUFSZ / EMUPAGESIZE) { + if (mem->card->dbg_level > 2) + device_printf(mem->card->dev, "emu_memalloc: memory request tool large\n"); return (NULL); + } /* find a free block in the bitmap */ found = 0; start = 1; @@ -1020,15 +1094,23 @@ emu_memalloc(struct emu_mem *mem, uint32_t sz, bus_addr_t * addr, const char *ow if (!found) start++; } - if (!found) + if (!found) { + if (mem->card->dbg_level > 2) + device_printf(mem->card->dev, "emu_memalloc: no free space in bitmap\n"); return (NULL); + } blk = malloc(sizeof(*blk), M_DEVBUF, M_NOWAIT); - if (blk == NULL) + if (blk == NULL) { + if (mem->card->dbg_level > 2) + device_printf(mem->card->dev, "emu_memalloc: buffer allocation failed\n"); return (NULL); + } bzero(blk, sizeof(*blk)); membuf = emu_malloc(mem, sz, &blk->buf_addr); *addr = blk->buf_addr; if (membuf == NULL) { + if (mem->card->dbg_level > 2) + device_printf(mem->card->dev, "emu_memalloc: can't setup HW memory\n"); free(blk, M_DEVBUF); return (NULL); } @@ -1037,9 +1119,6 @@ emu_memalloc(struct emu_mem *mem, uint32_t sz, bus_addr_t * addr, const char *ow blk->pte_size = blksz; strncpy(blk->owner, owner, 15); blk->owner[15] = '\0'; -#ifdef SND_EMU10KX_DEBUG - printf("emu10kx emu_memalloc: allocating %d for %s\n", blk->pte_size, blk->owner); -#endif ofs = 0; for (idx = start; idx < start + blksz; idx++) { mem->bmap[idx >> 3] |= 1 << (idx & 7); @@ -1064,9 +1143,6 @@ emu_memfree(struct emu_mem *mem, void *membuf) } if (blk == NULL) return (EINVAL); -#ifdef SND_EMU10KX_DEBUG - printf("emu10kx emu_memfree: freeing %d for %s\n", blk->pte_size, blk->owner); -#endif SLIST_REMOVE(&mem->blocks, blk, emu_memblk, link); emu_free(mem, membuf); tmp = (uint32_t) (mem->silent_page_addr) << 1; @@ -1184,9 +1260,11 @@ emu_vfree(struct emu_sc_info *sc, struct emu_voice *v) for (i = 0; i < NUM_G; i++) { if (v == &sc->voice[i] && sc->voice[i].busy) { v->busy = 0; - /* XXX What we should do with mono channels? - See -pcm.c emupchan_init for other side of - this problem */ + /* + * XXX What we should do with mono channels? + * See -pcm.c emupchan_init for other side of + * this problem + */ if (v->slave != NULL) r = emu_memfree(&sc->mem, v->vbuf); } @@ -1202,12 +1280,17 @@ emu_vinit(struct emu_sc_info *sc, struct emu_voice *m, struct emu_voice *s, bus_addr_t tmp_addr; vbuf = emu_memalloc(&sc->mem, sz, &tmp_addr, "vinit"); - if (vbuf == NULL) + if (vbuf == NULL) { + if(sc->dbg_level > 2) + device_printf(sc->dev, "emu_memalloc returns NULL in enu_vinit\n"); return (ENOMEM); + } if (b != NULL) sndbuf_setup(b, vbuf, sz); m->start = emu_memstart(&sc->mem, vbuf) * EMUPAGESIZE; - if (m->start == -1) { + if (m->start < 0) { + if(sc->dbg_level > 2) + device_printf(sc->dev, "emu_memstart returns (-1) in enu_vinit\n"); emu_memfree(&sc->mem, vbuf); return (ENOMEM); } @@ -1258,41 +1341,18 @@ emu_vsetup(struct emu_voice *v, int fmt, int spd) void emu_vroute(struct emu_sc_info *sc, struct emu_route *rt, struct emu_voice *v) { - unsigned int routing[8], amounts[8]; int i; for (i = 0; i < 8; i++) { - routing[i] = rt->routing_left[i]; - amounts[i] = rt->amounts_left[i]; + v->routing[i] = rt->routing_left[i]; + v->amounts[i] = rt->amounts_left[i]; } if ((v->stereo) && (v->ismaster == 0)) for (i = 0; i < 8; i++) { - routing[i] = rt->routing_right[i]; - amounts[i] = rt->amounts_right[i]; + v->routing[i] = rt->routing_right[i]; + v->amounts[i] = rt->amounts_right[i]; } - if (sc->is_emu10k1) { - emu_wrptr(sc, v->vnum, FXRT, ((routing[3] << 12) | - (routing[2] << 8) | - (routing[1] << 4) | - (routing[0] << 0)) << 16); - } else { - emu_wrptr(sc, v->vnum, A_FXRT1, (routing[3] << 24) | - (routing[2] << 16) | - (routing[1] << 8) | - (routing[0] << 0)); - emu_wrptr(sc, v->vnum, A_FXRT2, (routing[7] << 24) | - (routing[6] << 16) | - (routing[5] << 8) | - (routing[4] << 0)); - emu_wrptr(sc, v->vnum, A_SENDAMOUNTS, (amounts[7] << 24) | - (amounts[6] << 26) | - (amounts[5] << 8) | - (amounts[4] << 0)); - } - emu_wrptr(sc, v->vnum, PTRX, (amounts[0] << 8) | (amounts[1] << 0)); - emu_wrptr(sc, v->vnum, DSL, v->ea | (amounts[3] << 24)); - emu_wrptr(sc, v->vnum, PSST, v->sa | (amounts[2] << 24)); if ((v->stereo) && (v->slave != NULL)) emu_vroute(sc, rt, v->slave); } @@ -1301,7 +1361,7 @@ void emu_vwrite(struct emu_sc_info *sc, struct emu_voice *v) { int s; - uint32_t am_2, am_3, start, val, silent_page; + uint32_t start, val, silent_page; s = (v->stereo ? 1 : 0) + (v->b16 ? 1 : 0); @@ -1318,10 +1378,28 @@ emu_vwrite(struct emu_sc_info *sc, struct emu_voice *v) val *= v->b16 ? 1 : 2; start = v->sa + val; - am_3 = emu_rdptr(sc, v->vnum, DSL) & 0xff000000; - emu_wrptr(sc, v->vnum, DSL, v->ea | am_3); - am_2 = emu_rdptr(sc, v->vnum, PSST) & 0xff000000; - emu_wrptr(sc, v->vnum, PSST, v->sa | am_2); + if (sc->is_emu10k1) { + emu_wrptr(sc, v->vnum, FXRT, ((v->routing[3] << 12) | + (v->routing[2] << 8) | + (v->routing[1] << 4) | + (v->routing[0] << 0)) << 16); + } else { + emu_wrptr(sc, v->vnum, A_FXRT1, (v->routing[3] << 24) | + (v->routing[2] << 16) | + (v->routing[1] << 8) | + (v->routing[0] << 0)); + emu_wrptr(sc, v->vnum, A_FXRT2, (v->routing[7] << 24) | + (v->routing[6] << 16) | + (v->routing[5] << 8) | + (v->routing[4] << 0)); + emu_wrptr(sc, v->vnum, A_SENDAMOUNTS, (v->amounts[7] << 24) | + (v->amounts[6] << 26) | + (v->amounts[5] << 8) | + (v->amounts[4] << 0)); + } + emu_wrptr(sc, v->vnum, PTRX, (v->amounts[0] << 8) | (v->amounts[1] << 0)); + emu_wrptr(sc, v->vnum, DSL, v->ea | (v->amounts[3] << 24)); + emu_wrptr(sc, v->vnum, PSST, v->sa | (v->amounts[2] << 24)); emu_wrptr(sc, v->vnum, CCCA, start | (v->b16 ? 0 : CCCA_8BITSELECT)); emu_wrptr(sc, v->vnum, Z1, 0); @@ -1468,24 +1546,70 @@ emu_addefxmixer(struct emu_sc_info *sc, const char *mix_name, const int mix_id, volgpr = emu_rm_gpr_alloc(sc->rm, 1); emumix_set_fxvol(sc, volgpr, defvolume); - /* Mixer controls with NULL mix_name are handled by AC97 emulation - code or PCM mixer. */ + /* + * Mixer controls with NULL mix_name are handled + * by AC97 emulation code or PCM mixer. + */ if (mix_name != NULL) { - /* Temporary sysctls should start with underscore, + /* + * Temporary sysctls should start with underscore, * see freebsd-current mailing list, emu10kx driver - * discussion around 2006-05-24. */ + * discussion around 2006-05-24. + */ snprintf(sysctl_name, 32, "_%s", mix_name); SYSCTL_ADD_PROC(sc->ctx, SYSCTL_CHILDREN(sc->root), OID_AUTO, sysctl_name, CTLTYPE_INT | CTLFLAG_RW, sc, mix_id, - sysctl_emu_mixer_control, "I",""); + sysctl_emu_mixer_control, "I", ""); } return (volgpr); } -/* allocate cache GPRs that will hold mixed output channels +static int +sysctl_emu_digitalswitch_control(SYSCTL_HANDLER_ARGS) +{ + struct emu_sc_info *sc; + int new_val; + int err; + + sc = arg1; + + new_val = (sc->mode == MODE_DIGITAL) ? 1 : 0; + err = sysctl_handle_int(oidp, &new_val, 0, req); + + if (err || req->newptr == NULL) + return (err); + if (new_val < 0 || new_val > 1) + return (EINVAL); + + switch (new_val) { + case 0: + emumix_set_mode(sc, MODE_ANALOG); + break; + case 1: + emumix_set_mode(sc, MODE_DIGITAL); + break; + } + return (0); +} + +static void +emu_digitalswitch(struct emu_sc_info *sc) +{ + /* XXX temporary? */ + SYSCTL_ADD_PROC(sc->ctx, + SYSCTL_CHILDREN(sc->root), + OID_AUTO, "_digital", + CTLTYPE_INT | CTLFLAG_RW, sc, 0, + sysctl_emu_digitalswitch_control, "I", "Enable digital output"); + + return; +} + +/* + * Allocate cache GPRs that will hold mixed output channels * and clear it on every DSP run. */ #define EFX_CACHE(CACHE_IDX) do { \ @@ -1511,7 +1635,7 @@ emu_addefxmixer(struct emu_sc_info *sc, const char *mix_name, const int mix_id, } while (0) /* allocate GPR, OUT = IN * VOL */ -#define EFX_OUTPUT(TITLE,OUT_CACHE_IDX, OUT_GPR_IDX, OUTP_NR, DEF) do { \ +#define EFX_OUTPUT(TITLE, OUT_CACHE_IDX, OUT_GPR_IDX, OUTP_NR, DEF) do { \ sc->mixer_gpr[OUT_GPR_IDX] = emu_addefxmixer(sc, TITLE, OUT_GPR_IDX, DEF); \ sc->mixer_volcache[OUT_GPR_IDX] = DEF; \ emu_addefxop(sc, MACS, \ @@ -1523,29 +1647,37 @@ emu_addefxmixer(struct emu_sc_info *sc, const char *mix_name, const int mix_id, } while (0) /* like EFX_OUTPUT, but don't allocate mixer gpr */ -#define EFX_OUTPUTD(OUT_CACHE_IDX, OUT_GPR_IDX, OUTP_NR) do{ \ +#define EFX_OUTPUTD(OUT_CACHE_IDX, OUT_GPR_IDX, OUTP_NR) do { \ emu_addefxop(sc, MACS, \ OUTP(OUTP_NR), \ DSP_CONST(0), \ GPR(sc->cache_gpr[OUT_CACHE_IDX]), \ GPR(sc->mixer_gpr[OUT_GPR_IDX]), \ &pc); \ -} while(0) +} while (0) -/* mute, if FLAG != 0 */ -/* XXX */ -#define EFX_MUTEIF(GPR_IDX, FLAG) do { \ -} while(0) +/* skip next OPCOUNT instructions if FLAG != 0 */ +#define EFX_SKIP(OPCOUNT, FLAG_GPR) do { \ + emu_addefxop(sc, MACS, \ + DSP_CONST(0), \ + GPR(sc->mute_gpr[FLAG_GPR]), \ + DSP_CONST(0), \ + DSP_CONST(0), \ + &pc); \ + emu_addefxop(sc, SKIP, \ + DSP_CCR, \ + DSP_CCR, \ + COND_NEQ_ZERO, \ + OPCOUNT, \ + &pc); \ +} while (0) -/* allocate dummy GPR. It's content will be used somewhere */ -#define EFX_DUMMY(DUMMY_IDX, DUMMY_VALUE) do { \ - sc->dummy_gpr[DUMMY_IDX] = emu_rm_gpr_alloc(sc->rm, 1); \ - emumix_set_gpr(sc, sc->dummy_gpr[DUMMY_IDX], DUMMY_VALUE); \ - emu_addefxop(sc, ACC3, \ - FX2(DUMMY_IDX), \ - GPR(sc->dummy_gpr[DUMMY_IDX]), \ +#define EFX_COPY(TO, FROM) do { \ + emu_addefxop(sc, ACC3, \ + TO, \ DSP_CONST(0), \ DSP_CONST(0), \ + FROM, \ &pc); \ } while (0) @@ -1573,9 +1705,16 @@ emu_initefx(struct emu_sc_info *sc) } } + /* allocate GPRs for mute switches (EFX_SKIP). Mute by default */ + for (i = 0; i < NUM_MUTE; i++) { + sc->mute_gpr[i] = emu_rm_gpr_alloc(sc->rm, 1); + emumix_set_gpr(sc, sc->mute_gpr[i], 1); + } + emu_digitalswitch(sc); + pc = 0; - /* + /* * DSP code below is not good, because: * 1. It can be written smaller, if it can use DSP accumulator register * instead of cache_gpr[]. @@ -1598,8 +1737,8 @@ emu_initefx(struct emu_sc_info *sc) /* fx0 to front/record, 100%/muted by default */ EFX_ROUTE("pcm_front_l", FX(0), M_FX0_FRONT_L, C_FRONT_L, 100); EFX_ROUTE("pcm_front_r", FX(1), M_FX1_FRONT_R, C_FRONT_R, 100); - EFX_ROUTE("pcm_rec_l", FX(0), M_FX0_REC_L, C_REC_L, 0); - EFX_ROUTE("pcm_rec_r", FX(1), M_FX1_REC_R, C_REC_R, 0); + EFX_ROUTE(NULL, FX(0), M_FX0_REC_L, C_REC_L, 0); + EFX_ROUTE(NULL, FX(1), M_FX1_REC_R, C_REC_R, 0); /* in0, from AC97 codec output */ EFX_ROUTE("ac97_front_l", INP(IN_AC97_L), M_IN0_FRONT_L, C_FRONT_L, 0); @@ -1608,114 +1747,193 @@ emu_initefx(struct emu_sc_info *sc) EFX_ROUTE("ac97_rec_r", INP(IN_AC97_R), M_IN0_REC_R, C_REC_R, 0); /* in1, from CD S/PDIF */ - EFX_ROUTE("cdspdif_front_l", INP(IN_SPDIF_CD_L), M_IN1_FRONT_L, C_FRONT_L, 0); - EFX_MUTEIF(M_IN1_FRONT_L, CDSPDIFMUTE); - EFX_ROUTE("cdspdif_front_r", INP(IN_SPDIF_CD_R), M_IN1_FRONT_R, C_FRONT_R, 0); - EFX_MUTEIF(M_IN1_FRONT_R, CDSPDIFMUTE); - EFX_ROUTE("cdspdif_rec_l", INP(IN_SPDIF_CD_L), M_IN1_REC_L, C_REC_L, 0); - EFX_MUTEIF(M_IN1_REC_L, CDSPDIFMUTE); - EFX_ROUTE("cdspdif_rec_r", INP(IN_SPDIF_CD_R), M_IN1_REC_R, C_REC_R, 0); - EFX_MUTEIF(M_IN1_REC_L, CDSPDIFMUTE); -#ifdef SND_EMU10KX_DEBUG_OUTPUTS - /* in2, ZoomVide (???) */ - EFX_ROUTE("zoom_front_l", INP(IN_ZOOM_L), M_IN2_FRONT_L, C_FRONT_L, 0); - EFX_ROUTE("zoom_front_r", INP(IN_ZOOM_R), M_IN2_FRONT_R, C_FRONT_R, 0); - EFX_ROUTE("zoom_rec_l", INP(IN_ZOOM_L), M_IN2_REC_L, C_REC_L, 0); - EFX_ROUTE("zoom_rec_r", INP(IN_ZOOM_R), M_IN2_REC_R, C_REC_R, 0); -#endif -#ifdef SND_EMU10KX_DEBUG_OUTPUTS - /* in3, TOSLink (???) */ - EFX_ROUTE("toslink_front_l", INP(IN_TOSLINK_L), M_IN3_FRONT_L, C_FRONT_L, 0); - EFX_ROUTE("toslink_front_r", INP(IN_TOSLINK_R), M_IN3_FRONT_R, C_FRONT_R, 0); - EFX_ROUTE("toslink_rec_l", INP(IN_TOSLINK_L), M_IN3_REC_L, C_REC_L, 0); - EFX_ROUTE("toslink_rec_r", INP(IN_TOSLINK_R), M_IN3_REC_R, C_REC_R, 0); -#endif + /* XXX EFX_SKIP 4 assumes that each EFX_ROUTE is one DSP op */ + EFX_SKIP(4, CDSPDIFMUTE); + EFX_ROUTE(NULL, INP(IN_SPDIF_CD_L), M_IN1_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE(NULL, INP(IN_SPDIF_CD_R), M_IN1_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE(NULL, INP(IN_SPDIF_CD_L), M_IN1_REC_L, C_REC_L, 0); + EFX_ROUTE(NULL, INP(IN_SPDIF_CD_R), M_IN1_REC_R, C_REC_R, 0); + + if (sc->dbg_level > 0) { + /* in2, ZoomVide (???) */ + EFX_ROUTE("zoom_front_l", INP(IN_ZOOM_L), M_IN2_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("zoom_front_r", INP(IN_ZOOM_R), M_IN2_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("zoom_rec_l", INP(IN_ZOOM_L), M_IN2_REC_L, C_REC_L, 0); + EFX_ROUTE("zoom_rec_r", INP(IN_ZOOM_R), M_IN2_REC_R, C_REC_R, 0); + } + + /* in3, TOSLink */ + EFX_ROUTE(NULL, INP(IN_TOSLINK_L), M_IN3_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE(NULL, INP(IN_TOSLINK_R), M_IN3_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE(NULL, INP(IN_TOSLINK_L), M_IN3_REC_L, C_REC_L, 0); + EFX_ROUTE(NULL, INP(IN_TOSLINK_R), M_IN3_REC_R, C_REC_R, 0); /* in4, LineIn */ - EFX_ROUTE("linein_front_l", INP(IN_LINE1_L), M_IN4_FRONT_L, C_FRONT_L, 0); - EFX_ROUTE("linein_front_r", INP(IN_LINE1_R), M_IN4_FRONT_R, C_FRONT_R, 0); - EFX_ROUTE("linein_rec_l", INP(IN_LINE1_L), M_IN4_REC_L, C_REC_L, 0); - EFX_ROUTE("linein_rec_r", INP(IN_LINE1_R), M_IN4_REC_R, C_REC_R, 0); + EFX_ROUTE(NULL, INP(IN_LINE1_L), M_IN4_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE(NULL, INP(IN_LINE1_R), M_IN4_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE(NULL, INP(IN_LINE1_L), M_IN4_REC_L, C_REC_L, 0); + EFX_ROUTE(NULL, INP(IN_LINE1_R), M_IN4_REC_R, C_REC_R, 0); /* in5, on-card S/PDIF */ - EFX_ROUTE("spdif_front_l", INP(IN_COAX_SPDIF_L), M_IN5_FRONT_L, C_FRONT_L, 0); - EFX_ROUTE("spdif_front_r", INP(IN_COAX_SPDIF_R), M_IN5_FRONT_R, C_FRONT_R, 0); - EFX_ROUTE("spdif_rec_l", INP(IN_COAX_SPDIF_L), M_IN5_REC_L, C_REC_L, 0); - EFX_ROUTE("spdif_rec_r", INP(IN_COAX_SPDIF_R), M_IN5_REC_R, C_REC_R, 0); + EFX_ROUTE(NULL, INP(IN_COAX_SPDIF_L), M_IN5_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE(NULL, INP(IN_COAX_SPDIF_R), M_IN5_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE(NULL, INP(IN_COAX_SPDIF_L), M_IN5_REC_L, C_REC_L, 0); + EFX_ROUTE(NULL, INP(IN_COAX_SPDIF_R), M_IN5_REC_R, C_REC_R, 0); /* in6, Line2 on Live!Drive */ - EFX_ROUTE("line2_front_l", INP(IN_LINE2_L), M_IN6_FRONT_L, C_FRONT_L, 0); - EFX_ROUTE("line2_front_r", INP(IN_LINE2_R), M_IN6_FRONT_R, C_FRONT_R, 0); - EFX_ROUTE("line2_rec_l", INP(IN_LINE2_L), M_IN6_REC_L, C_REC_L, 0); - EFX_ROUTE("line2_rec_r", INP(IN_LINE2_R), M_IN6_REC_R, C_REC_R, 0); -#ifdef SND_EMU10KX_DEBUG_OUTPUTS - /* in7, unknown */ - EFX_ROUTE("in7_front_l", INP(0xE), M_IN7_FRONT_L, C_FRONT_L, 0); - EFX_ROUTE("in7_front_r", INP(0xF), M_IN7_FRONT_R, C_FRONT_R, 0); - EFX_ROUTE("in7_rec_l", INP(0xE), M_IN7_REC_L, C_REC_L, 0); - EFX_ROUTE("in7_rec_r", INP(0xF), M_IN7_REC_R, C_REC_R, 0); -#endif - /* front output to hedaphones and both analog and digital */ + EFX_ROUTE(NULL, INP(IN_LINE2_L), M_IN6_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE(NULL, INP(IN_LINE2_R), M_IN6_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE(NULL, INP(IN_LINE2_L), M_IN6_REC_L, C_REC_L, 0); + EFX_ROUTE(NULL, INP(IN_LINE2_R), M_IN6_REC_R, C_REC_R, 0); + + if (sc->dbg_level > 0) { + /* in7, unknown */ + EFX_ROUTE("in7_front_l", INP(0xE), M_IN7_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("in7_front_r", INP(0xF), M_IN7_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("in7_rec_l", INP(0xE), M_IN7_REC_L, C_REC_L, 0); + EFX_ROUTE("in7_rec_r", INP(0xF), M_IN7_REC_R, C_REC_R, 0); + } + + /* analog and digital */ EFX_OUTPUT("master_front_l", C_FRONT_L, M_MASTER_FRONT_L, OUT_AC97_L, 100); EFX_OUTPUT("master_front_r", C_FRONT_R, M_MASTER_FRONT_R, OUT_AC97_R, 100); + /* S/PDIF */ + EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, OUT_TOSLINK_L); + EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, OUT_TOSLINK_R); + /* Headphones */ EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, OUT_HEADPHONE_L); EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, OUT_HEADPHONE_R); /* rec output to "ADC" */ EFX_OUTPUT("master_rec_l", C_REC_L, M_MASTER_REC_L, OUT_ADC_REC_L, 100); EFX_OUTPUT("master_rec_r", C_REC_R, M_MASTER_REC_R, OUT_ADC_REC_R, 100); -#ifdef SND_EMU10KX_MULTICHANNEL - /* - * Additional channel volume is controlled by mixer in - * emu_dspmixer_set() in -pcm.c - */ - /* fx2/3 (pcm1) to rear */ - EFX_CACHE(C_REAR_L); - EFX_CACHE(C_REAR_R); - EFX_ROUTE(NULL, FX(2), M_FX2_REAR_L, C_REAR_L, 100); - EFX_ROUTE(NULL, FX(3), M_FX3_REAR_R, C_REAR_R, 100); + if (!(sc->mch_disabled)) { + /* + * Additional channel volume is controlled by mixer in + * emu_dspmixer_set() in -pcm.c + */ + + /* fx2/3 (pcm1) to rear */ + EFX_CACHE(C_REAR_L); + EFX_CACHE(C_REAR_R); + EFX_ROUTE(NULL, FX(2), M_FX2_REAR_L, C_REAR_L, 100); + EFX_ROUTE(NULL, FX(3), M_FX3_REAR_R, C_REAR_R, 100); + + EFX_OUTPUT(NULL, C_REAR_L, M_MASTER_REAR_L, OUT_REAR_L, 100); + EFX_OUTPUT(NULL, C_REAR_R, M_MASTER_REAR_R, OUT_REAR_R, 100); + if (sc->has_51) { + /* fx4 (pcm2) to center */ + EFX_CACHE(C_CENTER); + EFX_ROUTE(NULL, FX(4), M_FX4_CENTER, C_CENTER, 100); + EFX_OUTPUT(NULL, C_CENTER, M_MASTER_CENTER, OUT_D_CENTER, 100); + + /* XXX in digital mode (default) this should be muted because + this output is shared with digital out */ + EFX_SKIP(1, ANALOGMUTE); + EFX_OUTPUTD(C_CENTER, M_MASTER_CENTER, OUT_A_CENTER); + + /* fx5 (pcm3) to sub */ + EFX_CACHE(C_SUB); + EFX_ROUTE(NULL, FX(5), M_FX5_SUBWOOFER, C_SUB, 100); + EFX_OUTPUT(NULL, C_SUB, M_MASTER_SUBWOOFER, OUT_D_SUB, 100); + + /* XXX in digital mode (default) this should be muted because + this output is shared with digital out */ + EFX_SKIP(1, ANALOGMUTE); + EFX_OUTPUTD(C_SUB, M_MASTER_SUBWOOFER, OUT_A_SUB); - EFX_OUTPUT(NULL, C_REAR_L, M_MASTER_REAR_L, OUT_REAR_L, 100); - EFX_OUTPUT(NULL, C_REAR_R, M_MASTER_REAR_R, OUT_REAR_R, 100); - if (sc->has_51) { - /* fx4 (pcm2) to center */ - EFX_CACHE(C_CENTER); - EFX_ROUTE(NULL, FX(4), M_FX4_CENTER, C_CENTER, 100); - EFX_OUTPUT(NULL, C_CENTER, M_MASTER_CENTER, OUT_D_CENTER, 100); -#if 0 - /* XXX in digital mode (default) this should be muted because - this output is shared with digital out */ - EFX_OUTPUTD(C_CENTER, M_MASTER_CENTER, OUT_A_CENTER); -#endif - /* fx5 (pcm3) to sub */ - EFX_CACHE(C_SUB); - EFX_ROUTE(NULL, FX(5), M_FX5_SUBWOOFER, C_SUB, 100); - EFX_OUTPUT(NULL, C_SUB, M_MASTER_SUBWOOFER, OUT_D_SUB, 100); -#if 0 - /* XXX in digital mode (default) this should be muted because - this output is shared with digital out */ - EFX_OUTPUTD(C_SUB, M_MASTER_SUBWOOFER, OUT_A_SUB); -#endif - } -#ifdef SND_EMU10KX_MCH_RECORDING - /* MCH RECORDING , hight 16 slots. On 5.1 cards first 4 slots are used - as outputs and already filled with data */ - for(i = (sc->has_51 ? 4 : 0); i < 16; i++) { - /* XXX fill with dummy data */ - EFX_DUMMY(i,i*0x10000); - emu_addefxop(sc, ACC3, - FX2(i), - DSP_CONST(0), - DSP_CONST(0), - GPR(sc->dummy_gpr[i]), - &pc); + } + } else { + /* SND_EMU10KX_MULTICHANNEL_DISABLED */ + EFX_OUTPUT(NULL, C_FRONT_L, M_MASTER_REAR_L, OUT_REAR_L, 57); /* 75%*75% */ + EFX_OUTPUT(NULL, C_FRONT_R, M_MASTER_REAR_R, OUT_REAR_R, 57); /* 75%*75% */ - } -#endif -#else /* !SND_EMU10KX_MULTICHANNEL */ - EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, OUT_REAR_L); - EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, OUT_REAR_R); +#if 0 + /* XXX 5.1 does not work */ + + if (sc->has_51) { + /* (fx0+fx1)/2 to center */ + EFX_CACHE(C_CENTER); + emu_addefxop(sc, MACS, + GPR(sc->cache_gpr[C_CENTER]), + GPR(sc->cache_gpr[C_CENTER]), + DSP_CONST(0xd), /* = 1/2 */ + GPR(sc->cache_gpr[C_FRONT_L]), + &pc); + emu_addefxop(sc, MACS, + GPR(sc->cache_gpr[C_CENTER]), + GPR(sc->cache_gpr[C_CENTER]), + DSP_CONST(0xd), /* = 1/2 */ + GPR(sc->cache_gpr[C_FRONT_R]), + &pc); + EFX_OUTPUT(NULL, C_CENTER, M_MASTER_CENTER, OUT_D_CENTER, 100); + + /* XXX in digital mode (default) this should be muted because + this output is shared with digital out */ + EFX_SKIP(1, ANALOGMUTE); + EFX_OUTPUTD(C_CENTER, M_MASTER_CENTER, OUT_A_CENTER); + + /* (fx0+fx1)/2 to sub */ + EFX_CACHE(C_SUB); + emu_addefxop(sc, MACS, + GPR(sc->cache_gpr[C_CENTER]), + GPR(sc->cache_gpr[C_CENTER]), + DSP_CONST(0xd), /* = 1/2 */ + GPR(sc->cache_gpr[C_FRONT_L]), + &pc); + emu_addefxop(sc, MACS, + GPR(sc->cache_gpr[C_CENTER]), + GPR(sc->cache_gpr[C_CENTER]), + DSP_CONST(0xd), /* = 1/2 */ + GPR(sc->cache_gpr[C_FRONT_R]), + &pc); + /* XXX add lowpass filter here */ + + EFX_OUTPUT(NULL, C_SUB, M_MASTER_SUBWOOFER, OUT_D_SUB, 100); + + /* XXX in digital mode (default) this should be muted because + this output is shared with digital out */ + EFX_SKIP(1, ANALOGMUTE); + EFX_OUTPUTD(C_SUB, M_MASTER_SUBWOOFER, OUT_A_SUB); + } #endif + } /* !mch_disabled */ + if (sc->mch_rec) { + /* + * MCH RECORDING , hight 16 slots. On 5.1 cards first 4 slots + * are used as outputs and already filled with data + */ + /* + * XXX On Live! cards stream does not begin at zero offset. + * It can be HW, driver or sound buffering problem. + * Use sync substream (offset 0x3E) to let userland find + * correct data. + */ + + /* + * Substream map (in byte offsets, each substream is 2 bytes): + * 0x00..0x1E - outputs + * 0x20..0x3E - FX, inputs ans sync stream + */ + + /* First 2 channels (offset 0x20,0x22) are empty */ + for(i = (sc->has_51 ? 2 : 0); i < 2; i++) + EFX_COPY(FX2(i), DSP_CONST(0)); + + /* PCM Playback monitoring, offset 0x24..0x2A */ + for(i = 0; i < 4; i++) + EFX_COPY(FX2(i+2), FX(i)); + + /* Copy of some inputs, offset 0x2C..0x3C */ + for(i = 0; i < 9; i++) + EFX_COPY(FX2(i+8), INP(i)); + + /* sync data (0xc0de, offset 0x3E) */ + sc->dummy_gpr = emu_rm_gpr_alloc(sc->rm, 1); + emumix_set_gpr(sc, sc->dummy_gpr, 0xc0de0000); + + EFX_COPY(FX2(15), GPR(sc->dummy_gpr)); + } /* mch_rec */ } else /* emu10k2 and later */ { EFX_CACHE(C_FRONT_L); EFX_CACHE(C_FRONT_R); @@ -1729,58 +1947,62 @@ emu_initefx(struct emu_sc_info *sc) */ EFX_ROUTE(NULL, FX(0), M_FX0_FRONT_L, C_FRONT_L, 100); EFX_ROUTE(NULL, FX(1), M_FX1_FRONT_R, C_FRONT_R, 100); - EFX_ROUTE("pcm_rec_l", FX(0), M_FX0_REC_L, C_REC_L, 0); - EFX_ROUTE("pcm_rec_r", FX(1), M_FX1_REC_R, C_REC_R, 0); + EFX_ROUTE(NULL, FX(0), M_FX0_REC_L, C_REC_L, 0); + EFX_ROUTE(NULL, FX(1), M_FX1_REC_R, C_REC_R, 0); /* in0, from AC97 codec output */ - EFX_ROUTE("ac97_front_l", INP(A_IN_AC97_L), M_IN0_FRONT_L, C_FRONT_L, 100); - EFX_ROUTE("ac97_front_r", INP(A_IN_AC97_R), M_IN0_FRONT_R, C_FRONT_R, 100); - EFX_ROUTE("ac97_rec_l", INP(A_IN_AC97_L), M_IN0_REC_L, C_REC_L, 0); - EFX_ROUTE("ac97_rec_r", INP(A_IN_AC97_R), M_IN0_REC_R, C_REC_R, 0); + EFX_ROUTE(NULL, INP(A_IN_AC97_L), M_IN0_FRONT_L, C_FRONT_L, 100); + EFX_ROUTE(NULL, INP(A_IN_AC97_R), M_IN0_FRONT_R, C_FRONT_R, 100); + EFX_ROUTE(NULL, INP(A_IN_AC97_L), M_IN0_REC_L, C_REC_L, 0); + EFX_ROUTE(NULL, INP(A_IN_AC97_R), M_IN0_REC_R, C_REC_R, 0); /* in1, from CD S/PDIF */ - EFX_ROUTE("cdspdif_front_l", INP(A_IN_SPDIF_CD_L), M_IN1_FRONT_L, C_FRONT_L, 0); - EFX_ROUTE("cdspdif_front_r", INP(A_IN_SPDIF_CD_R), M_IN1_FRONT_R, C_FRONT_R, 0); - EFX_ROUTE("cdspdif_rec_l", INP(A_IN_SPDIF_CD_L), M_IN1_REC_L, C_REC_L, 0); - EFX_ROUTE("cdspdif_rec_r", INP(A_IN_SPDIF_CD_R), M_IN1_REC_R, C_REC_R, 0); + EFX_ROUTE(NULL, INP(A_IN_SPDIF_CD_L), M_IN1_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE(NULL, INP(A_IN_SPDIF_CD_R), M_IN1_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE(NULL, INP(A_IN_SPDIF_CD_L), M_IN1_REC_L, C_REC_L, 0); + EFX_ROUTE(NULL, INP(A_IN_SPDIF_CD_R), M_IN1_REC_R, C_REC_R, 0); /* in2, optical & coax S/PDIF on AudigyDrive*/ /* XXX Should be muted when GPRSCS valid stream == 0 */ - EFX_ROUTE("ospdif_front_l", INP(A_IN_O_SPDIF_L), M_IN2_FRONT_L, C_FRONT_L, 0); - EFX_ROUTE("ospdif_front_r", INP(A_IN_O_SPDIF_R), M_IN2_FRONT_R, C_FRONT_R, 0); - EFX_ROUTE("ospdif_rec_l", INP(A_IN_O_SPDIF_L), M_IN2_REC_L, C_REC_L, 0); - EFX_ROUTE("ospdif_rec_r", INP(A_IN_O_SPDIF_R), M_IN2_REC_R, C_REC_R, 0); -#ifdef SND_EMU10KX_DEBUG_OUTPUTS - /* in3, unknown */ - EFX_ROUTE("in3_front_l", INP(0x6), M_IN3_FRONT_L, C_FRONT_L, 0); - EFX_ROUTE("in3_front_r", INP(0x7), M_IN3_FRONT_R, C_FRONT_R, 0); - EFX_ROUTE("in3_rec_l", INP(0x6), M_IN3_REC_L, C_REC_L, 0); - EFX_ROUTE("in3_rec_r", INP(0x7), M_IN3_REC_R, C_REC_R, 0); -#endif + EFX_ROUTE(NULL, INP(A_IN_O_SPDIF_L), M_IN2_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE(NULL, INP(A_IN_O_SPDIF_R), M_IN2_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE(NULL, INP(A_IN_O_SPDIF_L), M_IN2_REC_L, C_REC_L, 0); + EFX_ROUTE(NULL, INP(A_IN_O_SPDIF_R), M_IN2_REC_R, C_REC_R, 0); + + if (sc->dbg_level > 0) { + /* in3, unknown */ + EFX_ROUTE("in3_front_l", INP(0x6), M_IN3_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("in3_front_r", INP(0x7), M_IN3_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("in3_rec_l", INP(0x6), M_IN3_REC_L, C_REC_L, 0); + EFX_ROUTE("in3_rec_r", INP(0x7), M_IN3_REC_R, C_REC_R, 0); + } + /* in4, LineIn 2 on AudigyDrive */ - EFX_ROUTE("linein2_front_l", INP(A_IN_LINE2_L), M_IN4_FRONT_L, C_FRONT_L, 0); - EFX_ROUTE("linein2_front_r", INP(A_IN_LINE2_R), M_IN4_FRONT_R, C_FRONT_R, 0); - EFX_ROUTE("linein2_rec_l", INP(A_IN_LINE2_L), M_IN4_REC_L, C_REC_L, 0); - EFX_ROUTE("linein2_rec_r", INP(A_IN_LINE2_R), M_IN4_REC_R, C_REC_R, 0); + EFX_ROUTE(NULL, INP(A_IN_LINE2_L), M_IN4_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE(NULL, INP(A_IN_LINE2_R), M_IN4_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE(NULL, INP(A_IN_LINE2_L), M_IN4_REC_L, C_REC_L, 0); + EFX_ROUTE(NULL, INP(A_IN_LINE2_R), M_IN4_REC_R, C_REC_R, 0); /* in5, on-card S/PDIF */ - EFX_ROUTE("spdif_front_l", INP(A_IN_R_SPDIF_L), M_IN5_FRONT_L, C_FRONT_L, 0); - EFX_ROUTE("spdif_front_r", INP(A_IN_R_SPDIF_R), M_IN5_FRONT_R, C_FRONT_R, 0); - EFX_ROUTE("spdif_rec_l", INP(A_IN_R_SPDIF_L), M_IN5_REC_L, C_REC_L, 0); - EFX_ROUTE("spdif_rec_r", INP(A_IN_R_SPDIF_R), M_IN5_REC_R, C_REC_R, 0); + EFX_ROUTE(NULL, INP(A_IN_R_SPDIF_L), M_IN5_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE(NULL, INP(A_IN_R_SPDIF_R), M_IN5_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE(NULL, INP(A_IN_R_SPDIF_L), M_IN5_REC_L, C_REC_L, 0); + EFX_ROUTE(NULL, INP(A_IN_R_SPDIF_R), M_IN5_REC_R, C_REC_R, 0); /* in6, AUX2 on AudigyDrive */ - EFX_ROUTE("aux2_front_l", INP(A_IN_AUX2_L), M_IN6_FRONT_L, C_FRONT_L, 0); - EFX_ROUTE("aux2_front_r", INP(A_IN_AUX2_R), M_IN6_FRONT_R, C_FRONT_R, 0); - EFX_ROUTE("aux2_rec_l", INP(A_IN_AUX2_L), M_IN6_REC_L, C_REC_L, 0); - EFX_ROUTE("aux2_rec_r", INP(A_IN_AUX2_R), M_IN6_REC_R, C_REC_R, 0); -#ifdef SND_EMU10KX_DEBUG_OUTPUTS - /* in7, unknown */ - EFX_ROUTE("in7_front_l", INP(0xE), M_IN7_FRONT_L, C_FRONT_L, 0); - EFX_ROUTE("in7_front_r", INP(0xF), M_IN7_FRONT_R, C_FRONT_R, 0); - EFX_ROUTE("in7_rec_l", INP(0xE), M_IN7_REC_L, C_REC_L, 0); - EFX_ROUTE("in7_rec_r", INP(0xF), M_IN7_REC_R, C_REC_R, 0); -#endif + EFX_ROUTE(NULL, INP(A_IN_AUX2_L), M_IN6_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE(NULL, INP(A_IN_AUX2_R), M_IN6_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE(NULL, INP(A_IN_AUX2_L), M_IN6_REC_L, C_REC_L, 0); + EFX_ROUTE(NULL, INP(A_IN_AUX2_R), M_IN6_REC_R, C_REC_R, 0); + + if (sc->dbg_level > 0) { + /* in7, unknown */ + EFX_ROUTE("in7_front_l", INP(0xE), M_IN7_FRONT_L, C_FRONT_L, 0); + EFX_ROUTE("in7_front_r", INP(0xF), M_IN7_FRONT_R, C_FRONT_R, 0); + EFX_ROUTE("in7_rec_l", INP(0xE), M_IN7_REC_L, C_REC_L, 0); + EFX_ROUTE("in7_rec_r", INP(0xF), M_IN7_REC_R, C_REC_R, 0); + } + /* front output to headphones and alog and digital *front */ /* volume controlled by AC97 emulation */ EFX_OUTPUT(NULL, C_FRONT_L, M_MASTER_FRONT_L, A_OUT_A_FRONT_L, 100); @@ -1794,73 +2016,142 @@ emu_initefx(struct emu_sc_info *sc) /* volume controlled by AC97 emulation */ EFX_OUTPUT(NULL, C_REC_L, M_MASTER_REC_L, A_OUT_ADC_REC_L, 100); EFX_OUTPUT(NULL, C_REC_R, M_MASTER_REC_R, A_OUT_ADC_REC_R, 100); -#ifdef SND_EMU10KX_MULTICHANNEL - /* - * Additional channel volume is controlled by mixer in - * emu_dspmixer_set() in -pcm.c - */ - /* fx2/3 (pcm1) to rear */ - EFX_CACHE(C_REAR_L); - EFX_CACHE(C_REAR_R); - EFX_ROUTE(NULL, FX(2), M_FX2_REAR_L, C_REAR_L, 100); - EFX_ROUTE(NULL, FX(3), M_FX3_REAR_R, C_REAR_R, 100); - - EFX_OUTPUT(NULL, C_REAR_L, M_MASTER_REAR_L, A_OUT_A_REAR_L, 100); - EFX_OUTPUT(NULL, C_REAR_R, M_MASTER_REAR_R, A_OUT_A_REAR_R, 100); - EFX_OUTPUTD(C_REAR_L, M_MASTER_REAR_L, A_OUT_D_REAR_L); - EFX_OUTPUTD(C_REAR_R, M_MASTER_REAR_R, A_OUT_D_REAR_R); - - /* fx4 (pcm2) to center */ - EFX_CACHE(C_CENTER); - EFX_ROUTE(NULL, FX(4), M_FX4_CENTER, C_CENTER, 100); - EFX_OUTPUT(NULL, C_CENTER, M_MASTER_CENTER, A_OUT_D_CENTER, 100); + if (!(sc->mch_disabled)) { + /* + * Additional channel volume is controlled by mixer in + * emu_dspmixer_set() in -pcm.c + */ + + /* fx2/3 (pcm1) to rear */ + EFX_CACHE(C_REAR_L); + EFX_CACHE(C_REAR_R); + EFX_ROUTE(NULL, FX(2), M_FX2_REAR_L, C_REAR_L, 100); + EFX_ROUTE(NULL, FX(3), M_FX3_REAR_R, C_REAR_R, 100); + + EFX_OUTPUT(NULL, C_REAR_L, M_MASTER_REAR_L, A_OUT_A_REAR_L, 100); + EFX_OUTPUT(NULL, C_REAR_R, M_MASTER_REAR_R, A_OUT_A_REAR_R, 100); + EFX_OUTPUTD(C_REAR_L, M_MASTER_REAR_L, A_OUT_D_REAR_L); + EFX_OUTPUTD(C_REAR_R, M_MASTER_REAR_R, A_OUT_D_REAR_R); + + /* fx4 (pcm2) to center */ + EFX_CACHE(C_CENTER); + EFX_ROUTE(NULL, FX(4), M_FX4_CENTER, C_CENTER, 100); + EFX_OUTPUT(NULL, C_CENTER, M_MASTER_CENTER, A_OUT_D_CENTER, 100); #if 0 - /* XXX in digital mode (default) this should be muted because - this output is shared with digital out */ - EFX_OUTPUTD(C_CENTER, M_MASTER_CENTER, A_OUT_A_CENTER); + /* + * XXX in digital mode (default) this should be muted + * because this output is shared with digital out + */ + EFX_OUTPUTD(C_CENTER, M_MASTER_CENTER, A_OUT_A_CENTER); #endif - /* fx5 (pcm3) to sub */ - EFX_CACHE(C_SUB); - EFX_ROUTE(NULL, FX(5), M_FX5_SUBWOOFER, C_SUB, 100); - EFX_OUTPUT(NULL, C_SUB, M_MASTER_SUBWOOFER, A_OUT_D_SUB, 100); + /* fx5 (pcm3) to sub */ + EFX_CACHE(C_SUB); + EFX_ROUTE(NULL, FX(5), M_FX5_SUBWOOFER, C_SUB, 100); + EFX_OUTPUT(NULL, C_SUB, M_MASTER_SUBWOOFER, A_OUT_D_SUB, 100); #if 0 - /* XXX in digital mode (default) this should be muted because - this output is shared with digital out */ - EFX_OUTPUTD(C_SUB, M_MASTER_SUBWOOFER, A_OUT_A_SUB); -#endif - if (sc->has_71) { - /* XXX this will broke headphones on AudigyDrive */ - /* fx6/7 (pcm4) to side */ - EFX_CACHE(C_SIDE_L); - EFX_CACHE(C_SIDE_R); - EFX_ROUTE(NULL, FX(6), M_FX6_SIDE_L, C_SIDE_L, 100); - EFX_ROUTE(NULL, FX(7), M_FX7_SIDE_R, C_SIDE_R, 100); - EFX_OUTPUT(NULL, C_SIDE_L, M_MASTER_SIDE_L, A_OUT_A_SIDE_L, 100); - EFX_OUTPUT(NULL, C_SIDE_R, M_MASTER_SIDE_R, A_OUT_A_SIDE_R, 100); - EFX_OUTPUTD(C_SIDE_L, M_MASTER_SIDE_L, A_OUT_D_SIDE_L); - EFX_OUTPUTD(C_SIDE_R, M_MASTER_SIDE_R, A_OUT_D_SIDE_R); - } -#ifdef SND_EMU10KX_MCH_RECORDING - /* MCH RECORDING, high 32 slots */ - for(i = 0; i < 32; i++) { - /* XXX fill with dummy data */ - EFX_DUMMY(i,i*0x10000); - emu_addefxop(sc, ACC3, - FX2(i), - DSP_CONST(0), - DSP_CONST(0), - GPR(sc->dummy_gpr[i]), - &pc); - } + /* + * XXX in digital mode (default) this should be muted + * because this output is shared with digital out + */ + EFX_OUTPUTD(C_SUB, M_MASTER_SUBWOOFER, A_OUT_A_SUB); #endif -#else /* !SND_EMU10KX_MULTICHANNEL */ - EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, A_OUT_A_REAR_L); - EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, A_OUT_A_REAR_R); - - EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, A_OUT_D_REAR_L); - EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, A_OUT_D_REAR_R); + if (sc->has_71) { + /* XXX this will broke headphones on AudigyDrive */ + /* fx6/7 (pcm4) to side */ + EFX_CACHE(C_SIDE_L); + EFX_CACHE(C_SIDE_R); + EFX_ROUTE(NULL, FX(6), M_FX6_SIDE_L, C_SIDE_L, 100); + EFX_ROUTE(NULL, FX(7), M_FX7_SIDE_R, C_SIDE_R, 100); + EFX_OUTPUT(NULL, C_SIDE_L, M_MASTER_SIDE_L, A_OUT_A_SIDE_L, 100); + EFX_OUTPUT(NULL, C_SIDE_R, M_MASTER_SIDE_R, A_OUT_A_SIDE_R, 100); + EFX_OUTPUTD(C_SIDE_L, M_MASTER_SIDE_L, A_OUT_D_SIDE_L); + EFX_OUTPUTD(C_SIDE_R, M_MASTER_SIDE_R, A_OUT_D_SIDE_R); + } + } else { /* mch_disabled */ + EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, A_OUT_A_REAR_L); + EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, A_OUT_A_REAR_R); + + EFX_OUTPUTD(C_FRONT_L, M_MASTER_FRONT_L, A_OUT_D_REAR_L); + EFX_OUTPUTD(C_FRONT_R, M_MASTER_FRONT_R, A_OUT_D_REAR_R); + + if (sc->has_51) { + /* (fx0+fx1)/2 to center */ + EFX_CACHE(C_CENTER); + emu_addefxop(sc, MACS, + GPR(sc->cache_gpr[C_CENTER]), + GPR(sc->cache_gpr[C_CENTER]), + DSP_CONST(0xd), /* = 1/2 */ + GPR(sc->cache_gpr[C_FRONT_L]), + &pc); + emu_addefxop(sc, MACS, + GPR(sc->cache_gpr[C_CENTER]), + GPR(sc->cache_gpr[C_CENTER]), + DSP_CONST(0xd), /* = 1/2 */ + GPR(sc->cache_gpr[C_FRONT_R]), + &pc); + EFX_OUTPUT(NULL, C_CENTER, M_MASTER_CENTER, A_OUT_D_CENTER, 100); + + /* XXX in digital mode (default) this should be muted because + this output is shared with digital out */ + EFX_SKIP(1, ANALOGMUTE); + EFX_OUTPUTD(C_CENTER, M_MASTER_CENTER, A_OUT_A_CENTER); + + /* (fx0+fx1)/2 to sub */ + EFX_CACHE(C_SUB); + emu_addefxop(sc, MACS, + GPR(sc->cache_gpr[C_SUB]), + GPR(sc->cache_gpr[C_SUB]), + DSP_CONST(0xd), /* = 1/2 */ + GPR(sc->cache_gpr[C_FRONT_L]), + &pc); + emu_addefxop(sc, MACS, + GPR(sc->cache_gpr[C_SUB]), + GPR(sc->cache_gpr[C_SUB]), + DSP_CONST(0xd), /* = 1/2 */ + GPR(sc->cache_gpr[C_FRONT_R]), + &pc); + /* XXX add lowpass filter here */ + + EFX_OUTPUT(NULL, C_SUB, M_MASTER_SUBWOOFER, A_OUT_D_SUB, 100); + + /* XXX in digital mode (default) this should be muted because + this output is shared with digital out */ + EFX_SKIP(1, ANALOGMUTE); + EFX_OUTPUTD(C_SUB, M_MASTER_SUBWOOFER, A_OUT_A_SUB); + } + } /* mch_disabled */ + if (sc->mch_rec) { + /* MCH RECORDING, high 32 slots */ + + /* + * Stream map (in byte offsets): + * 0x00..0x3E - outputs + * 0x40..0x7E - FX, inputs + * each substream is 2 bytes. + */ + /* + * XXX Audigy 2 Value cards (and, possibly, + * Audigy 4) write some unknown data in place of + * some outputs (offsets 0x20..0x3F) and one + * input (offset 0x7E). + */ + + /* PCM Playback monitoring, offsets 0x40..0x5E */ + for(i = 0; i < 16; i++) + EFX_COPY(FX2(i), FX(i)); + + /* Copy of all inputs, offsets 0x60..0x7E */ + for(i = 0; i < 16; i++) + EFX_COPY(FX2(i+16), INP(i)); +#if 0 + /* XXX Audigy seems to work correct and does not need this */ + /* sync data (0xc0de), offset 0x7E */ + sc->dummy_gpr = emu_rm_gpr_alloc(sc->rm, 1); + emumix_set_gpr(sc, sc->dummy_gpr, 0xc0de0000); + EFX_COPY(FX2(31), GPR(sc->dummy_gpr)); #endif + } /* mch_rec */ } sc->routing_code_end = pc; @@ -2007,6 +2298,8 @@ emu10kx_prepare(struct emu_sc_info *sc, struct sbuf *s) if (device_is_attached(sc->midi[0])) { sbuf_printf(s, "\tIR reciever MIDI events %s\n", sc->enable_ir ? "enabled" : "disabled"); } + sbuf_printf(s, "Card is in %s mode\n", (sc->mode == MODE_ANALOG) ? "analog" : "digital"); + sbuf_finish(s); return (sbuf_len(s)); } @@ -2017,7 +2310,7 @@ emu10kx_dev_init(struct emu_sc_info *sc) { int unit; - mtx_init(&sc->emu10kx_lock, "kxdevlock", NULL, 0); + mtx_init(&sc->emu10kx_lock, device_get_nameunit(sc->dev), "kxdevlock", 0); unit = device_get_unit(sc->dev); sc->cdev = make_dev(&emu10kx_cdevsw, unit2minor(unit), UID_ROOT, GID_WHEEL, 0640, "emu10kx%d", unit); @@ -2031,20 +2324,15 @@ emu10kx_dev_init(struct emu_sc_info *sc) static int emu10kx_dev_uninit(struct emu_sc_info *sc) { - intrmask_t s; - - s = spltty(); mtx_lock(&sc->emu10kx_lock); if (sc->emu10kx_isopen) { mtx_unlock(&sc->emu10kx_lock); - splx(s); return (EBUSY); } if (sc->cdev) destroy_dev(sc->cdev); sc->cdev = 0; - splx(s); mtx_destroy(&sc->emu10kx_lock); return (0); } @@ -2065,11 +2353,13 @@ emu_rm_init(struct emu_sc_info *sc) rm->card = sc; maxcount = sc->num_gprs; rm->num_used = 0; - mtx_init(&(rm->gpr_lock), "emu10k", "gpr alloc", MTX_DEF); + mtx_init(&(rm->gpr_lock), device_get_nameunit(sc->dev), "gpr alloc", MTX_DEF); rm->num_gprs = (maxcount < EMU_MAX_GPR ? maxcount : EMU_MAX_GPR); for (i = 0; i < rm->num_gprs; i++) rm->allocmap[i] = 0; - rm->last_free_gpr = 0; + /* pre-allocate gpr[0] */ + rm->allocmap[0] = 1; + rm->last_free_gpr = 1; return (0); } @@ -2077,15 +2367,16 @@ emu_rm_init(struct emu_sc_info *sc) int emu_rm_uninit(struct emu_sc_info *sc) { -#ifdef SND_EMU10KX_DEBUG int i; - mtx_lock(&(sc->rm->gpr_lock)); - for (i = 0; i < sc->rm->last_free_gpr; i++) - if (sc->rm->allocmap[i] > 0) - device_printf(sc->dev, "rm: gpr %d not free before uninit\n", i); - mtx_unlock(&(sc->rm->gpr_lock)); -#endif + if (sc->dbg_level > 1) { + mtx_lock(&(sc->rm->gpr_lock)); + for (i = 1; i < sc->rm->last_free_gpr; i++) + if (sc->rm->allocmap[i] > 0) + device_printf(sc->dev, "rm: gpr %d not free before uninit\n", i); + mtx_unlock(&(sc->rm->gpr_lock)); + } + mtx_destroy(&(sc->rm->gpr_lock)); free(sc->rm, M_DEVBUF); return (0); @@ -2169,7 +2460,7 @@ emumix_set_mode(struct emu_sc_info *sc, int mode) if (mode == MODE_DIGITAL) { if (sc->broken_digital) { - device_printf(sc->dev, "Digital mode is reported as broken on this card,\n"); + device_printf(sc->dev, "Digital mode is reported as broken on this card.\n"); } a_iocfg |= A_IOCFG_ENABLE_DIGITAL; hcfg |= HCFG_GPOUT0; @@ -2182,12 +2473,21 @@ emumix_set_mode(struct emu_sc_info *sc, int mode) a_iocfg |= 0x80; /* XXX */ if ((sc->is_ca0102) || (sc->is_ca0108)) - a_iocfg |= A_IOCFG_DISABLE_ANALOG; /* means "don't disable" - on this two cards. Means "disable" on emu10k2. */ + /* + * Setting A_IOCFG_DISABLE_ANALOG will do opposite things + * on diffrerent cards. + * "don't disable analog outs" on Audigy 2 (ca0102/ca0108) + * "disable analog outs" on Audigy (emu10k2) + */ + a_iocfg |= A_IOCFG_DISABLE_ANALOG; if (sc->is_ca0108) a_iocfg |= 0x20; /* XXX */ + /* Mute analog center & subwoofer before mode change */ + if (mode == MODE_DIGITAL) + emumix_set_gpr(sc, sc->mute_gpr[ANALOGMUTE], 1); + emu_wr(sc, HCFG, hcfg, 4); if ((sc->is_emu10k2) || (sc->is_ca0102) || (sc->is_ca0108)) { @@ -2196,11 +2496,12 @@ emumix_set_mode(struct emu_sc_info *sc, int mode) emu_wr(sc, A_IOCFG, tmp, 2); } - /* - * XXX Mute center/sub if we go digital on Audigy or later card. - * Route to analog center / sub in emu_initef should be disabled - * until this problem is fixed. - */ + /* Unmute if we have changed mode to analog. */ + + if (mode == MODE_ANALOG) + emumix_set_gpr(sc, sc->mute_gpr[ANALOGMUTE], 0); + + sc->mode = mode; } void @@ -2293,6 +2594,14 @@ emumix_set_fxvol(struct emu_sc_info *sc, unsigned gpr, int32_t vol) void emumix_set_gpr(struct emu_sc_info *sc, unsigned gpr, int32_t val) { + if (sc->dbg_level > 1) + if (gpr == 0) { + device_printf(sc->dev, "Zero gpr write access\n"); +#ifdef KDB + kdb_backtrace(); +#endif + return; + } emu_wrptr(sc, 0, GPR(gpr), val); } @@ -2398,6 +2707,7 @@ emu_init(struct emu_sc_info *sc) return (ENOMEM); } + sc->mem.card = sc; SLIST_INIT(&sc->mem.blocks); sc->mem.ptb_pages = emu_malloc(&sc->mem, EMU_MAXPAGES * sizeof(uint32_t), &sc->mem.ptb_pages_addr); if (sc->mem.ptb_pages == NULL) @@ -2633,16 +2943,29 @@ emu_read_ivar(device_t bus, device_t dev, int ivar_index, uintptr_t * result) struct sndcard_func *func = device_get_ivars(dev); struct emu_sc_info *sc = device_get_softc(bus); + if (func==NULL) + return (ENOMEM); + if (sc == NULL) + return (ENOMEM); + switch (ivar_index) { case EMU_VAR_FUNC: *result = func->func; break; case EMU_VAR_ROUTE: + if (func->varinfo == NULL) + return (ENOMEM); *result = ((struct emu_pcminfo *)func->varinfo)->route; break; case EMU_VAR_ISEMU10K1: *result = sc->is_emu10k1; break; + case EMU_VAR_MCH_DISABLED: + *result = sc->mch_disabled; + break; + case EMU_VAR_MCH_REC: + *result = sc->mch_rec; + break; default: return (ENOENT); } @@ -2686,6 +3009,9 @@ emu_pci_probe(device_t dev) sbuf_finish(s); device_set_desc_copy(dev, sbuf_data(s)); + + sbuf_delete(s); + return (BUS_PROBE_DEFAULT); } @@ -2696,23 +3022,58 @@ emu_pci_attach(device_t dev) struct sndcard_func *func; struct emu_sc_info *sc; struct emu_pcminfo *pcminfo; - struct emu_midiinfo *midiinfo[3]; +#if 0 + struct emu_midiinfo *midiinfo; +#endif uint32_t data; int i; int device_flags; char status[255]; int error = ENXIO; + int unit; sc = device_get_softc(dev); + unit = device_get_unit(dev); + + if (resource_disabled("emu10kx", unit)) { + device_printf(dev, "disabled by kernel hints\n"); + return (ENXIO); /* XXX to avoid unit reuse */ + } + + /* Get configuration */ + + sc->ctx = device_get_sysctl_ctx(dev); + if (sc->ctx == NULL) + goto bad; + sc->root = device_get_sysctl_tree(dev); + if (sc->root == NULL) + goto bad; + + if (resource_int_value("emu10kx", unit, "multichannel_disabled", &(sc->mch_disabled))) + RANGE(sc->mch_disabled, 0, 1); + SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "multichannel_disabled", CTLFLAG_RD, &(sc->mch_disabled), 0, "Multichannel playback setting"); + + if (resource_int_value("emu10kx", unit, "multichannel_recording", &(sc->mch_rec))) + RANGE(sc->mch_rec, 0, 1); + SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "multichannel_recording", CTLFLAG_RD, &(sc->mch_rec), 0, "Multichannel recording setting"); + + if (resource_int_value("emu10kx", unit, "debug", &(sc->dbg_level))) + RANGE(sc->mch_rec, 0, 2); + SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "debug", CTLFLAG_RW, &(sc->dbg_level), 0, "Debug level"); /* Fill in the softc. */ - mtx_init(&sc->lock, "emu10kx", "bridge conf", MTX_DEF); - mtx_init(&sc->rw, "emu10kx", "atomic op", MTX_DEF); + mtx_init(&sc->lock, device_get_nameunit(dev), "bridge conf", MTX_DEF); + mtx_init(&sc->rw, device_get_nameunit(dev), "exclusive io", MTX_DEF); sc->dev = dev; sc->type = pci_get_devid(dev); sc->rev = pci_get_revid(dev); sc->enable_ir = 0; - sc->enable_debug = 0; sc->has_ac97 = 0; sc->has_51 = 0; sc->has_71 = 0; @@ -2759,8 +3120,8 @@ emu_pci_attach(device_t dev) if ((sc->is_emu10k2) || (sc->is_ca0102) || (sc->is_ca0108)) { sc->opcode_shift = 24; sc->high_operand_shift = 12; - - /* DSP map */ + + /* DSP map */ /* sc->fx_base = 0x0 */ sc->input_base = 0x40; /* sc->p16vinput_base = 0x50; */ @@ -2785,7 +3146,7 @@ emu_pci_attach(device_t dev) sc->address_mask = A_PTR_ADDRESS_MASK; } if (sc->is_emu10k1) { - sc->has_51 = 0; /* We don't support 5.1 sound Live! 5.1 */ + sc->has_51 = 0; /* We don't support 5.1 sound on SB Live! 5.1 */ sc->opcode_shift = 20; sc->high_operand_shift = 10; sc->code_base = MICROCODEBASE; @@ -2795,8 +3156,8 @@ emu_pci_attach(device_t dev) sc->num_gprs = 0x100; sc->input_base = 0x10; sc->output_base = 0x20; - /* - * XXX 5.1 Analog outputs are inside efxc address space! + /* + * XXX 5.1 Analog outputs are inside efxc address space! * They use ouput+0x11/+0x12 (=efxc+1/+2). * Don't use this efx registers for recording on SB Live! 5.1! */ @@ -2832,7 +3193,7 @@ emu_pci_attach(device_t dev) i = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, RF_ACTIVE | RF_SHAREABLE); - if ((sc->irq == NULL) || snd_setup_intr(dev, sc->irq, INTR_MPSAFE, emu_intr, sc, &sc->ih)) { + if ((sc->irq == NULL) || bus_setup_intr(dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV, NULL, emu_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } @@ -2845,17 +3206,11 @@ emu_pci_attach(device_t dev) device_printf(dev, "unable to initialize CardBus interface\n"); goto bad; } - sc->ctx = device_get_sysctl_ctx(dev); - if (sc->ctx == NULL) - goto bad; - sc->root = device_get_sysctl_tree(dev); - if (sc->root == NULL) - goto bad; - if (emu_init(sc) == -1) { + if (emu_init(sc) != 0) { device_printf(dev, "unable to initialize the card\n"); goto bad; } - if (emu10kx_dev_init(sc) == ENXIO) { + if (emu10kx_dev_init(sc) != 0) { device_printf(dev, "unable to create control device\n"); goto bad; } @@ -2876,6 +3231,9 @@ emu_pci_attach(device_t dev) } /* PCM Audio */ + for (i = 0; i < RT_COUNT; i++) + sc->pcm[i] = NULL; + /* FRONT */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); if (func == NULL) { @@ -2895,27 +3253,8 @@ emu_pci_attach(device_t dev) sc->pcm[RT_FRONT] = device_add_child(dev, "pcm", -1); device_set_ivars(sc->pcm[RT_FRONT], func); -#ifdef SND_EMU10KX_MULTICHANNEL - /* REAR */ - func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); - if (func == NULL) { - error = ENOMEM; - goto bad; - } - pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); - if (pcminfo == NULL) { - error = ENOMEM; - goto bad; - } - pcminfo->card = sc; - pcminfo->route = RT_REAR; - - func->func = SCF_PCM; - func->varinfo = pcminfo; - sc->pcm[RT_REAR] = device_add_child(dev, "pcm", -1); - device_set_ivars(sc->pcm[RT_REAR], func); - if (sc->has_51) { - /* CENTER */ + if (!(sc->mch_disabled)) { + /* REAR */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); if (func == NULL) { error = ENOMEM; @@ -2927,33 +3266,73 @@ emu_pci_attach(device_t dev) goto bad; } pcminfo->card = sc; - pcminfo->route = RT_CENTER; + pcminfo->route = RT_REAR; func->func = SCF_PCM; func->varinfo = pcminfo; - sc->pcm[RT_CENTER] = device_add_child(dev, "pcm", -1); - device_set_ivars(sc->pcm[RT_CENTER], func); - /* SUB */ - func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); - if (func == NULL) { - error = ENOMEM; - goto bad; + sc->pcm[RT_REAR] = device_add_child(dev, "pcm", -1); + device_set_ivars(sc->pcm[RT_REAR], func); + if (sc->has_51) { + /* CENTER */ + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); + if (func == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); + if (pcminfo == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo->card = sc; + pcminfo->route = RT_CENTER; + + func->func = SCF_PCM; + func->varinfo = pcminfo; + sc->pcm[RT_CENTER] = device_add_child(dev, "pcm", -1); + device_set_ivars(sc->pcm[RT_CENTER], func); + /* SUB */ + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); + if (func == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); + if (pcminfo == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo->card = sc; + pcminfo->route = RT_SUB; + + func->func = SCF_PCM; + func->varinfo = pcminfo; + sc->pcm[RT_SUB] = device_add_child(dev, "pcm", -1); + device_set_ivars(sc->pcm[RT_SUB], func); } - pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); - if (pcminfo == NULL) { - error = ENOMEM; - goto bad; + if (sc->has_71) { + /* SIDE */ + func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); + if (func == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); + if (pcminfo == NULL) { + error = ENOMEM; + goto bad; + } + pcminfo->card = sc; + pcminfo->route = RT_SIDE; + + func->func = SCF_PCM; + func->varinfo = pcminfo; + sc->pcm[RT_SIDE] = device_add_child(dev, "pcm", -1); + device_set_ivars(sc->pcm[RT_SIDE], func); } - pcminfo->card = sc; - pcminfo->route = RT_SUB; + } /* mch_disabled */ - func->func = SCF_PCM; - func->varinfo = pcminfo; - sc->pcm[RT_SUB] = device_add_child(dev, "pcm", -1); - device_set_ivars(sc->pcm[RT_SUB], func); - } - if (sc->has_71) { - /* SIDE */ + if (sc->mch_rec) { func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); if (func == NULL) { error = ENOMEM; @@ -2965,36 +3344,19 @@ emu_pci_attach(device_t dev) goto bad; } pcminfo->card = sc; - pcminfo->route = RT_SIDE; + pcminfo->route = RT_MCHRECORD; func->func = SCF_PCM; func->varinfo = pcminfo; - sc->pcm[RT_SIDE] = device_add_child(dev, "pcm", -1); - device_set_ivars(sc->pcm[RT_SIDE], func); - }; -#ifdef SND_EMU10KX_MCH_RECORDING - /* MULTICHANNEL RECORDING */ - func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); - if (func == NULL) { - error = ENOMEM; - goto bad; - } - pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); - if (pcminfo == NULL) { - error = ENOMEM; - goto bad; - } - pcminfo->card = sc; - pcminfo->route = RT_MCHRECORD; - - func->func = SCF_PCM; - func->varinfo = pcminfo; - sc->pcm[RT_MCHRECORD] = device_add_child(dev, "pcm", -1); - device_set_ivars(sc->pcm[RT_MCHRECORD], func); + sc->pcm[RT_MCHRECORD] = device_add_child(dev, "pcm", -1); + device_set_ivars(sc->pcm[RT_MCHRECORD], func); + } /*mch_rec */ -#endif /* SMD_EMU10KX_MCH_RECORDING */ -#endif /* SND_EMU10KX_MULTICHANNEL */ + for (i = 0; i < 2; i++) + sc->midi[i] = NULL; + /* MIDI has some memory mangament and (possible) locking problems */ +#if 0 /* Midi Interface 1: Live!, Audigy, Audigy 2 */ if ((sc->is_emu10k1) || (sc->is_emu10k2) || (sc->is_ca0102)) { func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO); @@ -3002,22 +3364,22 @@ emu_pci_attach(device_t dev) error = ENOMEM; goto bad; } - midiinfo[0] = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_NOWAIT | M_ZERO); - if (midiinfo[0] == NULL) { + midiinfo = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_NOWAIT | M_ZERO); + if (midiinfo == NULL) { error = ENOMEM; goto bad; } - midiinfo[0]->card = sc; + midiinfo->card = sc; if (sc->is_emu10k2 || (sc->is_ca0102)) { - midiinfo[0]->port = A_MUDATA1; - midiinfo[0]->portnr = 1; + midiinfo->port = A_MUDATA1; + midiinfo->portnr = 1; } if (sc->is_emu10k1) { - midiinfo[0]->port = MUDATA; - midiinfo[0]->portnr = 1; + midiinfo->port = MUDATA; + midiinfo->portnr = 1; } func->func = SCF_MIDI; - func->varinfo = midiinfo[0]; + func->varinfo = midiinfo; sc->midi[0] = device_add_child(dev, "midi", -1); device_set_ivars(sc->midi[0], func); } @@ -3028,22 +3390,22 @@ emu_pci_attach(device_t dev) error = ENOMEM; goto bad; } - midiinfo[1] = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_NOWAIT | M_ZERO); - if (midiinfo[1] == NULL) { + midiinfo = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_NOWAIT | M_ZERO); + if (midiinfo == NULL) { error = ENOMEM; goto bad; } - midiinfo[1]->card = sc; + midiinfo->card = sc; - midiinfo[1]->port = A_MUDATA2; - midiinfo[1]->portnr = 2; + midiinfo->port = A_MUDATA2; + midiinfo->portnr = 2; func->func = SCF_MIDI; - func->varinfo = midiinfo[1]; + func->varinfo = midiinfo; sc->midi[1] = device_add_child(dev, "midi", -1); device_set_ivars(sc->midi[1], func); } - +#endif return (bus_generic_attach(dev)); bad: @@ -3058,8 +3420,8 @@ bad: bus_teardown_intr(dev, sc->irq, sc->ih); if (sc->irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); - mtx_destroy(&sc->lock); mtx_destroy(&sc->rw); + mtx_destroy(&sc->lock); return (error); } @@ -3067,38 +3429,68 @@ static int emu_pci_detach(device_t dev) { struct emu_sc_info *sc; + struct sndcard_func *func; int devcount, i; device_t *childlist; int r = 0; sc = device_get_softc(dev); - + for (i = 0; i < RT_COUNT; i++) { - if (sc->pcm[i] != NULL) + if (sc->pcm[i] != NULL) { + func = device_get_ivars(sc->pcm[i]); + if (func != NULL && func->func == SCF_PCM) { + device_set_ivars(sc->pcm[i], NULL); + free(func->varinfo, M_DEVBUF); + free(func, M_DEVBUF); + } r = device_delete_child(dev, sc->pcm[i]); - if (r) - return (r); + if (r) return (r); + } } - if (sc->midi[0] != NULL) + + if (sc->midi[0] != NULL) { + func = device_get_ivars(sc->midi[0]); + if (func != NULL && func->func == SCF_MIDI) { + device_set_ivars(sc->midi[0], NULL); + free(func->varinfo, M_DEVBUF); + free(func, M_DEVBUF); + } r = device_delete_child(dev, sc->midi[0]); - if (r) - return (r); - if (sc->midi[1] != NULL) + if (r) return (r); + } + + if (sc->midi[1] != NULL) { + func = device_get_ivars(sc->midi[1]); + if (func != NULL && func->func == SCF_MIDI) { + device_set_ivars(sc->midi[1], NULL); + free(func->varinfo, M_DEVBUF); + free(func, M_DEVBUF); + } r = device_delete_child(dev, sc->midi[1]); - if (r) - return (r); - (void)device_get_children(dev, &childlist, &devcount); - for (i = 0; i < devcount - 1; i++) { - device_printf(dev, "removing stale child %d (unit %d)\n", i, device_get_unit(childlist[i])); - device_delete_child(dev, childlist[i]); + if (r) return (r); } - free(childlist, M_TEMP); - /* shutdown chip */ - emu_uninit(sc); + if (device_get_children(dev, &childlist, &devcount) == 0) + for (i = 0; i < devcount - 1; i++) { + device_printf(dev, "removing stale child %d (unit %d)\n", i, device_get_unit(childlist[i])); + func = device_get_ivars(childlist[i]); + if (func != NULL && (func->func == SCF_MIDI || func->func == SCF_PCM)) { + device_set_ivars(childlist[i], NULL); + free(func->varinfo, M_DEVBUF); + free(func, M_DEVBUF); + } + device_delete_child(dev, childlist[i]); + } + if (childlist != NULL) + free(childlist, M_TEMP); + r = emu10kx_dev_uninit(sc); if (r) return (r); + + /* shutdown chip */ + emu_uninit(sc); emu_rm_uninit(sc); if (sc->mem.dmat) @@ -3108,8 +3500,9 @@ emu_pci_detach(device_t dev) bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), sc->reg); bus_teardown_intr(dev, sc->irq, sc->ih); bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); - mtx_destroy(&sc->lock); mtx_destroy(&sc->rw); + mtx_destroy(&sc->lock); + return (bus_generic_detach(dev)); } /* add suspend, resume */ diff --git a/sys/dev/sound/pci/emu10kx.h b/sys/dev/sound/pci/emu10kx.h index 302f175..d27c9db 100644 --- a/sys/dev/sound/pci/emu10kx.h +++ b/sys/dev/sound/pci/emu10kx.h @@ -38,7 +38,8 @@ #define EMUPAGESIZE 4096 #define NUM_G 64 -#define EMU_PLAY_BUFSZ EMUPAGESIZE*16 +/* XXX some (empty) samples are played when play buffer is > EMUPAGESIZE */ +#define EMU_PLAY_BUFSZ EMUPAGESIZE /* Recording is limited by EMUPAGESIZE*16=64K buffer */ #define EMU_REC_BUFSZ EMUPAGESIZE*16 #define EMU_MAX_BUFSZ EMUPAGESIZE*16 @@ -48,6 +49,8 @@ #define EMU_VAR_FUNC 0 #define EMU_VAR_ROUTE 1 #define EMU_VAR_ISEMU10K1 2 +#define EMU_VAR_MCH_DISABLED 3 +#define EMU_VAR_MCH_REC 4 #define RT_FRONT 0 #define RT_REAR 1 diff --git a/sys/modules/sound/driver/emu10kx/Makefile b/sys/modules/sound/driver/emu10kx/Makefile index ea26ddd..3037af7 100644 --- a/sys/modules/sound/driver/emu10kx/Makefile +++ b/sys/modules/sound/driver/emu10kx/Makefile @@ -2,10 +2,7 @@ .PATH: ${.CURDIR}/../../../../dev/sound/pci \ ${.CURDIR}/../../../../gnu/dev/sound/pci -WARNS?= 2 ## because sound is WARNS=2 only -## WARNS=3 fails on _class.refs in -pcm.c -## WARNS=4 fails on min/max in sound headers -## otherwise it should be WARNS=6 clean +WARNS?= 2 KMOD= snd_emu10kx SRCS= device_if.h bus_if.h pci_if.h @@ -40,12 +37,4 @@ CLEANFILES+= emu10k1-alsa%diked.h CLEANFILES+= p16v-alsa%diked.h CLEANFILES+= p17v-alsa%diked.h -.if !defined(KERNBUILDDIR) -opt_emu10kx.h: - echo "#define SND_EMU10KX_MULTICHANNEL" > opt_emu10kx.h - echo "#undef SND_EMU10KX_MCH_RECORDING" >> opt_emu10kx.h - echo "#undef SND_EMU10KX_DEBUG_OUTPUTS" >> opt_emu10kx.h - echo "#undef SND_EMU10KX_DEBUG" >> opt_emu10kx.h -.endif - .include <bsd.kmod.mk> |