Update snd_emu10kx driver with recent perforce changes (and some 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 rev. 1.9 changes by ariff@ (bus_setup_intr -> snd_setup_intr). Tested on: emu10kx0: port 0x9000-0x903f irq 17 at device 9.0 on pci0 emu10kx0: [ITHREAD] emu10kx1: port 0x9400-0x941f irq 18 at device 10.0 on pci0 emu10kx1: [ITHREAD] emu10kx2: port 0x9c00-0x9c3f irq 19 at device 11.0 on pci0 emu10kx2: [ITHREAD] emu10kx3: port 0xa400-0xa41f irq 16 at device 12.0 on pci0 emu10kx3: [ITHREAD] pcm0: on emu10kx0 pcm0: pcm1: on emu10kx0 pcm2: on emu10kx0 pcm3: on emu10kx0 pcm4: on emu10kx0 pcm5: on emu10kx1 pcm5: pcm6: on emu10kx1 pcm7: on emu10kx1 pcm8: on emu10kx1 pcm9: on emu10kx2 pcm9: pcm10: on emu10kx2 pcm11: on emu10kx2 pcm12: on emu10kx2 pcm13: on emu10kx3 pcm13: pcm14: on emu10kx3 Index: sys/conf/NOTES =================================================================== RCS file: /home/ncvs/src/sys/conf/NOTES,v retrieving revision 1.1447 diff -u -r1.1447 NOTES --- sys/conf/NOTES 24 Jul 2007 15:35:01 -0000 1.1447 +++ sys/conf/NOTES 29 Jul 2007 10:21:53 -0000 @@ -2043,7 +2043,6 @@ device snd_ds1 device snd_emu10k1 device snd_emu10kx -options SND_EMU10KX_MULTICHANNEL device snd_envy24 device snd_envy24ht device snd_es137x Index: sys/conf/options =================================================================== RCS file: /home/ncvs/src/sys/conf/options,v retrieving revision 1.603 diff -u -r1.603 options --- sys/conf/options 24 Jul 2007 15:35:01 -0000 1.603 +++ sys/conf/options 29 Jul 2007 10:21:53 -0000 @@ -750,8 +750,5 @@ # XFS XFS -# snd_emu10kx sound driver options -SND_EMU10KX_MULTICHANNEL opt_emu10kx.h - # Interrupt filtering INTR_FILTER opt_global.h Index: sys/dev/sound/pci/emu10kx-midi.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pci/emu10kx-midi.c,v retrieving revision 1.3 diff -u -r1.3 emu10kx-midi.c --- sys/dev/sound/pci/emu10kx-midi.c 16 Jul 2006 20:10:08 -0000 1.3 +++ sys/dev/sound/pci/emu10kx-midi.c 15 Jul 2007 06:21:59 -0000 @@ -46,7 +46,6 @@ #include #include "mpufoi_if.h" -#include "opt_emu10kx.h" #include #include "emu10k1-alsa%diked.h" @@ -117,9 +116,8 @@ 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 @@ 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 @@ 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. */ Index: sys/dev/sound/pci/emu10kx-pcm.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pci/emu10kx-pcm.c,v retrieving revision 1.10 diff -u -r1.10 emu10kx-pcm.c --- sys/dev/sound/pci/emu10kx-pcm.c 17 Jun 2007 06:10:42 -0000 1.10 +++ sys/dev/sound/pci/emu10kx-pcm.c 4 Aug 2007 08:12:36 -0000 @@ -1,6 +1,6 @@ /*- * Copyright (c) 1999 Cameron Grant - * 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 @@ -45,14 +45,13 @@ #include "mixer_if.h" -#include "opt_emu10kx.h" #include #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_rchinfo { int spd; int fmt; - int blksz; + unsigned int blksz; int run; uint32_t idxreg; uint32_t basereg; @@ -77,6 +76,7 @@ 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_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 @@ 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 @@ 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 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; - v = 0; - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (dspmix[i].present) - v |= 1 << i; + 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); + } + } + + sc->ac97_playdevs = p; + sc->ac97_recdevs = r; + } + + /* 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, v); - mix_setrecdevs(m, 0); + 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 @@ 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 @@ 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 @@ 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 @@ #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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ /* 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } 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 @@ } 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 @@ 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 @@ DEVMETHOD(device_probe, emu_pcm_probe), DEVMETHOD(device_attach, emu_pcm_attach), DEVMETHOD(device_detach, emu_pcm_detach), - {0, 0} }; Index: sys/dev/sound/pci/emu10kx.c =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pci/emu10kx.c,v retrieving revision 1.11 diff -u -r1.11 emu10kx.c --- sys/dev/sound/pci/emu10kx.c 4 Jun 2007 18:25:04 -0000 1.11 +++ sys/dev/sound/pci/emu10kx.c 5 Aug 2007 05:37:37 -0000 @@ -1,6 +1,6 @@ /*- * Copyright (c) 1999 Cameron Grant - * 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 @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -48,7 +49,6 @@ #include #include -#include "opt_emu10kx.h" #include /* 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 *slave; uint32_t sa; uint32_t ea; + uint32_t routing[8]; + uint32_t amounts[8]; }; struct emu_memblk { @@ -260,6 +267,7 @@ 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_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 @@ 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 @@ 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_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 @@ /* 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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ { 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 @@ 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 @@ { 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 @@ { 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 @@ { 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 @@ device_printf(sc->dev, "SB Live! IR MIDI events enabled.\n"); sc->enable_ir = 1; } - mtx_unlock(&sc->rw); } @@ -747,12 +799,16 @@ 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 @@ { 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 @@ 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 @@ 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 @@ 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 @@ (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 @@ 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 @@ 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 @@ { 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 @@ 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 @@ 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 @@ 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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } 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 @@ } 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) - -/* 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]), \ +/* 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) + +#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 @@ } } + /* 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 @@ /* 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 @@ 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); + + } + } 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% */ - 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); + /* XXX 5.1 does not work */ - } -#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 (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 @@ */ 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 @@ /* 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 @@ 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 @@ { 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ sbuf_finish(s); device_set_desc_copy(dev, sbuf_data(s)); + + sbuf_delete(s); + return (BUS_PROBE_DEFAULT); } @@ -2696,23 +3022,58 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } /* 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 */ Index: sys/dev/sound/pci/emu10kx.h =================================================================== RCS file: /home/ncvs/src/sys/dev/sound/pci/emu10kx.h,v retrieving revision 1.3 diff -u -r1.3 emu10kx.h --- sys/dev/sound/pci/emu10kx.h 6 Jan 2007 18:59:35 -0000 1.3 +++ sys/dev/sound/pci/emu10kx.h 7 Jul 2007 18:46:39 -0000 @@ -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 Index: sys/modules/sound/driver/emu10kx/Makefile =================================================================== RCS file: /home/ncvs/src/sys/modules/sound/driver/emu10kx/Makefile,v retrieving revision 1.3 diff -u -r1.3 Makefile --- sys/modules/sound/driver/emu10kx/Makefile 7 Jan 2007 19:43:59 -0000 1.3 +++ sys/modules/sound/driver/emu10kx/Makefile 15 Jul 2007 06:22:17 -0000 @@ -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+= 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 Index: share/man/man4/snd_emu10kx.4 =================================================================== RCS file: /home/ncvs/src/share/man/man4/snd_emu10kx.4,v retrieving revision 1.4 diff -u -r1.4 snd_emu10kx.4 --- share/man/man4/snd_emu10kx.4 14 Dec 2006 16:40:57 -0000 1.4 +++ share/man/man4/snd_emu10kx.4 29 Jul 2007 06:57:51 -0000 @@ -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: src/share/man/man4/snd_emu10kx.4,v 1.4 2006/12/14 16:40:57 mpp Exp $ .\" -.Dd July 15, 2006 +.Dd July 15, 2007 .Dt SND_EMU10KX 4 .Os .Sh NAME @@ -38,10 +37,6 @@ .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 @@ .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 @@ 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 @@ .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 @@ 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 @@ 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.