/* * Purpose: Creative/Ensoniq AudioPCI97 driver (ES1371/ES1373) * * This driver is used with the original Ensoniq AudioPCI97 card and many * PCI based Sound Blaster cards by Creative Technologies. For example * Sound Blaster PCI128 and Creative/Ectiva EV1938. */ /* * * 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_sbpci_cfg.h" #include "midi_core.h" #include "sbpci.h" #include "ac97.h" #include "oss_pci.h" extern int apci_latency; extern int apci_spdif; #define ENSONIQ_VENDOR_ID 0x1274 #define ECTIVA_VENDOR_ID 0x1102 #define ENSONIQ_ES1371 0x1371 #define ENSONIQ_ES5880 0x8001 #define ENSONIQ_ES5880A 0x8002 #define ENSONIQ_ES5880B 0x5880 #define ECTIVA_ES1938 0x8938 #define MAX_PORTC 2 typedef struct apci97_portc { /* Audio parameters */ int audiodev; int open_mode; int trigger_bits; int audio_enabled; int speed, bits, channels; int atype; /* 0=DAC/ADC, 1=Synth */ } apci97_portc; typedef struct apci97_devc { oss_device_t *osdev; oss_mutex_t mutex, low_mutex; oss_native_word base; int irq; char *chip_name; int revision; apci97_portc portc[MAX_PORTC]; /* * Mixer */ ac97_devc ac97devc; /* * MIDI */ int midi_opened; int midi_dev; oss_midi_inputbyte_t midi_input_intr; } apci97_devc; void SRCRegWrite (apci97_devc * devc, unsigned short reg, unsigned short val); void SRCSetRate (apci97_devc * devc, unsigned char base, unsigned short rate); static int ac97_read (void *devc_, int wAddr) { apci97_devc *devc = devc_; int i, dtemp, dinit; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); dtemp = INL (devc->osdev, devc->base + CONC_dCODECCTL_OFF); /* wait for WIP to go away saving the current state for later */ for (i = 0; i < 0x100UL; ++i) if (!(INL (devc->osdev, devc->base + CONC_dCODECCTL_OFF) & (1UL << 30))) break; /* write addr w/data=0 and assert read request ... */ /* save the current state for later */ dinit = INL (devc->osdev, devc->base + CONC_dSRCIO_OFF); /* enable SRC state data in SRC mux */ for (i = 0; i < 0x100UL; ++i) if (! ((dtemp = INL (devc->osdev, devc->base + CONC_dSRCIO_OFF)) & SRC_BUSY)) break; OUTL (devc->osdev, (dtemp & SRC_CTLMASK) | 0x00010000UL, devc->base + CONC_dSRCIO_OFF); /* wait for a SAFE time to write a read request and then do it, dammit */ for (i = 0; i < 0x100UL; ++i) { if ((INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & 0x00070000UL) == 0x00010000UL) break; } OUTL (devc->osdev, ((int) wAddr << 16) | (1UL << 23), devc->base + CONC_dCODECCTL_OFF); /* restore SRC reg */ for (i = 0; i < 0x100UL; ++i) if (! ((dtemp = INL (devc->osdev, devc->base + CONC_dSRCIO_OFF)) & SRC_BUSY)) break; OUTL (devc->osdev, dinit, devc->base + CONC_dSRCIO_OFF); /* now wait for the stinkin' data (RDY) */ for (i = 0; i < 0x100UL; ++i) if (INL (devc->osdev, devc->base + CONC_dCODECCTL_OFF) & (1UL << 31)) break; dtemp = INL (devc->osdev, devc->base + CONC_dCODECCTL_OFF); MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return dtemp & 0xffff; } static int ac97_write (void *devc_, int wAddr, int wData) { apci97_devc *devc = devc_; int i, dtemp, dinit; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); /* wait for WIP to go away */ for (i = 0; i < 0x100UL; ++i) if (!(INL (devc->osdev, devc->base + CONC_dCODECCTL_OFF) & (1UL << 30))) break; /* save the current state for later */ dinit = INL (devc->osdev, devc->base + CONC_dSRCIO_OFF); dtemp = INL (devc->osdev, devc->base + CONC_dSRCIO_OFF); /* enable SRC state data in SRC mux */ for (i = 0; i < 0x100UL; ++i) if (! ((dtemp = INL (devc->osdev, devc->base + CONC_dSRCIO_OFF)) & SRC_BUSY)) break; OUTL (devc->osdev, (dtemp & SRC_CTLMASK) | 0x00010000UL, devc->base + CONC_dSRCIO_OFF); /* wait for a SAFE time to write addr/data and then do it, dammit */ for (i = 0; i < 0x100UL; ++i) { if ((INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & 0x00070000UL) == 0x00010000UL) break; } OUTL (devc->osdev, ((int) wAddr << 16) | wData, devc->base + CONC_dCODECCTL_OFF); /* restore SRC reg */ for (i = 0; i < 0x100UL; ++i) if (!(INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & SRC_BUSY)) break; OUTL (devc->osdev, dinit, devc->base + CONC_dSRCIO_OFF); MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return 0; } void SRCInit (apci97_devc * devc) { int i; /* Clear all SRC RAM then init - keep SRC disabled until done */ for (i = 0; i < SRC_IOPOLL_COUNT; ++i) if (!(INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & SRC_BUSY)) break; OUTL (devc->osdev, SRC_DISABLE, devc->base + CONC_dSRCIO_OFF); for (i = 0; i < 0x80; ++i) SRCRegWrite (devc, (unsigned short) i, 0U); SRCRegWrite (devc, SRC_SYNTH_BASE + SRC_TRUNC_N_OFF, 16 << 4); SRCRegWrite (devc, SRC_SYNTH_BASE + SRC_INT_REGS_OFF, 16 << 10); SRCRegWrite (devc, SRC_DAC_BASE + SRC_TRUNC_N_OFF, 16 << 4); SRCRegWrite (devc, SRC_DAC_BASE + SRC_INT_REGS_OFF, 16 << 10); SRCRegWrite (devc, SRC_SYNTH_VOL_L, 1 << 12); SRCRegWrite (devc, SRC_SYNTH_VOL_R, 1 << 12); SRCRegWrite (devc, SRC_DAC_VOL_L, 1 << 12); SRCRegWrite (devc, SRC_DAC_VOL_R, 1 << 12); SRCRegWrite (devc, SRC_ADC_VOL_L, 1 << 12); SRCRegWrite (devc, SRC_ADC_VOL_R, 1 << 12); /* default some rates */ SRCSetRate (devc, SRC_SYNTH_BASE, 8000); SRCSetRate (devc, SRC_DAC_BASE, 8000); SRCSetRate (devc, SRC_ADC_BASE, 8000); /* now enable the whole deal */ for (i = 0; i < SRC_IOPOLL_COUNT; ++i) if (!(INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & SRC_BUSY)) break; OUTL (devc->osdev, 0, devc->base + CONC_dSRCIO_OFF); return; } unsigned short SRCRegRead (apci97_devc * devc, unsigned short reg) { int i, dtemp; dtemp = INL (devc->osdev, devc->base + CONC_dSRCIO_OFF); /* wait for ready */ for (i = 0; i < SRC_IOPOLL_COUNT; ++i) if (! ((dtemp = INL (devc->osdev, devc->base + CONC_dSRCIO_OFF)) & SRC_BUSY)) break; /* assert a read request */ OUTL (devc->osdev, (dtemp & SRC_CTLMASK) | ((int) reg << 25), devc->base + CONC_dSRCIO_OFF); /* now wait for the data */ for (i = 0; i < SRC_IOPOLL_COUNT; ++i) if (! ((dtemp = INL (devc->osdev, devc->base + CONC_dSRCIO_OFF)) & SRC_BUSY)) break; return (unsigned short) dtemp; } void SRCRegWrite (apci97_devc * devc, unsigned short reg, unsigned short val) { int i, dtemp; int writeval; dtemp = INL (devc->osdev, devc->base + CONC_dSRCIO_OFF); /* wait for ready */ for (i = 0; i < SRC_IOPOLL_COUNT; ++i) if (! ((dtemp = INL (devc->osdev, devc->base + CONC_dSRCIO_OFF)) & SRC_BUSY)) break; /* assert the write request */ writeval = (dtemp & SRC_CTLMASK) | SRC_WENABLE | ((int) reg << 25) | val; OUTL (devc->osdev, writeval, devc->base + CONC_dSRCIO_OFF); return; } typedef struct { unsigned char base; unsigned short rate; } SRC_RATE_REC; #define SRC_RATE_RECS (3) SRC_RATE_REC theSRCRates[SRC_RATE_RECS] = { {SRC_SYNTH_BASE, 0}, {SRC_DAC_BASE, 0}, {SRC_ADC_BASE, 0} }; /*ARGSUSED*/ unsigned short SRCGetRate (apci97_devc * devc, unsigned char base) { unsigned short i; for (i = 0; i < SRC_RATE_RECS; i++) if (theSRCRates[i].base == base) return theSRCRates[i].rate; return 0; } void SRCSetRate (apci97_devc * devc, unsigned char base, unsigned short rate) { int i, freq, dtemp; unsigned short N, truncM, truncStart; for (i = 0; i < SRC_RATE_RECS; i++) if (theSRCRates[i].base == base) { theSRCRates[i].rate = rate; break; } if (base != SRC_ADC_BASE) { /* freeze the channel */ dtemp = base == SRC_SYNTH_BASE ? SRC_SYNTHFREEZE : SRC_DACFREEZE; for (i = 0; i < SRC_IOPOLL_COUNT; ++i) if (!(INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & SRC_BUSY)) break; OUTL (devc->osdev, (INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & SRC_CTLMASK) | dtemp, devc->base + CONC_dSRCIO_OFF); /* calculate new frequency and write it - preserve accum */ freq = ((int) rate << 16) / 3000U; SRCRegWrite (devc, (unsigned short) base + SRC_INT_REGS_OFF, (SRCRegRead (devc, (unsigned short) base + SRC_INT_REGS_OFF) & 0x00ffU) | ((unsigned short) (freq >> 6) & 0xfc00)); SRCRegWrite (devc, (unsigned short) base + SRC_VFREQ_FRAC_OFF, (unsigned short) freq >> 1); /* un-freeze the channel */ for (i = 0; i < SRC_IOPOLL_COUNT; ++i) if (!(INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & SRC_BUSY)) break; OUTL (devc->osdev, (INL (devc->osdev, devc->base + CONC_dSRCIO_OFF) & SRC_CTLMASK) & ~dtemp, devc->base + CONC_dSRCIO_OFF); } else { /* derive oversample ratio */ N = rate / 3000U; if (N == 15 || N == 13 || N == 11 || N == 9) --N; /* truncate the filter and write n/trunc_start */ truncM = (21 * N - 1) | 1; if (rate >= 24000U) { if (truncM > 239) truncM = 239; truncStart = (239 - truncM) >> 1; SRCRegWrite (devc, base + SRC_TRUNC_N_OFF, (truncStart << 9) | (N << 4)); } else { if (truncM > 119) truncM = 119; truncStart = (119 - truncM) >> 1; SRCRegWrite (devc, base + SRC_TRUNC_N_OFF, 0x8000U | (truncStart << 9) | (N << 4)); } /* calculate new frequency and write it - preserve accum */ freq = ((48000UL << 16) / rate) * N; SRCRegWrite (devc, base + SRC_INT_REGS_OFF, (SRCRegRead (devc, (unsigned short) base + SRC_INT_REGS_OFF) & 0x00ff) | ((unsigned short) (freq >> 6) & 0xfc00)); SRCRegWrite (devc, base + SRC_VFREQ_FRAC_OFF, (unsigned short) freq >> 1); SRCRegWrite (devc, SRC_ADC_VOL_L, N << 8); SRCRegWrite (devc, SRC_ADC_VOL_R, N << 8); } return; } static void apci97_writemem (apci97_devc * devc, int page, int offs, int data) { int tmp; tmp = INL (devc->osdev, devc->base + 0xc); OUTL (devc->osdev, page, devc->base + 0xc); /* Select memory page */ OUTL (devc->osdev, data, devc->base + offs); OUTL (devc->osdev, tmp, devc->base + 0xc); /* Select the original memory page */ } static unsigned int apci97_readmem (apci97_devc * devc, int page, int offs) { unsigned int val; OUTL (devc->osdev, page, devc->base + 0xc); /* Select memory page */ val = INL (devc->osdev, devc->base + offs); return val; } static int apci97intr (oss_device_t * osdev) { int stats, i; int tmp; unsigned char ackbits = 0; unsigned char uart_stat; apci97_devc *devc = (apci97_devc *) osdev->devc; apci97_portc *portc; int served = 0; stats = INL (devc->osdev, devc->base + 0x04); /*cmn_err (CE_WARN, "AudioPCI97 intr status %08x\n", stats); */ if (!(stats & 0x80000000)) /* No interrupt pending */ return served; served = 1; for (i = 0; i < MAX_PORTC; i++) { portc = &devc->portc[i]; if (stats & 0x00000010) /* CCB interrupt */ { cmn_err (CE_WARN, "CCB interrupt\n"); } if ((stats & 0x00000004) && (portc->atype)) /* DAC1 (synth) interrupt */ { ackbits |= CONC_SERCTL_DAC1IE; if (portc->trigger_bits & PCM_ENABLE_OUTPUT) oss_audio_outputintr (portc->audiodev, 0); } if ((stats & 0x00000002) && (!portc->atype)) /* DAC2 interrupt */ { ackbits |= CONC_SERCTL_DAC2IE; if (portc->trigger_bits & PCM_ENABLE_OUTPUT) oss_audio_outputintr (portc->audiodev, 0); } if ((stats & 0x00000001) && (!portc->atype)) /* ADC interrupt */ { ackbits |= CONC_SERCTL_ADCIE; if (portc->trigger_bits & PCM_ENABLE_INPUT) { oss_audio_inputintr (portc->audiodev, 0); } } if (stats & 0x00000008) /* UART interrupt */ { uart_stat = INB (devc->osdev, devc->base + CONC_bUARTCSTAT_OFF); while (uart_stat & CONC_UART_RXRDY) { unsigned char d; d = INB (devc->osdev, devc->base + CONC_bUARTDATA_OFF); if (devc->midi_opened & OPEN_READ && devc->midi_input_intr) devc->midi_input_intr (devc->midi_dev, d); uart_stat = INB (devc->osdev, devc->base + CONC_bUARTCSTAT_OFF); } } /* Ack the interrupt */ tmp = INB (devc->osdev, devc->base + CONC_bSERCTL_OFF); OUTB (devc->osdev, (tmp & ~ackbits), devc->base + CONC_bSERCTL_OFF); /* Clear bits */ OUTB (devc->osdev, tmp | ackbits, devc->base + CONC_bSERCTL_OFF); /* Return them back on */ } return served; } /* * Audio routines */ static int apci97_audio_set_rate (int dev, int arg) { apci97_portc *portc = audio_engines[dev]->portc; if (arg == 0) return portc->speed; if (arg > 48000) arg = 48000; if (arg < 5000) arg = 5000; portc->speed = arg; return portc->speed; } static short apci97_audio_set_channels (int dev, short arg) { apci97_portc *portc = audio_engines[dev]->portc; if ((arg != 1) && (arg != 2)) return portc->channels; portc->channels = arg; return portc->channels; } static unsigned int apci97_audio_set_format (int dev, unsigned int arg) { apci97_portc *portc = audio_engines[dev]->portc; if (arg == 0) return portc->bits; if (!(arg & (AFMT_U8 | AFMT_S16_LE | AFMT_AC3))) return portc->bits; portc->bits = arg; return portc->bits; } /*ARGSUSED*/ static int apci97_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg) { return OSS_EINVAL; } static void apci97_audio_trigger (int dev, int state); static void apci97_audio_reset (int dev) { apci97_audio_trigger (dev, 0); } static void apci97_audio_reset_input (int dev) { apci97_portc *portc = audio_engines[dev]->portc; apci97_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT); } static void apci97_audio_reset_output (int dev) { apci97_portc *portc = audio_engines[dev]->portc; apci97_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT); } /*ARGSUSED*/ static int apci97_audio_open (int dev, int mode, int open_flags) { apci97_portc *portc = audio_engines[dev]->portc; apci97_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 apci97_audio_close (int dev, int mode) { apci97_portc *portc = audio_engines[dev]->portc; apci97_audio_reset (dev); portc->open_mode = 0; portc->audio_enabled &= ~mode; } /*ARGSUSED*/ static void apci97_audio_output_block (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { apci97_portc *portc = audio_engines[dev]->portc; portc->audio_enabled |= PCM_ENABLE_OUTPUT; portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; } /*ARGSUSED*/ static void apci97_audio_start_input (int dev, oss_native_word buf, int count, int fragsize, int intrflag) { apci97_portc *portc = audio_engines[dev]->portc; portc->audio_enabled |= PCM_ENABLE_INPUT; portc->trigger_bits &= ~PCM_ENABLE_INPUT; } static void apci97_audio_trigger (int dev, int state) { apci97_devc *devc = audio_engines[dev]->devc; apci97_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)) { if (portc->atype) { tmp = INB (devc->osdev, devc->base + CONC_bDEVCTL_OFF); tmp |= CONC_DEVCTL_DAC1_EN; OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF); } else { tmp = INB (devc->osdev, devc->base + CONC_bDEVCTL_OFF); tmp |= CONC_DEVCTL_DAC2_EN; OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF); } 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; if (portc->atype) { tmp = INB (devc->osdev, devc->base + CONC_bDEVCTL_OFF); tmp &= ~CONC_DEVCTL_DAC1_EN; OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF); tmp = INB (devc->osdev, devc->base + CONC_bSERCTL_OFF); tmp &= ~CONC_SERCTL_DAC1IE; OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF); } else { tmp = INB (devc->osdev, devc->base + CONC_bDEVCTL_OFF); tmp &= ~CONC_DEVCTL_DAC2_EN; OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF); tmp = INB (devc->osdev, devc->base + CONC_bSERCTL_OFF); tmp &= ~CONC_SERCTL_DAC2IE; OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF); } } } } if ((portc->open_mode & OPEN_READ) && !(audio_engines[dev]->flags & ADEV_NOINPUT)) { if (state & PCM_ENABLE_INPUT) { if ((portc->audio_enabled & PCM_ENABLE_INPUT) && !(portc->trigger_bits & PCM_ENABLE_INPUT)) { tmp = INB (devc->osdev, devc->base + CONC_bDEVCTL_OFF); tmp |= CONC_DEVCTL_ADC_EN; OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF); 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; tmp = INB (devc->osdev, devc->base + CONC_bDEVCTL_OFF); tmp &= ~CONC_DEVCTL_ADC_EN; OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF); tmp = INB (devc->osdev, devc->base + CONC_bSERCTL_OFF); tmp &= ~CONC_SERCTL_ADCIE; OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF); } } } MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); } /*ARGSUSED*/ static int apci97_audio_prepare_for_input (int dev, int bsize, int bcount) { dmap_t *dmap = audio_engines[dev]->dmap_in; apci97_devc *devc = audio_engines[dev]->devc; apci97_portc *portc = audio_engines[dev]->portc; int tmp = 0x00; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); /* Set physical address of the DMA buffer */ apci97_writemem (devc, CONC_ADCCTL_PAGE, CONC_dADCPADDR_OFF, dmap->dmabuf_phys); /* Set ADC rate */ SRCSetRate (devc, SRC_ADC_BASE, portc->speed); /* Set format */ tmp = INB (devc->osdev, devc->base + CONC_bSERFMT_OFF); tmp &= ~(CONC_PCM_ADC_STEREO | CONC_PCM_ADC_16BIT); if (portc->channels == 2) tmp |= CONC_PCM_ADC_STEREO; if (portc->bits == 16) { tmp |= CONC_PCM_ADC_16BIT; OUTB (devc->osdev, 0x10, devc->base + CONC_bSKIPC_OFF); /* Skip count register */ } else { OUTB (devc->osdev, 0x08, devc->base + CONC_bSKIPC_OFF); /* Skip count register */ } OUTB (devc->osdev, tmp, devc->base + CONC_bSERFMT_OFF); /* Set the frame count */ apci97_writemem (devc, CONC_ADCCTL_PAGE, CONC_wADCFC_OFF, (dmap->bytes_in_use / 4) - 1); /* Set # of samples between interrupts */ OUTW (devc->osdev, (dmap->fragment_size / ((portc->channels * portc->bits) / 8)) - 1, devc->base + CONC_wADCIC_OFF); /* Enable the wave interrupt */ tmp = INB (devc->osdev, devc->base + CONC_bSERCTL_OFF) & ~CONC_SERCTL_ADCIE; OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF); tmp |= CONC_SERCTL_ADCIE; OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF); portc->audio_enabled &= ~PCM_ENABLE_INPUT; portc->trigger_bits &= ~PCM_ENABLE_INPUT; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } /*ARGSUSED*/ static int apci97_audio_prepare_for_output (int dev, int bsize, int bcount) { dmap_t *dmap = audio_engines[dev]->dmap_out; apci97_devc *devc = audio_engines[dev]->devc; apci97_portc *portc = audio_engines[dev]->portc; int tmp; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->mutex, flags); if (devc->revision >= 4) { /* set SPDIF to PCM mode */ OUTL (devc->osdev, INL (devc->osdev, devc->base + 0x1c) & ~0x2, devc->base + 0x1c); if (portc->bits & AFMT_AC3) { portc->channels = 2; portc->bits = 16; portc->speed = 48000; /* set S/PDIF to AC3 Mode */ OUTL (devc->osdev, INL (devc->osdev, devc->base + 0x1c) | 0x2, devc->base + 0x1c); } } if (portc->atype) { /* Set physical address of the DMA buffer */ apci97_writemem (devc, CONC_SYNCTL_PAGE, CONC_dSYNPADDR_OFF, dmap->dmabuf_phys); /* Set DAC1 rate */ SRCSetRate (devc, SRC_SYNTH_BASE, portc->speed); /* Set format */ tmp = INB (devc->osdev, devc->base + CONC_bSERFMT_OFF); tmp &= ~((CONC_PCM_DAC_STEREO | CONC_PCM_DAC_16BIT) >> 2); if (portc->channels == 2) tmp |= (CONC_PCM_DAC_STEREO >> 2); if (portc->bits == 16) { tmp |= (CONC_PCM_DAC_16BIT >> 2); } OUTB (devc->osdev, tmp, devc->base + CONC_bSERFMT_OFF); /* Set the frame count */ apci97_writemem (devc, CONC_SYNCTL_PAGE, CONC_wSYNFC_OFF, (dmap->bytes_in_use / 4) - 1); /* Set # of samples between interrupts */ OUTW (devc->osdev, (dmap->fragment_size / ((portc->channels * portc->bits) / 8)) - 1, devc->base + CONC_wSYNIC_OFF); /* Enable the wave interrupt */ tmp = INB (devc->osdev, devc->base + CONC_bSERCTL_OFF) & ~CONC_SERCTL_DAC1IE; OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF); tmp |= CONC_SERCTL_DAC1IE; OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF); } else { /* Set physical address of the DMA buffer */ apci97_writemem (devc, CONC_DACCTL_PAGE, CONC_dDACPADDR_OFF, dmap->dmabuf_phys); /* Set DAC rate */ SRCSetRate (devc, SRC_DAC_BASE, portc->speed); /* Set format */ tmp = INB (devc->osdev, devc->base + CONC_bSERFMT_OFF); tmp &= ~(CONC_PCM_DAC_STEREO | CONC_PCM_DAC_16BIT); if (portc->channels == 2) tmp |= CONC_PCM_DAC_STEREO; if (portc->bits == 16) { tmp |= CONC_PCM_DAC_16BIT; OUTB (devc->osdev, 0x10, devc->base + CONC_bSKIPC_OFF); /* Skip count register */ } else { OUTB (devc->osdev, 0x08, devc->base + CONC_bSKIPC_OFF); /* Skip count register */ } OUTB (devc->osdev, tmp, devc->base + CONC_bSERFMT_OFF); /* Set the frame count */ apci97_writemem (devc, CONC_DACCTL_PAGE, CONC_wDACFC_OFF, (dmap->bytes_in_use / 4) - 1); /* Set # of samples between interrupts */ OUTW (devc->osdev, (dmap->fragment_size / ((portc->channels * portc->bits) / 8)) - 1, devc->base + CONC_wDACIC_OFF); /* Enable the wave interrupt */ tmp = INB (devc->osdev, devc->base + CONC_bSERCTL_OFF) & ~CONC_SERCTL_DAC2IE; OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF); tmp |= CONC_SERCTL_DAC2IE; OUTB (devc->osdev, tmp, devc->base + CONC_bSERCTL_OFF); } portc->audio_enabled &= ~PCM_ENABLE_OUTPUT; portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; MUTEX_EXIT_IRQRESTORE (devc->mutex, flags); return 0; } /*ARGSUSED*/ static int apci97_get_buffer_pointer (int dev, dmap_t * dmap, int direction) { apci97_devc *devc = audio_engines[dev]->devc; apci97_portc *portc = audio_engines[dev]->portc; int ptr = 0, port = 0, page = 0; oss_native_word flags; MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags); if (direction == PCM_ENABLE_OUTPUT) { if (portc->atype) { port = CONC_wSYNFC_OFF; page = CONC_SYNCTL_PAGE; } else { port = CONC_wDACFC_OFF; page = CONC_DACCTL_PAGE; } } if (direction == PCM_ENABLE_INPUT) { port = CONC_wADCFC_OFF; page = CONC_ADCCTL_PAGE; } ptr = apci97_readmem (devc, page, port); ptr >>= 16; ptr <<= 2; /* count is in dwords */ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags); return ptr; } audiodrv_t apci97_audio_driver = { apci97_audio_open, apci97_audio_close, apci97_audio_output_block, apci97_audio_start_input, apci97_audio_ioctl, apci97_audio_prepare_for_input, apci97_audio_prepare_for_output, apci97_audio_reset, NULL, NULL, apci97_audio_reset_input, apci97_audio_reset_output, apci97_audio_trigger, apci97_audio_set_rate, apci97_audio_set_format, apci97_audio_set_channels, NULL, NULL, NULL, NULL, NULL, /* apci97_alloc_buffer */ NULL, /* apci97_free_buffer */ NULL, NULL, apci97_get_buffer_pointer }; /*ARGSUSED*/ static int apci97_midi_open (int dev, int mode, oss_midi_inputbyte_t inputbyte, oss_midi_inputbuf_t inputbuf, oss_midi_outputintr_t outputintr) { apci97_devc *devc = (apci97_devc *) midi_devs[dev]->devc; if (devc->midi_opened) { return OSS_EBUSY; } devc->midi_input_intr = inputbyte; devc->midi_opened = mode; if (mode & OPEN_READ) { OUTB (devc->osdev, CONC_UART_RXINTEN, devc->base + CONC_bUARTCSTAT_OFF); } return 0; } /*ARGSUSED*/ static void apci97_midi_close (int dev, int mode) { apci97_devc *devc = (apci97_devc *) midi_devs[dev]->devc; OUTB (devc->osdev, 0x00, devc->base + CONC_bUARTCSTAT_OFF); devc->midi_opened = 0; } static int apci97_midi_out (int dev, unsigned char midi_byte) { apci97_devc *devc = (apci97_devc *) midi_devs[dev]->devc; int i; unsigned char uart_stat = INB (devc->osdev, devc->base + CONC_bUARTCSTAT_OFF); i = 0; while (i < 1000000 && !(uart_stat & CONC_UART_TXRDY)) { uart_stat = INB (devc->osdev, devc->base + CONC_bUARTCSTAT_OFF); i++; } if (!(uart_stat & CONC_UART_TXRDY)) return 0; OUTB (devc->osdev, midi_byte, devc->base + CONC_bUARTDATA_OFF); return 1; } /*ARGSUSED*/ static int apci97_midi_ioctl (int dev, unsigned cmd, ioctl_arg arg) { return OSS_EINVAL; } static midi_driver_t apci97_midi_driver = { apci97_midi_open, apci97_midi_close, apci97_midi_ioctl, apci97_midi_out }; static int apci97_control (int dev, int ctrl, unsigned int cmd, int value) { apci97_devc *devc = mixer_devs[dev]->hw_devc; if (cmd == SNDCTL_MIX_READ) { value = 0; switch (ctrl) { case 1: /* Speaker Mode */ value = (INL (devc->osdev, devc->base + 4) & (1 << 26) ? 1 : 0); break; case 2: /* Dual Dac Mode */ value = INL (devc->osdev, devc->base + 4) & (1 << 27) ? 1 : 0; break; } } if (cmd == SNDCTL_MIX_WRITE) { switch (ctrl) { case 1: /* Front/Rear Mirror */ if (value) { OUTL (devc->osdev, INL (devc->osdev, devc->base + 4) | (1 << 26), devc->base + 4); } else { OUTL (devc->osdev, INL (devc->osdev, devc->base + 4) & ~(1 << 26), devc->base + 4); } break; case 2: /* DAC1->Front DAC2->REAR */ if (value) { /* disable front/rear mirroring */ OUTL (devc->osdev, INL (devc->osdev, devc->base + 4) & ~(1 << 26), devc->base + 4); /* Enable Dual Dac mode */ OUTL (devc->osdev, INL (devc->osdev, devc->base + 4) | (1 << 27) | (1 << 24), devc->base + 4); } else { /* enable mirror */ OUTL (devc->osdev, INL (devc->osdev, devc->base + 4) | (1 << 26), devc->base + 4); /* disable dual dac */ OUTL (devc->osdev, INL (devc->osdev, devc->base + 4) & ~((1 << 27) | (1 << 24)), devc->base + 4); } break; } } return value; } static int apci97_mix_init (int dev) { int group, err; if ((group = mixer_ext_create_group (dev, 0, "MIXEXT")) < 0) return group; if ((err = mixer_ext_create_control (dev, group, 1, apci97_control, MIXT_ENUM, "SPKMODE", 2, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; if ((err = mixer_ext_create_control (dev, group, 2, apci97_control, MIXT_ONOFF, "DUALDAC", 1, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return err; return 0; } static int init_apci97 (apci97_devc * devc, int device_id) { int my_mixer; int tmp, i; int first_dev = 0; if ((device_id == ENSONIQ_ES5880) || (device_id == ENSONIQ_ES5880A) || (device_id == ENSONIQ_ES5880B) || (device_id == 0x1371 && devc->revision == 7) || (device_id == 0x1371 && devc->revision >= 9)) { int i; /* Have a ES5880 so enable the codec manually */ tmp = INB (devc->osdev, devc->base + CONC_bINTSUMM_OFF) & 0xff; tmp |= 0x20; OUTB (devc->osdev, tmp, devc->base + CONC_bINTSUMM_OFF); /* OUTB? */ for (i = 0; i < 2000; i++) oss_udelay (10); } SRCInit (devc); #if 0 OUTB (devc->osdev, 0x00, devc->base + CONC_bSERCTL_OFF); OUTB (devc->osdev, 0x00, devc->base + CONC_bNMIENA_OFF); /* NMI off */ OUTB (devc->osdev, 0x00, devc->base + CONC_wNMISTAT_OFF); /* OUTB? */ #endif /* * Turn on UART and CODEC */ tmp = INL (devc->osdev, devc->base + CONC_bDEVCTL_OFF) & 0xff; tmp &= ~(CONC_DEVCTL_PCICLK_DS | CONC_DEVCTL_XTALCLK_DS); OUTB (devc->osdev, tmp | CONC_DEVCTL_UART_EN | CONC_DEVCTL_JSTICK_EN, devc->base + CONC_bDEVCTL_OFF); OUTB (devc->osdev, 0x00, devc->base + CONC_bUARTCSTAT_OFF); /* Perform AC97 codec warm reset */ tmp = INB (devc->osdev, devc->base + CONC_bMISCCTL_OFF) & 0xff; OUTB (devc->osdev, tmp | CONC_MISCCTL_SYNC_RES, devc->base + CONC_bMISCCTL_OFF); oss_udelay (200); OUTB (devc->osdev, tmp, devc->base + CONC_bMISCCTL_OFF); oss_udelay (200); /* * Enable S/PDIF */ if (devc->revision >= 4) { if (apci_spdif) { /* enable SPDIF */ OUTL (devc->osdev, INL (devc->osdev, devc->base + 0x04) | (1 << 18), devc->base + 0x04); /* SPDIF out = data from DAC */ OUTL (devc->osdev, INL (devc->osdev, devc->base + 0x00) | (1 << 26), devc->base + 0x00); } else { /* disable spdif out */ OUTL (devc->osdev, INL (devc->osdev, devc->base + 0x04) & ~(1 << 18), devc->base + 0x04); OUTL (devc->osdev, INL (devc->osdev, devc->base + 0x00) & ~(1 << 26), devc->base + 0x00); } } /* * Init mixer */ my_mixer = ac97_install (&devc->ac97devc, "AC97 Mixer", ac97_read, ac97_write, devc, devc->osdev); if (my_mixer < 0) return 0; if (devc->revision >= 4) { /* enable 4 speaker mode */ OUTL (devc->osdev, INL (devc->osdev, devc->base + 4) | (1 << 26), devc->base + 4); mixer_ext_set_init_fn (my_mixer, apci97_mix_init, 5); } for (i = 0; i < MAX_PORTC; i++) { int adev; char tmp_name[100]; apci97_portc *portc = &devc->portc[i]; int caps = ADEV_AUTOMODE; int fmts = AFMT_U8 | AFMT_S16_LE; if (devc->revision >= 4) fmts |= AFMT_AC3; if (i == 0) { sprintf (tmp_name, "%s (rev %d)", devc->chip_name, devc->revision); caps |= ADEV_DUPLEX; } else { sprintf (tmp_name, "%s (playback only)", devc->chip_name); caps |= ADEV_NOINPUT; } if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION, devc->osdev, devc->osdev, tmp_name, &apci97_audio_driver, sizeof (audiodrv_t), caps, fmts, devc, -1)) < 0) { adev = -1; return 0; } else { if (i == 0) first_dev = adev; audio_engines[adev]->portc = portc; audio_engines[adev]->rate_source = first_dev; audio_engines[adev]->min_rate = 5000; audio_engines[adev]->max_rate = 48000; audio_engines[adev]->caps |= PCM_CAP_FREERATE; portc->open_mode = 0; portc->audiodev = adev; portc->atype = i; #ifdef CONFIG_OSS_VMIX if (i == 0) vmix_attach_audiodev(devc->osdev, adev, -1, 0); #endif } audio_engines[adev]->mixer_dev = my_mixer; } if ((devc->midi_dev = oss_install_mididev (OSS_MIDI_DRIVER_VERSION, "APCI97", "APCI97 UART", &apci97_midi_driver, sizeof (midi_driver_t), 0, devc, devc->osdev)) < 0) { cmn_err (CE_WARN, "Couldn't install MIDI device\n"); return 0; } devc->midi_opened = 0; return 1; } int oss_sbpci_attach (oss_device_t * osdev) { unsigned char pci_irq_line, pci_revision; unsigned short pci_command, vendor, device; unsigned int pci_ioaddr; apci97_devc *devc; int err; DDB (cmn_err (CE_WARN, "Entered AudioPCI97 probe routine\n")); pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor); pci_read_config_word (osdev, PCI_DEVICE_ID, &device); if ((vendor != ENSONIQ_VENDOR_ID && vendor != ECTIVA_VENDOR_ID) || (device != ENSONIQ_ES1371 && device != ENSONIQ_ES5880 && device != ENSONIQ_ES5880A && device != ECTIVA_ES1938 && device != ENSONIQ_ES5880B)) return 0; 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); if (pci_irq_line == 0) { cmn_err (CE_WARN, "IRQ not assigned by BIOS (%d). Can't continue\n", pci_irq_line); return 0; } if (pci_ioaddr == 0) { cmn_err (CE_WARN, "I/O address 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; switch (device) { case ENSONIQ_ES1371: devc->chip_name = "Creative AudioPCI97 (ES1371)"; break; case ECTIVA_ES1938: devc->chip_name = "Ectiva AudioPCI"; break; case ENSONIQ_ES5880: case ENSONIQ_ES5880A: case ENSONIQ_ES5880B: devc->chip_name = "Sound Blaster PCI128"; break; default: devc->chip_name = "AudioPCI97"; } devc->base = MAP_PCI_IOADDR (devc->osdev, 0, pci_ioaddr); /* Remove I/O space marker in bit 0. */ devc->base &= ~3; pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO; pci_write_config_word (osdev, PCI_COMMAND, pci_command); /* set the PCI latency to 32 */ if ((apci_latency == 32) || (apci_latency == 64) || (apci_latency == 96) || (apci_latency == 128)) pci_write_config_byte (osdev, 0x0d, apci_latency); oss_register_device (osdev, devc->chip_name); if ((err = oss_register_interrupts (osdev, 0, apci97intr, NULL)) < 0) { cmn_err (CE_WARN, "Can't allocate IRQ%d, err=%d\n", pci_irq_line, err); return 0; } MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV); MUTEX_INIT (devc->osdev, devc->low_mutex, MH_DRV + 1); devc->revision = pci_revision; return init_apci97 (devc, device); /* Detected */ } int oss_sbpci_detach (oss_device_t * osdev) { apci97_devc *devc = (apci97_devc *) osdev->devc; int tmp; if (oss_disable_device (osdev) < 0) return 0; tmp = INB (devc->osdev, devc->base + CONC_bDEVCTL_OFF) & ~(CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_DAC1_EN); OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF); OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF); OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF); OUTB (devc->osdev, tmp, devc->base + CONC_bDEVCTL_OFF); oss_unregister_interrupts (devc->osdev); MUTEX_CLEANUP (devc->mutex); MUTEX_CLEANUP (devc->low_mutex); UNMAP_PCI_IOADDR (devc->osdev, 0); oss_unregister_device (devc->osdev); return 1; }