Open Sound System |
Do you have problems with sound/audio application development? Don't panic! Click here for help! |
This file is part of Open Sound System.
Copyright (C) 4Front Technologies 1996-2008.
This this source file is released under GPL v2 license (no other versions). See the COPYING file included in the main directory of this source distribution for the license terms and conditions.
#include "oss_sblive_cfg.h" #include "midi_core.h" #include "oss_pci.h" #include "remux.h"
Include the DSP files for emu10k1 (Live!) and emu10k2 (Audigy) These headers have been generated using the emu10k tools (by 4Front). The _be.h files have been generated in a big endian system and the others in a litle endian (intel) system.
#ifdef OSS_BIG_ENDIAN # include "emu10k1_dsp_be.h" # include "emu10k2_dsp_be.h" #else # include "emu10k1_dsp.h" # include "emu10k2_dsp.h" #endif #define NO_EMU10K1_SYNTH #undef TEST_3D #define BOGUS_MIXER_CONTROLS ( \ SOUND_MASK_SPEAKER | \ SOUND_MASK_ALTPCM | \ SOUND_MASK_VIDEO | \ SOUND_MASK_DEPTH | \ SOUND_MASK_MONO \ ) #ifndef linux #define NO_EMU10K1_SYNTH #endif #include "ac97.h" #include "sblive.h" #include "eq1.h" #define MAX_SENDS 4 #define SEND_L 0 #define SEND_R 1 #define SEND_SL 2 #define SEND_SR 3 #define SEND_C 4 #define SEND_W 5 #define SEND_RL 6 #define SEND_RR 7 #define SPDIF_L 20 #define SPDIF_R 21 static unsigned char default_routing[MAX_SENDS] = { SEND_L, SEND_R, SEND_SL, SEND_SR }; static unsigned char front_routing[MAX_SENDS] = { SEND_L, SEND_R, 0x3f, 0x3f }; static unsigned char surr_routing[MAX_SENDS] = { SEND_SL, SEND_SR, 0x3f, 0x3f }; static unsigned char center_lfe_routing[MAX_SENDS] = { SEND_C, SEND_W, 0x3f, 0x3f }; static unsigned char rear_routing[MAX_SENDS] = { SEND_RL, SEND_RR, 0x3f, 0x3f }; static unsigned char spdif_routing[MAX_SENDS] = { SPDIF_L, SPDIF_R, 0x3f, 0x3f }; typedef struct { int speed; int pitch; int recbits; int audigy_recbits; int rom; } speed_ent; /* Note! with audigy speedsel=7 means 12 kHz */ static speed_ent speed_tab[] = { {8000, 0xb6a41b, 7, 8, ROM7}, {11025, 0xbe0b64, 6, 6, ROM6}, {16000, 0xc6a41b, 5, 5, ROM5}, {22050, 0xce0b64, 4, 3, ROM4}, {24000, 0xd00000, 3, 3, ROM3}, {32000, 0xd6a41b, 2, 2, ROM2}, {44100, 0xde0b64, 1, 1, ROM1}, {48000, 0xe00000, 0, 0, ROM0}, {0} }; #define PCI_VENDOR_ID_CREATIVE 0x1102 #define PCI_DEVICE_ID_SBLIVE 0x0002 #define PCI_DEVICE_ID_AUDIGY 0x0004 #define PCI_DEVICE_ID_AUDIGYVALUE 0x0008 #define PCI_DEVICE_ID_AUDIGY_CARDBUS 0x2001 #define LEFT_CH 0 #define RIGHT_CH 1 void sblive_init_voice (sblive_devc * devc, int chn); static void audigyuartintr (sblive_devc * devc); static int ac97_read (void *devc_, int wAddr) { sblive_devc *devc = devc_; int dtemp = 0, i; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); OUTB (devc->osdev, wAddr, devc->base + 0x1e); for (i = 0; i < 10000; i++) if (INB (devc->osdev, devc->base + 0x1e) & 0x80) break; if (i == 1000) { MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return OSS_EIO; /* Timeout */ } dtemp = INW (devc->osdev, devc->base + 0x1c); MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return dtemp & 0xffff; } static int ac97_write (void *devc_, int wAddr, int wData) { sblive_devc *devc = devc_; oss_native_word flags; int i; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); OUTB (devc->osdev, wAddr, devc->base + 0x1e); for (i = 0; i < 10000; i++) if (INB (devc->osdev, devc->base + 0x1e) & 0x80) break; OUTW (devc->osdev, wData, devc->base + 0x1c); MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return 0; } unsigned int sblive_read_reg (sblive_devc * devc, int reg, int chn) { oss_native_word flags; unsigned int ptr, ptr_addr_mask, val, mask, size, offset; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); ptr_addr_mask = (devc->feature_mask & SB_AUDIGY) ? 0x0fff0000 : 0x07ff0000; ptr = ((reg << 16) & ptr_addr_mask) | (chn & 0x3f); OUTL (devc->osdev, ptr, devc->base + 0x00); /* Pointer */ val = INL (devc->osdev, devc->base + 0x04); /* Data */ if (reg & 0xff000000) { size = (reg >> 24) & 0x3f; offset = (reg >> 16) & 0x1f; mask = ((1 << size) - 1) << offset; val &= mask; val >>= offset; } MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return val; } void sblive_write_reg (sblive_devc * devc, int reg, int chn, unsigned int value) { oss_native_word flags; unsigned int ptr, ptr_addr_mask, mask, size, offset; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); ptr_addr_mask = (devc->feature_mask & SB_AUDIGY) ? 0x0fff0000 : 0x07ff0000; ptr = ((reg << 16) & ptr_addr_mask) | (chn & 0x3f); OUTL (devc->osdev, ptr, devc->base + 0x00); /* Pointer */ if (reg & 0xff000000) { size = (reg >> 24) & 0x3f; offset = (reg >> 16) & 0x1f; mask = ((1 << size) - 1) << offset; value <<= offset; value &= mask; value |= INL (devc->osdev, devc->base + 0x04) & ~mask; /* data */ } OUTL (devc->osdev, value, devc->base + 0x04); /* Data */ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); } static void write_efx (sblive_devc * devc, int reg, unsigned int value) { sblive_write_reg (devc, reg, 0, value); } static void update_vu (sblive_devc * devc, sblive_portc * portc, dmap_p dmap, int frag) { int left = 0, right = 0; int i, l; frag %= dmap->nfrags; l = dmap->fragment_size / 2; if (portc->format == AFMT_AC3) /* Raw S/PDIF mode */ { if (devc->vu_tmp2 == 0) devc->vu_tmp2 = 2; portc->vu_left = devc->vu_tmp; portc->vu_right = 144 - devc->vu_tmp; devc->vu_tmp = devc->vu_tmp + devc->vu_tmp2; if (devc->vu_tmp >= 144) { devc->vu_tmp2 = -2; devc->vu_tmp = 144; } if (devc->vu_tmp <= 0) { devc->vu_tmp2 = 2; devc->vu_tmp = 0; } return; } if (portc->format == AFMT_U8) { unsigned char *p; int v; p = dmap->dmabuf + (frag * dmap->fragment_size); if (dmap->dmabuf != NULL) for (i = 0; i < l; i++) { v = *p; p++; v -= 128; if (v < 0) v = -v; if (v > left) left = v; v = *p; p++; v -= 128; if (v < 0) v = -v; if (v > right) right = v; } } else { short *p; int v; l /= 2; p = (short *) (dmap->dmabuf + (frag * dmap->fragment_size)); if (dmap->dmabuf != NULL) for (i = 0; i < l; i++) { v = SSWAP (*p++) >> 8; if (v < 0) v = -v; if (v > left) left = v; v = *p++ >> 8; if (v < 0) v = -v; if (v > right) right = v; } } if (portc->channels == 1) /* Mono */ { if (right > left) left = right; right = left; } if (left > portc->vu_left) portc->vu_left = left; if (right > portc->vu_right) portc->vu_right = right; } static int sbliveintr (oss_device_t * osdev) { int p; unsigned int status; #ifndef NO_EMU10K1_SYNTH extern int sblive_synth_enable; #endif sblive_devc *devc = osdev->devc;
TODO: Fix mutexes and move the inputintr/outputintr calls outside the mutex block.
/* oss_native_word flags; */ /* MUTEX_ENTER (devc->mutex, flags); */ status = INL (devc->osdev, devc->base + 0x08); #if !defined(sparc) if (status == 0) { /* MUTEX_EXIT (devc->mutex, flags); */ return 0; } #endif if (status & 0x00000080) /* MIDI RX interrupt */ { if (devc->feature_mask & SB_AUDIGY) audigyuartintr (devc); else uart401_irq (&devc->uart401devc); } if (status & 0x00008000) /* ADC buffer full intr */ { /* Force the starting position to match this moment */ unsigned int pos; pos = (INL (devc->osdev, devc->base + 0x10) >> 6) & 0xfffff; devc->portc[0].rec_starttime = pos; } if (status & 0x00000240) /* Interval timer or channel loop interrupt */ { for (p = 0; p < devc->n_audiodevs; p++) { sblive_portc *portc = &devc->portc[p]; if (portc->audiodev >= 0 && portc->audiodev < num_audio_engines && portc->trigger_bits & PCM_ENABLE_OUTPUT) { int pos, n; dmap_t *dmap = audio_engines[portc->audiodev]->dmap_out; pos = sblive_read_reg (devc, QKBCA, portc->voice_chn) & 0x00ffffff; /* Curr pos */ pos <<= portc->out_sz; pos -= dmap->driver_use_value; if (dmap->fragment_size == 0) { cmn_err (CE_WARN, "dmap->fragment_size == 0\n"); continue; } pos /= dmap->fragment_size; if (pos < 0 || pos >= dmap->nfrags) pos = 0;
If this was a full/half loop interrupt then use forced pointer
if (sblive_get_voice_loopintr (devc, portc->voice_chn)) pos = 0; /* Full loop boundary */ else if (sblive_get_voice_halfloopintr (devc, portc->voice_chn)) pos = dmap->nfrags / 2; /* Half loop boundary */ n = 0; while (dmap_get_qhead (dmap) != pos && n++ < dmap->nfrags) { update_vu (devc, portc, dmap, dmap_get_qhead (dmap)); oss_audio_outputintr (portc->audiodev, 0); } } if (num_audio_engines > 0 && portc->audiodev < num_audio_engines && portc->trigger_bits & PCM_ENABLE_INPUT) { int n; unsigned int pos; dmap_t *dmap = audio_engines[portc->audiodev]->dmap_in; /* Compute current pos based on the wall clock register */ pos = (INL (devc->osdev, devc->base + 0x10) >> 6) & 0xfffff; if (pos > portc->rec_starttime) pos = pos - portc->rec_starttime; else pos = 0xfffff - (portc->rec_starttime - pos); pos = (pos * (portc->speed / 25)) / (48000 / 25); pos *= 2; /* 16 bit->bytes */ pos *= portc->channels; pos = pos % dmap->bytes_in_use; if (dmap->fragment_size == 0) cmn_err (CE_WARN, "dmap->fragment_size==0\n"); else { pos /= dmap->fragment_size; if (pos >= dmap->nfrags) pos = 0; n = 0; while (dmap_get_qtail (dmap) != pos && n++ < dmap->nfrags) { oss_audio_inputintr (devc->recording_dev, 0); } } } } #ifndef NO_EMU10K1_SYNTH if (sblive_synth_enable) sblive_synth_interrupt (devc); #endif } OUTL (devc->osdev, status, devc->base + 0x08); /* Acknowledge them */ /* MUTEX_EXIT (devc->mutex, flags); */ return 1; } static int setup_passthrough (sblive_devc * devc, sblive_portc * portc, int pass) { int ctrl = devc->passthrough_gpr; if (ctrl < 0) return 0; if (pass == portc->uses_spdif) return 1; if (pass && devc->spdif_busy) return 0; portc->uses_spdif = pass; sblive_write_reg (devc, ctrl + GPR0, 0, pass); sblive_write_reg (devc, ctrl + GPR0 + 1, 0, !pass); if (pass) { devc->spdif_busy = 1; } else { devc->spdif_busy = 0; } return 1; } static int sblive_audio_set_rate (int dev, int arg) { sblive_portc *portc = audio_engines[dev]->portc; int i, n = -1, dif, best = -1, bestdif = 0x7fffffff; if (arg == 0) return portc->speed; for (i = 0; speed_tab[i].speed != 0 && n == -1; i++) { if (speed_tab[i].speed == arg) /* Exact match */ { n = i; break; } dif = arg - speed_tab[i].speed; if (dif < 0) dif = -dif; if (dif < bestdif) { best = i; bestdif = dif; } } if (n == -1) n = best; if (n == -1) n = 0; portc->speed = speed_tab[n].speed; portc->speedsel = n; return portc->speed; } static short sblive_audio_set_channels (int dev, short arg) { sblive_portc *portc = audio_engines[dev]->portc; if ((arg != 1) && (arg != 2)) return portc->channels; portc->channels = arg; return portc->channels; } static unsigned int sblive_audio_set_format (int dev, unsigned int arg) { sblive_devc *devc = audio_engines[dev]->devc; sblive_portc *portc = audio_engines[dev]->portc; if (arg == 0) return portc->format; if (portc->mode & OPEN_READ) arg = AFMT_S16_LE; if (arg != AFMT_U8 && arg != AFMT_S16_LE && arg != AFMT_AC3) return portc->format; /* Enforce stereo mode with AC3 */ if (arg == AFMT_AC3) { if (!setup_passthrough (devc, portc, 1)) return portc->format; portc->channels = 2; portc->speed = 48000; } else if (portc->input_type != ITYPE_SPDIF && portc->uses_spdif && arg != AFMT_AC3) { setup_passthrough (devc, portc, 0); } portc->format = arg; return portc->format; } static int mixer_ext_init (int dev); static int create_efx_mixer (int dev); static int sblive_set_gpr (int dev, int ctrl, unsigned int cmd, int value); static int is_special_gpr (int gpr) { if (gpr >= NEXT_FREE_GPR) return 0; if (SPECIAL_GPRS & (1 << gpr)) { return 1; } if (gpr > 0) if (SPECIAL_GPRS & (1 << (gpr - 1))) { return 1; } return 0; } static void update_output_device (sblive_devc * devc, sblive_portc * portc); static int sblive_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg) { sblive_devc *devc = audio_engines[dev]->devc; sblive_portc *portc = audio_engines[dev]->portc; sblive_reg *reg = (sblive_reg *) arg; gpr_info *gpr = (gpr_info *) arg; const_info *consts = (const_info *) arg; unsigned int *code = (unsigned int *) arg; int i; switch (cmd) { case SNDCTL_DSP_GETCHANNELMASK: return *arg = DSP_BIND_FRONT | DSP_BIND_REAR | DSP_BIND_SURR | DSP_BIND_CENTER_LFE; break; case SNDCTL_DSP_BIND_CHANNEL: { int val; val = *arg; portc->speaker_mode = SMODE_BIND; portc->binding = val; return *arg = val; } break; case SNDCTL_DSP_SETPLAYVOL: { int left, right, val; val = *arg; left = val & 0xff; right = (val >> 8) & 0xff; if (left < 0) left = 0; if (right < 0) right = 0; if (left > 100) left = 100; if (right > 100) right = 100; #if 0 if (right > left) left = right; #endif portc->playvol = left; update_output_device (devc, portc); return *arg = left | (left << 8); } break; case SNDCTL_DSP_GETPLAYVOL: { int vol; vol = (portc->playvol << 8) | portc->playvol; return *arg = vol; } break; case SBLIVE_READREG: #ifdef GET_PROCESS_UID if (GET_PROCESS_UID () != 0) /* Not root */ return OSS_EINVAL; #endif reg->value = sblive_read_reg (devc, reg->reg, reg->chn); return 0; break; case SBLIVE_WRITEREG: #ifdef GET_PROCESS_UID if (GET_PROCESS_UID () != 0) /* Not root */ return OSS_EINVAL; #endif sblive_write_reg (devc, reg->reg, reg->chn, reg->value); return 0; break; case SBLIVE_READGPIO: #ifdef GET_PROCESS_UID if (GET_PROCESS_UID () != 0) /* Not root */ return OSS_EINVAL; #endif return *arg = INW (devc->osdev, devc->base + 0x18); break; case SBLIVE_WRITEGPIO: { int val; val = *arg; #ifdef GET_PROCESS_UID if (GET_PROCESS_UID () != 0) /* Not root */ return OSS_EINVAL; #endif OUTW (devc->osdev, val, devc->base + 0x18); } return 0; case SBLIVE_WRITEPARMS: { #ifdef GET_PROCESS_UID if (GET_PROCESS_UID () != 0) /* Not root */ return OSS_EINVAL; #endif if (gpr->ngpr >= MAX_GPR_PARMS) return OSS_EIO; for (i = 0; i < gpr->ngpr; i++) { gpr->gpr[i].name[GPR_NAME_SIZE - 1] = 0; /* Overflow protection */ if (strlen (gpr->gpr[i].name) >= 32) /* Name may be bad */ { return OSS_EIO; } if (gpr->gpr[i].num >= MAX_GPR) { return OSS_EIO; } /* cmn_err(CE_CONT, "Gpr %d = %s (vol %x) type=%x\n", gpr->gpr[i].num, gpr->gpr[i].name, gpr->gpr[i].def, gpr->gpr[i].type); */ if (gpr->gpr[i].type != MIXT_GROUP) { if (is_special_gpr (gpr->gpr[i].num)) sblive_set_gpr (devc->mixer_dev, gpr->gpr[i].num, SNDCTL_MIX_WRITE, devc->gpr_values[gpr->gpr[i].num]); else sblive_set_gpr (devc->mixer_dev, gpr->gpr[i].num, SNDCTL_MIX_WRITE, gpr->gpr[i].def); } } if (devc->gpr == NULL) { devc->gpr = PMALLOC (devc->osdev, sizeof (gpr_info)); if (devc->gpr == NULL) { cmn_err (CE_WARN, "Out of memory (gpr)\n"); return OSS_ENOSPC; } memset (devc->gpr, 0, sizeof (gpr_info)); } memcpy (devc->gpr, gpr, sizeof (gpr_info)); create_efx_mixer (devc->mixer_dev); } return 0; break; case SBLIVE_WRITECONST: { #ifdef GET_PROCESS_UID if (GET_PROCESS_UID () != 0) /* Not root */ return OSS_EINVAL; #endif if (consts->nconst >= MAX_CONST_PARMS) return OSS_EIO; for (i = 0; i < consts->nconst; i++) { if (consts->consts[i].gpr >= MAX_GPR) { return OSS_EIO; } sblive_write_reg (devc, consts->consts[i].gpr + GPR0, 0, consts->consts[i].value); } } return 0; break; case SBLIVE_WRITECODE1: { int pc; #ifdef GET_PROCESS_UID if (GET_PROCESS_UID () != 0) /* Not root */ return OSS_EINVAL; #endif for (pc = 0; pc < 512; pc++) { write_efx (devc, UC0 + pc, code[pc]); } } sblive_write_reg (devc, DBG, 0, 0); return 0; break; case SBLIVE_WRITECODE2: { int pc; #ifdef GET_PROCESS_UID if (GET_PROCESS_UID () != 0) /* Not root */ return OSS_EINVAL; #endif for (pc = 0; pc < 512; pc++) { write_efx (devc, UC0 + 512 + pc, code[pc]); } } sblive_write_reg (devc, DBG, 0, 0); return 0; break; case SBLIVE_GETCHIPTYPE: #ifdef GET_PROCESS_UID if (GET_PROCESS_UID () != 0) /* Not root */ return OSS_EINVAL; #endif return *arg = devc->feature_mask & ~SB_AUDIGY2; break; } return OSS_EINVAL; } static void sblive_audio_trigger (int dev, int state); static void sblive_audio_reset (int dev) { sblive_audio_trigger (dev, 0); } static void sblive_audio_reset_input (int dev) { sblive_portc *portc = audio_engines[dev]->portc; sblive_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT); } static void sblive_audio_reset_output (int dev) { sblive_portc *portc = audio_engines[dev]->portc; sblive_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT); } static int sblive_set_vol (int dev, int ctrl, unsigned int cmd, int value); static void reset_portc_volume (sblive_devc * devc, sblive_portc * portc) { int v; #ifdef TEST_3D v = 100 | (50 << 8) | (0 << 16); /* vol=100, dist=50, angle=0 */ #else v = 100 | (100 < 8); #endif sblive_set_vol (devc->mixer_dev, portc->port_number, SNDCTL_MIX_WRITE, v); } #if MAX_ADEV == 2 static const unsigned int binding_map[MAX_ADEV] = { DSP_BIND_FRONT, DSP_BIND_SURR }; #else static const unsigned int binding_map[MAX_ADEV] = { DSP_BIND_FRONT, DSP_BIND_FRONT, DSP_BIND_SURR, DSP_BIND_CENTER_LFE, DSP_BIND_REAR }; #endif /*ARGSUSED*/ static int sblive_audio_open (int dev, int mode, int open_flags) { sblive_devc *devc = audio_engines[dev]->devc; sblive_portc *portc = audio_engines[dev]->portc; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); if (portc->mode) { MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return OSS_EBUSY; } portc->mode = mode; portc->audio_active &= ~mode; portc->resetvol = 0; portc->speaker_mode = devc->speaker_mode; portc->binding = binding_map[portc->port_number]; if (portc->binding == 0) portc->binding = DSP_BIND_FRONT; mixer_devs[devc->mixer_dev]->modify_counter++; /* Force update of mixer */ audio_engines[dev]->flags &= ~ADEV_FIXEDRATE; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); if (devc->autoreset || portc->resetvol) reset_portc_volume (devc, portc); if (portc->input_type == ITYPE_SPDIF) { if (!setup_passthrough (devc, portc, 1)) { portc->mode = 0; return OSS_EBUSY; } } else { /* Enable AC3 format if possible */ if (!devc->spdif_busy) audio_engines[dev]->oformat_mask |= AFMT_AC3; else audio_engines[dev]->oformat_mask &= ~AFMT_AC3; } return 0; } static void sblive_audio_close (int dev, int mode) { sblive_devc *devc = audio_engines[dev]->devc; sblive_portc *portc = audio_engines[dev]->portc; sblive_audio_reset (dev); portc->mode = 0; portc->audio_active &= ~mode; mixer_devs[devc->mixer_dev]->modify_counter++; setup_passthrough (devc, portc, 0); audio_engines[dev]->oformat_mask |= AFMT_AC3; } #ifdef TEST_3D /* Sin table for Q1 (*10000) */ static const int sincos[] = { 0, 174, 348, 523, 697, 871, 1045, 1218, 1391, 1564, 1736, 1908, 2079, 2249, 2419, 2588, 2756, 2923, 3090, 3255, 3420, 3583, 3746, 3907, 4067, 4226, 4383, 4539, 4694, 4848, 5000, 5150, 5299, 5446, 5591, 5735, 5877, 6018, 6156, 6293, 6427, 6560, 6691, 6819, 6946, 7071, 7193, 7313, 7431, 7547, 7660, 7771, 7880, 7986, 8090, 8191, 8290, 8386, 8480, 8571, 8660, 8746, 8829, 8910, 8987, 9063, 9135, 9205, 9271, 9335, 9396, 9455, 9510, 9563, 9612, 9659, 9702, 9743, 9781, 9816, 9848, 9876, 9902, 9925, 9945, 9961, 9975, 9986, 9993, 9998, 10000, }; static __inline__ int oss_sin (int angle) { int a; int f; a = angle % 90; if ((angle / 90) & 1) a = 90 - a; f = sincos[a]; if (angle >= 180) f = -f; return f; } static __inline__ int oss_cos (int angle) { int a, q; int f; a = angle % 90; q = angle / 90; if (!(q & 1)) a = 90 - a; f = sincos[a]; if (angle >= 90 && angle < 270) f = -f; return f; } static void compute_3d (sblive_devc * devc, sblive_portc * portc, int voice, int chn, int *send) { int angle = portc->playangle; int dist, opening = 45; int i; /* left, right, rear_right, rear_left */ static int spk_angles[4] = { 315, 45, 135, 225 }; int gain = 50, leak = 0; int v[4]; dist = portc->playdist; if (dist < 0) dist = 0; if (dist > 100) dist = 100; portc->playdist = dist; dist = 100 - dist; /* Invert distance */ opening = (90 * dist) / 100; if (dist < 50) { /* Attenuate distant sounds */ gain = dist; } else { /* "Expand" close sounds by leaking signal to silent channels */ leak = dist - 50; } if (portc->channels == 2) { if (chn == LEFT_CH) angle -= opening; else angle += opening; } if (angle < 0) angle += 360; angle %= 360; for (i = 0; i < 4; i++) v[i] = (gain * portc->playvol * 255 + 25) / 50; for (i = 0; i < 4; i++) { int a = spk_angles[i] - angle; if (a < 0) a = -a; /* ABS */ if (a > 180) a = 360 - a; if (a >= 90) /* Too far */ { v[i] = 0; /* Muted speaker */ continue; } else v[i] = ((v[i] * oss_cos (a) + 5000) / 10000) / 100; } if (leak > 0) { leak = (255 * portc->playvol * leak + 2500) / 5000; for (i = 0; i < 4; i++) if (v[i] < leak) v[i] = leak; } send[0] = v[0]; /* Left */ send[1] = v[1]; /* Right */ send[2] = v[3]; /* Rear left */ send[3] = v[2]; /* Rear right */ } #endif static void write_routing (sblive_devc * devc, int voice, unsigned char *routing) { int i; if (routing == NULL) routing = default_routing; if (devc->feature_mask & SB_AUDIGY) { unsigned int srda = 0; for (i = 0; i < 4; i++) srda |= routing[i] << (i * 8); sblive_write_reg (devc, SRDA, voice, srda); } else { int fxrt = 0; for (i = 0; i < 4; i++) fxrt |= routing[i] << ((i * 4) + 16); sblive_write_reg (devc, FXRT, voice, fxrt); } } /*ARGSUSED*/ static void compute_bind (sblive_devc * devc, sblive_portc * portc, unsigned char *send, int chn) { memset (send, 0, MAX_SENDS); if (chn == LEFT_CH) send[0] = (0xff * portc->playvol + 50) / 100; else send[1] = (0xff * portc->playvol + 50) / 100; switch (portc->binding) { case DSP_BIND_FRONT: portc->routing = front_routing; break; case DSP_BIND_SURR: portc->routing = surr_routing; break; case DSP_BIND_CENTER_LFE: portc->routing = center_lfe_routing; break; case DSP_BIND_REAR: portc->routing = rear_routing; break; default: portc->routing = default_routing; } } static void update_output_volume (int dev, int voice, int chn) { sblive_devc *devc = audio_engines[dev]->devc; sblive_portc *portc = audio_engines[dev]->portc; oss_native_word flags; int left, right; unsigned int loop_start, loop_end, tmp; unsigned char send[MAX_SENDS]; send[0] = 0xff; /* Max */ send[1] = 0xff; /* Max */ send[2] = 0xff; /* Max */ send[3] = 0xff; /* Max */ if (portc->input_type == ITYPE_SPDIF || portc->format == AFMT_AC3) { /* Digital voice */ send[2] = 0; /* Muted */ send[3] = 0; /* Muted */ /* sends are revered between Audigy2 and Audigy */ left = (devc->feature_mask & SB_AUDIGY2) ? 1 : 0; right = !left; if (portc->channels > 1) { if (chn == LEFT_CH) { send[left] = 0; } else { send[right] = 0; } } } else { /* Analog voice */ if (portc->channels > 1) { if (chn == LEFT_CH) { send[1] = 0; } else { send[0] = 0; } } send[2] = send[0]; send[3] = send[1]; #ifdef TEST_3D if (portc->speaker_mode == SMODE_3D) compute_3d (devc, portc, voice, chn, send); else #endif { send[0] = (send[0] * portc->playvol + 50) / 100; send[1] = (send[1] * portc->playvol + 50) / 100; send[2] = (send[2] * portc->playvol + 50) / 100; send[3] = (send[3] * portc->playvol + 50) / 100; switch (portc->speaker_mode) { case SMODE_FRONT: send[2] = send[3] = 0; break; case SMODE_SURR: send[0] = send[1] = 0; break; case SMODE_FRONTREAR: break; case SMODE_BIND: compute_bind (devc, portc, send, chn); break; } } /* Analog voice */ } MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); loop_end = sblive_read_reg (devc, SDL, voice) & 0xffffff; sblive_write_reg (devc, SDL, voice, loop_end | (send[3] << 24)); loop_start = sblive_read_reg (devc, SCSA, voice) & 0xffffff; sblive_write_reg (devc, SCSA, voice, loop_start | (send[2] << 24)); tmp = sblive_read_reg (devc, PTAB, voice) & 0xffff0000; sblive_write_reg (devc, PTAB, voice, tmp | (send[0] << 8) | send[1]); write_routing (devc, voice, portc->routing); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); } static void setup_audio_voice (int dev, int voice, int chn) { sblive_devc *devc = audio_engines[dev]->devc; sblive_portc *portc = audio_engines[dev]->portc; dmap_t *dmap = audio_engines[dev]->dmap_out; unsigned int nCRA = 0; unsigned int loop_start, loop_end; int sz = 1; int start_pos; sblive_write_reg (devc, VEDS, voice, 0x0); /* OFF */ sblive_write_reg (devc, VTFT, voice, 0xffff); sblive_write_reg (devc, CVCF, voice, 0xffff); if (devc->feature_mask & SB_AUDIGY) sblive_write_reg (devc, SRDA, voice, 0x03020100); else sblive_write_reg (devc, FXRT, voice, 0x32100000); sz = (((portc->format == AFMT_S16_LE || portc->format == AFMT_AC3)) ? 1 : 0) + ((portc->channels == 2) ? 1 : 0); loop_start = dmap->driver_use_value >> sz; loop_end = (dmap->driver_use_value + dmap->bytes_in_use) >> sz; /* set mono/stereo */ sblive_write_reg (devc, CPF, voice, (portc->channels > 1) ? 0x8000 : 0); nCRA = (portc->channels > 1) ? 28 : 30; nCRA *= (portc->format == AFMT_S16_LE || portc->format == AFMT_AC3) ? 1 : 2; start_pos = loop_start + nCRA; /* SDL, ST, CA */ portc->out_sz = sz; sblive_write_reg (devc, SDL, voice, loop_end); sblive_write_reg (devc, SCSA, voice, loop_start); sblive_write_reg (devc, PTAB, voice, 0); update_output_volume (dev, voice, chn); /* Set volume */ if (portc->format == AFMT_S16_LE || portc->format == AFMT_AC3) sblive_write_reg (devc, QKBCA, voice, start_pos); else sblive_write_reg (devc, QKBCA, voice, start_pos | BYTESIZE); sblive_write_reg (devc, Z1, voice, 0); sblive_write_reg (devc, Z2, voice, 0); sblive_write_reg (devc, MAPA, voice, 0x1fff | (devc->silent_page_phys << 1)); /* This is really a physical address */ sblive_write_reg (devc, MAPB, voice, 0x1fff | (devc->silent_page_phys << 1)); /* This is really a physical address */ sblive_write_reg (devc, VTFT, voice, 0x0000ffff); sblive_write_reg (devc, CVCF, voice, 0x0000ffff); sblive_write_reg (devc, MEHA, voice, 0); sblive_write_reg (devc, MEDS, voice, 0x7f); sblive_write_reg (devc, MLV, voice, 0x8000); sblive_write_reg (devc, VLV, voice, 0x8000); sblive_write_reg (devc, VFM, voice, 0); sblive_write_reg (devc, TMFQ, voice, 0); sblive_write_reg (devc, VVFQ, voice, 0); sblive_write_reg (devc, MEV, voice, 0x8000); sblive_write_reg (devc, VEHA, voice, 0x7f7f); /* OK */ sblive_write_reg (devc, VEV, voice, 0x8000); /* No volume envelope delay (OK) */ sblive_write_reg (devc, PEFE_FILTERAMOUNT, voice, 0x7f); sblive_write_reg (devc, PEFE_PITCHAMOUNT, voice, 0x00); } /*ARGSUSED*/ static void update_output_device (sblive_devc * devc, sblive_portc * portc) { int voiceL = portc->voice_chn, voiceR = portc->voice_chn + 1; if (!(portc->audio_active & PCM_ENABLE_OUTPUT)) return; update_output_volume (portc->audiodev, voiceL, LEFT_CH); update_output_volume (portc->audiodev, voiceR, RIGHT_CH); } /*ARGSUSED*/ static void sblive_audio_output_block (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { sblive_portc *portc = audio_engines[dev]->portc; portc->audio_active |= PCM_ENABLE_OUTPUT; portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; } /*ARGSUSED*/ static void sblive_audio_start_input (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { sblive_portc *portc = audio_engines[dev]->portc; portc->audio_active |= PCM_ENABLE_INPUT; portc->trigger_bits &= ~PCM_ENABLE_INPUT; } void sblive_set_loop_stop (sblive_devc * devc, int voice, int s) { unsigned int tmp; int offs, bit; offs = voice / 32; bit = voice % 32; s = !!s; tmp = sblive_read_reg (devc, SOLL + offs, 0); tmp &= ~(1 << bit); if (s) tmp |= (1 << bit); sblive_write_reg (devc, SOLL + offs, 0, tmp); } int sblive_get_voice_loopintr (sblive_devc * devc, int voice) { unsigned int tmp; int offs, bit; offs = voice / 32; bit = voice % 32; tmp = sblive_read_reg (devc, CLIPL + offs, 0); tmp &= 1 << bit; sblive_write_reg (devc, CLIPL + offs, 0, tmp); /* Ack the interrupt */ return !!tmp; } int sblive_get_voice_halfloopintr (sblive_devc * devc, int voice) { unsigned int tmp; int offs, bit; offs = voice / 32; bit = voice % 32; tmp = sblive_read_reg (devc, HLIPL + offs, 0); tmp &= 1 << bit; sblive_write_reg (devc, HLIPL + offs, 0, tmp); /* Ack the interrupt */ return !!tmp; } void sblive_set_voice_intr (sblive_devc * devc, int voice, int s) { unsigned int tmp; int offs, bit; offs = voice / 32; bit = voice % 32; s = !!s; tmp = sblive_read_reg (devc, CLIEL + offs, 0); tmp &= ~(1 << bit); if (s) tmp |= (1 << bit); sblive_write_reg (devc, CLIEL + offs, 0, tmp); sblive_write_reg (devc, HLIEL + offs, 0, tmp); } static unsigned int emu_rate_to_pitch (unsigned int rate) { static unsigned int logMagTable[128] = { 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2, 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5, 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081, 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191, 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7, 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829, 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e, 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26, 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d, 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885, 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899, 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c, 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3, 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3, 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83, 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df }; static char logSlopeTable[128] = { 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58, 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53, 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f, 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b, 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47, 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44, 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41, 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37, 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35, 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34, 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32, 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f }; int i; if (rate == 0) return 0; /* Bail out if no leading "1" */ rate *= 11185; /* Scale 48000 to 0x20002380 */ for (i = 31; i > 0; i--) { if (rate & 0x80000000) { /* Detect leading "1" */ return (((unsigned int) (i - 15) << 20) + logMagTable[0x7f & (rate >> 24)] + (0x7f & (rate >> 17)) * logSlopeTable[0x7f & (rate >> 24)]); } rate <<= 1; } return 0; /* Should never reach this point */ } static unsigned int emu_rate_to_linearpitch (unsigned int rate) { rate = (rate << 8) / 375; return (rate >> 1) + (rate & 1); } static void start_audio_voice (int dev, int voice, int chn) { sblive_devc *devc = audio_engines[dev]->devc; sblive_portc *portc = audio_engines[dev]->portc; unsigned int sample, initial_pitch, pitch_target; unsigned int cra, cs, ccis, i; /* setup CCR regs */ cra = 64; cs = (portc->channels > 1) ? 4 : 2; ccis = (portc->channels > 1) ? 28 : 30; ccis *= (portc->format == AFMT_S16_LE || portc->format == AFMT_AC3) ? 1 : 2; sample = (portc->format == AFMT_S16_LE || portc->format == AFMT_AC3) ? 0x00000000 : 0x80808080; for (i = 0; i < cs; i++) sblive_write_reg (devc, CD0 + i, voice, sample); sblive_write_reg (devc, CCR_CACHEINVALIDSIZE, voice, 0); sblive_write_reg (devc, CCR_READADDRESS, voice, cra); sblive_write_reg (devc, CCR_CACHEINVALIDSIZE, voice, ccis); /* Set current pitch */ sblive_write_reg (devc, IFA, voice, 0xff00); sblive_write_reg (devc, VTFT, voice, 0xffffffff); sblive_write_reg (devc, CVCF, voice, 0xffffffff); sblive_set_loop_stop (devc, voice, 0); pitch_target = emu_rate_to_linearpitch (portc->speed); initial_pitch = emu_rate_to_pitch (portc->speed) >> 8; sblive_write_reg (devc, PTRX_PITCHTARGET, voice, pitch_target); sblive_write_reg (devc, CPF_CURRENTPITCH, voice, pitch_target); sblive_write_reg (devc, IP, voice, initial_pitch); if (chn == LEFT_CH) { sblive_get_voice_loopintr (devc, voice); sblive_get_voice_halfloopintr (devc, voice); sblive_set_voice_intr (devc, voice, 1); } sblive_write_reg (devc, VEDS, voice, /*0x80 | */ 0x7f7f); /* Trigger (OK) */ } /*ARGSUSED*/ static void stop_audio_voice (int dev, int voice, int chn) { sblive_devc *devc = audio_engines[dev]->devc; sblive_write_reg (devc, IFA, voice, 0xffff); sblive_write_reg (devc, VTFT, voice, 0xffff); sblive_write_reg (devc, PTRX_PITCHTARGET, voice, 0); sblive_write_reg (devc, CPF_CURRENTPITCH, voice, 0); sblive_write_reg (devc, IP, voice, 0); sblive_set_loop_stop (devc, voice, 1); sblive_set_voice_intr (devc, voice, 0); } static void sblive_audio_trigger (int dev, int state) { sblive_portc *portc = audio_engines[dev]->portc; sblive_devc *devc = audio_engines[dev]->devc; int voiceL = portc->voice_chn, voiceR = portc->voice_chn + 1; if (portc->mode & OPEN_WRITE) { if (state & PCM_ENABLE_OUTPUT) { if ((portc->audio_active & PCM_ENABLE_OUTPUT) && !(portc->trigger_bits & PCM_ENABLE_OUTPUT)) { start_audio_voice (dev, voiceL, LEFT_CH); /* sblive_dump_regs(devc, voiceL); */ if (portc->channels > 1) start_audio_voice (dev, voiceR, RIGHT_CH); portc->trigger_bits |= PCM_ENABLE_OUTPUT; } } else { if ((portc->audio_active & PCM_ENABLE_OUTPUT) && (portc->trigger_bits & PCM_ENABLE_OUTPUT)) { portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; portc->audio_active &= ~PCM_ENABLE_OUTPUT; stop_audio_voice (dev, voiceL, LEFT_CH); stop_audio_voice (dev, voiceR, RIGHT_CH); } } } if (portc->mode & OPEN_READ && !(audio_engines[dev]->flags & ADEV_NOINPUT)) { if (state & PCM_ENABLE_INPUT) { if ((portc->audio_active & PCM_ENABLE_INPUT) && !(portc->trigger_bits & PCM_ENABLE_INPUT)) { int tmp = sblive_read_reg (devc, ADCSR, 0); unsigned int pos; if (portc->input_type == ITYPE_SPDIF) { /* Start recording from S/PDIF input A */ sblive_write_reg (devc, SPRC, 0, portc->in_szbits | 0x00); } else { if (devc->feature_mask & SB_AUDIGY) { tmp |= 0x10; /* Left channel enable */ if (portc->channels > 1) tmp |= 0x20; /* Right channel enable */ } else { tmp |= 0x08; /* Left channel enable */ if (portc->channels > 1) tmp |= 0x10; /* Right channel enable */ } sblive_write_reg (devc, ADCBS, 0, portc->in_szbits); sblive_write_reg (devc, ADCSR, 0, tmp); /* GO */ } pos = (INL (devc->osdev, devc->base + 0x10) >> 6) & 0xfffff; portc->rec_starttime = pos; portc->trigger_bits |= PCM_ENABLE_INPUT; } } else { if ((portc->audio_active & PCM_ENABLE_INPUT) && (portc->trigger_bits & PCM_ENABLE_INPUT)) { if (portc->input_type == ITYPE_SPDIF) sblive_write_reg (devc, SPRC, 0, 0); else sblive_write_reg (devc, ADCSR, 0, 0); portc->trigger_bits &= ~PCM_ENABLE_INPUT; portc->audio_active &= ~PCM_ENABLE_INPUT; } } } } /*ARGSUSED*/ static int sblive_audio_prepare_for_input (int dev, int bsize, int bcount) { sblive_devc *devc = audio_engines[dev]->devc; sblive_portc *portc = audio_engines[dev]->portc; dmap_t *dmap = audio_engines[dev]->dmap_in; int sz = -1; if (audio_engines[dev]->flags & ADEV_NOINPUT) { cmn_err (CE_WARN, "Audio device %d is output only\n", dev); return OSS_EIO; } if (dmap->buffsize > 65536) { cmn_err (CE_WARN, "Recording buffer bigger than 64k\n"); dmap->buffsize = 65536; } #ifdef sun1 if (dmap->buffsize == 36864) { dmap->buffsize = 32768; } #endif switch (dmap->buffsize) { case 4096: sz = 15; break; case 8192: sz = 19; break; case 16384: sz = 23; break; case 32768: sz = 27; break; case 65536: sz = 31; break; default: cmn_err (CE_WARN, "Unsupported input buffer size %d\n", dmap->buffsize); return OSS_ENOSPC; } if (portc->input_type == ITYPE_SPDIF) { sblive_write_reg (devc, SPRA, 0, dmap->dmabuf_phys); sblive_write_reg (devc, SPRC, 0, 0); } else { sblive_write_reg (devc, ADCBA, 0, dmap->dmabuf_phys); sblive_write_reg (devc, ADCBS, 0, 0); } portc->in_szbits = sz; sblive_write_reg (devc, ADCSR, 0, 0x0); if (portc->input_type == ITYPE_ANALOG) { if (devc->feature_mask & SB_AUDIGY) sblive_write_reg (devc, ADCSR, 0, speed_tab[portc->speedsel].audigy_recbits); else sblive_write_reg (devc, ADCSR, 0, speed_tab[portc->speedsel].recbits); } devc->recording_dev = dev; portc->audio_active |= PCM_ENABLE_INPUT; portc->trigger_bits &= ~PCM_ENABLE_INPUT; return 0; } /*ARGSUSED*/ static int sblive_audio_prepare_for_output (int dev, int bsize, int bcount) { sblive_devc *devc = audio_engines[dev]->devc; sblive_portc *portc = audio_engines[dev]->portc; int voiceL = portc->voice_chn, voiceR = portc->voice_chn + 1; if (audio_engines[dev]->flags & ADEV_NOOUTPUT) return OSS_EIO; /* AC3 needs stereo too */ if (portc->format == AFMT_AC3 || portc->input_type == ITYPE_SPDIF) { portc->channels = 2; portc->speed = 48000; portc->routing = spdif_routing; } else portc->routing = default_routing; /* Left channel */ sblive_write_reg (devc, IFA, voiceL, 0xffff); /* Intial filter cutoff and attenuation */ sblive_write_reg (devc, VEDS, voiceL, 0x0); /* Volume envelope decay and sustain */ sblive_write_reg (devc, VTFT, voiceL, 0xffff); /* Volume target and Filter cutoff target */ sblive_write_reg (devc, PTAB, voiceL, 0x0); /* Pitch target and sends A and B */ /* The same for right channel */ sblive_write_reg (devc, IFA, voiceR, 0xffff); sblive_write_reg (devc, VEDS, voiceR, 0x0); sblive_write_reg (devc, VTFT, voiceR, 0xffff); sblive_write_reg (devc, PTAB, voiceR, 0x0); /* now setup the voices and go! */ setup_audio_voice (dev, voiceL, LEFT_CH); if (portc->channels == 2) setup_audio_voice (dev, voiceR, RIGHT_CH); portc->audio_active |= PCM_ENABLE_OUTPUT; portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; if (portc->uses_spdif) { if (portc->format == AFMT_AC3) { sblive_write_reg (devc, SCS0, 0, 0x2109206); } else { sblive_write_reg (devc, SCS0, 0, 0x2108504); } } return 0; } static int sblive_alloc_buffer (int dev, dmap_t * dmap, int direction) { int err, i, n; sblive_devc *devc = audio_engines[dev]->devc; sblive_portc *portc = audio_engines[dev]->portc; if (dmap->dmabuf != NULL) return 0; if ((err = oss_alloc_dmabuf (dev, dmap, direction)) < 0) { cmn_err (CE_WARN, "Cannot allocate DMA buffer\n"); return err; } if (dmap->buffsize > DMABUF_SIZE) { cmn_err (CE_NOTE, "DMA buffer was too large - truncated\n"); dmap->buffsize = DMABUF_SIZE; } if (devc->feature_mask & SB_LIVE) if (dmap->dmabuf_phys & 0x80000000) { cmn_err (CE_CONT, "Got DMA buffer address beyond 2G limit.\n"); oss_free_dmabuf (dev, dmap); dmap->dmabuf = NULL; return OSS_ENOSPC; } if (direction == PCM_ENABLE_OUTPUT) { dmap->driver_use_value = portc->memptr; n = portc->memptr / 4096;
Fill the page table
for (i = 0; i < dmap->buffsize / 4096; i++) { FILL_PAGE_MAP_ENTRY (n + i, dmap->dmabuf_phys + i * 4096); } } return 0; } /*ARGSUSED*/ static int sblive_free_buffer (int dev, dmap_t * dmap, int direction) { if (dmap->dmabuf == NULL) return 0; oss_free_dmabuf (dev, dmap); dmap->dmabuf = NULL; return 0; } /*ARGSUSED*/ static int sblive_get_buffer_pointer (int dev, dmap_t * dmap, int direction) { sblive_devc *devc = audio_engines[dev]->devc; sblive_portc *portc = audio_engines[dev]->portc; int pos; if (!(portc->trigger_bits & direction)) return 0; if (direction == OPEN_WRITE) { pos = sblive_read_reg (devc, QKBCA, portc->voice_chn) & 0x00ffffff; /* Curr pos */ pos <<= portc->out_sz; pos -= dmap->driver_use_value; } else { /* Compute current pos based on the wall clock register */ pos = (INL (devc->osdev, devc->base + 0x10) >> 6) & 0xfffff; if (pos > portc->rec_starttime) pos = pos - portc->rec_starttime; else pos = 0xfffff - (portc->rec_starttime - pos); pos = (pos * (portc->speed / 25)) / (48000 / 25); pos *= 2; /* 16 bit->bytes */ pos *= portc->channels; pos = pos % dmap->bytes_in_use; } if (pos < 0) pos = 0; return pos; } static audiodrv_t sblive_audio_driver = { sblive_audio_open, sblive_audio_close, sblive_audio_output_block, sblive_audio_start_input, sblive_audio_ioctl, sblive_audio_prepare_for_input, sblive_audio_prepare_for_output, sblive_audio_reset, NULL, NULL, sblive_audio_reset_input, sblive_audio_reset_output, sblive_audio_trigger, sblive_audio_set_rate, sblive_audio_set_format, sblive_audio_set_channels, NULL, NULL, NULL, /* sblive_check_input, */ NULL, /* sblive_check_output, */ sblive_alloc_buffer, sblive_free_buffer, NULL, NULL, sblive_get_buffer_pointer, NULL, NULL, NULL, NULL, NULL, sblive_audio_ioctl /* bind */ }; #define DATAPORT (devc->base) #define COMDPORT (devc->base+1) #define STATPORT (devc->base+1) static __inline__ int audigyuart_status (sblive_devc * devc) { return sblive_read_reg (devc, MUASTAT, 0); } #define input_avail(devc) (!(audigyuart_status(devc)&INPUT_AVAIL)) #define output_ready(devc) (!(audigyuart_status(devc)&OUTPUT_READY)) static void audigyuart_cmd (sblive_devc * devc, unsigned char cmd) { sblive_write_reg (devc, MUACMD, 0, cmd); } static __inline__ int audigyuart_read (sblive_devc * devc) { return sblive_read_reg (devc, MUADAT, 0); } static __inline__ void audigyuart_write (sblive_devc * devc, unsigned char byte) { sblive_write_reg (devc, MUADAT, 0, byte); } #define OUTPUT_READY 0x40 #define INPUT_AVAIL 0x80 #define MPU_ACK 0xFE #define MPU_RESET 0xFF #define UART_MODE_ON 0x3F static int reset_audigyuart (sblive_devc * devc); static void enter_uart_mode (sblive_devc * devc); typedef struct { int keycode; int action; int local_action; } ir_code_t; static void sblive_key_action (sblive_devc * devc, ir_code_t * code) { int value, left, right, dev; dev = devc->mixer_dev; switch (code->local_action) { case 1: /* Volume- */ value = sblive_set_gpr (dev, GPR_VOLUME, SNDCTL_MIX_READ, 0); left = value & 0xff; right = (value >> 8) & 0xff; left -= 5; if (left < 0) left = 0; right -= 5; if (right < 0) right = 0; value = left | (right << 8); sblive_set_gpr (dev, GPR_VOLUME, SNDCTL_MIX_WRITE, value); mixer_devs[dev]->modify_counter++; return; break; case 2: /* Volume+ */ value = sblive_set_gpr (dev, GPR_VOLUME, SNDCTL_MIX_READ, 0); left = value & 0xff; right = (value >> 8) & 0xff; left += 5; if (left > 100) left = 100; right += 5; if (right > 100) right = 100; value = left | (right << 8); sblive_set_gpr (dev, GPR_VOLUME, SNDCTL_MIX_WRITE, value); mixer_devs[dev]->modify_counter++; return; break; } } static void sblive_handle_ir (sblive_devc * devc, unsigned char c) {
Receive a MIDI SysEx message and check if it's an IR remote command
#if 1
Sysex code sent by the Live!DRIVE IR unit
static unsigned char remote_id[] = { 0xf0, 0x00, 0x20, 0x21, 0x60, 0x00, 0x02, 0x00, 0xf7 }; static ir_code_t codes[] = { /* Creative RM-900B remote control unit */ {0x09017e}, /* 0 */ {0x0a512e}, /* 1 */ {0x0a710e}, /* 2 */ {0x090976}, /* 3 */ {0x09512e}, /* 4 */ {0x09215e}, /* 5 */ {0x091e61}, /* 6 */ {0x0a116e}, /* 7 */ {0x0a413e}, /* 8 */ {0x0a6e11}, /* 9 */ {0x0a1e61}, /* Play/Pause */ {0x0a215e}, /* Stop/Eject */ {0x0a3e41}, /* Slow */ {0x0a7e01}, /* Prev */ {0x095e21}, /* Next */ {0x097e01}, /* Step */ {0x097609}, /* Mute */ {0x0a4639, 0, 1}, /* Vol- */ {0x094639, 0, 2}, /* Vol+ */ /* Speaker ??? */ {0x09314e}, /* EAX */ {0x09413e}, /* Options */ {0x096e11}, /* Display */ {0x09710e}, /* Return */ {0x09116e}, /* Start */ {0x093e41}, /* Cancel */ {0x0a5e21}, /* Up */ {0x0a611e}, /* << */ {0x0a017e}, /* Select/OK */ {0x0a2e51}, /* >> */ {0x0a314e}, /* Down */ /* Creative RM-1000 remote control unit */ {0x0a0679}, /* Power */ {0x0a0e71}, /* CMSS */ {0x0a4e31}, /* Rec */ /* Creative Inspire 5.1 Digital 5700 remote */ {0x0a0778}, /* Power */ {0x097708}, /* Mute */ {0x0a7708}, /* Test */ {0x0a4738}, /* Vol- */ {0x094738}, /* Vol+ */ {0x0a0f70}, /* Effect */ {0x0a5728}, /* Analog */ {0x0a2758}, /* Pro logic */ {0x094f30}, /* Dynamic mode */ {0x093748}, /* Digital/PCM audio */ {0} }; #endif if (c == 0xf0) /* Sysex start */ { devc->sysex_buf[0] = c; devc->sysex_p = 1; return; } if (devc->sysex_p <= 0) return; if (devc->sysex_p >= 20) /* Too long */ { devc->sysex_p = 0; return; } if (c == 0xf7) /* Sysex end */ { int i, l, v; unsigned char *buf; devc->sysex_buf[devc->sysex_p] = c; devc->sysex_p++; l = devc->sysex_p; devc->sysex_p = 0; buf = devc->sysex_buf; if (l == 9) { int ok = 1; for (i = 0; i < sizeof (remote_id); i++) if (buf[i] != remote_id[i]) ok = 0; if (ok) { /* cmn_err (CE_CONT, "Live!DRIVE IR detected\n"); */ return; } return; } if (l != 13) /* Wrong length */ return; if (buf[0] != 0xf0 || buf[12] != 0xf7) /* Not sysex */ return; /* Verify that this is an IR receiver sysex */ if (buf[1] != 0x00 || buf[2] != 0x20 || buf[3] != 0x21) return; if (buf[4] != 0x60 || buf[5] != 0x00 || buf[6] != 0x01) return; #if 0 if (buf[7] != 0x09 && buf[7] != 0x0a) /* Remote ID */ return; #endif if (buf[8] != 0x41 || buf[9] != 0x44) return; v = (buf[7] << 16) | (buf[10] << 8) | buf[11]; for (i = 0; codes[i].keycode != 0; i++) if (codes[i].keycode == v) { sblive_key_action (devc, &codes[i]); return; } return; } /* Ordinary byte */ devc->sysex_buf[devc->sysex_p] = c; devc->sysex_p++; } static void sblive_ir_callback (int dev, unsigned char c) { sblive_devc *devc; oss_device_t *osdev = midi_devs[dev]->osdev; devc = osdev->devc; if (devc->midi_dev != dev) return; sblive_handle_ir (devc, c); } static void audigyuart_input_loop (sblive_devc * devc) { int t = 0; while (input_avail (devc) && t++ < 1000) { unsigned char c = audigyuart_read (devc); sblive_handle_ir (devc, c); if (c == MPU_ACK) devc->input_byte = c; else if (devc->midi_opened & OPEN_READ && devc->midi_input_intr) devc->midi_input_intr (devc->midi_dev, c); } } static void audigyuartintr (sblive_devc * devc) { oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); audigyuart_input_loop (devc); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); } /*ARGSUSED*/ static int audigyuart_open (int dev, int mode, oss_midi_inputbyte_t inputbyte, oss_midi_inputbuf_t inputbuf, oss_midi_outputintr_t outputintr) { sblive_devc *devc = (sblive_devc *) midi_devs[dev]->devc; if (devc->midi_opened) { return OSS_EBUSY; } while (input_avail (devc)) audigyuart_read (devc); devc->midi_input_intr = inputbyte; devc->midi_opened = mode; enter_uart_mode (devc); devc->midi_disabled = 0; return 0; } /*ARGSUSED*/ static void audigyuart_close (int dev, int mode) { sblive_devc *devc = (sblive_devc *) midi_devs[dev]->devc; reset_audigyuart (devc); oss_udelay (10); enter_uart_mode (devc); reset_audigyuart (devc); devc->midi_opened = 0; } static int audigyuart_out (int dev, unsigned char midi_byte) { sblive_devc *devc = (sblive_devc *) midi_devs[dev]->devc; if (devc->midi_disabled) return 1; if (!output_ready (devc)) { return 0; } audigyuart_write (devc, midi_byte); return 1; } /*ARGSUSED*/ static int audigyuart_ioctl (int dev, unsigned cmd, ioctl_arg arg) { return OSS_EINVAL; } static midi_driver_t audigy_midi_driver = { audigyuart_open, audigyuart_close, audigyuart_ioctl, audigyuart_out }; static void enter_uart_mode (sblive_devc * devc) { int ok, timeout; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--); devc->input_byte = 0; audigyuart_cmd (devc, UART_MODE_ON); ok = 0; for (timeout = 50000; timeout > 0 && !ok; timeout--) if (devc->input_byte == MPU_ACK) ok = 1; else if (input_avail (devc)) if (audigyuart_read (devc) == MPU_ACK) ok = 1; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); } void attach_audigyuart (sblive_devc * devc) { enter_uart_mode (devc); devc->midi_dev = oss_install_mididev (OSS_MIDI_DRIVER_VERSION, "AUDIGY", "Audigy UART", &audigy_midi_driver, sizeof (midi_driver_t), 0, devc, devc->osdev); devc->midi_opened = 0; } static int reset_audigyuart (sblive_devc * devc) { int ok, timeout, n; oss_native_word flags;
Send the RESET command. Try again if no success at the first time.
ok = 0; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); for (n = 0; n < 2 && !ok; n++) { for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--); devc->input_byte = 0; audigyuart_cmd (devc, MPU_RESET);
Wait at least 25 msec. This method is not accurate so let's make the loop bit longer. Cannot sleep since this is called during boot.
for (timeout = 50000; timeout > 0 && !ok; timeout--) if (devc->input_byte == MPU_ACK) /* Interrupt */ ok = 1; else if (input_avail (devc)) if (audigyuart_read (devc) == MPU_ACK) ok = 1; } if (ok)
Flush input before enabling interrupts
MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return ok; } int probe_audigyuart (sblive_devc * devc) { int ok = 0; DDB (cmn_err (CE_CONT, "Entered probe_audigyuart\n")); devc->midi_input_intr = NULL; devc->midi_opened = 0; devc->input_byte = 0; ok = reset_audigyuart (devc); if (ok) { DDB (cmn_err (CE_CONT, "Reset UART401 OK\n")); } else { DDB (cmn_err (CE_CONT, "Reset UART401 failed (no hardware present?).\n")); DDB (cmn_err (CE_CONT, "mpu401 status %02x\n", audigyuart_status (devc))); } DDB (cmn_err (CE_CONT, "audigyuart detected OK\n")); return ok; } void unload_audigyuart (sblive_devc * devc) { reset_audigyuart (devc); } static void attach_mpu (sblive_devc * devc) { char tmp[128]; int ndevs = num_mididevs; oss_native_word flags; sprintf (tmp, "%s external MIDI", devc->card_name); if (devc->feature_mask & SB_AUDIGY) { if (!probe_audigyuart (devc)) { cmn_err (CE_NOTE, "MIDI UART was not detected\n"); return; } DDB (cmn_err (CE_CONT, "SB Audigy: MIDI UART detected - Good\n")); devc->mpu_attached = 1; attach_audigyuart (devc); } else { MUTEX_ENTER (devc->mutex, flags); if (uart401_init (&devc->uart401devc, devc->osdev, devc->base + 0x18, tmp) >= 0) devc->mpu_attached = 1; MUTEX_EXIT (devc->mutex, flags); if (ndevs != num_mididevs) { devc->midi_dev = ndevs; midi_devs[ndevs]->input_callback = sblive_ir_callback; } } } static void load_dsp (sblive_devc * devc, unsigned char *buf, int len) { emu10k1_file *code; int pc, i; if (len != sizeof (*code)) { cmn_err (CE_NOTE, "DSP file size mismatch\n"); return; } code = (emu10k1_file *) buf; for (pc = 0; pc < 1024; pc++) { write_efx (devc, UC0 + pc, code->code[pc]); } if (code->parms.ngpr < MAX_GPR_PARMS) for (i = 0; i < code->parms.ngpr; i++) { code->parms.gpr[i].name[GPR_NAME_SIZE - 1] = 0; /* Overflow protection */ if (strlen (code->parms.gpr[i].name) >= 32) /* Name may be bad */ { return; } /* cmn_err(CE_CONT, "Gpr %d = %s (vol %x) type=%x\n", gpr->gpr[i].num, gpr->gpr[i].name, gpr->gpr[i].def, gpr->gpr[i].type); */ if (code->parms.gpr[i].num < MAX_GPR) if (code->parms.gpr[i].type != MIXT_GROUP) { if (is_special_gpr (code->parms.gpr[i].num)) sblive_set_gpr (devc->mixer_dev, code->parms.gpr[i].num, SNDCTL_MIX_WRITE, devc->gpr_values[code->parms.gpr[i].num]); else sblive_set_gpr (devc->mixer_dev, code->parms.gpr[i].num, SNDCTL_MIX_WRITE, code->parms.gpr[i].def); } } if (devc->gpr == NULL) { devc->gpr = PMALLOC (devc->osdev, sizeof (gpr_info)); if (devc->gpr == NULL) { cmn_err (CE_WARN, "Out of memory (gpr)\n"); return; } memset (devc->gpr, 0, sizeof (gpr_info)); } memcpy (devc->gpr, &code->parms, sizeof (gpr_info)); create_efx_mixer (devc->mixer_dev); if (code->consts.nconst >= MAX_CONST_PARMS) return; for (i = 0; i < code->consts.nconst; i++) { if (code->consts.consts[i].gpr >= MAX_GPR) { return; } sblive_write_reg (devc, code->consts.consts[i].gpr + GPR0, 0, code->consts.consts[i].value); } } #define LIVE_NOP() \ write_efx(devc, UC0+(pc*2), 0x10040); \ write_efx(devc, UC0+(pc*2+1), 0x610040);pc++ #define LIVE_ACC3(r, a, x, y) /* z=w+x+y */ \ write_efx(devc, UC0+(pc*2), (x << 10) | y); \ write_efx(devc, UC0+(pc*2+1), (6 << 20) | (r << 10) | a);pc++ #define AUDIGY_ACC3(r, a, x, y) /* z=w+x+y */ \ write_efx(devc, UC0+(pc*2), (x << 12) | y); \ write_efx(devc, UC0+(pc*2+1), (6 << 24) | (r << 12) | a);pc++ #define AUDIGY_NOP() AUDIGY_ACC3(0xc0, 0xc0, 0xc0, 0xc0) static int init_effects (sblive_devc * devc) { int i; unsigned short pc; if (devc->feature_mask & SB_AUDIGY) { pc = 0; for (i = 0; i < 512; i++) { AUDIGY_NOP (); } for (i = 0; i < 256; i++) write_efx (devc, GPR0 + i, 0); sblive_write_reg (devc, AUDIGY_DBG, 0, 0); load_dsp (devc, emu10k2_dsp, sizeof (emu10k2_dsp)); } else { pc = 0; for (i = 0; i < 512; i++) { LIVE_NOP (); } for (i = 0; i < 256; i++) write_efx (devc, GPR0 + i, 0); sblive_write_reg (devc, DBG, 0, 0); load_dsp (devc, emu10k1_dsp, sizeof (emu10k1_dsp)); } return 1; } static void init_emu10k1 (sblive_devc * devc) { unsigned int tmp, i; extern int sblive_memlimit; #ifndef NO_EMU10K1_SYNTH extern int sblive_synth_enable; #endif int xmem_mode = 0; unsigned int reg, val; extern int sblive_digital_din; extern int audigy_digital_din; oss_native_word phaddr; unsigned int memlimit = MEMLIMIT_31BITS; OUTL (devc->osdev, 0x00000000, devc->base + 0x0c); /* Intr disable */ OUTL (devc->osdev, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, devc->base + 0x14); sblive_write_reg (devc, MBS, 0, 0x0); sblive_write_reg (devc, MBA, 0, 0x0); sblive_write_reg (devc, FXBS, 0, 0x0); sblive_write_reg (devc, FXBA, 0, 0x0); sblive_write_reg (devc, ADCBS, 0, 0x0); sblive_write_reg (devc, ADCBA, 0, 0x0); sblive_write_reg (devc, CLIEL, 0, 0x0); sblive_write_reg (devc, CLIEH, 0, 0x0); sblive_write_reg (devc, SOLL, 0, 0xffffffff); sblive_write_reg (devc, SOLH, 0, 0xffffffff); if (devc->feature_mask & SB_AUDIGY) { memlimit=MEMLIMIT_32BITS; sblive_write_reg (devc, 0x5e, 0, 0xf00); /* ?? */ sblive_write_reg (devc, 0x5f, 0, 0x3); /* ?? */ } #ifndef NO_EMU10K1_SYNTH if (!sblive_synth_enable) sblive_memlimit = 0; #endif if (sblive_memlimit < 4096) /* Given in megabytes */ sblive_memlimit *= (1024 * 1024); devc->max_mem = sblive_memlimit; if (devc->max_mem < 1024 * 1024) devc->max_mem = 1024 * 1024; devc->max_mem += AUDIO_MEMSIZE; /* SB Live/Audigy supports at most 32M of memory) */ if (devc->max_mem > 32 * 1024 * 1024) devc->max_mem = 32 * 1024 * 1024; devc->max_pages = devc->max_mem / 4096; if (devc->max_pages < 1024) devc->max_pages = 1024; devc->page_map = (int *) CONTIG_MALLOC (devc->osdev, devc->max_pages * 4, memlimit, &phaddr, devc->page_map_dma_handle); devc->vpage_map = KERNEL_MALLOC (devc->max_pages * sizeof (unsigned char *)); if (devc->page_map == NULL || devc->vpage_map == NULL) { cmn_err (CE_WARN, "Can't allocate the PTBA table\n"); return; } memset (devc->vpage_map, 0, devc->max_pages * 4); tmp = phaddr; if (devc->feature_mask & SB_LIVE) { if (tmp & 0x80000000) { cmn_err (CE_CONT, "SB Live Error: Page table is beyond the 2G limit\n"); } } else { if (tmp & 0x80000000) { DDB (cmn_err (CE_CONT, "Audigy: Using 4G PCI addressing mode\n")); xmem_mode = 1; devc->emu_page_shift = 0; if (devc->max_mem > 16 * 1024 * 1034) { devc->max_mem = 16 * 1024 * 1024; DDB (cmn_err (CE_NOTE, "Max memory dropped to 16M due to need for extended PCI address mode.\n")); } } } devc->synth_memlimit = devc->max_mem - AUDIO_MEMSIZE; devc->synth_membase = SYNTH_MEMBASE; devc->synth_memtop = devc->synth_membase; devc->synth_memptr = devc->synth_membase; devc->silent_page = (int *) CONTIG_MALLOC (devc->osdev, 4096, memlimit, &phaddr, devc->silent_page_dma_handle); if (devc->silent_page == NULL) { cmn_err (CE_WARN, "Can't allocate a silent page\n"); return; } devc->silent_page_phys = phaddr; if (devc->feature_mask & SB_LIVE) if (devc->silent_page_phys & 0x80000000) { cmn_err (CE_CONT, "SB Live warning: Silent page is beyond the 2G limit\n"); } devc->audio_memptr = 4096; /* Skip the silence page */ memset (devc->silent_page, 0, 4096); for (i = 0; i < devc->max_pages; i++) { FILL_PAGE_MAP_ENTRY (i, devc->silent_page_phys); devc->vpage_map[i] = NULL; } for (i = 0; i < 64; i++) sblive_init_voice (devc, i); if (devc->feature_mask & SB_AUDIGY) { sblive_write_reg (devc, SCS0, 0, 0x2108504); sblive_write_reg (devc, SCS1, 0, 0x2108504); sblive_write_reg (devc, SCS2, 0, 0x2108504); } else { sblive_write_reg (devc, SCS0, 0, 0x2109204); sblive_write_reg (devc, SCS1, 0, 0x2109204); sblive_write_reg (devc, SCS2, 0, 0x2109204); } sblive_write_reg (devc, PTBA, 0, tmp); tmp = sblive_read_reg (devc, PTBA, 0); sblive_write_reg (devc, TCBA, 0, 0x0); sblive_write_reg (devc, TCBS, 0, 0x4); OUTL (devc->osdev, IE_RXA | IE_AB | IE_IT, devc->base + IE); /* Intr enable */
SB Live 5.1 support. Turn on S/PDIF output
if (devc->subvendor == 0x80611102) /* Live 5.1 */ { tmp = INL (devc->osdev, devc->base + 0x14); tmp |= 0x00001000; /* Turn GPO0 pin on to enable S/PDIF outputs */ OUTL (devc->osdev, tmp, devc->base + 0x14); } if (devc->subvendor == 0x80661102) { sblive_write_reg (devc, AC97SLOT, 0, AC97SLOT_CENTER | AC97SLOT_LFE | AC97SLOT_REAR_LEFT | AC97SLOT_REAR_RIGHT); } if (devc->feature_mask & SB_AUDIGY2) { /* Enable analog outputs on Audigy2 */ int tmp; /* Setup SRCMulti_I2S SamplingRate */ tmp = sblive_read_reg (devc, EHC, 0); tmp &= 0xfffff1ff; tmp |= (0x2 << 9); sblive_write_reg (devc, EHC, 0, tmp); /* sblive_write_reg (devc, SOC, 0, 0x00000000); */ /* Setup SRCSel (Enable Spdif,I2S SRCMulti) */ OUTL (devc->osdev, 0x600000, devc->base + 0x20); OUTL (devc->osdev, 0x14, devc->base + 0x24); /* Setup SRCMulti Input Audio Enable */ /* Setup SRCMulti Input Audio Enable */ if (devc->feature_mask & SB_AUDIGY2VAL) OUTL (devc->osdev, 0x7B0000, devc->base + 0x20); else OUTL (devc->osdev, 0x6E0000, devc->base + 0x20); OUTL (devc->osdev, 0xFF00FF00, devc->base + 0x24); /* Setup I2S ASRC Enable (HC register) */ tmp = INL (devc->osdev, devc->base + 0x14); tmp |= 0x00000070; OUTL (devc->osdev, tmp, devc->base + 0x14);
Unmute Analog now. Set GPO6 to 1 for Apollo. This has to be done after init ALice3 I2SOut beyond 48KHz. So, sequence is important
tmp = INL (devc->osdev, devc->base + 0x18); tmp |= 0x0040; if (devc->feature_mask & SB_AUDIGY2VAL) tmp |= 0x0060; OUTL (devc->osdev, tmp, devc->base + 0x18); } sblive_write_reg (devc, SOLL, 0, 0xffffffff); sblive_write_reg (devc, SOLH, 0, 0xffffffff); if (devc->feature_mask & SB_AUDIGY) { unsigned int mode = 0; if (devc->feature_mask & SB_AUDIGY2) mode |= HCFG_AC3ENABLE_GPSPDIF | HCFG_AC3ENABLE_CDSPDIF; if (xmem_mode) { OUTL (devc->osdev, HCFG_AUDIOENABLE | HCFG_AUTOMUTE | HCFG_JOYENABLE | A_HCFG_VMUTE | A_HCFG_AUTOMUTE | A_HCFG_XM | mode, devc->base + 0x14); } else OUTL (devc->osdev, HCFG_AUDIOENABLE | HCFG_AUTOMUTE | HCFG_JOYENABLE | A_HCFG_VMUTE | A_HCFG_AUTOMUTE | mode, devc->base + 0x14); OUTL (devc->osdev, INL (devc->osdev, devc->base + 0x18) | 0x0004, devc->base + 0x18); /* GPIO (S/PDIF enable) */ /* enable IR port */ tmp = INL (devc->osdev, devc->base + 0x18); OUTL (devc->osdev, tmp | A_IOCFG_GPOUT2, devc->base + 0x18); oss_udelay (500); OUTL (devc->osdev, tmp | A_IOCFG_GPOUT1 | A_IOCFG_GPOUT2, devc->base + 0x18); oss_udelay (100); OUTL (devc->osdev, tmp, devc->base + 0x18); } else OUTL (devc->osdev, HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE_MASK | HCFG_AUTOMUTE | HCFG_JOYENABLE, devc->base + 0x14); /* enable IR port */ tmp = INL (devc->osdev, devc->base + 0x14); OUTL (devc->osdev, tmp | HCFG_GPOUT2, devc->base + 0x14); oss_udelay (500); OUTL (devc->osdev, tmp | HCFG_GPOUT1 | HCFG_GPOUT2, devc->base + 0x14); oss_udelay (100); OUTL (devc->osdev, tmp, devc->base + 0x14); /* Switch the shared SPDIF/OUT3 to DIGITAL or ANALOG mode */ /* depending on whether the port is SPDIF or analog */ if ((devc->feature_mask == SB_AUDIGY) || ((devc->feature_mask & SB_AUDIGY2) && (audigy_digital_din == 0))) { reg = INL (devc->osdev, devc->base + 0x18) & ~A_IOCFG_GPOUT0; val = (audigy_digital_din) ? 0x4 : 0; reg |= val; OUTL (devc->osdev, reg, devc->base + 0x18); } if (devc->feature_mask & SB_LIVE) /* SBLIVE */ { reg = INL (devc->osdev, devc->base + 0x14) & ~HCFG_GPOUT0; val = (sblive_digital_din) ? HCFG_GPOUT0 : 0; reg |= val; OUTL (devc->osdev, reg, devc->base + 0x14); } } void sblive_init_voice (sblive_devc * devc, int voice) { sblive_set_loop_stop (devc, voice, 1); sblive_write_reg (devc, VEDS, voice, 0x0); sblive_write_reg (devc, IP, voice, 0x0); sblive_write_reg (devc, VTFT, voice, 0xffff); sblive_write_reg (devc, CVCF, voice, 0xffff); sblive_write_reg (devc, PTAB, voice, 0x0); sblive_write_reg (devc, CPF, voice, 0x0); sblive_write_reg (devc, CCR, voice, 0x0); sblive_write_reg (devc, SCSA, voice, 0x0); sblive_write_reg (devc, SDL, voice, 0x10); sblive_write_reg (devc, QKBCA, voice, 0x0); sblive_write_reg (devc, Z1, voice, 0x0); sblive_write_reg (devc, Z2, voice, 0x0); if (devc->feature_mask & SB_AUDIGY) sblive_write_reg (devc, SRDA, voice, 0x03020100); sblive_write_reg (devc, FXRT, voice, 0x32100000); sblive_write_reg (devc, MEHA, voice, 0x0); sblive_write_reg (devc, MEDS, voice, 0x0); sblive_write_reg (devc, IFA, voice, 0xffff); sblive_write_reg (devc, PEFE, voice, 0x0); sblive_write_reg (devc, VFM, voice, 0x0); sblive_write_reg (devc, TMFQ, voice, 24); sblive_write_reg (devc, VVFQ, voice, 24); sblive_write_reg (devc, TMPE, voice, 0x0); sblive_write_reg (devc, VLV, voice, 0x0); sblive_write_reg (devc, MLV, voice, 0x0); sblive_write_reg (devc, VEHA, voice, 0x0); sblive_write_reg (devc, VEV, voice, 0x0); sblive_write_reg (devc, MEV, voice, 0x0); if (devc->feature_mask & SB_AUDIGY) { sblive_write_reg (devc, CSBA, voice, 0x0); sblive_write_reg (devc, CSDC, voice, 0x0); sblive_write_reg (devc, CSFE, voice, 0x0); sblive_write_reg (devc, CSHG, voice, 0x0); sblive_write_reg (devc, SRHE, voice, 0x3f3f3f3f); } } #ifndef NO_EMU10K1_SYNTH extern void sblive_install_synth (sblive_devc * devc); extern void sblive_remove_synth (sblive_devc * devc); #endif static const unsigned char peak_cnv[256] = { 0, 18, 29, 36, 42, 47, 51, 54, 57, 60, 62, 65, 67, 69, 71, 72, 74, 75, 77, 78, 79, 81, 82, 83, 84, 85, 86, 87, 88, 89, 89, 90, 91, 92, 93, 93, 94, 95, 95, 96, 97, 97, 98, 99, 99, 100, 100, 101, 101, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108, 108, 109, 109, 110, 110, 110, 111, 111, 111, 112, 112, 113, 113, 113, 114, 114, 114, 115, 115, 115, 115, 116, 116, 116, 117, 117, 117, 118, 118, 118, 118, 119, 119, 119, 119, 120, 120, 120, 121, 121, 121, 121, 122, 122, 122, 122, 122, 123, 123, 123, 123, 124, 124, 124, 124, 125, 125, 125, 125, 125, 126, 126, 126, 126, 126, 127, 127, 127, 127, 127, 128, 128, 128, 128, 128, 129, 129, 129, 129, 129, 130, 130, 130, 130, 130, 130, 131, 131, 131, 131, 131, 131, 132, 132, 132, 132, 132, 132, 133, 133, 133, 133, 133, 133, 134, 134, 134, 134, 134, 134, 134, 135, 135, 135, 135, 135, 135, 135, 136, 136, 136, 136, 136, 136, 136, 137, 137, 137, 137, 137, 137, 137, 138, 138, 138, 138, 138, 138, 138, 138, 139, 139, 139, 139, 139, 139, 139, 139, 140, 140, 140, 140, 140, 140, 140, 140, 141, 141, 141, 141, 141, 141, 141, 141, 141, 142, 142, 142, 142, 142, 142, 142, 142, 142, 143, 143, 143, 143, 143, 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, }; static void set_equalizer (sblive_devc * devc, int ctrl, int band, int value) { const unsigned int *row; int i; switch (band) { case 0: row = (unsigned int *) &eq_band1_data[value][0]; break; case 1: row = (unsigned int *) &eq_band2_data[value][0]; break; case 2: row = (unsigned int *) &eq_band3_data[value][0]; break; case 3: row = (unsigned int *) &eq_band4_data[value][0]; break; default: cmn_err (CE_CONT, "%s: bad equalizer band %d\n", devc->card_name, band); return; } for (i = 0; i < 5; i++) { sblive_write_reg (devc, ctrl + GPR0 + i, 0, row[i]); } } static const int db2lin_101[101] = { 0x00000000, 0x0024B53A, 0x002750CA, 0x002A1BC6, 0x002D198D, 0x00304DBA, 0x0033BC2A, 0x00376901, 0x003B58AF, 0x003F8FF1, 0x004413DF, 0x0048E9EA, 0x004E17E9, 0x0053A419, 0x0059952C, 0x005FF24E, 0x0066C32A, 0x006E0FFB, 0x0075E18D, 0x007E414F, 0x0087395B, 0x0090D482, 0x009B1E5B, 0x00A6234F, 0x00B1F0A7, 0x00BE94A1, 0x00CC1E7C, 0x00DA9E8D, 0x00EA2650, 0x00FAC881, 0x010C9931, 0x011FADDC, 0x01341D87, 0x014A00D8, 0x01617235, 0x017A8DE6, 0x01957233, 0x01B23F8D, 0x01D118B1, 0x01F222D4, 0x021585D1, 0x023B6C57, 0x0264041D, 0x028F7E19, 0x02BE0EBD, 0x02EFEE33, 0x032558A2, 0x035E8E7A, 0x039BD4BC, 0x03DD7551, 0x0423BF61, 0x046F07B5, 0x04BFA91B, 0x051604D5, 0x0572830D, 0x05D59354, 0x063FAD27, 0x06B15080, 0x072B0673, 0x07AD61CD, 0x0838FFCA, 0x08CE88D3, 0x096EB147, 0x0A1A3A53, 0x0AD1F2E0, 0x0B96B889, 0x0C6978A5, 0x0D4B316A, 0x0E3CF31B, 0x0F3FE155, 0x10553469, 0x117E3AD9, 0x12BC5AEA, 0x14111457, 0x157E0219, 0x1704DC5E, 0x18A77A97, 0x1A67D5B6, 0x1C480A87, 0x1E4A5C45, 0x2071374D, 0x22BF3412, 0x25371A37, 0x27DBE3EF, 0x2AB0C18F, 0x2DB91D6F, 0x30F89FFD, 0x34733433, 0x382D0C46, 0x3C2AA6BD, 0x4070D3D9, 0x4504BB66, 0x49EBE2F1, 0x4F2C346F, 0x54CC0565, 0x5AD21E86, 0x6145C3E7, 0x682EBDBD, 0x6F9561C4, 0x77829D4D, 0x7fffffff }; static __inline__ int convert_fixpoint (int val) { if (val < 0) val = 0; if (val > 100) val = 100; return db2lin_101[val]; } static int sblive_set_gpr (int dev, int ctrl, unsigned int cmd, int value) { sblive_devc *devc = mixer_devs[dev]->hw_devc; int typ, i; if (devc == NULL) return 0; if (devc->gpr == NULL) { int left, right; if (ctrl >= NEXT_FREE_GPR) return 0; if (cmd != SNDCTL_MIX_WRITE) return 0; left = value & 0xff; right = (value >> 8) & 0xff; if (left < 0) left = 0; if (left > 100) left = 100; if (right < 0) right = 0; if (right > 100) right = 100; value = left | (right << 8); devc->gpr_values[ctrl] = value; left = convert_fixpoint (left); sblive_write_reg (devc, ctrl + GPR0, 0, left); right = convert_fixpoint (right); sblive_write_reg (devc, ctrl + GPR0 + 1, 0, right); return value; } if (ctrl < 0 || ctrl >= MAX_GPR) return OSS_EIO; typ = MIXT_SLIDER; for (i = 0; i < devc->gpr->ngpr; i++) if (devc->gpr->gpr[i].num == ctrl && devc->gpr->gpr[i].type != MIXT_GROUP) typ = devc->gpr->gpr[i].type; if (typ == MIXT_GROUP) { return OSS_EIO; } if (cmd == SNDCTL_MIX_READ) { if (typ == MIXT_STEREOPEAK || typ == MIXT_STEREOVU) { int v, l, r; /* Get the sample values and scale them to 0-144 dB range */ v = sblive_read_reg (devc, ctrl + GPR0, 0); l = v >> 23; v = sblive_read_reg (devc, ctrl + GPR0 + 1, 0); r = v >> 23; if (l < 0) l = -l; if (r < 0) r = -r; l = peak_cnv[l]; r = peak_cnv[r]; /* Reset values back to 0 */ sblive_write_reg (devc, ctrl + GPR0, 0, 0); sblive_write_reg (devc, ctrl + GPR0 + 1, 0, 0); return l | (r << 8); } return devc->gpr_values[ctrl]; } if (cmd == SNDCTL_MIX_WRITE) { switch (typ) { case MIXT_STEREOSLIDER: { int left, right; left = value & 0xff; right = (value >> 8) & 0xff; if (left < 0) left = 0; if (left > 100) left = 100; if (right < 0) right = 0; if (right > 100) right = 100; value = left | (right << 8); devc->gpr_values[ctrl] = value; left = convert_fixpoint (left); sblive_write_reg (devc, ctrl + GPR0, 0, left); right = convert_fixpoint (right); sblive_write_reg (devc, ctrl + GPR0 + 1, 0, right); } break; case MIXT_ONOFF: { value = !!value; devc->gpr_values[ctrl] = value; sblive_write_reg (devc, ctrl + GPR0, 0, value); sblive_write_reg (devc, ctrl + GPR0 + 1, 0, !value); } break; case EMU_MIXT_EQ1: case EMU_MIXT_EQ2: case EMU_MIXT_EQ3: case EMU_MIXT_EQ4: { int band; band = typ & 3; value = value & 0xff; set_equalizer (devc, ctrl, band, value); devc->gpr_values[ctrl] = value; } break; default: { int tmp; value = value & 0xff; if (value > 100) value = 100; devc->gpr_values[ctrl] = value; tmp = convert_fixpoint (value); sblive_write_reg (devc, ctrl + GPR0, 0, tmp); } } return value; } return OSS_EINVAL; } static int sblive_set_vol (int dev, int ctrl, unsigned int cmd, int value) { sblive_devc *devc = mixer_devs[dev]->hw_devc; sblive_portc *portc; if (ctrl < 0 || ctrl >= devc->n_audiodevs) return OSS_EINVAL; portc = &devc->portc[ctrl]; if (portc->input_type == ITYPE_SPDIF) { mixer_devs[dev]->modify_counter++; return 100 | (100 << 8); } if (cmd == SNDCTL_MIX_READ) { #ifdef TEST_3D return (devc->portc[ctrl].playvol & 0x00ff) | ((devc->portc[ctrl].playangle & 0xffff) << 16) | ((devc->portc[ctrl].playdist & 0xff) << 8); #else return devc->portc[ctrl].playvol & 0xff; #endif } if (cmd == SNDCTL_MIX_WRITE) { #ifdef TEST_3D int angle, dist; angle = (value >> 16) & 0xffff; /* Rotation angle */ dist = (value >> 8) & 0xff; /* Distance */ value &= 0x00ff; /* Volume */ if (value < 0) value = 0; if (value > 100) value = 100; switch (portc->speaker_mode) { case SMODE_FRONT: angle = 0; dist = 50; break; case SMODE_SURR: angle = 180; dist = 50; break; case SMODE_FRONTREAR: angle = 0; dist = 50; break; case SMODE_3D: break; } devc->portc[ctrl].playvol = value; devc->portc[ctrl].playdist = dist; devc->portc[ctrl].playangle = angle; update_output_device (devc, &devc->portc[ctrl]); return (value & 0x00ff) | (angle << 16) | ((dist & 0xff) << 8); #else value &= 0xff; /* Only left channel */ if (value < 0) value = 0; if (value > 100) value = 100; devc->portc[ctrl].playvol = value; update_output_device (devc, &devc->portc[ctrl]); return value; #endif } return OSS_EINVAL; } /*ARGSUSED*/ static int sblive_get_peak (int dev, int ctrl, unsigned int cmd, int value) { sblive_devc *devc = mixer_devs[dev]->hw_devc; if (ctrl < 0 || ctrl >= devc->n_audiodevs) return OSS_EINVAL; if (cmd == SNDCTL_MIX_READ) { int l, r, vol; l = devc->portc[ctrl].vu_left & 0xff; r = devc->portc[ctrl].vu_right & 0xff; #if 1 vol = devc->portc[ctrl].playvol; /* if (vol<1) vol=5; */ l = (l * vol + 50) / 100; r = (r * vol + 50) / 100; #endif devc->portc[ctrl].vu_left = 0; devc->portc[ctrl].vu_right = 0; return peak_cnv[l] | (peak_cnv[r] << 8); } return OSS_EINVAL; } static int sblive_set_parm (int dev, int ctrl, unsigned int cmd, int value) { sblive_devc *devc = mixer_devs[dev]->hw_devc; if (cmd == SNDCTL_MIX_READ) { switch (ctrl) { case 1: return devc->autoreset; case 2: return devc->speaker_mode; } } if (cmd == SNDCTL_MIX_WRITE) { switch (ctrl) { case 1: return devc->autoreset = !!(value); case 2: if (devc->speaker_mode != value) { int i; for (i = 0; i < devc->n_audiodevs; i++) devc->portc[i].resetvol = 1; } return devc->speaker_mode = value; break; } } return OSS_EINVAL; } static int create_soft_mixer (int dev) { sblive_devc *devc = mixer_devs[dev]->hw_devc; int group = 0, err, i, n; char tmp[100]; if ((err = mixer_ext_create_control (dev, 0, 1, sblive_set_parm, MIXT_ONOFF, "SBLIVE_AUTORESET", 2, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; #ifdef TEST_3D n = 5; #else n = 4; #endif if ((err = mixer_ext_create_control (dev, 0, 2, sblive_set_parm, MIXT_ENUM, "SBLIVE_SPKMODE", n, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; for (i = 0; i < devc->n_audiodevs; i++) { if (devc->n_audiodevs > devc->min_audiodevs) {
Use the traditional dspN naming system for sliders.
if ((i % 8) == 0) if ((group = mixer_ext_create_group (dev, 0, "/dev")) < 0) return group; sprintf (tmp, "@pcm%d", devc->portc[i].audiodev); } else {
Use front/rear/etc naming style
if ((i % 8) == 0) if ((group = mixer_ext_create_group (dev, 0, "pcm")) < 0) return group; switch (i) { case 0: strcpy (tmp, "main"); break; /* Duplex device */ case 1: strcpy (tmp, "front"); break; case 2: strcpy (tmp, "side"); break; case 3: strcpy (tmp, "C/L"); break; case 4: strcpy (tmp, "rear"); break; } } if ((err = mixer_ext_create_control (dev, group, i, sblive_set_vol, #ifdef TEST_3D MIXT_3D, #else MIXT_SLIDER, #endif tmp, 100, MIXF_PCMVOL | MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; if ((err = mixer_ext_create_control (dev, group, i, sblive_get_peak, MIXT_STEREOPEAK, "-", 144, MIXF_READABLE)) < 0) return err; } return 0; } static int create_efx_mixer (int dev) { sblive_devc *devc = mixer_devs[dev]->hw_devc; int group = 0, err = 0, i, mode; int group_created = 0; int typ, maxval; if (!devc->extinfo_loaded) { return 0; } if (devc->gpr == NULL) { return 0; } if (devc->mixer_group >= 0) mixer_ext_truncate (dev, devc->mixer_group); devc->mixer_group = -1; if (devc->gpr->ngpr >= MAX_GPR_PARMS) return OSS_EINVAL; for (i = 0; i < devc->gpr->ngpr; i++) { if (devc->gpr->gpr[i].num >= MAX_GPR) continue; typ = devc->gpr->gpr[i].type; if (typ == MIXT_GROUP) { if ((group = mixer_ext_create_group (dev, 0, devc->gpr->gpr[i].name)) < 0) return group; if (!group_created) devc->mixer_group = group; group_created = 1; continue; } #if 0 if (!group_created) { cmn_err (CE_WARN, "Mixer initialization sequence error\n"); return OSS_EINVAL; } #endif mode = MIXF_READABLE; maxval = 144; switch (typ) { case EMU_MIXT_EQ1: case EMU_MIXT_EQ2: case EMU_MIXT_EQ3: case EMU_MIXT_EQ4: { mode |= MIXF_WRITEABLE; maxval = 255; typ = MIXT_SLIDER; } break; case MIXT_STEREOSLIDER: case MIXT_SLIDER: case MIXT_MONOSLIDER: { mode |= MIXF_WRITEABLE; maxval = 100; } break; case MIXT_STEREOVU: typ = MIXT_STEREOPEAK; break; case MIXT_ONOFF: { mode |= MIXF_WRITEABLE; maxval = 1; } break; } if (devc->gpr->gpr[i].name[0] == '_') { /* Hidden control */ if (strcmp (devc->gpr->gpr[i].name, "_PASSTHROUGH") == 0) { int ctrl = devc->gpr->gpr[i].num; devc->passthrough_gpr = ctrl; sblive_write_reg (devc, ctrl + GPR0, 0, 1); sblive_write_reg (devc, ctrl + GPR0 + 1, 0, 0); } } else { /* Visible control */ if ((err = mixer_ext_create_control (dev, group, devc->gpr->gpr[i].num, sblive_set_gpr, typ, devc->gpr->gpr[i].name, maxval, mode)) < 0) return err; } if (!group_created) devc->mixer_group = err; group_created = 1; if (is_special_gpr (devc->gpr->gpr[i].num)) { sblive_set_gpr (dev, devc->gpr->gpr[i].num, SNDCTL_MIX_WRITE, devc->gpr_values[devc->gpr->gpr[i].num]); } else { sblive_set_gpr (dev, devc->gpr->gpr[i].num, SNDCTL_MIX_WRITE, devc->gpr->gpr[i].def); } } return 0; } static int mixer_ext_init (int dev) { sblive_devc *devc = mixer_devs[dev]->hw_devc; devc->extinfo_loaded = 1; create_soft_mixer (dev); create_efx_mixer (dev); return 0; } static int mixer_override (int dev, int audiodev, unsigned int cmd, int val) { sblive_devc *devc = mixer_devs[dev]->hw_devc; switch (cmd) { case SOUND_MIXER_READ_VOLUME: return sblive_set_gpr (dev, GPR_VOLUME, SNDCTL_MIX_READ, 0); break; case SOUND_MIXER_WRITE_VOLUME: return sblive_set_gpr (dev, GPR_VOLUME, SNDCTL_MIX_WRITE, val); break; case SOUND_MIXER_READ_PCM: if (audiodev >= 0 && audiodev < num_audio_engines) { sblive_portc *portc = NULL; int i; for (i = 0; i < devc->n_audiodevs && portc == NULL; i++) if (devc->portc[i].audiodev == audiodev) portc = &devc->portc[i]; if (portc == NULL) return OSS_EIO; return portc->playvol | (portc->playvol << 8); } return sblive_set_gpr (dev, GPR_PCM, SNDCTL_MIX_READ, 0); break; case SOUND_MIXER_WRITE_PCM: if (audiodev >= 0 && audiodev < num_audio_engines) { sblive_portc *portc = NULL; int i, left, right; for (i = 0; i < devc->n_audiodevs && portc == NULL; i++) if (devc->portc[i].audiodev == audiodev) portc = &devc->portc[i]; if (portc == NULL) return OSS_EIO; left = val & 0xff; right = (val >> 8) & 0xff; if (left < 0) left = 0; if (right < 0) right = 0; if (left > 100) left = 100; if (right > 100) right = 100; if (right > left) left = right; portc->playvol = left; mixer_devs[devc->mixer_dev]->modify_counter++; /* Force update of mixer */ update_output_device (devc, portc); return portc->playvol | (portc->playvol << 8); } return sblive_set_gpr (dev, GPR_PCM, SNDCTL_MIX_WRITE, val); break; } return 0; } static const char *port_names[] = { "front out", "side out", "center/lfe out", "rear out" }; static const __inline__ char * get_port_name (sblive_devc * devc, int n) { int max_names = 3; if (devc->feature_mask & SB_AUDIGY) max_names = 3; if (devc->feature_mask & SB_LIVE) max_names = 2; n = n - 1; if (n > max_names) return "extra out"; return port_names[n]; } static void unload_mpu (sblive_devc * devc) { if (devc == NULL) return; if (devc->feature_mask & SB_AUDIGY) unload_audigyuart (devc); else uart401_disable (&devc->uart401devc); } int oss_sblive_attach (oss_device_t * osdev) { sblive_devc *devc; int i, err; int frontdev = -1, ndevs = 0; int first_dev = -1; unsigned char pci_irq_line, pci_revision /*, pci_latency */ ; unsigned short pci_command, vendor, device; unsigned int pci_ioaddr; unsigned int subvendor; adev_p adev; extern int sblive_devices; int audiodevs_to_create = sblive_devices; char tmp[64]; DDB (cmn_err (CE_CONT, "sblive_attach entered\n")); pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor); pci_read_config_word (osdev, PCI_DEVICE_ID, &device); if (vendor != PCI_VENDOR_ID_CREATIVE) { cmn_err (CE_WARN, "Unrecognized SB live vendor %x\n", vendor); return 0; } if (device != PCI_DEVICE_ID_SBLIVE && device != PCI_DEVICE_ID_AUDIGY && device != PCI_DEVICE_ID_AUDIGY_CARDBUS && device != PCI_DEVICE_ID_AUDIGYVALUE) { cmn_err (CE_WARN, "Unrecognized SB live device %x:%x\n", vendor, device); return 0; } #ifdef AUDIGY_ONLY if (device == PCI_DEVICE_ID_SBLIVE) { cmn_err (CE_CONT, "Error: Due to hardware limitations SB Live is not\n"); cmn_err (CE_CONT, "supported under this hardware architecture.\n"); cmn_err (CE_CONT, "Consider upgrading to SB Audigy which is supported.\n"); return 0; } #endif pci_read_config_dword (osdev, 0x2c, &subvendor); pci_read_config_byte (osdev, PCI_REVISION_ID, &pci_revision); pci_read_config_word (osdev, PCI_COMMAND, &pci_command); pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line); pci_read_config_dword (osdev, PCI_BASE_ADDRESS_0, &pci_ioaddr); pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO; pci_command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY); pci_write_config_word (osdev, PCI_COMMAND, pci_command); if (pci_ioaddr == 0) { cmn_err (CE_WARN, "I/O address not assigned by BIOS.\n"); return 0; } if (pci_irq_line == 0) { cmn_err (CE_WARN, "IRQ not assigned by BIOS.\n"); return 0; } if ((devc = PMALLOC (osdev, sizeof (*devc))) == NULL) { cmn_err (CE_WARN, "Out of memory\n"); return 0; } memset (devc, 0, sizeof (*devc)); devc->osdev = osdev; osdev->devc = devc; MUTEX_INIT (osdev, devc->mutex, MH_DRV); MUTEX_INIT (osdev, devc->low_mutex, MH_DRV + 1); devc->emu_page_shift = 1; /* Default page shift */ devc->card_name = "Generic SB Live!"; devc->subvendor = subvendor; devc->min_audiodevs = 5; /* Audigy supports 7.1 */ if (device == PCI_DEVICE_ID_AUDIGYVALUE) { /* SOLWAY subvendor id is 0x10211103 */ if ((devc->subvendor == 0x10211102) || (devc->subvendor == 0x10211103)) devc->card_name = "SB Audigy4"; else devc->card_name = "SB Audigy2 Value"; devc->feature_mask = SB_AUDIGY | SB_AUDIGY2 | SB_AUDIGY2VAL; } else if (device == PCI_DEVICE_ID_AUDIGY) { if (devc->subvendor >= 0x10021102 && devc->subvendor <= 0x20051102) { devc->card_name = "SB Audigy2"; devc->feature_mask = SB_AUDIGY | SB_AUDIGY2; } else { devc->card_name = "SB Audigy"; devc->feature_mask = SB_AUDIGY; } } else if (device == PCI_DEVICE_ID_AUDIGY_CARDBUS) { if (devc->subvendor >= 0x10021102 && devc->subvendor <= 0x20051102) { devc->card_name = "SB Audigy2 ZS Notebook"; devc->feature_mask = SB_AUDIGY | SB_AUDIGY2; } else { devc->card_name = "SB Audigy"; devc->feature_mask = SB_AUDIGY; } DDB (cmn_err (CE_CONT, "emu10k2 chip rev %d, pcb rev %d\n", pci_revision, sblive_read_reg (devc, 0x5f, 0))); } else { devc->card_name = "SB Live"; devc->feature_mask = SB_LIVE; devc->min_audiodevs = 4; /* Just 5.1 */ } if (audiodevs_to_create < devc->min_audiodevs) audiodevs_to_create = devc->min_audiodevs; if (audiodevs_to_create > MAX_ADEV) audiodevs_to_create = MAX_ADEV; devc->base = MAP_PCI_IOADDR (devc->osdev, 0, pci_ioaddr); devc->base &= ~0x3; devc->gpr = NULL; oss_register_device (osdev, devc->card_name); devc->irq = pci_irq_line; devc->page_map = NULL; devc->vpage_map = NULL; devc->nr_pages = 0; devc->max_pages = 0; devc->max_mem = 0; devc->silent_page = NULL; devc->subvendor = subvendor; devc->passthrough_gpr = -1; if ((err = oss_register_interrupts (devc->osdev, 0, sbliveintr, NULL)) < 0) { cmn_err (CE_WARN, "Can't register interrupt handler, err=%d\n", err); return 0; } devc->mixer_group = -1; devc->extinfo_loaded = 0; devc->autoreset = 1; devc->speaker_mode = SMODE_FRONTREAR;
Init mixer
devc->mixer_dev = ac97_install (&devc->ac97devc, devc->card_name, ac97_read, ac97_write, devc, devc->osdev); if (devc->mixer_dev < 0) { cmn_err (CE_WARN, "Mixer install failed - cannot continue\n"); return 0; } devc->ac97devc.mixer_ext = 0; devc->ac97devc.spdifout_support = 0; devc->ac97devc.spdifin_support = 0; if (ac97_init_ext (devc->mixer_dev, &devc->ac97devc, mixer_ext_init, 100) < 0) { cmn_err (CE_WARN, "Mixer ext install failed\n"); } /* first set the AC97 PCM to max - otherwise sound is too low */ ac97_mixer_set (&devc->ac97devc, SOUND_MIXER_PCM, 100 | (100 << 8)); ac97_remove_control (&devc->ac97devc, BOGUS_MIXER_CONTROLS, 0); ac97_override_control (&devc->ac97devc, SOUND_MIXER_VOLUME, mixer_override, 100 | (100 << 8)); ac97_override_control (&devc->ac97devc, SOUND_MIXER_PCM, mixer_override, 100 | (100 << 8)); attach_mpu (devc);
Audio initialization
init_emu10k1 (devc); for (i = 0; i < audiodevs_to_create; i++) { sblive_portc *portc = &devc->portc[i]; int caps = ADEV_AUTOMODE; int fmts = 0; devc->n_audiodevs = i + 1; portc->memptr = devc->audio_memptr; devc->audio_memptr += (DMABUF_SIZE + 4095) & ~4095; if (devc->audio_memptr > AUDIO_MEMSIZE) { cmn_err (CE_WARN, "Audio memory block exhausted (%d/%d)\n", devc->audio_memptr, AUDIO_MEMSIZE); return OSS_ENOSPC; } if (i == 0) { strcpy (tmp, devc->card_name); sprintf (tmp, "%s main", devc->card_name); caps |= ADEV_DUPLEX; } else { sprintf (tmp, "%s %s", devc->card_name, get_port_name (devc, i)); caps |= ADEV_NOINPUT; #if 0 if (i >= devc->min_audiodevs) caps |= ADEV_HWMIX; #endif if (i >= devc->min_audiodevs + 1) caps |= ADEV_SHADOW; } if ((devc->feature_mask & SB_AUDIGY) && i == audiodevs_to_create - 1) { sprintf (tmp, "%s raw S/PDIF (output only)", devc->card_name); caps &= ~(ADEV_SHADOW /* | ADEV_HWMIX*/); caps |= ADEV_SPECIAL; fmts |= AFMT_AC3; } #if 0 if (devc->feature_mask & SB_AUDIGY) caps |= ADEV_COLD; #endif if ((portc->audiodev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION, devc->osdev, devc->osdev, tmp, &sblive_audio_driver, sizeof (audiodrv_t), caps, fmts | AFMT_U8 | AFMT_S16_LE, devc, -1)) < 0) { portc->audiodev = -1; return (i > 0); } else { int x; adev = audio_engines[portc->audiodev]; adev->nrates=0; for (x = 0; speed_tab[x].speed != 0; x++) adev->rates[adev->nrates++] = speed_tab[x].speed; if (i == 0) first_dev = portc->audiodev; adev->devc = devc; adev->portc = portc; adev->rate_source = first_dev; adev->mixer_dev = devc->mixer_dev; adev->min_rate = 8000; adev->max_rate = 48000; if (!(devc->feature_mask & SB_AUDIGY)) {
SB Live supports only 31 PCI address bits
adev->dmabuf_maxaddr = MEMLIMIT_31BITS; } portc->mode = 0; adev->oformat_mask |= AFMT_AC3; portc->input_type = ITYPE_ANALOG; if ((devc->feature_mask & SB_AUDIGY) && i == audiodevs_to_create - 1) portc->input_type = ITYPE_SPDIF; if (i == 1) frontdev = portc->audiodev; if (i > 0) ndevs++; portc->playvol = 100; portc->playangle = 0; portc->playdist = 50; portc->vu_left = 0; portc->vu_right = 0; portc->audio_active = 0; portc->voice_chn = i * 2; portc->port_number = i; devc->voice_busy[i * 2] = 1; devc->voice_busy[i * 2 + 1] = 1; portc->resetvol = 0; if (devc->feature_mask & SB_LIVE) {
Do not enable vmix by default on Live! It would cause enormous latencies because emu10k1 doesn't have working full/half buffer DMA interrupts.
adev->vmix_flags = VMIX_MULTIFRAG; adev->max_intrate = 50; adev->min_block = 4096; } else { adev->max_fragments = 2; }
Hide vmix main volume control and peak meters if no real HW mixing devices are enabled.
#if 0 if (audiodevs_to_create <= devc->min_audiodevs) adev->vmix_flags |= VMIX_NOMAINVOL; #endif adev->iformat_mask = AFMT_S16_LE; /* No 8 bit recording */ if (i == 0) { if (devc->feature_mask & SB_LIVE) adev->magic = EMU10K1_MAGIC; else adev->magic = EMU10K2_MAGIC; } #ifdef CONFIG_OSS_VMIX if (i == 0) vmix_attach_audiodev(devc->osdev, first_dev, -1, 0); #endif } adev->mixer_dev = devc->mixer_dev; } #ifdef USE_REMUX /* Install Remux (only 5.1 support for the time being) */ sprintf (tmp, "%s 5.1 output device", devc->card_name); if (frontdev > 0 && ndevs >= 3) /* Have enough devices for 5.1 */ remux_install (tmp, devc->osdev, frontdev, frontdev + 1, frontdev + 2, -1); #endif #ifndef NO_EMU10K1_SYNTH sblive_install_synth (devc); #endif touch_mixer (devc->mixer_dev); init_effects (devc); return 1; } int oss_sblive_detach (oss_device_t * osdev) { sblive_devc *devc; if (oss_disable_device (osdev) < 0) return 0; devc = osdev->devc; OUTL (devc->osdev, 0, devc->base + 0x0c); /* Intr enable (all off) */ OUTL (devc->osdev, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, devc->base + 0x14); sblive_write_reg (devc, ADCSR, 0, 0x0); sblive_write_reg (devc, ADCBA, 0, 0x0); sblive_write_reg (devc, ADCBA, 0, 0x0); sblive_write_reg (devc, PTBA, 0, 0); #ifndef NO_EMU10K1_SYNTH sblive_remove_synth (devc); #endif if (devc->page_map != NULL) CONTIG_FREE (devc->osdev, devc->page_map, devc->max_pages * 4, devc->page_map_dma_handle); if (devc->vpage_map != NULL) KERNEL_FREE (devc->vpage_map); if (devc->silent_page != NULL) CONTIG_FREE (devc->osdev, devc->silent_page, 4096, devc->silent_page_dma_handle); devc->max_pages = 0; devc->max_mem = 0; devc->page_map = NULL; devc->vpage_map = NULL; devc->silent_page = NULL; unload_mpu (devc); oss_unregister_interrupts (devc->osdev); MUTEX_CLEANUP (devc->mutex); MUTEX_CLEANUP (devc->low_mutex); UNMAP_PCI_IOADDR (devc->osdev, 0); oss_unregister_device (osdev); return 1; }