Open Sound System |
Do you have problems with sound/audio application development? Don't panic! Click here for help! |
This device is usually called as SB Live! 5.1 and it has been used in some Dell machines. However it has nothing to do with the original SB Live! design.
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_emu10k1x_cfg.h" #include "oss_pci.h" #include "ac97.h" #include "midi_core.h" #include "remux.h" #define PCI_VENDOR_ID_CREATIVE 0x1102 #define PCI_DEVICE_ID_CREATIVE_EMU10K1X 0x0006 #define USE_DUALBUF
Indirect registers
#define PTBA 0x000 #define PTBS 0x001 #define PTCA 0x002 #define PFBA 0x004 #define PFBS 0x005 #define CPFA 0x006 #define PFEA 0x007 #define CPCAV 0x008 #define RFBA 0x010 #define RFBS 0x011 #define CRFA 0x012 #define CRCAV 0x013 #define CDL 0x020 #define CDR 0x030 #define SA 0x040 #define EA_aux 0x041 #define SCS0 0x042 #define SCS1 0x043 #define SCS2 0x044 #define SPC 0x045 #define WMARK 0x046 #define MUDAT 0x047 #define MUCMD 0x048 #define RCD 0x050
#define INTR_RFF (1<<19) #define INTR_RFH (1<<16) #define INTR_PFF (1<<11) #define INTR_PFH (1<<8) #define INTR_EAI (1<<29) #define INTR_PCI 1 #define INTR_UART_RX 2 #define INTR_UART_TX 4 #define INTR_AC97 0x10 #define INTR_GPIO 0x40 #define PLAY_INTR_ENABLE (INTR_PFF|INTR_PFH) #define RECORD_INTR_ENABLE (INTR_RFF|INTR_RFH) #define EMU_BUFSIZE 32*1024 #define MAX_PORTC 3 extern int emu10k1x_spdif_enable; typedef struct { int audio_dev; int port_number; int open_mode; int trigger_bits; int audio_enabled; int channels; int fmt; int speed; unsigned char *playbuf, *recbuf; oss_native_word playbuf_phys, recbuf_phys; oss_dma_handle_t playbuf_dma_handle, recbuf_dma_handle; int play_cfrag, play_chalf, rec_cfrag, rec_chalf; } emu10k1x_portc; typedef struct { oss_device_t *osdev; int loaded; oss_native_word base; oss_mutex_t mutex; oss_mutex_t low_mutex; int irq; char *card_name; unsigned int subvendor; int mixer_dev; ac97_devc ac97devc;
UART
oss_midi_inputbyte_t midi_input_intr; int midi_opened, midi_disabled; volatile unsigned char input_byte; int midi_dev; int mpu_attached; emu10k1x_portc portc[MAX_PORTC]; } emu10k1x_devc; static void emu10k1xuartintr (emu10k1x_devc * devc); static unsigned int read_reg (emu10k1x_devc * devc, int reg, int chn) { oss_native_word flags; unsigned int val; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); OUTL (devc->osdev, (reg << 16) | (chn & 0xffff), devc->base + 0x00); /* Pointer */ val = INL (devc->osdev, devc->base + 0x04); /* Data */ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); /* printk("Read reg %03x (ch %d) = %08x\n", reg, chn, val); */ return val; } static void write_reg (emu10k1x_devc * devc, int reg, int chn, unsigned int value) { oss_native_word flags; /* printk("Write reg %03x (ch %d) = %08x\n", reg, chn, value); */ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); OUTL (devc->osdev, (reg << 16) | (chn & 0xffff), devc->base + 0x00); /* Pointer */ OUTL (devc->osdev, value, devc->base + 0x04); /* Data */ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); #if 0 { char tmp[100]; int ok = 1; sprintf (tmp, "@w%d %04x/%s %x", chn, reg, emu_regname (reg, &ok), value); if (ok) oss_do_timing (tmp); } #endif } static void recording_intr (emu10k1x_devc * devc, emu10k1x_portc * portc, int status) { #ifdef USE_DUALBUF unsigned char *frombuf, *tobuf; dmap_p dmap = audio_engines[portc->audio_dev]->dmap_in; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); /* "Auto sync" the play half counters with the device */ if (status & INTR_RFH) /* 1st half completed */ portc->rec_chalf = 0; /* Reuse the first half */ else portc->rec_chalf = 1; /* Reuse the second half */ tobuf = dmap->dmabuf + (portc->rec_cfrag * dmap->fragment_size); frombuf = portc->recbuf + (portc->rec_chalf * dmap->fragment_size); memcpy (tobuf, frombuf, dmap->fragment_size); /* printk("rec %d/%d\n", portc->rec_cfrag, portc->rec_chalf); */ portc->rec_cfrag = (portc->rec_cfrag + 1) % dmap->nfrags; portc->rec_chalf = !portc->rec_chalf; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); #endif oss_audio_inputintr (portc->audio_dev, 0); } static void playback_intr (emu10k1x_devc * devc, emu10k1x_portc * portc, unsigned int status) { #ifdef USE_DUALBUF dmap_p dmap = audio_engines[portc->audio_dev]->dmap_out; unsigned char *frombuf, *tobuf; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); /* "Auto sync" the play half counters with the device */ if (status & (INTR_PFH << portc->port_number)) /* 1st half completed */ { portc->play_chalf = 0; /* Reuse the first half */ } else { portc->play_chalf = 1; /* Reuse the second half */ } frombuf = dmap->dmabuf + (portc->play_cfrag * dmap->fragment_size); tobuf = portc->playbuf + (portc->play_chalf * dmap->fragment_size); memcpy (tobuf, frombuf, dmap->fragment_size); /* printk("play %d/%d\n", portc->play_cfrag, portc->play_chalf); */ portc->play_cfrag = (portc->play_cfrag + 1) % dmap->nfrags; portc->play_chalf = !portc->play_chalf; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); #endif oss_audio_outputintr (portc->audio_dev, 0); } static int emu10k1xintr (oss_device_t * osdev) { int serviced = 0; unsigned int status; emu10k1x_devc *devc = (emu10k1x_devc *) osdev->devc; int portnum; status = INL (devc->osdev, devc->base + 0x08); if (status & 0x2) /* MIDI RX interrupt */ { emu10k1xuartintr (devc); serviced = 1; } if (status & (INTR_PFF | INTR_PFH | INTR_RFF | INTR_RFH)) { for (portnum = 0; portnum < 3; portnum++) { emu10k1x_portc *portc = &devc->portc[portnum]; if ((portc->trigger_bits & PCM_ENABLE_OUTPUT) && (status & ((INTR_PFF | INTR_PFH) << portc->port_number))) playback_intr (devc, portc, status); if ((portc->trigger_bits & PCM_ENABLE_INPUT) && (status & (INTR_RFF | INTR_RFH))) recording_intr (devc, portc, status); } serviced = 1; OUTL (devc->osdev, status, devc->base + 0x08); /* Acknowledge */ } return serviced; } /*ARGSUSED*/ static int emu10k1x_set_rate (int dev, int arg) { emu10k1x_portc *portc = audio_engines[dev]->portc; return portc->speed = 48000; } static short emu10k1x_set_channels (int dev, short arg) { emu10k1x_portc *portc = audio_engines[dev]->portc; if (arg == 0) return portc->channels; if (portc->open_mode & OPEN_READ) return portc->channels = 2; if (arg != 1 && arg != 2) return portc->channels = 2; return portc->channels = arg; } static unsigned int emu10k1x_set_format (int dev, unsigned int arg) { emu10k1x_portc *portc = audio_engines[dev]->portc; if (arg == 0) return portc->fmt; if (arg == AFMT_AC3) if ((portc->open_mode & OPEN_READ) || !emu10k1x_spdif_enable) arg = AFMT_S16_LE; if (arg != AFMT_AC3 && arg != AFMT_S16_LE) return portc->fmt = AFMT_S16_LE; return portc->fmt = arg; } /*ARGSUSED*/ static int emu10k1x_ioctl (int dev, unsigned int cmd, ioctl_arg arg) { return OSS_EINVAL; } static void emu10k1x_trigger (int dev, int state); static void emu10k1x_reset (int dev) { emu10k1x_trigger (dev, 0); } static void emu10k1x_reset_input (int dev) { emu10k1x_portc *portc = audio_engines[dev]->portc; emu10k1x_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT); } static void emu10k1x_reset_output (int dev) { emu10k1x_portc *portc = audio_engines[dev]->portc; emu10k1x_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT); } /*ARGSUSED*/ static int emu10k1x_open (int dev, int mode, int open_flags) { emu10k1x_portc *portc = audio_engines[dev]->portc; emu10k1x_devc *devc = audio_engines[dev]->devc; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); if (portc->open_mode) { MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return OSS_EBUSY; } portc->open_mode = mode; portc->audio_enabled = ~mode; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } static void emu10k1x_close (int dev, int mode) { emu10k1x_portc *portc = audio_engines[dev]->portc; emu10k1x_trigger (dev, 0); portc->open_mode = 0; portc->audio_enabled &= ~mode; } /*ARGSUSED*/ static void emu10k1x_output_block (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { } /*ARGSUSED*/ static void emu10k1x_start_input (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { emu10k1x_portc *portc = audio_engines[dev]->portc; portc->audio_enabled |= PCM_ENABLE_INPUT; portc->trigger_bits &= ~PCM_ENABLE_INPUT; } static void emu10k1x_trigger (int dev, int state) { emu10k1x_devc *devc = audio_engines[dev]->devc; emu10k1x_portc *portc = audio_engines[dev]->portc; int tmp; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); if (portc->open_mode & OPEN_WRITE) { if (state & PCM_ENABLE_OUTPUT) { if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) && !(portc->trigger_bits & PCM_ENABLE_OUTPUT)) { /* Enable play channel and set mono/stereo mode */ tmp = read_reg (devc, SA, 0); tmp &= ~(0x10000 << portc->port_number); if (portc->channels == 1) tmp |= (0x10000 << portc->port_number); tmp |= 1 << portc->port_number; write_reg (devc, SA, 0, tmp); portc->trigger_bits |= PCM_ENABLE_OUTPUT; } } else { if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) && (portc->trigger_bits & PCM_ENABLE_OUTPUT)) { portc->audio_enabled &= ~PCM_ENABLE_OUTPUT; portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; /* Disable Play channel */ tmp = read_reg (devc, SA, 0); tmp &= ~(1 << portc->port_number); write_reg (devc, SA, 0, tmp); } } } if (portc->open_mode & OPEN_READ) { if (state & PCM_ENABLE_INPUT) { if ((portc->audio_enabled & PCM_ENABLE_INPUT) && !(portc->trigger_bits & PCM_ENABLE_INPUT)) { /* Enable Rec Channel */ tmp = read_reg (devc, SA, 0); tmp |= 0x100; write_reg (devc, SA, 0, tmp); portc->trigger_bits |= PCM_ENABLE_INPUT; } } else { if ((portc->audio_enabled & PCM_ENABLE_INPUT) && (portc->trigger_bits & PCM_ENABLE_INPUT)) { portc->audio_enabled &= ~PCM_ENABLE_INPUT; portc->trigger_bits &= ~PCM_ENABLE_INPUT; /* disable channel */ tmp = read_reg (devc, SA, 0); tmp &= ~0x100; write_reg (devc, SA, 0, tmp); } } } MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); } /*ARGSUSED*/ static int emu10k1x_prepare_for_input (int dev, int bsize, int bcount) { emu10k1x_devc *devc = audio_engines[dev]->devc; emu10k1x_portc *portc = audio_engines[dev]->portc; dmap_p dmap = audio_engines[dev]->dmap_in; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); #ifndef USE_DUALBUF /* Single buffering mode */ dmap->nfrags = 2; dmap->bytes_in_use = dmap->nfrags * dmap->fragment_size; write_reg (devc, RFBA, 0, dmap->dmabuf_phys); write_reg (devc, RFBS, 0, (dmap->bytes_in_use - 4) << 16); #else write_reg (devc, RFBA, 0, portc->recbuf_phys); write_reg (devc, RFBS, 0, (dmap->fragment_size * 2) << 16); #endif memset (portc->recbuf, 0, EMU_BUFSIZE); portc->rec_cfrag = portc->rec_chalf = 0; portc->audio_enabled &= ~PCM_ENABLE_INPUT; portc->trigger_bits &= ~PCM_ENABLE_INPUT; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } /*ARGSUSED*/ static int emu10k1x_prepare_for_output (int dev, int bsize, int bcount) { emu10k1x_devc *devc = audio_engines[dev]->devc; emu10k1x_portc *portc = audio_engines[dev]->portc; dmap_p dmap = audio_engines[dev]->dmap_out; unsigned int tmp; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); if (portc->fmt == AFMT_AC3) portc->channels = 2; write_reg (devc, PTBA, portc->port_number, 0); write_reg (devc, PTBS, portc->port_number, 0); write_reg (devc, PTCA, portc->port_number, 0); write_reg (devc, CPFA, portc->port_number, 0); write_reg (devc, PFEA, portc->port_number, 0); write_reg (devc, CPCAV, portc->port_number, 0); #ifndef USE_DUALBUF /* Single buffering mode */ dmap->nfrags = 2; dmap->bytes_in_use = dmap->nfrags * dmap->fragment_size; write_reg (devc, PFBA, portc->port_number, dmap->dmabuf_phys); write_reg (devc, PFBS, portc->port_number, (dmap->bytes_in_use - 4) << 16); #else /* Dual buffering mode */ write_reg (devc, PFBA, portc->port_number, portc->playbuf_phys); write_reg (devc, PFBS, portc->port_number, (dmap->fragment_size * 2) << 16); #endif memset (portc->playbuf, 0, EMU_BUFSIZE); portc->play_cfrag = portc->play_chalf = 0; if (portc->fmt == AFMT_AC3) { tmp = read_reg (devc, EA_aux, 0); tmp &= ~(0x03 << (portc->port_number * 2)); if (portc->port_number == 2) tmp &= ~0x10000; write_reg (devc, EA_aux, 0, tmp); write_reg (devc, SCS0 + portc->port_number, 0, 0x02108506); /* Data */ } else { tmp = read_reg (devc, EA_aux, 0); tmp |= (0x03 << (portc->port_number * 2)); if (emu10k1x_spdif_enable == 0) if (portc->port_number == 2) tmp |= 0x10000; write_reg (devc, EA_aux, 0, tmp); write_reg (devc, SCS0 + portc->port_number, 0, 0x02108504); /* Audio */ } portc->audio_enabled |= PCM_ENABLE_OUTPUT; portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } static int emu10k1x_alloc_buffer (int dev, dmap_t * dmap, int direction) { int err; emu10k1x_devc *devc = audio_engines[dev]->devc; emu10k1x_portc *portc = audio_engines[dev]->portc; oss_native_word phaddr; if ((err = oss_alloc_dmabuf (dev, dmap, direction)) < 0) return err; if (direction == OPEN_READ) { if (portc->port_number == 0) { portc->recbuf = CONTIG_MALLOC (devc->osdev, EMU_BUFSIZE, MEMLIMIT_32BITS, &phaddr, portc->recbuf_dma_handle); if (portc->recbuf == NULL) return OSS_ENOMEM; portc->recbuf_phys = phaddr; } } else { portc->playbuf = CONTIG_MALLOC (devc->osdev, EMU_BUFSIZE, MEMLIMIT_32BITS, &phaddr, portc->playbuf_dma_handle); if (portc->playbuf == NULL) return OSS_ENOMEM; portc->playbuf_phys = phaddr; } return 0; } /*ARGSUSED*/ static int emu10k1x_free_buffer (int dev, dmap_t * dmap, int direction) { emu10k1x_devc *devc = audio_engines[dev]->devc; emu10k1x_portc *portc = audio_engines[dev]->portc; oss_free_dmabuf (dev, dmap); if (portc->playbuf != NULL) { CONTIG_FREE (devc->osdev, portc->playbuf, EMU_BUFSIZE, portc->playbuf_dma_handle); portc->playbuf = NULL; } if (portc->recbuf != NULL) { CONTIG_FREE (devc->osdev, portc->recbuf, EMU_BUFSIZE, portc->recbuf_dma_handle); portc->recbuf = NULL; } return 0; } #if 0 static int emu10k1x_get_buffer_pointer (int dev, dmap_t * dmap, int direction) { unsigned int p = 0; emu10k1x_devc *devc = audio_engines[dev]->devc; emu10k1x_portc *portc = audio_engines[dev]->portc; dmap = audio_engines[dev]->dmap_out; if (direction == PCM_ENABLE_OUTPUT) p = read_reg (devc, CPFA, portc->port_number); if (direction == PCM_ENABLE_INPUT) p = read_reg (devc, CRFA, portc->port_number); p %= (dmap->bytes_in_use - 4); return p; } #endif static audiodrv_t emu10k1x_audio_driver = { emu10k1x_open, emu10k1x_close, emu10k1x_output_block, emu10k1x_start_input, emu10k1x_ioctl, emu10k1x_prepare_for_input, emu10k1x_prepare_for_output, emu10k1x_reset, NULL, NULL, emu10k1x_reset_input, emu10k1x_reset_output, emu10k1x_trigger, emu10k1x_set_rate, emu10k1x_set_format, emu10k1x_set_channels, NULL, NULL, NULL, NULL, emu10k1x_alloc_buffer, emu10k1x_free_buffer, NULL, NULL, NULL /*emu10k1x_get_buffer_pointer */ }; #define MUADAT 0x47 #define MUACMD 0x48 #define MUASTAT 0x48 static __inline__ int emu10k1xuart_status (emu10k1x_devc * devc) { return read_reg (devc, MUASTAT, 0); } #define input_avail(devc) (!(emu10k1xuart_status(devc)&INPUT_AVAIL)) #define output_ready(devc) (!(emu10k1xuart_status(devc)&OUTPUT_READY)) static void emu10k1xuart_cmd (emu10k1x_devc * devc, unsigned char cmd) { write_reg (devc, MUACMD, 0, cmd); } static __inline__ int emu10k1xuart_read (emu10k1x_devc * devc) { return read_reg (devc, MUADAT, 0); } static __inline__ void emu10k1xuart_write (emu10k1x_devc * devc, unsigned char byte) { 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_emu10k1xuart (emu10k1x_devc * devc); static void enter_uart_mode (emu10k1x_devc * devc); static void emu10k1xuart_input_loop (emu10k1x_devc * devc) { while (input_avail (devc)) { unsigned char c = emu10k1xuart_read (devc); 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 emu10k1xuartintr (emu10k1x_devc * devc) { emu10k1xuart_input_loop (devc); } /*ARGSUSED*/ static int emu10k1xuart_open (int dev, int mode, oss_midi_inputbyte_t inputbyte, oss_midi_inputbuf_t inputbuf, oss_midi_outputintr_t outputintr) { emu10k1x_devc *devc = (emu10k1x_devc *) midi_devs[dev]->devc; if (devc->midi_opened) { return OSS_EBUSY; } while (input_avail (devc)) emu10k1xuart_read (devc); devc->midi_input_intr = inputbyte; devc->midi_opened = mode; enter_uart_mode (devc); devc->midi_disabled = 0; return 0; } /*ARGSUSED*/ static void emu10k1xuart_close (int dev, int mode) { emu10k1x_devc *devc = (emu10k1x_devc *) midi_devs[dev]->devc; reset_emu10k1xuart (devc); oss_udelay (10); enter_uart_mode (devc); reset_emu10k1xuart (devc); devc->midi_opened = 0; } static int emu10k1xuart_out (int dev, unsigned char midi_byte) { int timeout; emu10k1x_devc *devc = (emu10k1x_devc *) midi_devs[dev]->devc; oss_native_word flags;
Test for input since pending input seems to block the output.
MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); if (input_avail (devc)) emu10k1xuart_input_loop (devc); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
Sometimes it takes about 130000 loops before the output becomes ready (After reset). Normally it takes just about 10 loops.
for (timeout = 130000; timeout > 0 && !output_ready (devc); timeout--); if (!output_ready (devc)) { cmn_err (CE_WARN, "UART timeout - Device not responding\n"); devc->midi_disabled = 1; reset_emu10k1xuart (devc); enter_uart_mode (devc); return 1; } emu10k1xuart_write (devc, midi_byte); return 1; } /*ARGSUSED*/ static int emu10k1xuart_ioctl (int dev, unsigned cmd, ioctl_arg arg) { return OSS_EINVAL; } static midi_driver_t emu10k1x_midi_driver = { emu10k1xuart_open, emu10k1xuart_close, emu10k1xuart_ioctl, emu10k1xuart_out }; static void enter_uart_mode (emu10k1x_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; emu10k1xuart_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 (emu10k1xuart_read (devc) == MPU_ACK) ok = 1; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); } void attach_emu10k1xuart (emu10k1x_devc * devc) { enter_uart_mode (devc); devc->midi_dev = oss_install_mididev (OSS_MIDI_DRIVER_VERSION, "EMU10K1X", "SB P16X UART", &emu10k1x_midi_driver, sizeof (midi_driver_t), 0, devc, devc->osdev); devc->midi_opened = 0; } static int reset_emu10k1xuart (emu10k1x_devc * devc) { int ok, timeout, n;
Send the RESET command. Try again if no success at the first time.
ok = 0; for (n = 0; n < 2 && !ok; n++) { for (timeout = 30000; timeout > 0 && !output_ready (devc); timeout--); devc->input_byte = 0; emu10k1xuart_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 (emu10k1xuart_read (devc) == MPU_ACK) ok = 1; } if (ok)
Flush input before enabling interrupts
return ok; } int probe_emu10k1xuart (emu10k1x_devc * devc) { int ok = 0; oss_native_word flags; DDB (cmn_err (CE_CONT, "Entered probe_emu10k1xuart\n")); devc->midi_input_intr = NULL; devc->midi_opened = 0; devc->input_byte = 0; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); ok = reset_emu10k1xuart (devc); MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); if (ok) { DDB (cmn_err (CE_CONT, "Reset UART401 OK\n")); } else { DDB (cmn_err (CE_WARN, "Reset UART401 failed (no hardware present?).\n")); DDB (cmn_err (CE_WARN, "mpu401 status %02x\n", emu10k1xuart_status (devc))); } DDB (cmn_err (CE_WARN, "emu10k1xuart detected OK\n")); return ok; } void unload_emu10k1xuart (emu10k1x_devc * devc) { reset_emu10k1xuart (devc); } static void attach_mpu (emu10k1x_devc * devc) { devc->mpu_attached = 1; attach_emu10k1xuart (devc); } static int emu10k1x_ac97_read (void *devc_, int wAddr) { emu10k1x_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; dtemp = INW (devc->osdev, devc->base + 0x1c); MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return dtemp & 0xffff; } static int emu10k1x_ac97_write (void *devc_, int wAddr, int wData) { emu10k1x_devc *devc = devc_; int 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; OUTW (devc->osdev, wData, devc->base + 0x1c); MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return 0; } static const int bindings[MAX_PORTC] = { DSP_BIND_FRONT, DSP_BIND_SURR, DSP_BIND_CENTER_LFE }; static void install_audio_devices (emu10k1x_devc * devc) { int i; unsigned int tmp; int firstdev = -1; char name[64]; #if 0 if (emu10k1x_spdif_enable == 1) n = 2; #endif /* Enable play interrupts for all 3 channels */ for (i = 0; i < MAX_PORTC; i++) { tmp = INL (devc->osdev, devc->base + 0x0c); tmp |= PLAY_INTR_ENABLE << i; OUTL (devc->osdev, tmp, devc->base + 0x0c); } /* Enable record interrupts */ tmp = INL (devc->osdev, devc->base + 0x0c); tmp |= RECORD_INTR_ENABLE; OUTL (devc->osdev, tmp, devc->base + 0x0c); for (i = 0; i < MAX_PORTC; i++) { int adev, flags; emu10k1x_portc *portc = &devc->portc[i]; flags = ADEV_AUTOMODE | ADEV_FIXEDRATE | ADEV_16BITONLY | ADEV_COLD; switch (i) { case 0: sprintf (name, "%s (front)", devc->card_name); break; case 1: sprintf (name, "%s (surround)", devc->card_name); break; case 2: if (emu10k1x_spdif_enable == 1) sprintf (name, "%s (SPDIF)", devc->card_name); else sprintf (name, "%s (center/LFE)", devc->card_name); break; } if (i == 0) flags |= ADEV_DUPLEX; else flags |= ADEV_NOINPUT; if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION, devc->osdev, devc->osdev, name, &emu10k1x_audio_driver, sizeof (audiodrv_t), flags, AFMT_S16_LE | AFMT_AC3, devc, -1)) < 0) { return; } if (i == 0) firstdev = adev; audio_engines[adev]->portc = portc; audio_engines[adev]->mixer_dev = devc->mixer_dev; audio_engines[adev]->rate_source = firstdev; audio_engines[adev]->min_rate = 48000; audio_engines[adev]->max_rate = 48000; audio_engines[adev]->caps |= PCM_CAP_FREERATE; /*audio_engines[adev]->max_block = EMU_BUFSIZE / 2; *//* Never change this */ audio_engines[adev]->fixed_rate = 48000; audio_engines[adev]->binding = bindings[i]; audio_engines[adev]->vmix_flags = VMIX_MULTIFRAG; portc->audio_dev = adev; portc->open_mode = 0; portc->port_number = i; portc->channels = 2; portc->fmt = AFMT_S16_LE; #ifdef CONFIG_OSS_VMIX if (i == 0) vmix_attach_audiodev(devc->osdev, adev, -1, 0); #endif } #ifdef USE_REMUX if (firstdev >= 0) { if (emu10k1x_spdif_enable == 1) { sprintf (name, "%s 4.0 output", devc->card_name); remux_install (name, devc->osdev, firstdev, firstdev + 1, -1, -1); } else { sprintf (name, "%s 5.1 output", devc->card_name); remux_install (name, devc->osdev, firstdev, firstdev + 1, firstdev + 2, -1); } } #endif } static void select_out3_mode (emu10k1x_devc * devc, int mode) {
Set the out3/spdif combo jack format. mode0=analog rear/center, 1=spdif
if (mode == 0) { write_reg (devc, SPC, 0, 0x00000700); write_reg (devc, EA_aux, 0, 0x0001000f); } else { write_reg (devc, SPC, 0, 0x00000000); write_reg (devc, EA_aux, 0, 0x0000070f); } } int oss_emu10k1x_attach (oss_device_t * osdev) { unsigned char pci_irq_line, pci_revision; unsigned short pci_command, vendor, device; unsigned int pci_ioaddr; unsigned int subvendor; int err; emu10k1x_devc *devc = NULL; DDB (cmn_err (CE_WARN, "Entered EMU10K1X probe routine\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 || device != PCI_DEVICE_ID_CREATIVE_EMU10K1X) return 0; 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; } devc->osdev = osdev; osdev->devc = devc; devc->card_name = "Sound Blaster Live (P16X)"; devc->subvendor = subvendor; devc->base = MAP_PCI_IOADDR (devc->osdev, 0, pci_ioaddr); devc->base &= ~0x3; devc->irq = pci_irq_line; MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV); MUTEX_INIT (devc->osdev, devc->low_mutex, MH_DRV + 1); oss_register_device (osdev, devc->card_name); if ((err = oss_register_interrupts (devc->osdev, 0, emu10k1xintr, NULL)) < 0) { cmn_err (CE_WARN, "Can't register interrupt handler, err=%d\n", err); return 0; }
Init mixer
devc->mixer_dev = ac97_install (&devc->ac97devc, devc->card_name, emu10k1x_ac97_read, emu10k1x_ac97_write, devc, devc->osdev); if (devc->mixer_dev < 0) { cmn_err (CE_WARN, "Mixer install failed - cannot continue\n"); return 0; } write_reg (devc, SCS0, 0, 0x02108504); write_reg (devc, SCS1, 0, 0x02108504); write_reg (devc, SCS2, 0, 0x02108504); select_out3_mode (devc, emu10k1x_spdif_enable); OUTL (devc->osdev, 0x00000000, devc->base + 0x18); /* GPIO */ OUTL (devc->osdev, INTR_PCI | INTR_UART_RX, devc->base + 0x0c); OUTL (devc->osdev, 0x00000009, devc->base + 0x14); /* Enable audio */ install_audio_devices (devc); attach_mpu (devc); return 1; } static void unload_mpu (emu10k1x_devc * devc) { if (devc->mpu_attached) { unload_emu10k1xuart (devc); devc->mpu_attached = 0; } } int oss_emu10k1x_detach (oss_device_t * osdev) { emu10k1x_devc *devc = (emu10k1x_devc *) osdev->devc; if (oss_disable_device (osdev) < 0) return 0; write_reg (devc, SA, 0, 0); OUTL (devc->osdev, 0x00000000, devc->base + 0x0c); /* Interrupt disable */ OUTL (devc->osdev, 0x00000001, devc->base + 0x14); 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; }