diff options
Diffstat (limited to 'attic/drv/oss_maestro/oss_maestro.c')
-rw-r--r-- | attic/drv/oss_maestro/oss_maestro.c | 2397 |
1 files changed, 2397 insertions, 0 deletions
diff --git a/attic/drv/oss_maestro/oss_maestro.c b/attic/drv/oss_maestro/oss_maestro.c new file mode 100644 index 0000000..d8ea3ff --- /dev/null +++ b/attic/drv/oss_maestro/oss_maestro.c @@ -0,0 +1,2397 @@ +/* + * Purpose: Driver for ESS Maestro1/2 (PCI) audio controller + */ +/* + * + * 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_maestro_cfg.h" +#include "oss_pci.h" +#include "uart401.h" +#include "ac97.h" + +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif + +#define BYTE unsigned char +#define WORD unsigned short +#define DWORD unsigned int +#define HIWORD(dw) ((dw >> 16) & 0xffff) +#define LOWORD(dw) (dw & 0xffff) +#define HIBYTE(w) ((w >> 8) & 0xff) +#define LOBYTE(w) (w & 0xff) + +#define gwPTBaseIO devc->base +#define outpw(p,d) OUTW(devc->osdev, d, p) + +#define DISABLE 0 +#define ENABLE 1 + +#define CHANNEL0 0x0 +#define bAPURPlay 0x0 +#define bAPULPlay 0x1 + +#define bAPULSrc 0x2 +#define bAPURSrc 0x3 +#define bAPULMix 0x4 +#define bAPURMix 0x5 + + +#define ESS_VENDOR_ID 0x1285 +#define ESS_MAESTRO 0x0100 +#define ESS2_VENDOR_ID 0x125d +#define ESS_MAESTRO2 0x1968 +#define ESS_MAESTRO2E 0x1978 + +#define WSETBIT(w, b) (w | (1 << b)) +#define WMSKBIT(w, b) (w & ~(1 << b)) +#define DWSETBIT(w, b) (w | (1 << b)) +#define DWMSKBIT(w, b) (w & ~(1 << b)) + +static int SYSCLK; +#define ESSM_CFMT_STEREO 0x01 +#define ESSM_CFMT_16BIT 0x02 +#define ESSM_CFMT_MASK 0x03 +#define ESSM_CFMT_ASHIFT 0 +#define ESSM_CFMT_CSHIFT 4 + +#define MAXCOUNTDOWN 31 +DWORD dwFrequencyTable[] = { + 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536 +}; + +#define IO_PT_CODEC_CMD ( devc->base + 0x30 ) +#define IO_PT_CODEC_STATUS ( devc->base + 0x30 ) +#define IO_PT_CODEC_DATA ( devc->base + 0x32 ) +#define IO_PT_CODEC_FORMATA ( devc->base + 0x34 ) +#define IO_PT_CODEC_FORMATB ( devc->base + 0x36 ) + +#define _DST_MONO 0x00 +#define _DST_STEREO 0x08 + +#define _DST_NONE 0x00 +#define _DST_DAC 0x01 +#define _DST_MODEM 0x02 +#define _DST_RESERVED1 0x03 +#define _DST_DIRECTSOUND 0x04 +#define _DST_ASSP 0x05 +#define _DST_RESERVED2 0x06 +#define _DST_RESERVED3 0x07 + +#define PT101_MIXER_DEVS (SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | \ + SOUND_MASK_MIC | SOUND_MASK_VOLUME | \ + SOUND_MASK_LINE3 | \ + SOUND_MASK_SYNTH | SOUND_MASK_CD | \ + SOUND_MASK_LINE | SOUND_MASK_PCM | \ + SOUND_MASK_IGAIN) + +#define PT101_MIXER_RECDEVS (SOUND_MASK_LINE | SOUND_MASK_MIC | \ + SOUND_MASK_CD | SOUND_MASK_LINE1 | \ + SOUND_MASK_LINE2 | SOUND_MASK_LINE3) + +#define PT101_MIXER_STEREODEVS (SOUND_MASK_LINE | SOUND_MASK_IGAIN | \ + SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | \ + SOUND_MASK_CD | \ + SOUND_MASK_PCM | SOUND_MASK_LINE1 | \ + SOUND_MASK_LINE2 | SOUND_MASK_LINE3) + +static int default_mixer_levels[32] = { + 0x3232, /* Master Volume */ + 0x3232, /* Bass */ + 0x3232, /* Treble */ + 0x4b4b, /* FM */ + 0x3232, /* PCM */ + 0x1515, /* PC Speaker */ + 0x2020, /* Ext Line */ + 0x1010, /* Mic */ + 0x4b4b, /* CD */ + 0x0000, /* Recording monitor */ + 0x4b4b, /* Second PCM */ + 0x4b4b, /* Recording level */ + 0x4b4b, /* Input gain */ + 0x4b4b, /* Output gain */ + 0x2020, /* Line1 */ + 0x2020, /* Line2 */ + 0x1515 /* Line3 (usually line in) */ +}; + +typedef struct maestro_portc +{ + int speed, bits, channels; + int open_mode; + int audiodev; + int trigger_bits; + int audio_enabled; +} +maestro_portc; + +#define MAX_PORTC 2 + +typedef struct maestro_devc +{ + oss_device_t *osdev; + oss_native_word base; + int irq; + int model; + char *chip_name; + int open_mode; + /* Data table for virtualizing write-only registers */ + WORD wIDRRegDataTable[0x1F]; + WORD gwWCRegTable[0x200]; +#define MD_MAESTRO1 1 +#define MD_MAESTRO2 2 +#define MD_MAESTRO2E 3 + unsigned short gpio; + oss_mutex_t lock; + oss_mutex_t low_lock; + + struct dmabuf + { + unsigned char *rawbuf; + unsigned dmasize; + oss_native_word base; /* Offset for ptr */ + } + dma_adc, dma_dac, dma_mix; + int wApuBufferSize; + unsigned char *dmapages; + oss_native_word dmapages_phys; + int dmalen; + + /* Mixer parameters */ + ac97_devc ac97devc; + /* PT101 Mixer parameters */ + int my_mixer; + int *levels; + int recdevs; + int recmask; + + maestro_portc portc[MAX_PORTC]; +} +maestro_devc; + +extern WORD wRdAPUReg (maestro_devc * devc, BYTE _bChannel, BYTE _bRegIndex); + +static int +maestrointr (oss_device_t * osdev) +{ + maestro_devc *devc = (maestro_devc *) osdev->devc; + maestro_portc *portc; + int i; + int serviced = 0; + + if (!(INW (devc->osdev, devc->base + 0x1A))) + return 0; + + OUTW (devc->osdev, INW (devc->osdev, devc->base + 0x04) | 0x1, + devc->base + 0x04); + serviced = 1; + + /* ack all interrupts */ + OUTB (devc->osdev, 0xFF, devc->base + 0x1A); + for (i = 0; i < MAX_PORTC; i++) + { + portc = &devc->portc[i]; + + /* Handle Playback */ + if (portc->trigger_bits & PCM_ENABLE_OUTPUT) + { + dmap_t *dmapout = audio_engines[portc->audiodev]->dmap_out; + WORD wApuCurrentPos; + int n; + + wApuCurrentPos = wRdAPUReg (devc, bAPURPlay, 0x05); + wApuCurrentPos = (wApuCurrentPos - devc->dma_dac.base) & 0xFFFE; + wApuCurrentPos = (wApuCurrentPos % devc->wApuBufferSize) << 1; + wApuCurrentPos /= dmapout->fragment_size; /*Actual qhead */ + if (wApuCurrentPos == 0 || wApuCurrentPos > dmapout->nfrags) + wApuCurrentPos = 0; + n = 0; + while (dmap_get_qhead (dmapout) != wApuCurrentPos + && n++ < dmapout->nfrags) + oss_audio_outputintr (portc->audiodev, 0); + } + + /* Handle recording */ + if (portc->trigger_bits & PCM_ENABLE_INPUT) + { + dmap_t *dmapin = audio_engines[portc->audiodev]->dmap_in; + WORD wApuCurrentPos; + int n; + + wApuCurrentPos = wRdAPUReg (devc, bAPULSrc, 0x05); + wApuCurrentPos = (wApuCurrentPos - devc->dma_adc.base) & 0xFFFE; + wApuCurrentPos = (wApuCurrentPos % devc->wApuBufferSize) << 1; + wApuCurrentPos /= dmapin->fragment_size; /* Actual qtail */ + if (wApuCurrentPos == 0 || wApuCurrentPos > dmapin->nfrags) + wApuCurrentPos = 0; + n = 0; + while (dmap_get_qtail (dmapin) != wApuCurrentPos + && n++ < dmapin->nfrags) + oss_audio_inputintr (portc->audiodev, 0); + } + } + return serviced; +} + +static int +ac97_read (void *devc_, int addr) +{ + maestro_devc *devc = devc_; + int data, i; + oss_native_word flags; + int sanity = 10000; + + MUTEX_ENTER_IRQDISABLE (devc->low_lock, flags); + + for (i = 0; i < 100000; i++) + if (!(INB (devc->osdev, devc->base + 0x30) & 0x01)) + break; + OUTW (devc->osdev, addr | 0x80, devc->base + 0x30); + + while (INB (devc->osdev, devc->base + 0x30) & 1) + { + sanity--; + if (!sanity) + { + cmn_err (CE_WARN, "ac97 codec timeout - 0x%x.\n", addr); + MUTEX_EXIT_IRQRESTORE (devc->low_lock, flags); + return 0; + } + } + + data = INW (devc->osdev, devc->base + 0x32); + oss_udelay (100); + + MUTEX_EXIT_IRQRESTORE (devc->low_lock, flags); + return data & 0xffff; +} + +static int +ac97_write (void *devc_, int addr, int data) +{ + maestro_devc *devc = devc_; + int i; + oss_native_word flags; + + MUTEX_ENTER_IRQDISABLE (devc->low_lock, flags); + + for (i = 0; i < 10000; i++) + if (!(INB (devc->osdev, devc->base + 0x30) & 0x01)) + break; + OUTW (devc->osdev, data & 0xffff, devc->base + 0x32); + oss_udelay (100); + OUTW (devc->osdev, (addr & 0x7f) & ~0x80, devc->base + 0x30); + oss_udelay (100); + + MUTEX_EXIT_IRQRESTORE (devc->low_lock, flags); + + return 0; +} + +/**************************************************** + * PT101 CODEC Routines * + ****************************************************/ + +/************************************************************************/ +/* PT101 CODEC Read */ +/************************************************************************/ + +static int +pt101_set_recmask (maestro_devc * devc, int mask) +{ + int bits = 0; + mask &= devc->recmask; + + mask = mask & (devc->recdevs ^ mask); /* Pick the one recently turned on */ + + if (!mask) + return devc->recdevs; + + devc->recdevs = mask; + + switch (mask) + { + case SOUND_MASK_LINE: + bits = 0x0000; + break; + case SOUND_MASK_MIC: + bits = 0x0020; + break; + case SOUND_MASK_CD: + case SOUND_MASK_LINE1: + bits = 0x0040; + break; + /*CD*/ case SOUND_MASK_LINE2: + bits = 0x0060; + break; /*Video */ + case SOUND_MASK_LINE3: + bits = 0x0080; + break; /*Modem */ + default: /* Unknown bit (combination) */ + mask = SOUND_MASK_MIC; + bits = 0x0020; + } + + ac97_write (devc, 0x02, bits); + + return devc->levels[31] = devc->recdevs; +} + +static int +pt101_mixer_get (maestro_devc * devc, int dev) +{ + if (!((1 << dev) & PT101_MIXER_DEVS)) + return OSS_EINVAL; + + return devc->levels[dev]; +} + +static int +pt101_mixer_set (maestro_devc * devc, int dev, int value) +{ + + int left, right, lvl; + int lch, rch; + static const char pt101_mix_map[101] = { + 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, + 12, 12, + 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 9, 9, 9, + 9, + 9, 9, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, + 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 2, 2, + 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + + if (!((1 << dev) & PT101_MIXER_DEVS)) + return OSS_EINVAL; + + if (!((1 << dev) & PT101_MIXER_STEREODEVS)) + { + lvl = value & 0xff; + if (lvl > 100) + lvl = 100; + lch = rch = lvl; + value = lvl | (lvl << 8); + } + else + { + lch = value & 0xff; + rch = (value >> 8) & 0xff; + if (lch > 100) + lch = 100; + if (rch > 100) + rch = 100; + value = lch | (rch << 8); + } + +/* Now adjust the left and right channel to the mixer map*/ + left = pt101_mix_map[lch]; + right = pt101_mix_map[rch]; + + switch (dev) + { + case SOUND_MIXER_CD: + case SOUND_MIXER_LINE1: + if (rch == 0) + ac97_write (devc, 0x04, (ac97_read (devc, 0x04) & 0xFF7F) | 0x0080); + else + ac97_write (devc, 0x04, (ac97_read (devc, 0x04) & 0xFF7F)); + + if (lch == 0) + ac97_write (devc, 0x04, (ac97_read (devc, 0x04) & 0x7FFF) | 0x8000); + else + ac97_write (devc, 0x04, (ac97_read (devc, 0x04) & 0x7FFF)); + + ac97_write (devc, 0x04, ((ac97_read (devc, 0x04) & 0xFFE1) | (right << 1))); /*Right */ + ac97_write (devc, 0x04, ((ac97_read (devc, 0x04) & 0xE1FF) | (left << 9))); /*Left */ + break; + + case SOUND_MIXER_LINE2: + if (rch == 0) + ac97_write (devc, 0x05, (ac97_read (devc, 0x05) & 0xFF7F) | 0x0080); + else + ac97_write (devc, 0x05, (ac97_read (devc, 0x05) & 0xFF7F)); + + if (lch == 0) + ac97_write (devc, 0x05, (ac97_read (devc, 0x05) & 0x7FFF) | 0x8000); + else + ac97_write (devc, 0x05, (ac97_read (devc, 0x05) & 0x7FFF)); + + ac97_write (devc, 0x05, (WORD) ((ac97_read (devc, 0x05) & 0xFFE1) | ((WORD) right << 1))); /*Right */ + ac97_write (devc, 0x05, (WORD) ((ac97_read (devc, 0x05) & 0xE1FF) | ((WORD) left << 9))); /*Left */ + break; + + case SOUND_MIXER_LINE3: + if (rch == 0) + ac97_write (devc, 0x06, (ac97_read (devc, 0x06) & 0xFF7F) | 0x0080); + else + ac97_write (devc, 0x06, (ac97_read (devc, 0x06) & 0xFF7F)); + + if (lch == 0) + ac97_write (devc, 0x06, (ac97_read (devc, 0x06) & 0x7FFF) | 0x8000); + else + ac97_write (devc, 0x06, (ac97_read (devc, 0x06) & 0x7FFF)); + + ac97_write (devc, 0x06, (WORD) ((ac97_read (devc, 0x06) & 0xFFE1) | ((WORD) right << 1))); /*Right */ + ac97_write (devc, 0x06, (WORD) ((ac97_read (devc, 0x06) & 0xE1FF) | ((WORD) left << 9))); /*Left */ + break; + + case SOUND_MIXER_LINE: + if (rch == 0) + ac97_write (devc, 0x1D, (ac97_read (devc, 0x1D) & 0xFF7F) | 0x0080); + else + ac97_write (devc, 0x1D, (ac97_read (devc, 0x1D) & 0xFF7F)); + + if (lch == 0) + ac97_write (devc, 0x1D, (ac97_read (devc, 0x1D) & 0x7FFF) | 0x8000); + else + ac97_write (devc, 0x1D, (ac97_read (devc, 0x1D) & 0x7FFF)); + + ac97_write (devc, 0x1D, (WORD) ((ac97_read (devc, 0x1D) & 0xFFE1) | ((WORD) right << 1))); /*Right */ + ac97_write (devc, 0x1D, (WORD) ((ac97_read (devc, 0x1D) & 0xE1FF) | ((WORD) left << 9))); /*Left */ + break; + + case SOUND_MIXER_MIC: + if (rch == 0) + ac97_write (devc, 0x07, (ac97_read (devc, 0x07) & 0xFF7F) | 0x0080); + else + ac97_write (devc, 0x07, (ac97_read (devc, 0x07) & 0xFF7F)); + + if (lch == 0) + ac97_write (devc, 0x07, (ac97_read (devc, 0x07) & 0x7FFF) | 0x8000); + else + ac97_write (devc, 0x07, (ac97_read (devc, 0x07) & 0x7FFF)); + + left = right; + ac97_write (devc, 0x07, + (WORD) ((ac97_read (devc, 0x07) & 0xFFE1) | + ((WORD) right << 1))); + ac97_write (devc, 0x07, + (WORD) ((ac97_read (devc, 0x07) & 0xE1FF) | + ((WORD) right << 9))); + break; + + case SOUND_MIXER_SYNTH: + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_PCM: + if (rch == 0) + ac97_write (devc, 0x09, (ac97_read (devc, 0x09) & 0xFF7F) | 0x0080); + else + ac97_write (devc, 0x09, (ac97_read (devc, 0x09) & 0xFF7F)); + + if (lch == 0) + ac97_write (devc, 0x09, (ac97_read (devc, 0x09) & 0x7FFF) | 0x8000); + else + ac97_write (devc, 0x09, (ac97_read (devc, 0x09) & 0x7FFF)); + + ac97_write (devc, 0x09, (WORD) ((ac97_read (devc, 0x09) & 0xFFE1) | ((WORD) right << 1))); /*Right */ + ac97_write (devc, 0x09, (WORD) ((ac97_read (devc, 0x09) & 0xE1FF) | ((WORD) left << 9))); /*Left */ + break; + + case SOUND_MIXER_IGAIN: + ac97_write (devc, 0x02, (WORD) ((ac97_read (devc, 0x02) & 0xFFF0) | ((WORD) right))); /*Right */ + ac97_write (devc, 0x02, (WORD) ((ac97_read (devc, 0x02) & 0xF0FF) | ((WORD) left << 8))); /*Left */ + break; + } + + return devc->levels[dev] = value; +} + +static void +pt101_mixer_reset (maestro_devc * devc) +{ + int i; + + devc->levels = load_mixer_volumes ("ESS PT101", default_mixer_levels, 1); + devc->recmask = PT101_MIXER_RECDEVS & PT101_MIXER_DEVS; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + pt101_mixer_set (devc, i, devc->levels[i]); + pt101_set_recmask (devc, SOUND_MASK_MIC); +} + +/*ARGSUSED*/ +static int +pt101_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg) +{ + maestro_devc *devc = mixer_devs[dev]->devc; + int val; + + if (((cmd >> 8) & 0xff) == 'M') + { + if (IOC_IS_OUTPUT (cmd)) + switch (cmd & 0xff) + { + case SOUND_MIXER_RECSRC: + val = *arg; + return *arg = pt101_set_recmask (devc, val); + break; + + default: + val = *arg; + return *arg = pt101_mixer_set (devc, cmd & 0xff, val); + } + else + switch (cmd & 0xff) + { + + case SOUND_MIXER_RECSRC: + return *arg = devc->recdevs; + break; + + case SOUND_MIXER_DEVMASK: + return *arg = PT101_MIXER_DEVS; + break; + + case SOUND_MIXER_STEREODEVS: + return *arg = PT101_MIXER_STEREODEVS; + break; + + case SOUND_MIXER_RECMASK: + return *arg = devc->recmask; + break; + + case SOUND_MIXER_CAPS: + return *arg = SOUND_CAP_EXCL_INPUT; + break; + + default: + return *arg = pt101_mixer_get (devc, cmd & 0xff); + } + } + else + return OSS_EINVAL; +} + +/* + * Low level I/O routines + */ +#define WAVE_CACHE_INDEX (devc->base + 0x10) +#define WAVE_CACHE_DATA (devc->base + 0x12) +#define WAVE_CACHE_CONTROL (devc->base + 0x14) + +/*****************************************************************************/ +/* Write Wave Cache Address I/O Port */ +/* */ +/*****************************************************************************/ +static void +vWrWCRegIndex (maestro_devc * devc, WORD _wRegIndex) +{ + OUTW (devc->osdev, LOWORD (_wRegIndex), WAVE_CACHE_INDEX); +} + +/*****************************************************************************/ +/* Write Wave Cache Data I/O Port */ +/* */ +/*****************************************************************************/ +static void +vWrWCRegData (maestro_devc * devc, WORD _wRegData) +{ + OUTW (devc->osdev, _wRegData, WAVE_CACHE_DATA); +} + +/*****************************************************************************/ +/* Read Wave Cache Addr I/O Port */ +/* */ +/*****************************************************************************/ +WORD +wRdWCRegIndex (maestro_devc * devc) +{ + return ((WORD) INW (devc->osdev, WAVE_CACHE_INDEX)); +} + +/*****************************************************************************/ +/* Read Wave Cache Data I/O Port */ +/* */ +/*****************************************************************************/ +WORD +wRdWCRegData (maestro_devc * devc) +{ + return ((WORD) (INW (devc->osdev, WAVE_CACHE_DATA))); +} + +/*****************************************************************************/ +/* Write Wave Cache Memory */ +/* */ +/*****************************************************************************/ +void +vWrWCReg (maestro_devc * devc, WORD _wRegIndex, WORD _wRegData) +{ + vWrWCRegIndex (devc, _wRegIndex); + vWrWCRegData (devc, _wRegData); +} + +/*****************************************************************************/ +/* Read Wave Cache Memory */ +/* */ +/*****************************************************************************/ +WORD +wRdWCReg (maestro_devc * devc, WORD _wRegIndex) +{ + WORD wRetVal; + + vWrWCRegIndex (devc, _wRegIndex); + wRetVal = wRdWCRegData (devc); + + return (wRetVal); +} + +/*****************************************************************************/ +/* */ +/* */ +/*****************************************************************************/ +void +vSetWCChannelControlBit (maestro_devc * devc, BYTE _bChannel, BYTE _bBit) +{ + vWrWCReg (devc, (WORD) (_bChannel << 3), + WSETBIT (wRdWCReg (devc, (WORD) (_bChannel << 3)), _bBit)); +} + +/*****************************************************************************/ +/* */ +/* */ +/*****************************************************************************/ +void +vMskWCChannelControlBit (maestro_devc * devc, BYTE _bChannel, BYTE _bBit) +{ + + vWrWCReg (devc, (WORD) (_bChannel << 3), + WMSKBIT (wRdWCReg (devc, (WORD) (_bChannel << 3)), _bBit)); +} + + +/*****************************************************************************/ +/* */ +/* */ +/*****************************************************************************/ +void +vSetWCChannelStereo (maestro_devc * devc, BYTE _bChannel, BYTE _bState) +{ + switch (_bState) + { + case 0: + /*MONO*/ vMskWCChannelControlBit (devc, _bChannel, 1); + break; + + case 1: + /*STEREO*/ vSetWCChannelControlBit (devc, _bChannel, 1); + break; + } +} + +/*****************************************************************************/ +/* */ +/* */ +/*****************************************************************************/ +void +vSetWCChannelWordSize (maestro_devc * devc, BYTE _bChannel, BYTE _bState) +{ + switch (_bState) + { + case 0: /* 8BIT */ + vSetWCChannelControlBit (devc, _bChannel, 2); + break; + + case 1: /* 16BIT */ + vMskWCChannelControlBit (devc, _bChannel, 2); + break; + } +} + +/*****************************************************************************/ +/* */ +/* */ +/*****************************************************************************/ +void +vSetWCChannelTagAddr (maestro_devc * devc, BYTE _bChannel, WORD _wTagAddr) +{ + vWrWCReg (devc, (WORD) (_bChannel << 3), + (WORD) ((wRdWCReg (devc, (WORD) (_bChannel << 3)) & 0x0007) | + (WORD) (_wTagAddr << 3))); +} + +#define WAVERAM_START 0x400000 + +#define IDR0_DATA_PORT 0x00 +#define IDR1_CRAM_POINTER 0x01 +#define IDR2_CRAM_INCREMENT 0x02 +#define IDR3_WAVE_DATA 0x03 +#define IDR4_WAVE_PTR_LO 0x04 +#define IDR5_WAVE_PTR_HI 0x05 +#define IDR6_TIMER_CTRL 0x06 +#define IDR7_WAVE_ROMRAM 0x07 + +/* Read/Write access for Indexed Data Registers */ +#define _RW 0 /* Read/Write */ +#define _WO 1 /* Write Only */ +#define _RO 2 /* Read Only */ + +static BYTE bIDRAccessTable[] = { + _RW, _RW, _RW, _RW, + _RW, _RW, _WO, _WO, + _WO, _WO, _WO, _WO, + _WO, _WO, _WO, _WO, + _WO, _WO, _RW, _WO, + _RO, _RW, _RW, _WO +}; + +#define PT_DATA_PORT devc->base +#define PT_INDEX_PORT (devc->base + 0x02) + +/*****************************************************************************/ +/* Write IDR Register Index */ +/* */ +/*****************************************************************************/ +static void +vWrIDRIndex (maestro_devc * devc, WORD _wRegIndex) +{ + OUTW (devc->osdev, _wRegIndex, PT_INDEX_PORT); +} + +/*****************************************************************************/ +/* Write IDR Register Data */ +/* */ +/*****************************************************************************/ +static void +vWrIDRData (maestro_devc * devc, WORD _wRegData) +{ + OUTW (devc->osdev, _wRegData, PT_DATA_PORT); +} + +/*****************************************************************************/ +/* Read IDR Register Data */ +/* */ +/*****************************************************************************/ +static WORD +wRdIDRData (maestro_devc * devc) +{ + return ((WORD) INW (devc->osdev, PT_DATA_PORT)); +} + +/*****************************************************************************/ +/* Write IDR Register */ +/* */ +/*****************************************************************************/ +static void +vWrIDR (maestro_devc * devc, WORD _wRegIndex, WORD _wRegData) +{ + vWrIDRIndex (devc, _wRegIndex); + + vWrIDRData (devc, _wRegData); + + devc->wIDRRegDataTable[_wRegIndex] = _wRegData; +} + +/*****************************************************************************/ +/* Read IDR Register */ +/* */ +/*****************************************************************************/ +static WORD +wRdIDR (maestro_devc * devc, WORD _wRegIndex) +{ + WORD wRetVal = 0; + + vWrIDRIndex (devc, _wRegIndex); + + switch (bIDRAccessTable[_wRegIndex]) + { + case _RW: + case _RO: + wRetVal = wRdIDRData (devc); + break; + + case _WO: + wRetVal = devc->wIDRRegDataTable[_wRegIndex]; + break; + } + + return (wRetVal); +} + +/*****************************************************************************/ +/* Set IDR Register Bit */ +/* */ +/*****************************************************************************/ +static void +vSetIDRBit (maestro_devc * devc, WORD _wRegIndex, BYTE _bRegBit) +{ + vWrIDR (devc, _wRegIndex, WSETBIT (wRdIDR (devc, _wRegIndex), _bRegBit)); +} + +/*****************************************************************************/ +/* Mask IDR Register Bit */ +/* */ +/*****************************************************************************/ +static void +vMskIDRBit (maestro_devc * devc, WORD _wRegIndex, BYTE _bRegBit) +{ + vWrIDR (devc, _wRegIndex, WMSKBIT (wRdIDR (devc, _wRegIndex), _bRegBit)); +} + +/**************************/ +#define _APU_MIXER 0x06 +#define _APU_SYNR 0x3C +#define _APU_SYNL 0x3D +#define _APU_DIGL 0x3E +#define _APU_DIGR 0x3F + +/* APU Modes */ +#define _APU_OFF 0x00 +#define _APU_16BITLINEAR 0x01 /* 16-Bit Linear Sample Player */ +#define _APU_16BITSTEREO 0x02 /* 16-Bit Stereo Sample Player */ +#define _APU_8BITLINEAR 0x03 /* 8-Bit Linear Sample Player */ +#define _APU_8BITSTEREO 0x04 /* 8-Bit Stereo Sample Player */ +#define _APU_8BITDIFF 0x05 /* 8-Bit Differential Sample Playrer */ +#define _APU_DIGITALDELAY 0x06 /* Digital Delay Line */ +#define _APU_DUALTAP 0x07 /* Dual Tap Reader */ +#define _APU_CORRELATOR 0x08 /* Correlator */ +#define _APU_INPUTMIXER 0x09 /* Input Mixer */ +#define _APU_WAVETABLE 0x0A /* Wave Table Mode */ +#define _APU_SRCONVERTOR 0x0B /* Sample Rate Convertor */ +#define _APU_16BITPINGPONG 0x0C /* 16-Bit Ping-Pong Sample Player */ + +#define _APU_RESERVED1 0x0D /* Reserved 1 */ +#define _APU_RESERVED2 0x0E /* Reserved 2 */ +#define _APU_RESERVED3 0x0F /* Reserved 3 */ + +/* APU Filtey Q Control */ +#define _APU_FILTER_LESSQ 0x00 +#define _APU_FILTER_MOREQ 0x03 + +/* APU Filter Control */ +#define _APU_FILTER_2POLE_LOPASS 0x00 +#define _APU_FILTER_2POLE_BANDPASS 0x01 +#define _APU_FILTER_2POLE_HIPASS 0x02 +#define _APU_FILTER_1POLE_LOPASS 0x03 +#define _APU_FILTER_1POLE_HIPASS 0x04 +#define _APU_FILTER_OFF 0x05 + +/* Polar Pan Control */ +#define _APU_PAN_CENTER_CIRCLE 0x00 +#define _APU_PAN_MIDDLE_RADIUS 0x01 +#define _APU_PAN_OUTSIDE_RADIUS 0x02 +#define _APU_PAN_ + +/* APU ATFP Type */ +#define _APU_ATFP_AMPLITUDE 0x00 +#define _APU_ATFP_TREMELO 0x01 +#define _APU_ATFP_FILTER 0x02 +#define _APU_ATFP_PAN 0x03 + +/* APU ATFP Flags */ +#define _APU_ATFP_FLG_OFF 0x00 +#define _APU_ATFP_FLG_WAIT 0x01 +#define _APU_ATFP_FLG_DONE 0x02 +#define _APU_ATFP_FLG_INPROCESS 0x03 + +static WORD +wRdAPURegIndex (maestro_devc * devc) +{ + return (wRdIDR (devc, IDR1_CRAM_POINTER)); +} + +static void +vWrAPURegIndex (maestro_devc * devc, WORD _wRegIndex) +{ + vWrIDR (devc, IDR1_CRAM_POINTER, _wRegIndex); + + while (wRdAPURegIndex (devc) != _wRegIndex) + { + } +} + +static WORD +wRdAPURegData (maestro_devc * devc) +{ + return (wRdIDR (devc, IDR0_DATA_PORT)); +} + +static void +vWrAPURegData (maestro_devc * devc, WORD _wRegData) +{ + while (wRdAPURegData (devc) != _wRegData) + { + vWrIDR (devc, IDR0_DATA_PORT, _wRegData); + } +} + +static void +vWrAPUReg (maestro_devc * devc, BYTE _bChannel, BYTE _bRegIndex, + WORD _wRegData) +{ + vWrAPURegIndex (devc, (WORD) ((_bChannel << 4) + _bRegIndex)); + vWrAPURegData (devc, _wRegData); +} + +/* Should be static */ WORD +wRdAPUReg (maestro_devc * devc, BYTE _bChannel, BYTE _bRegIndex) +{ + vWrAPURegIndex (devc, (WORD) ((_bChannel << 4) + _bRegIndex)); + return (wRdAPURegData (devc)); +} + +/*****************************************************************************/ +/* */ +/* */ +/*****************************************************************************/ +DWORD +wCalculateAPUFrequency (DWORD freq) +{ + if (freq == 48000) + return 0x10000; + + return ((freq / (SYSCLK / 1024L)) << 16) + + (((freq % (SYSCLK / 1024L)) << 16) / (SYSCLK / 1024L)); +} + + +static void +vSetAPUFrequency (maestro_devc * devc, BYTE _bChannel, DWORD _dwFrequency) +{ + vWrAPUReg (devc, _bChannel, 0x02, + (WORD) ((wRdAPUReg (devc, _bChannel, 0x02) & 0x00FF) | + (WORD) (LOBYTE (_dwFrequency) << 8))); + vWrAPUReg (devc, _bChannel, 0x03, (WORD) (_dwFrequency >> 8)); +} + +static void +vSetAPURegBit (maestro_devc * devc, BYTE _bChannel, BYTE _bRegIndex, + BYTE _bRegBit) +{ + vWrAPUReg (devc, _bChannel, _bRegIndex, + WSETBIT (wRdAPUReg (devc, _bChannel, _bRegIndex), _bRegBit)); +} + +static void +vMskAPURegBit (maestro_devc * devc, BYTE _bChannel, BYTE _bRegIndex, + BYTE _bRegBit) +{ + vWrAPUReg (devc, _bChannel, _bRegIndex, + WMSKBIT (wRdAPUReg (devc, _bChannel, _bRegIndex), _bRegBit)); +} + +static void +vSetAPUSubmixMode (maestro_devc * devc, BYTE _bChannel, BYTE _bState) +{ + switch (_bState) + { + case DISABLE: + vMskAPURegBit (devc, _bChannel, 0x02, 3); + break; + + case ENABLE: + vSetAPURegBit (devc, _bChannel, 0x02, 3); + break; + } +} + +static void +vSetAPUSubmixGroup (maestro_devc * devc, BYTE _bChannel, BYTE _bSubmixGroup) +{ + vWrAPUReg (devc, _bChannel, 0x02, + (WORD) ((wRdAPUReg (devc, _bChannel, 0x02) & 0xFFF8) | + _bSubmixGroup)); +} + +static void +vSetAPU6dB (maestro_devc * devc, BYTE _bChannel, BYTE _bState) +{ + switch (_bState) + { + case DISABLE: + vMskAPURegBit (devc, _bChannel, 0x02, 4); + break; + + case ENABLE: + vSetAPURegBit (devc, _bChannel, 0x02, 4); + break; + } +} + +static void +vSetAPUType (maestro_devc * devc, BYTE _bChannel, BYTE _bType) +{ + vWrAPUReg (devc, _bChannel, 0x00, + (wRdAPUReg (devc, _bChannel, 0x00) & 0xFF0F) | (_bType << 4)); +} + +static void +vSetAPUDMA (maestro_devc * devc, BYTE _bChannel, BYTE _bState) +{ + switch (_bState) + { + case DISABLE: + vMskAPURegBit (devc, _bChannel, 0x00, 14); + break; + + case ENABLE: + vSetAPURegBit (devc, _bChannel, 0x00, 14); + break; + } +} + +static void +vSetAPUFilterType (maestro_devc * devc, BYTE _bChannel, BYTE _bFilterType) +{ + vWrAPUReg (devc, _bChannel, 0x00, + (WORD) ((wRdAPUReg (devc, _bChannel, 0x00) & 0xFFF3) | + (_bFilterType << 2))); +} + +static void +vSetAPUFilterQ (maestro_devc * devc, BYTE _bChannel, BYTE _bFilterQ) +{ + vWrAPUReg (devc, _bChannel, 0x00, + (WORD) ((wRdAPUReg (devc, _bChannel, 0x00) & 0xFFFC) | + _bFilterQ)); +} + +static void +vSetAPUFilterTuning (maestro_devc * devc, BYTE _bChannel, BYTE _bFilterTuning) +{ + vWrAPUReg (devc, _bChannel, 0x0A, + (WORD) ((wRdAPUReg (devc, _bChannel, 0x0A) & 0x00FF) | + ((WORD) _bFilterTuning << 8))); +} + +static void +vSetAPUPolarPan (maestro_devc * devc, BYTE _bChannel, BYTE _bPolarPan) +{ + vWrAPUReg (devc, _bChannel, 0x0A, + (WORD) ((wRdAPUReg (devc, _bChannel, 0x0A) & 0xFFC0) | + _bPolarPan)); +} + +static void +vSetAPUDataSourceA (maestro_devc * devc, BYTE _bChannel, BYTE _bDataSourceA) +{ + vWrAPUReg (devc, _bChannel, 0x0B, + (WORD) ((wRdAPUReg (devc, _bChannel, 0x0B) & 0xFF80) | + _bDataSourceA)); +} + +static void +vSetAPUAmplitudeNow (maestro_devc * devc, BYTE _bChannel, BYTE _bAmplitude) +{ + vWrAPUReg (devc, _bChannel, 0x09, + (WORD) ((wRdAPUReg (devc, _bChannel, 0x09) & 0x00FF) | + (((WORD) _bAmplitude << 8)))); +} + +static void +vSetAPUWave64kPage (maestro_devc * devc, BYTE _bChannel, DWORD _wWaveStart) +{ + vWrAPUReg (devc, _bChannel, 0x04, ((_wWaveStart >> 16) & 0xFF) << 8); +} + +static void +vSetAPUWaveStart (maestro_devc * devc, BYTE _bChannel, WORD _wWaveStart) +{ + vWrAPUReg (devc, _bChannel, 0x05, _wWaveStart); +} + +static void +vSetAPUWaveEnd (maestro_devc * devc, BYTE _bChannel, WORD _wWaveEnd) +{ + vWrAPUReg (devc, _bChannel, 0x06, _wWaveEnd); +} + +static void +vSetAPUWaveLoop (maestro_devc * devc, BYTE _bChannel, WORD _wWaveLoop) +{ + vWrAPUReg (devc, _bChannel, 0x07, _wWaveLoop); +} + +static void +vSetAPUWavePtr (maestro_devc * devc, BYTE _bChannel, DWORD _dwWaveStart, + WORD _wWaveLength) +{ + /* start of sample */ + vSetAPUWave64kPage (devc, _bChannel, _dwWaveStart); + vSetAPUWaveStart (devc, _bChannel, LOWORD (_dwWaveStart)); + vSetAPUWaveEnd (devc, _bChannel, LOWORD ((_dwWaveStart + _wWaveLength))); + vSetAPUWaveLoop (devc, _bChannel, _wWaveLength); +} + +/*****************************************************************************/ +/* Write Wave Cache Control I/O Port */ +/* */ +/*****************************************************************************/ +static void +vWrWCControlReg (maestro_devc * devc, WORD _wWCControlRegFlags) +{ + OUTW (devc->osdev, _wWCControlRegFlags, WAVE_CACHE_CONTROL); +} + +/*****************************************************************************/ +/* Read Wave Cache Control I/O Port */ +/* */ +/*****************************************************************************/ +static WORD +wRdWCControlReg (maestro_devc * devc) +{ + return ((WORD) (INW (devc->osdev, WAVE_CACHE_CONTROL))); +} + +/*****************************************************************************/ +/* Set Wave Cache Control Bit Flag */ +/* */ +/*****************************************************************************/ +static void +vSetWCControlRegBit (maestro_devc * devc, BYTE _bWCControlRegBit) +{ + vWrWCControlReg (devc, WSETBIT (wRdWCControlReg (devc), _bWCControlRegBit)); +} + +/*****************************************************************************/ +/* Mask Wave Cache Control Bit Flag */ +/* */ +/*****************************************************************************/ +static void +vMskWCControlRegBit (maestro_devc * devc, BYTE _bWCControlRegBit) +{ + vWrWCControlReg (devc, WMSKBIT (wRdWCControlReg (devc), _bWCControlRegBit)); +} + +/*****************************************************************************/ +/* */ +/* */ +/*****************************************************************************/ +static void +vSetWCEnable (maestro_devc * devc, BYTE _bState) +{ + switch (_bState) + { + case DISABLE: + vMskWCControlRegBit (devc, 8); + break; + + case ENABLE: + vSetWCControlRegBit (devc, 8); + break; + } +} + +/*****************************************************************************/ +/* */ +/* */ +/*****************************************************************************/ +/*ARGSUSED*/ +void +vSetTimer (maestro_devc * devc, WORD _wInterruptFreq) +{ + + int prescale; + int divide; + + /* XXX make freq selector much smarter, see calc_bob_rate */ + int freq = 200; + + /* compute ideal interrupt frequency for buffer size & play rate */ + /* first, find best prescaler value to match freq */ + for (prescale = 5; prescale < 12; prescale++) + if (freq > (SYSCLK >> (prescale + 9))) + break; + + /* next, back off prescaler whilst getting divider into optimum range */ + divide = 1; + while ((prescale > 5) && (divide < 32)) + { + prescale--; + divide <<= 1; + } + divide >>= 1; + + /* now fine-tune the divider for best match */ + for (; divide < 31; divide++) + if (freq >= ((SYSCLK >> (prescale + 9)) / (divide + 1))) + break; + + /* divide = 0 is illegal, but don't let prescale = 4! */ + if (divide == 0) + { + divide++; + if (prescale > 5) + prescale--; + } + vWrIDR (devc, 0x06, (WORD) (0x9000 | (prescale << 5) | divide)); +} + +/* + ***************************************************************************** + */ + +static int +maestro_audio_set_rate (int dev, int arg) +{ + maestro_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 +maestro_audio_set_channels (int dev, short arg) +{ + maestro_portc *portc = audio_engines[dev]->portc; + + if ((arg != 1) && (arg != 2)) + return portc->channels; + portc->channels = arg; + + return portc->channels; +} + +static unsigned int +maestro_audio_set_format (int dev, unsigned int arg) +{ + maestro_portc *portc = audio_engines[dev]->portc; + + if (arg == 0) + return portc->bits; + + if (!(arg & (AFMT_U8 | AFMT_S16_LE))) + return portc->bits; + portc->bits = arg; + + return portc->bits; +} + +/*ARGSUSED*/ +static int +maestro_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg) +{ + return OSS_EINVAL; +} + +static void maestro_audio_trigger (int dev, int state); + +static void +maestro_audio_reset (int dev) +{ + maestro_audio_trigger (dev, 0); +} + +static void +maestro_audio_reset_input (int dev) +{ + maestro_portc *portc = audio_engines[dev]->portc; + maestro_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_INPUT); +} + +static void +maestro_audio_reset_output (int dev) +{ + maestro_portc *portc = audio_engines[dev]->portc; + maestro_audio_trigger (dev, portc->trigger_bits & ~PCM_ENABLE_OUTPUT); +} + +/*ARGSUSED*/ +static int +maestro_audio_open (int dev, int mode, int open_flags) +{ + oss_native_word flags; + maestro_portc *portc = audio_engines[dev]->portc; + maestro_devc *devc = audio_engines[dev]->devc; + + MUTEX_ENTER_IRQDISABLE (devc->lock, flags); + if (portc->open_mode) + { + MUTEX_EXIT_IRQRESTORE (devc->lock, flags); + return OSS_EBUSY; + } +#if 1 + if ((mode & OPEN_READ)) + { + audio_engines[dev]->flags |= ADEV_16BITONLY; + } + else + { + audio_engines[dev]->flags &= ~(ADEV_16BITONLY); + } +#endif + if (devc->open_mode & mode) + { + MUTEX_EXIT_IRQRESTORE (devc->lock, flags); + return OSS_EBUSY; + } + + devc->open_mode |= mode; + + portc->open_mode = mode; + portc->audio_enabled &= ~mode; + MUTEX_EXIT_IRQRESTORE (devc->lock, flags); + + return 0; +} + +static void +maestro_audio_close (int dev, int mode) +{ + maestro_portc *portc = audio_engines[dev]->portc; + maestro_devc *devc = audio_engines[dev]->devc; + + maestro_audio_reset (dev); + portc->open_mode = 0; + devc->open_mode &= ~mode; + portc->audio_enabled &= ~mode; +} + +/*ARGSUSED*/ +static void +maestro_audio_output_block (int dev, oss_native_word buf, int count, + int fragsize, int intrflag) +{ + maestro_portc *portc = audio_engines[dev]->portc; + + portc->audio_enabled |= PCM_ENABLE_OUTPUT; + portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; +} + +/*ARGSUSED*/ +static void +maestro_audio_start_input (int dev, oss_native_word buf, int count, + int fragsize, int intrflag) +{ + maestro_portc *portc = audio_engines[dev]->portc; + + portc->audio_enabled |= PCM_ENABLE_INPUT; + portc->trigger_bits &= ~PCM_ENABLE_INPUT; + +} + +static void +maestro_audio_trigger (int dev, int state) +{ + oss_native_word flags; + maestro_portc *portc = audio_engines[dev]->portc; + maestro_devc *devc = audio_engines[dev]->devc; + + MUTEX_ENTER_IRQDISABLE (devc->lock, 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->bits == AFMT_U8) + { + vSetAPUType (devc, bAPURPlay, 0x03); + if (portc->channels == 2) + { + vSetAPUType (devc, bAPURPlay, 0x04); + vSetAPUType (devc, bAPULPlay, 0x04); + } + } + if (portc->bits == AFMT_S16_LE) + { + vSetAPUType (devc, bAPURPlay, 0x01); + if (portc->channels == 2) + { + vSetAPUType (devc, bAPURPlay, 0x02); + vSetAPUType (devc, bAPULPlay, 0x02); + } + } + portc->trigger_bits |= PCM_ENABLE_OUTPUT; + } + } + else + { + if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) && + (portc->trigger_bits & PCM_ENABLE_OUTPUT)) + { + portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; + portc->audio_enabled &= ~PCM_ENABLE_OUTPUT; + vSetAPUType (devc, bAPURPlay, 0x0000); + vSetAPUType (devc, bAPULPlay, 0x0000); + } + } + } + + if (portc->open_mode & OPEN_READ) + { + if (state & PCM_ENABLE_INPUT) + { + if ((portc->audio_enabled & PCM_ENABLE_INPUT) && + !(portc->trigger_bits & PCM_ENABLE_INPUT)) + { + vSetAPUType (devc, bAPULMix, 0x09); + vSetAPUType (devc, bAPULSrc, 0x0B); + if (portc->channels == 2) + { + vSetAPUType (devc, bAPURMix, 0x09); + vSetAPUType (devc, bAPURSrc, 0x0B); + } + portc->trigger_bits |= PCM_ENABLE_INPUT; + } + } + else + { + if ((portc->audio_enabled & PCM_ENABLE_INPUT) && + (portc->trigger_bits & PCM_ENABLE_INPUT)) + { + portc->trigger_bits &= ~PCM_ENABLE_INPUT; + portc->audio_enabled &= ~PCM_ENABLE_INPUT; + vSetAPUType (devc, bAPURMix, 0x00); + vSetAPUType (devc, bAPURSrc, 0x00); + vSetAPUType (devc, bAPULMix, 0x00); + vSetAPUType (devc, bAPULSrc, 0x00); + } + } + } + MUTEX_EXIT_IRQRESTORE (devc->lock, flags); +} + +/*ARGSUSED*/ +static int +maestro_audio_prepare_for_input (int dev, int bsize, int bcount) +{ + maestro_devc *devc = audio_engines[dev]->devc; + maestro_portc *portc = audio_engines[dev]->portc; + dmap_t *dmap = audio_engines[dev]->dmap_in; + + oss_native_word flags; + + WORD wIndex, channel; + unsigned int wSampleRate = portc->speed; + int skip = 2; + +#if 0 + if (portc->channels == 2) + { + cmn_err (CE_WARN, "Stereo recording not supported\n"); + return OSS_EIO; + } +#endif + + MUTEX_ENTER_IRQDISABLE (devc->lock, flags); + + devc->wApuBufferSize = dmap->bytes_in_use >> 1; + + if (wSampleRate > 47999) + wSampleRate = 47999; + if (wSampleRate < 5000) + wSampleRate = 5000; + + + if (portc->channels == 2) + { + devc->wApuBufferSize >>= 1; + if (portc->bits == 16) + wSampleRate <<= 1; + skip = 1; + } + + for (channel = 2; channel < 6; channel += skip) + { + int bsize, route; + unsigned int physaddr; + unsigned int rate; + + /* Clear out the APU */ + for (wIndex = 0; wIndex < 16; wIndex++) + vWrAPUReg (devc, channel, (WORD) wIndex, 0x0000); + + /* data seems to flow from the codec, through an apu into + the 'mixbuf' bit of page, then through the SRC apu + and out to the real 'buffer'. */ + + if (channel & 0x04) + { + /* codec to mixbuf left */ + if (!(channel & 0x01)) + physaddr = devc->dma_mix.base; + else + /* codec to mixbuf right */ + physaddr = devc->dma_mix.base + (PAGE_SIZE >> 4); + + + bsize = PAGE_SIZE >> 5; /* 256 bytes needed for Mixbuf */ + route = 0x14 + (channel - 4); /* input routed from parallelin base */ + + /* The Mixer always runs at 48Khz. */ + rate = 0x10000; + vSetAPUFrequency (devc, channel, rate); + + } + else + { + /* sample rate converter takes input from the mixer apu and + outputs it to system memory */ + + if (!(channel & 0x01)) + /* left channel records its half */ + physaddr = dmap->dmabuf_phys; + + else + /* right channel records its half */ + physaddr = dmap->dmabuf_phys + devc->wApuBufferSize * 2; + + bsize = devc->wApuBufferSize; + /* get input from inputing apu */ + route = channel + 2; + + /* SRC takes 48Khz data from mixer and converts it to requested rate */ + rate = (DWORD) wCalculateAPUFrequency (wSampleRate); + vSetAPUFrequency (devc, channel, rate); + + + } + + /* set the wavecache control reg */ + vSetWCChannelTagAddr (devc, channel, + (LOWORD (physaddr) - 0x10) & 0xFFF8); + vSetWCChannelStereo (devc, channel, 0); + vSetWCChannelWordSize (devc, channel, 1); + + physaddr -= devc->dmapages_phys; + physaddr >>= 1; /* words */ + + /* base offset of dma calcs when reading the pointer + on this left one */ +#if 0 + if (channel == 2) + devc->dma_adc.base = physaddr & 0xFFFF; +#endif + physaddr |= 0x00400000; /* bit 22 -> System RAM */ + + /* Load the buffer into the wave engine */ + vSetAPUWavePtr (devc, channel, physaddr, bsize); + + /* Now set the rest of the APU params */ + vSetAPUSubmixMode (devc, channel, ENABLE); + vSetAPUSubmixGroup (devc, channel, 0x0); + vSetAPU6dB (devc, channel, DISABLE); + vSetAPUAmplitudeNow (devc, channel, 0xF0); + vSetAPUFilterTuning (devc, channel, 0x8F); + vSetAPUPolarPan (devc, channel, 0x08); + vSetAPUFilterType (devc, channel, 0x03); + vSetAPUFilterQ (devc, channel, 0x03); + vSetAPUDMA (devc, channel, ENABLE); + + /* route input */ + vSetAPUDataSourceA (devc, channel, route); + } + + vMskIDRBit (devc, 0x11, 0); + vMskIDRBit (devc, 0x17, 0); /* Disable Bob Timer Interrupts */ + vSetTimer (devc, SYSCLK / (devc->wApuBufferSize)); + vSetIDRBit (devc, 0x11, 0); + vSetIDRBit (devc, 0x17, 0); /* Enable Bob Timer Interrupts */ + portc->audio_enabled &= ~PCM_ENABLE_INPUT; + portc->trigger_bits &= ~PCM_ENABLE_INPUT; + + MUTEX_EXIT_IRQRESTORE (devc->lock, flags); + return 0; +} + +/*ARGSUSED*/ +static int +maestro_audio_prepare_for_output (int dev, int bsize, int bcount) +{ + maestro_devc *devc = audio_engines[dev]->devc; + maestro_portc *portc = audio_engines[dev]->portc; + dmap_t *dmap = audio_engines[dev]->dmap_out; + + oss_native_word flags; + + WORD wIndex, i; + DWORD dwPhysAddr; + unsigned int wSampleRate = portc->speed; + int numchans = 0; + + MUTEX_ENTER_IRQDISABLE (devc->lock, flags); + + devc->wApuBufferSize = dmap->bytes_in_use >> 1; + + if ((portc->bits == 8) && (portc->channels == 1)) + { + wSampleRate >>= 1; + } + + if (portc->channels == 2) + { + numchans++; + if (portc->bits == 16) + devc->wApuBufferSize >>= 1; + } + + for (i = 0; i <= numchans; i++) + { + + dwPhysAddr = dmap->dmabuf_phys; + + vSetWCChannelTagAddr (devc, i, + (WORD) ((LOWORD (dwPhysAddr) - 0x10) & 0xFFF8)); + + if (portc->bits == 16) + vSetWCChannelWordSize (devc, i, 1); + else + vSetWCChannelWordSize (devc, i, 0); + + if (portc->channels == 2) + vSetWCChannelStereo (devc, i, 1); + else + vSetWCChannelStereo (devc, i, 0); + + /* Calculate WP base address */ + dwPhysAddr -= devc->dmapages_phys; + dwPhysAddr >>= 1; /* adjust for word size */ +#if 0 + if (i) + devc->dma_dac.base = dwPhysAddr & 0xFFFF; +#endif + dwPhysAddr |= 0x00400000L; /* Enable bit 22 for system ram access */ + + + if (portc->channels == 2) + { + if (!i) + dwPhysAddr |= 0x00800000; + if (portc->bits == 16) + { + dwPhysAddr >>= 1; + } + } + for (wIndex = 0; wIndex < 16; wIndex++) + vWrAPUReg (devc, i, (BYTE) wIndex, 0x0000); + + vSetAPUFrequency (devc, i, + (DWORD) wCalculateAPUFrequency (wSampleRate)); + vSetAPUWavePtr (devc, i, dwPhysAddr, devc->wApuBufferSize); + vSetAPU6dB (devc, i, DISABLE); + vSetAPUAmplitudeNow (devc, i, 0xF0); + vSetAPUFilterTuning (devc, i, 0x8F); + if (portc->channels == 2) + vSetAPUPolarPan (devc, i, (i ? 0x10 : 0x00)); + else + vSetAPUPolarPan (devc, i, 0x08); + vSetAPUFilterType (devc, i, 0x03); + vSetAPUFilterQ (devc, i, 0x03); + vSetAPUDMA (devc, i, ENABLE); + } + + vMskIDRBit (devc, 0x11, 0); + vMskIDRBit (devc, 0x17, 0); /* Disable Bob Timer Interrupts */ + vSetTimer (devc, SYSCLK / devc->wApuBufferSize); + vSetIDRBit (devc, 0x11, 0); + vSetIDRBit (devc, 0x17, 0); /* Enable Bob Timer Interrupts */ + portc->audio_enabled &= ~PCM_ENABLE_OUTPUT; + portc->trigger_bits &= ~PCM_ENABLE_OUTPUT; + MUTEX_EXIT_IRQRESTORE (devc->lock, flags); + return 0; +} + +static int +maestro_alloc_buffer (int dev, dmap_t * dmap, int direction) +{ + maestro_devc *devc = audio_engines[dev]->devc; + + if (dmap->dmabuf != NULL) + return 0; + + if (direction == PCM_ENABLE_OUTPUT) + { + dmap->dmabuf = devc->dma_dac.rawbuf; + dmap->dmabuf_phys = devc->dma_dac.base; + dmap->buffsize = devc->dma_dac.dmasize; + } + + if (direction == PCM_ENABLE_INPUT) + { + dmap->dmabuf = devc->dma_adc.rawbuf; + dmap->dmabuf_phys = devc->dma_adc.base; + dmap->buffsize = devc->dma_adc.dmasize; + } + return 0; +} + +/*ARGSUSED*/ +static int +maestro_free_buffer (int dev, dmap_t * dmap, int direction) +{ + if (dmap->dmabuf == NULL) + return 0; + + dmap->dmabuf = NULL; + dmap->dmabuf_phys = 0; + dmap->buffsize = 0; + + return 0; +} + +/*ARGSUSED*/ +static int +maestro_get_buffer_pointer (int dev, dmap_t * dmap, int direction) +{ + maestro_devc *devc = audio_engines[dev]->devc; + maestro_portc *portc = audio_engines[dev]->portc; + int ptr = 0; + oss_native_word flags; + + if (!(portc->open_mode & direction)) + return 0; + + MUTEX_ENTER_IRQDISABLE (devc->low_lock, flags); + if (direction == PCM_ENABLE_INPUT) + { + ptr = wRdAPUReg (devc, bAPULSrc, 0x05); + ptr = (ptr - devc->dma_adc.base) & 0xFFFE; + } + + if (direction == PCM_ENABLE_OUTPUT) + { + ptr = wRdAPUReg (devc, bAPURPlay, 0x05); + ptr = (ptr - devc->dma_dac.base) & 0xFFFE; + } + ptr = (ptr % devc->wApuBufferSize) << 1; + MUTEX_EXIT_IRQRESTORE (devc->low_lock, flags); + return (ptr); +} + +static void +set_maestro_base (maestro_devc * devc) +{ + unsigned long packed_phys = devc->dmapages_phys >> 12; + vWrWCReg (devc, 0x01FC, packed_phys); + vWrWCReg (devc, 0x01FD, packed_phys); + vWrWCReg (devc, 0x01FE, packed_phys); + vWrWCReg (devc, 0x01FF, packed_phys); +} + +static int +allocate_maestro_bufs (maestro_devc * devc) +{ + unsigned char *rawbuf = NULL; + int size, extra, start; + oss_native_word phaddr; + + /* size = size of rec/play buffers + * start = offset from beginning for rec/play buffers + * extra=size of mix buf + start + */ +#if defined(sun) || defined(linux) + size = 32 * 1024; + start = 16 * 1024; + extra = 16 * 1024; +#else +#if defined (__FreeBSD__) + size = 64 * 1024; + start = 8 * 1024; + extra = 16 * 1024; +#else + size = 96 * 1024; + start = 16 * 1024; + extra = 16 * 1024; +#endif +#endif + devc->dmalen = size + extra; + + rawbuf = + (void *) CONTIG_MALLOC (devc->osdev, devc->dmalen, MEMLIMIT_28BITS, + &phaddr, TODO); + + if (!rawbuf) + return 1; + + DDB (cmn_err (CE_WARN, "addr=%p, size=%d\n", (void *) rawbuf, size)); + + if ((phaddr + size - 1) & ~((1 << 28) - 1)) + { + cmn_err (CE_WARN, "DMA buffer beyond 256MB\n"); + CONTIG_FREE (devc->osdev, rawbuf, size + extra, TODO); + return 1; + } + + devc->dmapages = rawbuf; + devc->dmapages_phys = phaddr; + + devc->dma_dac.rawbuf = rawbuf + start; + devc->dma_dac.dmasize = size / 2; + devc->dma_dac.base = phaddr + start; + + devc->dma_adc.rawbuf = devc->dma_dac.rawbuf + size / 2; + devc->dma_adc.dmasize = size / 2; + devc->dma_adc.base = devc->dma_dac.base + size / 2; + + devc->dma_mix.rawbuf = rawbuf + 4096; + devc->dma_mix.base = phaddr + 4096; + devc->dma_mix.dmasize = 4096; +#ifdef linux + /* reserve the pages for MMAP */ + oss_reserve_pages ((oss_native_word) devc->dma_dac.rawbuf, + (oss_native_word) devc->dma_dac.rawbuf + + devc->dma_dac.dmasize - 1); +#endif + return 0; +} + +static audiodrv_t maestro_audio_driver = { + maestro_audio_open, + maestro_audio_close, + maestro_audio_output_block, + maestro_audio_start_input, + maestro_audio_ioctl, + maestro_audio_prepare_for_input, + maestro_audio_prepare_for_output, + maestro_audio_reset, + NULL, + NULL, + maestro_audio_reset_input, + maestro_audio_reset_output, + maestro_audio_trigger, + maestro_audio_set_rate, + maestro_audio_set_format, + maestro_audio_set_channels, + NULL, + NULL, + NULL, /* maestro_check_input, */ + NULL, /* maestro_check_output, */ + maestro_alloc_buffer, + maestro_free_buffer, + NULL, + NULL, + maestro_get_buffer_pointer +}; + +static mixer_driver_t pt101_mixer_driver = { + pt101_mixer_ioctl +}; + + +static void +maestro_ac97_reset (maestro_devc * devc) +{ + int save_68; + +/* Reset the Codec */ + OUTW (devc->osdev, INW (devc->osdev, devc->base + 0x38) & 0xfffc, + devc->base + 0x38); + OUTW (devc->osdev, INW (devc->osdev, devc->base + 0x3a) & 0xfffc, + devc->base + 0x3a); + OUTW (devc->osdev, INW (devc->osdev, devc->base + 0x3c) & 0xfffc, + devc->base + 0x3c); + /* reset the first codec */ + OUTW (devc->osdev, 0x0000, devc->base + 0x36); + save_68 = INW (devc->osdev, devc->base + 0x68); + if (devc->gpio & 0x1) + save_68 |= 0x10; + OUTW (devc->osdev, 0xfffe, devc->base + 0x64); /* tickly gpio 0.. */ + OUTW (devc->osdev, 0x0001, devc->base + 0x68); + OUTW (devc->osdev, 0x0000, devc->base + 0x60); + oss_udelay (10); + OUTW (devc->osdev, 0x0001, devc->base + 0x60); + oss_udelay (100); + OUTW (devc->osdev, save_68 | 0x1, devc->base + 0x68); /* now restore .. */ + OUTW (devc->osdev, (INW (devc->osdev, devc->base + 0x38) & 0xfffc) | 0x1, + devc->base + 0x38); + OUTW (devc->osdev, (INW (devc->osdev, devc->base + 0x3a) & 0xfffc) | 0x1, + devc->base + 0x3a); + OUTW (devc->osdev, (INW (devc->osdev, devc->base + 0x3c) & 0xfffc) | 0x1, + devc->base + 0x3c); + + /* now the second codec */ + OUTW (devc->osdev, 0x0000, devc->base + 0x36); + OUTW (devc->osdev, 0xfff7, devc->base + 0x64); + save_68 = INW (devc->osdev, devc->base + 0x68); + OUTW (devc->osdev, 0x0009, devc->base + 0x68); + OUTW (devc->osdev, 0x0001, devc->base + 0x60); + oss_udelay (10); + OUTW (devc->osdev, 0x0009, devc->base + 0x60); + oss_udelay (100); + OUTW (devc->osdev, INW (devc->osdev, devc->base + 0x38) & 0xfffc, + devc->base + 0x38); + OUTW (devc->osdev, INW (devc->osdev, devc->base + 0x3a) & 0xfffc, + devc->base + 0x3a); + OUTW (devc->osdev, INW (devc->osdev, devc->base + 0x3c) & 0xfffc, + devc->base + 0x3c); +} + +static int +init_maestro (maestro_devc * devc) +{ + int my_mixer; + int i; + oss_native_word n; + unsigned short w; + int wIndex; + int first_dev = 0; + int codec_id; + int bRow, bAPU; + +/* Sound reset */ + OUTW (devc->osdev, 0x2000, 0x18 + devc->base); + oss_udelay (10); + OUTW (devc->osdev, 0x0000, 0x18 + devc->base); + oss_udelay (10); + +/* Setup 0x34 and 0x36 regs */ + OUTW (devc->osdev, 0xc090, devc->base + 0x34); + oss_udelay (1000); + OUTW (devc->osdev, 0x3000, devc->base + 0x36); + oss_udelay (1000); + +/* reset ac97 link */ + maestro_ac97_reset (devc); + +/* DirectSound*/ + n = INL (devc->osdev, devc->base + 0x34); + n &= ~0xF000; + n |= 12 << 12; /* Direct Sound, Stereo */ + OUTL (devc->osdev, n, devc->base + 0x34); + + n = INL (devc->osdev, devc->base + 0x34); + n &= ~0x0F00; /* Modem off */ + OUTL (devc->osdev, n, devc->base + 0x34); + + n = INL (devc->osdev, devc->base + 0x34); + n &= ~0x00F0; + n |= 9 << 4; /* DAC, Stereo */ + OUTL (devc->osdev, n, devc->base + 0x34); + + n = INL (devc->osdev, devc->base + 0x34); + n &= ~0x000F; /* ASSP off */ + OUTL (devc->osdev, n, devc->base + 0x34); + + n = INL (devc->osdev, devc->base + 0x34); + n |= (1 << 29); /* Enable ring bus */ + OUTL (devc->osdev, n, devc->base + 0x34); + + n = INL (devc->osdev, devc->base + 0x34); + n |= (1 << 28); /* Enable serial bus */ + OUTL (devc->osdev, n, devc->base + 0x34); + + n = INL (devc->osdev, devc->base + 0x34); + n &= ~0x00F00000; /* MIC off */ + OUTL (devc->osdev, n, devc->base + 0x34); + + n = INL (devc->osdev, devc->base + 0x34); + n &= ~0x000F0000; /* I2S off */ + OUTL (devc->osdev, n, devc->base + 0x34); + + w = INW (devc->osdev, devc->base + 0x18); + w &= ~(1 << 7); /* ClkRun off */ + OUTW (devc->osdev, w, devc->base + 0x18); + + w = INW (devc->osdev, devc->base + 0x18); + w &= ~(1 << 6); /* Harpo off */ + OUTW (devc->osdev, w, devc->base + 0x18); + + w = INW (devc->osdev, devc->base + 0x18); + w &= ~(1 << 4); /* ASSP irq off */ + OUTW (devc->osdev, w, devc->base + 0x18); + + w = INW (devc->osdev, devc->base + 0x18); + w &= ~(1 << 3); /* ISDN irq off */ + OUTW (devc->osdev, w, devc->base + 0x18); + + w = INW (devc->osdev, devc->base + 0x18); + w |= (1 << 2); /* Direct Sound IRQ on */ + OUTW (devc->osdev, w, devc->base + 0x18); + + w = INW (devc->osdev, devc->base + 0x18); + w &= ~(1 << 1); /* MPU401 IRQ off */ + OUTW (devc->osdev, w, devc->base + 0x18); + + w = INW (devc->osdev, devc->base + 0x18); + w &= ~(1 << 0); /* SB IRQ on */ + OUTW (devc->osdev, w, devc->base + 0x18); + + OUTB (devc->osdev, 0x00, gwPTBaseIO + 0xA4); + OUTB (devc->osdev, 0x03, gwPTBaseIO + 0xA2); + OUTB (devc->osdev, 0x00, gwPTBaseIO + 0xA6); + + if (devc->model == ESS_MAESTRO) + { +/* Clear out wavecache */ + for (wIndex = 0; wIndex < 0x0200; wIndex++) + vWrWCReg (devc, wIndex, 0x0000); +/* Clear out APU */ + for (wIndex = 0; wIndex < 0x40; wIndex++) + vWrAPUReg (devc, wIndex, 0x00, 0x0000); +/* Clear out WP */ + for (wIndex = 0; wIndex < 0x1F; wIndex++) + devc->wIDRRegDataTable[wIndex] = 0x0000; + } + else + { +/* Clear out Wave Cache */ + for (bRow = 0; bRow < 0x10; bRow++) + { + vWrWCReg (devc, (WORD) (0x01E0 + bRow), 0x0000); + } + + /* Clear Control RAM */ + for (bAPU = 0x00; bAPU < 0x40; bAPU++) + { + for (bRow = 0; bRow < 0x0E; bRow++) + { + vWrAPUReg (devc, bAPU, bRow, 0x0000); + } + } + } + + vWrIDR (devc, 0x02, 0x0000); /* CRam Increment */ + vWrIDR (devc, 0x08, 0xB004); /* Audio Serial Configuration */ + vWrIDR (devc, 0x09, 0x001B); /* Audio Serial Configuration */ + vWrIDR (devc, 0x0A, 0x8000); /* Audio Serial Configuration */ + vWrIDR (devc, 0x0B, 0x3F37); /* Audio Serial Configuration */ + vWrIDR (devc, 0x0C, 0x0098); + + /* parallel out */ + vWrIDR (devc, 0x0C, (wRdIDR (devc, 0x0C) & ~0xF000) | 0x8000); + /* parallel in */ + vWrIDR (devc, 0x0C, (wRdIDR (devc, 0x0C) & ~0x0F00) | 0x0500); + + vWrIDR (devc, 0x0D, 0x7632); /*Audio Serial Configuration */ + OUTW (devc->osdev, INW (devc->osdev, 0x14 + devc->base) | (1 << 8), + 0x14 + devc->base); + OUTW (devc->osdev, INW (devc->osdev, 0x14 + devc->base) & 0xFE03, + 0x14 + devc->base); + OUTW (devc->osdev, (INW (devc->osdev, 0x14 + devc->base) & 0xFFFC), + 0x14 + devc->base); + OUTW (devc->osdev, INW (devc->osdev, 0x14 + devc->base) | (1 << 7), + 0x14 + devc->base); + + /* enable the Wavecache */ + vSetWCEnable (devc, ENABLE); + /* Unmask WP interrupts */ + OUTW (devc->osdev, (WORD) (INW (devc->osdev, devc->base + 0x18) + | 0x0004), devc->base + 0x18); + + if (devc->model == ESS_MAESTRO) + { + OUTW (devc->osdev, 0xA1A0, gwPTBaseIO + 0x14); + } + else + { + OUTW (devc->osdev, 0xA1A0, gwPTBaseIO + 0x14); + } + +/* First get the Codec ID type - check if it is PT101 or AC97*/ + codec_id = ac97_read (devc, 0x00); + +/* initialize the PT101 Codec */ + if (codec_id == 0x80) + { + ac97_write (devc, 0x09, 0x0000); /*DAC Mute */ + ac97_write (devc, 0x0F, 0x0000); + ac97_write (devc, 0x11, 0x0000); + ac97_write (devc, 0x14, 0x0000); + ac97_write (devc, 0x1A, 0x0105); + ac97_write (devc, 0x1E, 0x0101); /*set PT101 reg 0x1F */ + ac97_write (devc, 0x1F, 0x8080); /*set PT101 reg 0x1F */ + } + + if ((devc->model == ESS_MAESTRO2E) || (devc->model == ESS_MAESTRO2)) + { + outpw (gwPTBaseIO + 0x02, (WORD) 0x0001); + outpw (gwPTBaseIO + 0x00, (WORD) 0x03C0); + outpw (gwPTBaseIO + 0x02, (WORD) 0x1); + outpw (gwPTBaseIO + 0x02, (WORD) 0x1); + outpw (gwPTBaseIO + 0x00, (WORD) 0x3c0); + outpw (gwPTBaseIO + 0x02, (WORD) 0x0000); + outpw (gwPTBaseIO + 0x00, (WORD) 0x4010); + outpw (gwPTBaseIO + 0x02, (WORD) 0x0001); + outpw (gwPTBaseIO + 0x00, (WORD) 0x03D0); + outpw (gwPTBaseIO + 0x02, (WORD) 0x1); + outpw (gwPTBaseIO + 0x02, (WORD) 0x1); + outpw (gwPTBaseIO + 0x00, (WORD) 0x3d0); + outpw (gwPTBaseIO + 0x02, (WORD) 0x0000); + outpw (gwPTBaseIO + 0x00, (WORD) 0x4010); + } + else + { + outpw (gwPTBaseIO + 0x02, (WORD) 0x0001); + outpw (gwPTBaseIO + 0x00, (WORD) 0x03C0); + outpw (gwPTBaseIO + 0x02, (WORD) 0x00); + outpw (gwPTBaseIO + 0x00, (WORD) 0x401F); + + outpw (gwPTBaseIO + 0x02, (WORD) 0x0001); + outpw (gwPTBaseIO + 0x00, (WORD) 0x03D0); + outpw (gwPTBaseIO + 0x02, (WORD) 0x00); + outpw (gwPTBaseIO + 0x00, (WORD) 0x401F); + } + +/***********************ALLOCATE MEMORY************************/ + { + int ret; + + ret = allocate_maestro_bufs (devc); + if (ret != 0) + { + cmn_err (CE_WARN, "Couldn't allocate Maestro memory\n"); + return 1; + } + set_maestro_base (devc); + } +/******************INIT MIXERS*********************************/ + if (codec_id == 0x80) + { + my_mixer = oss_install_mixer (OSS_MIXER_DRIVER_VERSION, + devc->osdev, + devc->osdev, + "ESS PT101", + &pt101_mixer_driver, + sizeof (mixer_driver_t), devc); + if (my_mixer < 0) + return 0; + else + pt101_mixer_reset (devc); + } + else + { + /* Reset the codec */ + my_mixer = ac97_install (&devc->ac97devc, "Maestro2 AC97 Mixer", + ac97_read, ac97_write, devc, devc->osdev); + if (my_mixer < 0) + return 0; + } + for (i = 0; i < MAX_PORTC; i++) + { + int adev; + int caps = ADEV_AUTOMODE; + maestro_portc *portc = &devc->portc[i]; + char tmp_name[100]; + strcpy (tmp_name, devc->chip_name); + + if (i == 0) + { + strcpy (tmp_name, devc->chip_name); + caps |= ADEV_DUPLEX; + } + else + { + strcpy (tmp_name, devc->chip_name); + caps |= ADEV_DUPLEX | ADEV_SHADOW; + } + + if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION, + devc->osdev, + devc->osdev, + tmp_name, + &maestro_audio_driver, + sizeof (audiodrv_t), + caps, + AFMT_U8 | AFMT_S16_LE, 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; +#if 0 + audio_engines[adev]->min_block = 4096; + audio_engines[adev]->max_block = 4096; +#endif + audio_engines[adev]->min_rate = 5000; + audio_engines[adev]->max_rate = 48000; + audio_engines[adev]->caps |= PCM_CAP_FREERATE; + portc->audiodev = adev; + portc->open_mode = 0; + portc->trigger_bits = 0; + portc->speed = 0; + portc->bits = 0; + portc->channels = 0; + audio_engines[adev]->mixer_dev = my_mixer; + audio_engines[adev]->vmix_flags = VMIX_MULTIFRAG; +#ifdef CONFIG_OSS_VMIX + if (i == 0) + vmix_attach_audiodev(devc->osdev, adev, -1, 0); +#endif + } + } + return 1; +} + +/* NEC Versas ? */ +#define NEC_VERSA_SUBID1 0x80581033 +#define NEC_VERSA_SUBID2 0x803c1033 + +int +oss_maestro_attach (oss_device_t * osdev) +{ + unsigned short sdata; + unsigned char pci_irq_line, pci_revision; + unsigned short pci_command, vendor, device; + unsigned int pci_ioaddr, subid; + maestro_devc *devc; + + DDB (cmn_err (CE_WARN, "Entered ESS Maestro probe routine\n")); + + pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor); + pci_read_config_dword (osdev, PCI_SUBSYSTEM_VENDOR_ID, &subid); + pci_read_config_byte (osdev, PCI_REVISION_ID, &pci_revision); + pci_read_config_word (osdev, PCI_COMMAND, &pci_command); + pci_read_config_word (osdev, PCI_DEVICE_ID, &device); + pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line); + pci_read_config_dword (osdev, PCI_BASE_ADDRESS_0, &pci_ioaddr); + if ((vendor != ESS_VENDOR_ID && vendor != ESS2_VENDOR_ID) || + (device != ESS_MAESTRO && device != ESS_MAESTRO2 && + device != ESS_MAESTRO2E)) + + return 0; + + DDB (cmn_err (CE_WARN, "Maestro I/O base %04x\n", pci_ioaddr)); + + + 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 (%d).\n", pci_irq_line); + 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->base = MAP_PCI_IOADDR (devc->osdev, 0, pci_ioaddr); + /* Remove I/O space marker in bit 0. */ + devc->base &= ~0x3; + + pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_IO; + pci_write_config_word (osdev, PCI_COMMAND, pci_command); + + switch (device) + { + case ESS_MAESTRO2E: + devc->model = MD_MAESTRO2E; + devc->chip_name = "Maestro-2E"; + SYSCLK = 50000000L; + break; + + case ESS_MAESTRO2: + devc->model = MD_MAESTRO2; + devc->chip_name = "Maestro-2"; + SYSCLK = 50000000L; + break; + + case ESS_MAESTRO: + default: + devc->model = MD_MAESTRO1; + devc->chip_name = "Maestro-1"; + SYSCLK = 49152000L; + break; + } + + if (subid == NEC_VERSA_SUBID1 || subid == NEC_VERSA_SUBID2) + { + /* turn on external amp? */ + OUTW (devc->osdev, 0xf9ff, devc->base + 0x64); + OUTW (devc->osdev, INW (devc->osdev, devc->base + 0x68) | 0x600, + devc->base + 0x68); + OUTW (devc->osdev, 0x0209, devc->base + 0x60); + } + + + /* Legacy Audio Control */ + pci_read_config_word (osdev, 0x40, &sdata); + sdata |= (1 << 15); /* legacy decode off */ + sdata &= ~(1 << 14); /* Disable SIRQ */ + sdata &= ~(0x1f); /* disable mpu irq/io, game port, fm, SB */ + + pci_write_config_word (osdev, 0x40, sdata); + + /* MIDI/SB I/O, IRQ Control */ + pci_read_config_word (osdev, 0x50, &sdata); + sdata &= ~(1 << 5); + pci_write_config_word (osdev, 0x50, sdata); + + /* GPIO Control */ + pci_read_config_word (osdev, 0x52, &sdata); + sdata &= ~(1 << 15); /* Turn off internal clock multiplier */ + /* XXX how do we know which to use? */ + sdata &= ~(1 << 14); /* External clock */ + sdata &= ~(1 << 7); /* HWV off */ + sdata &= ~(1 << 6); /* Debounce off */ + sdata &= ~(1 << 5); /* GPIO 4:5 */ + sdata |= (1 << 4); /* Disconnect from the CHI. */ + /* Enabling this made dell 7500 work. */ + sdata &= ~(1 << 2); /* MIDI fix off (undoc) */ + sdata &= ~(1 << 1); /* reserved, always write 0 */ + pci_write_config_word (osdev, 0x52, sdata); + if (devc->model == ESS_MAESTRO2E) + { + pci_write_config_word (osdev, 0x54, 0x0000); + pci_write_config_word (osdev, 0x56, 0x0000); + pci_write_config_word (osdev, 0x58, 0x0000); + pci_write_config_word (osdev, 0xC4, 0x8000); + pci_read_config_word (osdev, 0x68, &devc->gpio); + } + + MUTEX_INIT (devc->osdev, devc->lock, MH_DRV); + MUTEX_INIT (devc->osdev, devc->low_lock, MH_DRV + 1); + + oss_register_device (osdev, devc->chip_name); + + if (oss_register_interrupts (devc->osdev, 0, maestrointr, NULL) < 0) + { + cmn_err (CE_WARN, "Can't allocate IRQ%d\n", pci_irq_line); + return 0; + } + + return init_maestro (devc); /* Detected */ +} + + +int +oss_maestro_detach (oss_device_t * osdev) +{ + maestro_devc *devc = (maestro_devc *) osdev->devc; + + if (oss_disable_device (osdev) < 0) + return 0; + + /* Stop WP interrutps */ + OUTW (devc->osdev, (WORD) (INW (devc->osdev, devc->base + 0x18) & 0xFFF8), + devc->base + 0x18); + OUTW (devc->osdev, 0x0001, devc->base + 0x04); + vSetWCEnable (devc, DISABLE); + vMskIDRBit (devc, 0x17, 0); /* Disable Bob Timer Interrupts */ + + oss_unregister_interrupts (devc->osdev); + if (devc->dmapages != NULL) + CONTIG_FREE (devc->osdev, devc->dmapages, devc->dmalen, TODO); + + MUTEX_CLEANUP (devc->lock); + MUTEX_CLEANUP (devc->low_lock); + UNMAP_PCI_IOADDR (devc->osdev, 0); + + oss_unregister_device (osdev); + return 1; + +} |