/* * Purpose: Driver for Creative SB Live/Audigy/2/4. Audio, MIDI and mixer services. */ /* * * 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) audigyuart_input_loop (devc); /* * 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; }