188447a05SGarrett D'Amore /*
288447a05SGarrett D'Amore * CDDL HEADER START
388447a05SGarrett D'Amore *
488447a05SGarrett D'Amore * The contents of this file are subject to the terms of the
588447a05SGarrett D'Amore * Common Development and Distribution License (the "License").
688447a05SGarrett D'Amore * You may not use this file except in compliance with the License.
788447a05SGarrett D'Amore *
888447a05SGarrett D'Amore * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
988447a05SGarrett D'Amore * or http://www.opensolaris.org/os/licensing.
1088447a05SGarrett D'Amore * See the License for the specific language governing permissions
1188447a05SGarrett D'Amore * and limitations under the License.
1288447a05SGarrett D'Amore *
1388447a05SGarrett D'Amore * When distributing Covered Code, include this CDDL HEADER in each
1488447a05SGarrett D'Amore * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1588447a05SGarrett D'Amore * If applicable, add the following below this CDDL HEADER, with the
1688447a05SGarrett D'Amore * fields enclosed by brackets "[]" replaced with your own identifying
1788447a05SGarrett D'Amore * information: Portions Copyright [yyyy] [name of copyright owner]
1888447a05SGarrett D'Amore *
1988447a05SGarrett D'Amore * CDDL HEADER END
2088447a05SGarrett D'Amore */
2188447a05SGarrett D'Amore /*
22239924d3SGarrett D'Amore * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
235b162753SGarrett D'Amore * Copyright 2016 Garrett D'Amore <garrett@damore.org>
2488447a05SGarrett D'Amore */
2588447a05SGarrett D'Amore /*
2688447a05SGarrett D'Amore * Purpose: Creative/Ensoniq AudioPCI97 driver (ES1371/ES1373)
2788447a05SGarrett D'Amore *
2888447a05SGarrett D'Amore * This driver is used with the original Ensoniq AudioPCI97 card and many
2988447a05SGarrett D'Amore * PCI based Sound Blaster cards by Creative Technologies. For example
3088447a05SGarrett D'Amore * Sound Blaster PCI128 and Creative/Ectiva EV1938.
3188447a05SGarrett D'Amore */
3288447a05SGarrett D'Amore
3388447a05SGarrett D'Amore /*
3488447a05SGarrett D'Amore * This file is part of Open Sound System
3588447a05SGarrett D'Amore *
3688447a05SGarrett D'Amore * Copyright (C) 4Front Technologies 1996-2008.
3788447a05SGarrett D'Amore *
3888447a05SGarrett D'Amore * This software is released under CDDL 1.0 source license.
3988447a05SGarrett D'Amore * See the COPYING file included in the main directory of this source
4088447a05SGarrett D'Amore * distribution for the license terms and conditions.
4188447a05SGarrett D'Amore */
4288447a05SGarrett D'Amore
4388447a05SGarrett D'Amore #include <sys/audio/audio_driver.h>
4488447a05SGarrett D'Amore #include <sys/audio/ac97.h>
4588447a05SGarrett D'Amore #include <sys/note.h>
4688447a05SGarrett D'Amore #include <sys/pci.h>
475b162753SGarrett D'Amore
485b162753SGarrett D'Amore /*
495b162753SGarrett D'Amore * For VMWare platforms, we have to utilize the (emulated) hardware interrupts
505b162753SGarrett D'Amore * of the device. This is necessary for audio playback to function, as
515b162753SGarrett D'Amore * the toggling of the interrupt bits apparently triggers logic inside the
525b162753SGarrett D'Amore * emulated device. So we need to detect this platform, and conditionally
535b162753SGarrett D'Amore * wire up the interrupt handler.
545b162753SGarrett D'Amore */
555b162753SGarrett D'Amore #ifdef __x86
565b162753SGarrett D'Amore #include <sys/x86_archext.h>
575b162753SGarrett D'Amore #endif
585b162753SGarrett D'Amore
5988447a05SGarrett D'Amore #include "audioens.h"
6088447a05SGarrett D'Amore
6188447a05SGarrett D'Amore /*
6288447a05SGarrett D'Amore * Set the latency to 32, 64, 96, 128 clocks - some APCI97 devices exhibit
6388447a05SGarrett D'Amore * garbled audio in some cases and setting the latency to higer values fixes it
6488447a05SGarrett D'Amore * Values: 32, 64, 96, 128 - Default: 64 (or defined by bios)
6588447a05SGarrett D'Amore */
6688447a05SGarrett D'Amore int audioens_latency = 0;
6788447a05SGarrett D'Amore
6888447a05SGarrett D'Amore /*
6988447a05SGarrett D'Amore * Enable SPDIF port on SoundBlaster 128D or Sound Blaster Digital-4.1 models
7088447a05SGarrett D'Amore * Values: 1=Enable 0=Disable Default: 0
7188447a05SGarrett D'Amore */
7288447a05SGarrett D'Amore int audioens_spdif = 0;
7388447a05SGarrett D'Amore
7488447a05SGarrett D'Amore /*
7588447a05SGarrett D'Amore * Note: Latest devices can support SPDIF with AC3 pass thru.
7688447a05SGarrett D'Amore * However, in order to do this, one of the two DMA engines must be
7788447a05SGarrett D'Amore * dedicated to this, which would prevent the card from supporting 4
7888447a05SGarrett D'Amore * channel audio. For now we don't bother with the AC3 pass through
7988447a05SGarrett D'Amore * mode, and instead just focus on 4 channel support. In the future,
8088447a05SGarrett D'Amore * this could be selectable via a property.
8188447a05SGarrett D'Amore */
8288447a05SGarrett D'Amore
8388447a05SGarrett D'Amore #define ENSONIQ_VENDOR_ID 0x1274
8488447a05SGarrett D'Amore #define CREATIVE_VENDOR_ID 0x1102
8588447a05SGarrett D'Amore #define ECTIVA_VENDOR_ID 0x1102
8688447a05SGarrett D'Amore #define ENSONIQ_ES1371 0x1371
8788447a05SGarrett D'Amore #define ENSONIQ_ES5880 0x8001
8888447a05SGarrett D'Amore #define ENSONIQ_ES5880A 0x8002
8988447a05SGarrett D'Amore #define ENSONIQ_ES5880B 0x5880
9088447a05SGarrett D'Amore #define ECTIVA_ES1938 0x8938
9188447a05SGarrett D'Amore
9288447a05SGarrett D'Amore #define DEFRATE 48000
9388447a05SGarrett D'Amore #define DRVNAME "audioens"
9488447a05SGarrett D'Amore
9588447a05SGarrett D'Amore typedef struct audioens_port
9688447a05SGarrett D'Amore {
9788447a05SGarrett D'Amore /* Audio parameters */
9888447a05SGarrett D'Amore int speed;
9988447a05SGarrett D'Amore
10088447a05SGarrett D'Amore int num;
10188447a05SGarrett D'Amore #define PORT_DAC 0
10288447a05SGarrett D'Amore #define PORT_ADC 1
10388447a05SGarrett D'Amore #define PORT_MAX PORT_ADC
10488447a05SGarrett D'Amore
10588447a05SGarrett D'Amore caddr_t kaddr;
10688447a05SGarrett D'Amore uint32_t paddr;
10788447a05SGarrett D'Amore ddi_acc_handle_t acch;
10888447a05SGarrett D'Amore ddi_dma_handle_t dmah;
10988447a05SGarrett D'Amore int nchan;
11088447a05SGarrett D'Amore unsigned nframes;
1115b162753SGarrett D'Amore unsigned iframes;
11288447a05SGarrett D'Amore unsigned frameno;
11388447a05SGarrett D'Amore uint64_t count;
11488447a05SGarrett D'Amore
11588447a05SGarrett D'Amore struct audioens_dev *dev;
1165b162753SGarrett D'Amore audio_engine_t *engine;
11788447a05SGarrett D'Amore } audioens_port_t;
11888447a05SGarrett D'Amore
11988447a05SGarrett D'Amore typedef struct audioens_dev
12088447a05SGarrett D'Amore {
12188447a05SGarrett D'Amore audio_dev_t *osdev;
12288447a05SGarrett D'Amore kmutex_t mutex;
12388447a05SGarrett D'Amore uint16_t devid;
12488447a05SGarrett D'Amore uint8_t revision;
12588447a05SGarrett D'Amore dev_info_t *dip;
12688447a05SGarrett D'Amore
12788447a05SGarrett D'Amore audioens_port_t port[PORT_MAX + 1];
12888447a05SGarrett D'Amore
12988447a05SGarrett D'Amore ac97_t *ac97;
13088447a05SGarrett D'Amore
13188447a05SGarrett D'Amore caddr_t regs;
13288447a05SGarrett D'Amore ddi_acc_handle_t acch;
1335b162753SGarrett D'Amore
1345b162753SGarrett D'Amore boolean_t suspended;
1355b162753SGarrett D'Amore
1365b162753SGarrett D'Amore #ifdef __x86
1375b162753SGarrett D'Amore boolean_t useintr;
1385b162753SGarrett D'Amore ddi_intr_handle_t intrh;
1395b162753SGarrett D'Amore uint_t intrpri;
1405b162753SGarrett D'Amore #endif
14188447a05SGarrett D'Amore } audioens_dev_t;
14288447a05SGarrett D'Amore
14388447a05SGarrett D'Amore static ddi_device_acc_attr_t acc_attr = {
14488447a05SGarrett D'Amore DDI_DEVICE_ATTR_V0,
14588447a05SGarrett D'Amore DDI_STRUCTURE_LE_ACC,
14688447a05SGarrett D'Amore DDI_STRICTORDER_ACC
14788447a05SGarrett D'Amore };
14888447a05SGarrett D'Amore
14988447a05SGarrett D'Amore static ddi_device_acc_attr_t buf_attr = {
15088447a05SGarrett D'Amore DDI_DEVICE_ATTR_V0,
15188447a05SGarrett D'Amore DDI_NEVERSWAP_ACC,
15288447a05SGarrett D'Amore DDI_STRICTORDER_ACC
15388447a05SGarrett D'Amore };
15488447a05SGarrett D'Amore
15588447a05SGarrett D'Amore static ddi_dma_attr_t dma_attr = {
15688447a05SGarrett D'Amore DMA_ATTR_VERSION, /* dma_attr_version */
15788447a05SGarrett D'Amore 0x0, /* dma_attr_addr_lo */
15888447a05SGarrett D'Amore 0xffffffffU, /* dma_attr_addr_hi */
15988447a05SGarrett D'Amore 0x3ffff, /* dma_attr_count_max */
16088447a05SGarrett D'Amore 0x8, /* dma_attr_align */
16188447a05SGarrett D'Amore 0x7f, /* dma_attr_burstsizes */
16288447a05SGarrett D'Amore 0x1, /* dma_attr_minxfer */
16388447a05SGarrett D'Amore 0x3ffff, /* dma_attr_maxxfer */
16488447a05SGarrett D'Amore 0x3ffff, /* dma_attr_seg */
16588447a05SGarrett D'Amore 0x1, /* dma_attr_sgllen */
16688447a05SGarrett D'Amore 0x1, /* dma_attr_granular */
16788447a05SGarrett D'Amore 0 /* dma_attr_flags */
16888447a05SGarrett D'Amore };
16988447a05SGarrett D'Amore
17088447a05SGarrett D'Amore #define GET8(dev, offset) \
17188447a05SGarrett D'Amore ddi_get8(dev->acch, (uint8_t *)(dev->regs + (offset)))
17288447a05SGarrett D'Amore #define GET16(dev, offset) \
17388447a05SGarrett D'Amore ddi_get16(dev->acch, (uint16_t *)(void *)(dev->regs + (offset)))
17488447a05SGarrett D'Amore #define GET32(dev, offset) \
17588447a05SGarrett D'Amore ddi_get32(dev->acch, (uint32_t *)(void *)(dev->regs + (offset)))
17688447a05SGarrett D'Amore #define PUT8(dev, offset, v) \
17788447a05SGarrett D'Amore ddi_put8(dev->acch, (uint8_t *)(dev->regs + (offset)), v)
17888447a05SGarrett D'Amore #define PUT16(dev, offset, v) \
17988447a05SGarrett D'Amore ddi_put16(dev->acch, (uint16_t *)(void *)(dev->regs + (offset)), v)
18088447a05SGarrett D'Amore #define PUT32(dev, offset, v) \
18188447a05SGarrett D'Amore ddi_put32(dev->acch, (uint32_t *)(void *)(dev->regs + (offset)), v)
18288447a05SGarrett D'Amore
18388447a05SGarrett D'Amore #define CLR8(dev, offset, v) PUT8(dev, offset, GET8(dev, offset) & ~(v))
18488447a05SGarrett D'Amore #define SET8(dev, offset, v) PUT8(dev, offset, GET8(dev, offset) | (v))
18588447a05SGarrett D'Amore #define CLR32(dev, offset, v) PUT32(dev, offset, GET32(dev, offset) & ~(v))
18688447a05SGarrett D'Amore #define SET32(dev, offset, v) PUT32(dev, offset, GET32(dev, offset) | (v))
18788447a05SGarrett D'Amore
18888447a05SGarrett D'Amore static void audioens_init_hw(audioens_dev_t *);
18988447a05SGarrett D'Amore
19088447a05SGarrett D'Amore static uint16_t
audioens_rd97(void * dev_,uint8_t wAddr)19188447a05SGarrett D'Amore audioens_rd97(void *dev_, uint8_t wAddr)
19288447a05SGarrett D'Amore {
19388447a05SGarrett D'Amore audioens_dev_t *dev = dev_;
19488447a05SGarrett D'Amore int i, dtemp;
19588447a05SGarrett D'Amore
19688447a05SGarrett D'Amore mutex_enter(&dev->mutex);
19788447a05SGarrett D'Amore dtemp = GET32(dev, CONC_dCODECCTL_OFF);
19888447a05SGarrett D'Amore /* wait for WIP to go away saving the current state for later */
19988447a05SGarrett D'Amore for (i = 0; i < 0x100UL; ++i) {
20088447a05SGarrett D'Amore dtemp = GET32(dev, CONC_dCODECCTL_OFF);
20188447a05SGarrett D'Amore if ((dtemp & (1UL << 30)) == 0)
20288447a05SGarrett D'Amore break;
20388447a05SGarrett D'Amore }
20488447a05SGarrett D'Amore
20588447a05SGarrett D'Amore /* write addr w/data=0 and assert read request ... */
20688447a05SGarrett D'Amore PUT32(dev, CONC_dCODECCTL_OFF, ((int)wAddr << 16) | (1UL << 23));
20788447a05SGarrett D'Amore
20888447a05SGarrett D'Amore /* now wait for the data (RDY) */
20988447a05SGarrett D'Amore for (i = 0; i < 0x100UL; ++i) {
21088447a05SGarrett D'Amore dtemp = GET32(dev, CONC_dCODECCTL_OFF);
21188447a05SGarrett D'Amore if (dtemp & (1UL << 31))
21288447a05SGarrett D'Amore break;
21388447a05SGarrett D'Amore }
21488447a05SGarrett D'Amore dtemp = GET32(dev, CONC_dCODECCTL_OFF);
21588447a05SGarrett D'Amore mutex_exit(&dev->mutex);
21688447a05SGarrett D'Amore
21788447a05SGarrett D'Amore return (dtemp & 0xffff);
21888447a05SGarrett D'Amore }
21988447a05SGarrett D'Amore
22088447a05SGarrett D'Amore static void
audioens_wr97(void * dev_,uint8_t wAddr,uint16_t wData)22188447a05SGarrett D'Amore audioens_wr97(void *dev_, uint8_t wAddr, uint16_t wData)
22288447a05SGarrett D'Amore {
22388447a05SGarrett D'Amore audioens_dev_t *dev = dev_;
22488447a05SGarrett D'Amore int i, dtemp;
22588447a05SGarrett D'Amore
22688447a05SGarrett D'Amore mutex_enter(&dev->mutex);
22788447a05SGarrett D'Amore /* wait for WIP to go away */
22888447a05SGarrett D'Amore for (i = 0; i < 0x100UL; ++i) {
22988447a05SGarrett D'Amore dtemp = GET32(dev, CONC_dCODECCTL_OFF);
23088447a05SGarrett D'Amore if ((dtemp & (1UL << 30)) == 0)
23188447a05SGarrett D'Amore break;
23288447a05SGarrett D'Amore }
23388447a05SGarrett D'Amore
23488447a05SGarrett D'Amore PUT32(dev, CONC_dCODECCTL_OFF, ((int)wAddr << 16) | wData);
23588447a05SGarrett D'Amore
23688447a05SGarrett D'Amore mutex_exit(&dev->mutex);
23788447a05SGarrett D'Amore }
23888447a05SGarrett D'Amore
23988447a05SGarrett D'Amore static unsigned short
SRCRegRead(audioens_dev_t * dev,unsigned short reg)24088447a05SGarrett D'Amore SRCRegRead(audioens_dev_t *dev, unsigned short reg)
24188447a05SGarrett D'Amore {
24288447a05SGarrett D'Amore int i, dtemp;
24388447a05SGarrett D'Amore
24488447a05SGarrett D'Amore dtemp = GET32(dev, CONC_dSRCIO_OFF);
24588447a05SGarrett D'Amore /* wait for ready */
24688447a05SGarrett D'Amore for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
24788447a05SGarrett D'Amore dtemp = GET32(dev, CONC_dSRCIO_OFF);
24888447a05SGarrett D'Amore if ((dtemp & SRC_BUSY) == 0)
24988447a05SGarrett D'Amore break;
25088447a05SGarrett D'Amore }
25188447a05SGarrett D'Amore
25288447a05SGarrett D'Amore /* assert a read request */
25388447a05SGarrett D'Amore PUT32(dev, CONC_dSRCIO_OFF, (dtemp & SRC_CTLMASK) | ((int)reg << 25));
25488447a05SGarrett D'Amore
25588447a05SGarrett D'Amore /* now wait for the data */
25688447a05SGarrett D'Amore for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
25788447a05SGarrett D'Amore dtemp = GET32(dev, CONC_dSRCIO_OFF);
25888447a05SGarrett D'Amore if ((dtemp & SRC_BUSY) == 0)
25988447a05SGarrett D'Amore break;
26088447a05SGarrett D'Amore }
26188447a05SGarrett D'Amore
26288447a05SGarrett D'Amore return ((unsigned short) dtemp);
26388447a05SGarrett D'Amore }
26488447a05SGarrett D'Amore
26588447a05SGarrett D'Amore static void
SRCRegWrite(audioens_dev_t * dev,unsigned short reg,unsigned short val)26688447a05SGarrett D'Amore SRCRegWrite(audioens_dev_t *dev, unsigned short reg, unsigned short val)
26788447a05SGarrett D'Amore {
26888447a05SGarrett D'Amore int i, dtemp;
26988447a05SGarrett D'Amore int writeval;
27088447a05SGarrett D'Amore
27188447a05SGarrett D'Amore dtemp = GET32(dev, CONC_dSRCIO_OFF);
27288447a05SGarrett D'Amore /* wait for ready */
27388447a05SGarrett D'Amore for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
27488447a05SGarrett D'Amore dtemp = GET32(dev, CONC_dSRCIO_OFF);
27588447a05SGarrett D'Amore if ((dtemp & SRC_BUSY) == 0)
27688447a05SGarrett D'Amore break;
27788447a05SGarrett D'Amore }
27888447a05SGarrett D'Amore
27988447a05SGarrett D'Amore /* assert the write request */
28088447a05SGarrett D'Amore writeval = (dtemp & SRC_CTLMASK) | SRC_WENABLE |
28188447a05SGarrett D'Amore ((int)reg << 25) | val;
28288447a05SGarrett D'Amore PUT32(dev, CONC_dSRCIO_OFF, writeval);
28388447a05SGarrett D'Amore }
28488447a05SGarrett D'Amore
28588447a05SGarrett D'Amore static void
SRCSetRate(audioens_dev_t * dev,unsigned char base,unsigned short rate)28688447a05SGarrett D'Amore SRCSetRate(audioens_dev_t *dev, unsigned char base, unsigned short rate)
28788447a05SGarrett D'Amore {
28888447a05SGarrett D'Amore int i, freq, dtemp;
28988447a05SGarrett D'Amore unsigned short N, truncM, truncStart;
29088447a05SGarrett D'Amore
29188447a05SGarrett D'Amore if (base != SRC_ADC_BASE) {
29288447a05SGarrett D'Amore /* freeze the channel */
29388447a05SGarrett D'Amore dtemp = (base == SRC_DAC1_BASE) ?
29488447a05SGarrett D'Amore SRC_DAC1FREEZE : SRC_DAC2FREEZE;
29588447a05SGarrett D'Amore for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
29688447a05SGarrett D'Amore if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
29788447a05SGarrett D'Amore break;
29888447a05SGarrett D'Amore }
29988447a05SGarrett D'Amore PUT32(dev, CONC_dSRCIO_OFF,
30088447a05SGarrett D'Amore (GET32(dev, CONC_dSRCIO_OFF) & SRC_CTLMASK) | dtemp);
30188447a05SGarrett D'Amore
30288447a05SGarrett D'Amore /* calculate new frequency and write it - preserve accum */
30388447a05SGarrett D'Amore freq = ((int)rate << 16) / 3000U;
30488447a05SGarrett D'Amore SRCRegWrite(dev, (unsigned short) base + SRC_INT_REGS_OFF,
30588447a05SGarrett D'Amore (SRCRegRead(dev, (unsigned short) base + SRC_INT_REGS_OFF)
30688447a05SGarrett D'Amore & 0x00ffU) | ((unsigned short) (freq >> 6) & 0xfc00));
30788447a05SGarrett D'Amore SRCRegWrite(dev, (unsigned short) base + SRC_VFREQ_FRAC_OFF,
30888447a05SGarrett D'Amore (unsigned short) freq >> 1);
30988447a05SGarrett D'Amore
31088447a05SGarrett D'Amore /* un-freeze the channel */
31188447a05SGarrett D'Amore for (i = 0; i < SRC_IOPOLL_COUNT; ++i)
31288447a05SGarrett D'Amore if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
31388447a05SGarrett D'Amore break;
31488447a05SGarrett D'Amore PUT32(dev, CONC_dSRCIO_OFF,
31588447a05SGarrett D'Amore (GET32(dev, CONC_dSRCIO_OFF) & SRC_CTLMASK) & ~dtemp);
31688447a05SGarrett D'Amore } else {
31788447a05SGarrett D'Amore /* derive oversample ratio */
31888447a05SGarrett D'Amore N = rate / 3000U;
31988447a05SGarrett D'Amore if (N == 15 || N == 13 || N == 11 || N == 9)
32088447a05SGarrett D'Amore --N;
32188447a05SGarrett D'Amore
32288447a05SGarrett D'Amore /* truncate the filter and write n/trunc_start */
32388447a05SGarrett D'Amore truncM = (21 * N - 1) | 1;
32488447a05SGarrett D'Amore if (rate >= 24000U) {
32588447a05SGarrett D'Amore if (truncM > 239)
32688447a05SGarrett D'Amore truncM = 239;
32788447a05SGarrett D'Amore truncStart = (239 - truncM) >> 1;
32888447a05SGarrett D'Amore
32988447a05SGarrett D'Amore SRCRegWrite(dev, base + SRC_TRUNC_N_OFF,
33088447a05SGarrett D'Amore (truncStart << 9) | (N << 4));
33188447a05SGarrett D'Amore } else {
33288447a05SGarrett D'Amore if (truncM > 119)
33388447a05SGarrett D'Amore truncM = 119;
33488447a05SGarrett D'Amore truncStart = (119 - truncM) >> 1;
33588447a05SGarrett D'Amore
33688447a05SGarrett D'Amore SRCRegWrite(dev, base + SRC_TRUNC_N_OFF,
33788447a05SGarrett D'Amore 0x8000U | (truncStart << 9) | (N << 4));
33888447a05SGarrett D'Amore }
33988447a05SGarrett D'Amore
34088447a05SGarrett D'Amore /* calculate new frequency and write it - preserve accum */
34188447a05SGarrett D'Amore freq = ((48000UL << 16) / rate) * N;
34288447a05SGarrett D'Amore SRCRegWrite(dev, base + SRC_INT_REGS_OFF,
34388447a05SGarrett D'Amore (SRCRegRead(dev, (unsigned short) base + SRC_INT_REGS_OFF)
34488447a05SGarrett D'Amore & 0x00ff) | ((unsigned short) (freq >> 6) & 0xfc00));
34588447a05SGarrett D'Amore SRCRegWrite(dev, base + SRC_VFREQ_FRAC_OFF,
34688447a05SGarrett D'Amore (unsigned short) freq >> 1);
34788447a05SGarrett D'Amore
34888447a05SGarrett D'Amore SRCRegWrite(dev, SRC_ADC_VOL_L, N << 8);
34988447a05SGarrett D'Amore SRCRegWrite(dev, SRC_ADC_VOL_R, N << 8);
35088447a05SGarrett D'Amore }
35188447a05SGarrett D'Amore }
35288447a05SGarrett D'Amore
35388447a05SGarrett D'Amore static void
SRCInit(audioens_dev_t * dev)35488447a05SGarrett D'Amore SRCInit(audioens_dev_t *dev)
35588447a05SGarrett D'Amore {
35688447a05SGarrett D'Amore int i;
35788447a05SGarrett D'Amore
35888447a05SGarrett D'Amore /* Clear all SRC RAM then init - keep SRC disabled until done */
35988447a05SGarrett D'Amore for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
36088447a05SGarrett D'Amore if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
36188447a05SGarrett D'Amore break;
36288447a05SGarrett D'Amore }
36388447a05SGarrett D'Amore PUT32(dev, CONC_dSRCIO_OFF, SRC_DISABLE);
36488447a05SGarrett D'Amore
36588447a05SGarrett D'Amore for (i = 0; i < 0x80; ++i)
36688447a05SGarrett D'Amore SRCRegWrite(dev, (unsigned short) i, 0U);
36788447a05SGarrett D'Amore
36888447a05SGarrett D'Amore SRCRegWrite(dev, SRC_DAC1_BASE + SRC_TRUNC_N_OFF, 16 << 4);
36988447a05SGarrett D'Amore SRCRegWrite(dev, SRC_DAC1_BASE + SRC_INT_REGS_OFF, 16 << 10);
37088447a05SGarrett D'Amore SRCRegWrite(dev, SRC_DAC2_BASE + SRC_TRUNC_N_OFF, 16 << 4);
37188447a05SGarrett D'Amore SRCRegWrite(dev, SRC_DAC2_BASE + SRC_INT_REGS_OFF, 16 << 10);
37288447a05SGarrett D'Amore SRCRegWrite(dev, SRC_DAC1_VOL_L, 1 << 12);
37388447a05SGarrett D'Amore SRCRegWrite(dev, SRC_DAC1_VOL_R, 1 << 12);
37488447a05SGarrett D'Amore SRCRegWrite(dev, SRC_DAC2_VOL_L, 1 << 12);
37588447a05SGarrett D'Amore SRCRegWrite(dev, SRC_DAC2_VOL_R, 1 << 12);
37688447a05SGarrett D'Amore SRCRegWrite(dev, SRC_ADC_VOL_L, 1 << 12);
37788447a05SGarrett D'Amore SRCRegWrite(dev, SRC_ADC_VOL_R, 1 << 12);
37888447a05SGarrett D'Amore
37988447a05SGarrett D'Amore /* default some rates */
38088447a05SGarrett D'Amore SRCSetRate(dev, SRC_DAC1_BASE, 48000);
38188447a05SGarrett D'Amore SRCSetRate(dev, SRC_DAC2_BASE, 48000);
38288447a05SGarrett D'Amore SRCSetRate(dev, SRC_ADC_BASE, 48000);
38388447a05SGarrett D'Amore
38488447a05SGarrett D'Amore /* now enable the whole deal */
38588447a05SGarrett D'Amore for (i = 0; i < SRC_IOPOLL_COUNT; ++i) {
38688447a05SGarrett D'Amore if (!(GET32(dev, CONC_dSRCIO_OFF) & SRC_BUSY))
38788447a05SGarrett D'Amore break;
38888447a05SGarrett D'Amore }
38988447a05SGarrett D'Amore PUT32(dev, CONC_dSRCIO_OFF, 0);
39088447a05SGarrett D'Amore }
39188447a05SGarrett D'Amore
39288447a05SGarrett D'Amore static void
audioens_writemem(audioens_dev_t * dev,uint32_t page,uint32_t offs,uint32_t data)39388447a05SGarrett D'Amore audioens_writemem(audioens_dev_t *dev, uint32_t page, uint32_t offs,
39488447a05SGarrett D'Amore uint32_t data)
39588447a05SGarrett D'Amore {
39688447a05SGarrett D'Amore /* Select memory page */
39788447a05SGarrett D'Amore PUT32(dev, CONC_bMEMPAGE_OFF, page);
39888447a05SGarrett D'Amore PUT32(dev, offs, data);
39988447a05SGarrett D'Amore }
40088447a05SGarrett D'Amore
40188447a05SGarrett D'Amore static uint32_t
audioens_readmem(audioens_dev_t * dev,uint32_t page,uint32_t offs)40288447a05SGarrett D'Amore audioens_readmem(audioens_dev_t *dev, uint32_t page, uint32_t offs)
40388447a05SGarrett D'Amore {
40488447a05SGarrett D'Amore PUT32(dev, CONC_bMEMPAGE_OFF, page); /* Select memory page */
40588447a05SGarrett D'Amore return (GET32(dev, offs));
40688447a05SGarrett D'Amore }
40788447a05SGarrett D'Amore
4085b162753SGarrett D'Amore #ifdef __x86
4095b162753SGarrett D'Amore static unsigned
audioens_intr(caddr_t arg1,caddr_t arg2)4105b162753SGarrett D'Amore audioens_intr(caddr_t arg1, caddr_t arg2)
4115b162753SGarrett D'Amore {
4125b162753SGarrett D'Amore audioens_dev_t *dev = (void *)arg1;
4135b162753SGarrett D'Amore uint32_t status;
4145b162753SGarrett D'Amore uint32_t frameno;
4155b162753SGarrett D'Amore uint32_t n;
4165b162753SGarrett D'Amore audioens_port_t *port;
4175b162753SGarrett D'Amore
4185b162753SGarrett D'Amore _NOTE(ARGUNUSED(arg2));
4195b162753SGarrett D'Amore
4205b162753SGarrett D'Amore mutex_enter(&dev->mutex);
4215b162753SGarrett D'Amore if (dev->suspended || !dev->useintr) {
4225b162753SGarrett D'Amore mutex_exit(&dev->mutex);
4235b162753SGarrett D'Amore return (DDI_INTR_UNCLAIMED);
4245b162753SGarrett D'Amore }
4255b162753SGarrett D'Amore
4265b162753SGarrett D'Amore status = GET32(dev, CONC_dSTATUS_OFF);
4275b162753SGarrett D'Amore if ((status & CONC_STATUS_PENDING) == 0) {
4285b162753SGarrett D'Amore mutex_exit(&dev->mutex);
4295b162753SGarrett D'Amore return (DDI_INTR_UNCLAIMED);
4305b162753SGarrett D'Amore }
4315b162753SGarrett D'Amore
4325b162753SGarrett D'Amore /* Three interrupts, DAC1, DAC2, and ADC. The UART we just toss. */
4335b162753SGarrett D'Amore
4345b162753SGarrett D'Amore if (status & CONC_STATUS_DAC1INT) {
4355b162753SGarrett D'Amore port = &dev->port[PORT_DAC];
4365b162753SGarrett D'Amore
4375b162753SGarrett D'Amore /* current frame counter is in high nybble */
4385b162753SGarrett D'Amore frameno = audioens_readmem(dev,
4395b162753SGarrett D'Amore CONC_DAC1CTL_PAGE, CONC_wDAC1FC_OFF) >> 16;
4405b162753SGarrett D'Amore n = frameno >= port->frameno ?
4415b162753SGarrett D'Amore frameno - port->frameno :
4425b162753SGarrett D'Amore frameno + port->nframes - port->frameno;
4435b162753SGarrett D'Amore port->frameno = frameno;
4445b162753SGarrett D'Amore port->count += n;
4455b162753SGarrett D'Amore CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE);
4465b162753SGarrett D'Amore SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE);
4475b162753SGarrett D'Amore }
4485b162753SGarrett D'Amore if (status & CONC_STATUS_ADCINT) {
4495b162753SGarrett D'Amore port = &dev->port[PORT_ADC];
4505b162753SGarrett D'Amore
4515b162753SGarrett D'Amore /* current frame counter is in high nybble */
4525b162753SGarrett D'Amore frameno = audioens_readmem(dev,
4535b162753SGarrett D'Amore CONC_ADCCTL_PAGE, CONC_wADCFC_OFF) >> 16;
4545b162753SGarrett D'Amore n = frameno >= port->frameno ?
4555b162753SGarrett D'Amore frameno - port->frameno :
4565b162753SGarrett D'Amore frameno + port->nframes - port->frameno;
4575b162753SGarrett D'Amore port->frameno = frameno;
4585b162753SGarrett D'Amore port->count += n;
4595b162753SGarrett D'Amore CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE);
4605b162753SGarrett D'Amore SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE);
4615b162753SGarrett D'Amore }
4625b162753SGarrett D'Amore if (status & CONC_STATUS_DAC2INT) {
4635b162753SGarrett D'Amore CLR8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC2IE);
4645b162753SGarrett D'Amore SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC2IE);
4655b162753SGarrett D'Amore }
4665b162753SGarrett D'Amore if (status & CONC_STATUS_UARTINT) {
4675b162753SGarrett D'Amore /*
4685b162753SGarrett D'Amore * Consume data in the UART RX FIFO. We don't support
4695b162753SGarrett D'Amore * the UART for now, so just eat it.
4705b162753SGarrett D'Amore */
4715b162753SGarrett D'Amore while (GET8(dev, CONC_bUARTCSTAT_OFF) & CONC_UART_RXRDY)
4725b162753SGarrett D'Amore continue;
4735b162753SGarrett D'Amore }
4745b162753SGarrett D'Amore mutex_exit(&dev->mutex);
4755b162753SGarrett D'Amore
4765b162753SGarrett D'Amore return (DDI_INTR_CLAIMED);
4775b162753SGarrett D'Amore }
4785b162753SGarrett D'Amore
4795b162753SGarrett D'Amore static int
audioens_setup_intr(audioens_dev_t * dev)4805b162753SGarrett D'Amore audioens_setup_intr(audioens_dev_t *dev)
4815b162753SGarrett D'Amore {
4825b162753SGarrett D'Amore int act;
4835b162753SGarrett D'Amore uint_t ipri;
4845b162753SGarrett D'Amore
4855b162753SGarrett D'Amore if ((ddi_intr_alloc(dev->dip, &dev->intrh, DDI_INTR_TYPE_FIXED, 0, 1,
4865b162753SGarrett D'Amore &act, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) || (act != 1)) {
4875b162753SGarrett D'Amore audio_dev_warn(dev->osdev, "can't alloc intr handle");
4885b162753SGarrett D'Amore goto fail;
4895b162753SGarrett D'Amore }
4905b162753SGarrett D'Amore
4915b162753SGarrett D'Amore if (ddi_intr_get_pri(dev->intrh, &ipri) != DDI_SUCCESS) {
4925b162753SGarrett D'Amore audio_dev_warn(dev->osdev, "can't get interrupt priority");
4935b162753SGarrett D'Amore goto fail;
4945b162753SGarrett D'Amore }
4955b162753SGarrett D'Amore if (ddi_intr_add_handler(dev->intrh, audioens_intr, dev, NULL) !=
4965b162753SGarrett D'Amore DDI_SUCCESS) {
4975b162753SGarrett D'Amore audio_dev_warn(dev->osdev, "cannot add interrupt handler");
4985b162753SGarrett D'Amore goto fail;
4995b162753SGarrett D'Amore }
5005b162753SGarrett D'Amore dev->intrpri = ipri;
5015b162753SGarrett D'Amore return (DDI_SUCCESS);
5025b162753SGarrett D'Amore
5035b162753SGarrett D'Amore fail:
5045b162753SGarrett D'Amore if (dev->intrh != NULL) {
5055b162753SGarrett D'Amore (void) ddi_intr_free(dev->intrh);
5065b162753SGarrett D'Amore dev->intrh = NULL;
5075b162753SGarrett D'Amore }
5085b162753SGarrett D'Amore return (DDI_FAILURE);
5095b162753SGarrett D'Amore }
5105b162753SGarrett D'Amore
5115b162753SGarrett D'Amore #endif /* __x86 */
5125b162753SGarrett D'Amore
51388447a05SGarrett D'Amore /*
51488447a05SGarrett D'Amore * Audio routines
51588447a05SGarrett D'Amore */
51688447a05SGarrett D'Amore static int
audioens_format(void * arg)51788447a05SGarrett D'Amore audioens_format(void *arg)
51888447a05SGarrett D'Amore {
51988447a05SGarrett D'Amore _NOTE(ARGUNUSED(arg));
52088447a05SGarrett D'Amore
52188447a05SGarrett D'Amore /* hardware can also do AUDIO_FORMAT_U8, but no need for it */
52288447a05SGarrett D'Amore return (AUDIO_FORMAT_S16_LE);
52388447a05SGarrett D'Amore }
52488447a05SGarrett D'Amore
52588447a05SGarrett D'Amore static int
audioens_channels(void * arg)52688447a05SGarrett D'Amore audioens_channels(void *arg)
52788447a05SGarrett D'Amore {
52888447a05SGarrett D'Amore audioens_port_t *port = arg;
52988447a05SGarrett D'Amore
53088447a05SGarrett D'Amore return (port->nchan);
53188447a05SGarrett D'Amore }
53288447a05SGarrett D'Amore
53388447a05SGarrett D'Amore static int
audioens_rate(void * arg)53488447a05SGarrett D'Amore audioens_rate(void *arg)
53588447a05SGarrett D'Amore {
53688447a05SGarrett D'Amore audioens_port_t *port = arg;
53788447a05SGarrett D'Amore
53888447a05SGarrett D'Amore return (port->speed);
53988447a05SGarrett D'Amore }
54088447a05SGarrett D'Amore
54168c47f65SGarrett D'Amore static int
audioens_open(void * arg,int flag,unsigned * nframes,caddr_t * bufp)54268c47f65SGarrett D'Amore audioens_open(void *arg, int flag, unsigned *nframes, caddr_t *bufp)
54388447a05SGarrett D'Amore {
54468c47f65SGarrett D'Amore audioens_port_t *port = arg;
54588447a05SGarrett D'Amore audioens_dev_t *dev = port->dev;
54688447a05SGarrett D'Amore
54768c47f65SGarrett D'Amore _NOTE(ARGUNUSED(flag));
54868c47f65SGarrett D'Amore
54968c47f65SGarrett D'Amore mutex_enter(&dev->mutex);
55068c47f65SGarrett D'Amore
55168c47f65SGarrett D'Amore port->count = 0;
55268c47f65SGarrett D'Amore
55368c47f65SGarrett D'Amore *nframes = port->nframes;
55468c47f65SGarrett D'Amore *bufp = port->kaddr;
55568c47f65SGarrett D'Amore mutex_exit(&dev->mutex);
55668c47f65SGarrett D'Amore
55768c47f65SGarrett D'Amore return (0);
55868c47f65SGarrett D'Amore }
55968c47f65SGarrett D'Amore
56068c47f65SGarrett D'Amore static int
audioens_start(void * arg)56168c47f65SGarrett D'Amore audioens_start(void *arg)
56268c47f65SGarrett D'Amore {
56368c47f65SGarrett D'Amore audioens_port_t *port = arg;
56468c47f65SGarrett D'Amore audioens_dev_t *dev = port->dev;
56568c47f65SGarrett D'Amore uint32_t tmp;
56668c47f65SGarrett D'Amore
56768c47f65SGarrett D'Amore mutex_enter(&dev->mutex);
56888447a05SGarrett D'Amore
56988447a05SGarrett D'Amore switch (port->num) {
57088447a05SGarrett D'Amore case PORT_DAC:
57188447a05SGarrett D'Amore /* Set physical address of the DMA buffer */
57288447a05SGarrett D'Amore audioens_writemem(dev, CONC_DAC1CTL_PAGE, CONC_dDAC1PADDR_OFF,
57388447a05SGarrett D'Amore port->paddr);
57488447a05SGarrett D'Amore audioens_writemem(dev, CONC_DAC2CTL_PAGE, CONC_dDAC2PADDR_OFF,
57588447a05SGarrett D'Amore port->paddr + (port->nframes * sizeof (int16_t) * 2));
57688447a05SGarrett D'Amore
57788447a05SGarrett D'Amore /* Set DAC rate */
57888447a05SGarrett D'Amore SRCSetRate(dev, SRC_DAC1_BASE, port->speed);
57988447a05SGarrett D'Amore SRCSetRate(dev, SRC_DAC2_BASE, port->speed);
58088447a05SGarrett D'Amore
58188447a05SGarrett D'Amore /* Configure the channel setup - SPDIF only uses front */
58288447a05SGarrett D'Amore tmp = GET32(dev, CONC_dSTATUS_OFF);
58388447a05SGarrett D'Amore tmp &= ~(CONC_STATUS_SPKR_MASK | CONC_STATUS_SPDIF_MASK);
58488447a05SGarrett D'Amore tmp |= CONC_STATUS_SPKR_4CH | CONC_STATUS_SPDIF_P1;
58588447a05SGarrett D'Amore PUT32(dev, CONC_dSTATUS_OFF, tmp);
58688447a05SGarrett D'Amore
58788447a05SGarrett D'Amore /* Set format */
58888447a05SGarrett D'Amore PUT8(dev, CONC_bSKIPC_OFF, 0x10);
58988447a05SGarrett D'Amore SET8(dev, CONC_bSERFMT_OFF,
59088447a05SGarrett D'Amore CONC_PCM_DAC1_16BIT | CONC_PCM_DAC2_16BIT |
59188447a05SGarrett D'Amore CONC_PCM_DAC1_STEREO | CONC_PCM_DAC2_STEREO);
59288447a05SGarrett D'Amore
59388447a05SGarrett D'Amore /* Set the frame count */
59488447a05SGarrett D'Amore audioens_writemem(dev, CONC_DAC1CTL_PAGE, CONC_wDAC1FC_OFF,
59588447a05SGarrett D'Amore port->nframes - 1);
59688447a05SGarrett D'Amore audioens_writemem(dev, CONC_DAC2CTL_PAGE, CONC_wDAC2FC_OFF,
59788447a05SGarrett D'Amore port->nframes - 1);
59888447a05SGarrett D'Amore
5995b162753SGarrett D'Amore PUT16(dev, CONC_wDAC1IC_OFF, port->iframes - 1);
6005b162753SGarrett D'Amore PUT16(dev, CONC_wDAC2IC_OFF, port->iframes - 1);
60168c47f65SGarrett D'Amore SET8(dev, CONC_bDEVCTL_OFF,
60268c47f65SGarrett D'Amore CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN);
6035b162753SGarrett D'Amore #ifdef __x86
6045b162753SGarrett D'Amore if (dev->useintr) {
6055b162753SGarrett D'Amore SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_DAC1IE);
6065b162753SGarrett D'Amore }
6075b162753SGarrett D'Amore #endif
60868c47f65SGarrett D'Amore
60988447a05SGarrett D'Amore break;
61088447a05SGarrett D'Amore
61188447a05SGarrett D'Amore case PORT_ADC:
61288447a05SGarrett D'Amore /* Set physical address of the DMA buffer */
61388447a05SGarrett D'Amore audioens_writemem(dev, CONC_ADCCTL_PAGE, CONC_dADCPADDR_OFF,
61488447a05SGarrett D'Amore port->paddr);
61588447a05SGarrett D'Amore
61688447a05SGarrett D'Amore /* Set ADC rate */
61788447a05SGarrett D'Amore SRCSetRate(dev, SRC_ADC_BASE, port->speed);
61888447a05SGarrett D'Amore
61988447a05SGarrett D'Amore /* Set format - for input we only support 16 bit input */
62088447a05SGarrett D'Amore tmp = GET8(dev, CONC_bSERFMT_OFF);
62188447a05SGarrett D'Amore tmp |= CONC_PCM_ADC_16BIT;
62288447a05SGarrett D'Amore tmp |= CONC_PCM_ADC_STEREO;
62388447a05SGarrett D'Amore
62488447a05SGarrett D'Amore PUT8(dev, CONC_bSKIPC_OFF, 0x10);
62588447a05SGarrett D'Amore
62688447a05SGarrett D'Amore PUT8(dev, CONC_bSERFMT_OFF, tmp);
62788447a05SGarrett D'Amore
62888447a05SGarrett D'Amore /* Set the frame count */
62988447a05SGarrett D'Amore audioens_writemem(dev, CONC_ADCCTL_PAGE, CONC_wADCFC_OFF,
63088447a05SGarrett D'Amore port->nframes - 1);
63188447a05SGarrett D'Amore
63288447a05SGarrett D'Amore /* Set # of frames between interrupts */
6335b162753SGarrett D'Amore PUT16(dev, CONC_wADCIC_OFF, port->iframes - 1);
63488447a05SGarrett D'Amore
63568c47f65SGarrett D'Amore SET8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN);
6365b162753SGarrett D'Amore #ifdef __x86
6375b162753SGarrett D'Amore if (dev->useintr) {
6385b162753SGarrett D'Amore SET8(dev, CONC_bSERCTL_OFF, CONC_SERCTL_ADCIE);
6395b162753SGarrett D'Amore }
6405b162753SGarrett D'Amore #endif
64188447a05SGarrett D'Amore break;
64288447a05SGarrett D'Amore }
64388447a05SGarrett D'Amore
64488447a05SGarrett D'Amore port->frameno = 0;
64588447a05SGarrett D'Amore mutex_exit(&dev->mutex);
64688447a05SGarrett D'Amore
64788447a05SGarrett D'Amore return (0);
64888447a05SGarrett D'Amore }
64988447a05SGarrett D'Amore
65088447a05SGarrett D'Amore static void
audioens_stop(void * arg)65168c47f65SGarrett D'Amore audioens_stop(void *arg)
65288447a05SGarrett D'Amore {
65388447a05SGarrett D'Amore audioens_port_t *port = arg;
65488447a05SGarrett D'Amore audioens_dev_t *dev = port->dev;
65588447a05SGarrett D'Amore
65688447a05SGarrett D'Amore mutex_enter(&dev->mutex);
65768c47f65SGarrett D'Amore switch (port->num) {
65868c47f65SGarrett D'Amore case PORT_DAC:
65968c47f65SGarrett D'Amore CLR8(dev, CONC_bDEVCTL_OFF,
66068c47f65SGarrett D'Amore CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN);
66168c47f65SGarrett D'Amore break;
66268c47f65SGarrett D'Amore case PORT_ADC:
66368c47f65SGarrett D'Amore CLR8(dev, CONC_bDEVCTL_OFF, CONC_DEVCTL_ADC_EN);
66468c47f65SGarrett D'Amore break;
66588447a05SGarrett D'Amore }
66688447a05SGarrett D'Amore mutex_exit(&dev->mutex);
66788447a05SGarrett D'Amore }
66888447a05SGarrett D'Amore
66968c47f65SGarrett D'Amore static uint64_t
audioens_count(void * arg)67068c47f65SGarrett D'Amore audioens_count(void *arg)
67188447a05SGarrett D'Amore {
67288447a05SGarrett D'Amore audioens_port_t *port = arg;
67388447a05SGarrett D'Amore audioens_dev_t *dev = port->dev;
67468c47f65SGarrett D'Amore uint64_t val;
67588447a05SGarrett D'Amore uint32_t page, offs;
67688447a05SGarrett D'Amore int frameno, n;
67788447a05SGarrett D'Amore
67888447a05SGarrett D'Amore switch (port->num) {
67988447a05SGarrett D'Amore case PORT_DAC:
68088447a05SGarrett D'Amore page = CONC_DAC1CTL_PAGE;
68188447a05SGarrett D'Amore offs = CONC_wDAC1FC_OFF;
68288447a05SGarrett D'Amore break;
68388447a05SGarrett D'Amore
68488447a05SGarrett D'Amore case PORT_ADC:
68588447a05SGarrett D'Amore page = CONC_ADCCTL_PAGE;
68688447a05SGarrett D'Amore offs = CONC_wADCFC_OFF;
68788447a05SGarrett D'Amore break;
688*001481f6SToomas Soome default:
689*001481f6SToomas Soome panic("Unknown port number: %d\n", port->num);
69088447a05SGarrett D'Amore }
69188447a05SGarrett D'Amore
69268c47f65SGarrett D'Amore mutex_enter(&dev->mutex);
6935b162753SGarrett D'Amore #ifdef __x86
6945b162753SGarrett D'Amore if (!dev->useintr) {
6955b162753SGarrett D'Amore #endif
6965b162753SGarrett D'Amore
69788447a05SGarrett D'Amore /*
69888447a05SGarrett D'Amore * Note that the current frame counter is in the high nybble.
69988447a05SGarrett D'Amore */
70088447a05SGarrett D'Amore frameno = audioens_readmem(port->dev, page, offs) >> 16;
70188447a05SGarrett D'Amore n = frameno >= port->frameno ?
70288447a05SGarrett D'Amore frameno - port->frameno :
70388447a05SGarrett D'Amore frameno + port->nframes - port->frameno;
70488447a05SGarrett D'Amore port->frameno = frameno;
70588447a05SGarrett D'Amore port->count += n;
70688447a05SGarrett D'Amore
7075b162753SGarrett D'Amore #ifdef __x86
7085b162753SGarrett D'Amore }
7095b162753SGarrett D'Amore #endif
7105b162753SGarrett D'Amore
71188447a05SGarrett D'Amore val = port->count;
71288447a05SGarrett D'Amore mutex_exit(&dev->mutex);
71368c47f65SGarrett D'Amore
71488447a05SGarrett D'Amore return (val);
71588447a05SGarrett D'Amore }
71688447a05SGarrett D'Amore
71788447a05SGarrett D'Amore static void
audioens_close(void * arg)71888447a05SGarrett D'Amore audioens_close(void *arg)
71988447a05SGarrett D'Amore {
72068c47f65SGarrett D'Amore _NOTE(ARGUNUSED(arg));
72188447a05SGarrett D'Amore }
72288447a05SGarrett D'Amore
72388447a05SGarrett D'Amore static void
audioens_sync(void * arg,unsigned nframes)72488447a05SGarrett D'Amore audioens_sync(void *arg, unsigned nframes)
72588447a05SGarrett D'Amore {
72688447a05SGarrett D'Amore audioens_port_t *port = arg;
72788447a05SGarrett D'Amore
72888447a05SGarrett D'Amore _NOTE(ARGUNUSED(nframes));
72988447a05SGarrett D'Amore
73088447a05SGarrett D'Amore if (port->num == PORT_ADC) {
73168c47f65SGarrett D'Amore (void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORKERNEL);
73288447a05SGarrett D'Amore } else {
73388447a05SGarrett D'Amore (void) ddi_dma_sync(port->dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
73488447a05SGarrett D'Amore }
73588447a05SGarrett D'Amore }
73688447a05SGarrett D'Amore
73788447a05SGarrett D'Amore static void
audioens_chinfo(void * arg,int chan,unsigned * offset,unsigned * incr)73888447a05SGarrett D'Amore audioens_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
73988447a05SGarrett D'Amore {
74088447a05SGarrett D'Amore audioens_port_t *port = arg;
74188447a05SGarrett D'Amore
74288447a05SGarrett D'Amore if ((port->num == PORT_DAC) && (chan >= 2)) {
74388447a05SGarrett D'Amore *offset = (port->nframes * 2) + (chan % 2);
74488447a05SGarrett D'Amore *incr = 2;
74588447a05SGarrett D'Amore } else {
74688447a05SGarrett D'Amore *offset = chan;
74788447a05SGarrett D'Amore *incr = 2;
74888447a05SGarrett D'Amore }
74988447a05SGarrett D'Amore }
75088447a05SGarrett D'Amore
75188447a05SGarrett D'Amore audio_engine_ops_t audioens_engine_ops = {
75288447a05SGarrett D'Amore AUDIO_ENGINE_VERSION, /* version number */
75388447a05SGarrett D'Amore audioens_open,
75488447a05SGarrett D'Amore audioens_close,
75588447a05SGarrett D'Amore audioens_start,
75688447a05SGarrett D'Amore audioens_stop,
75788447a05SGarrett D'Amore audioens_count,
75888447a05SGarrett D'Amore audioens_format,
75988447a05SGarrett D'Amore audioens_channels,
76088447a05SGarrett D'Amore audioens_rate,
76188447a05SGarrett D'Amore audioens_sync,
762f9ead4a5SGarrett D'Amore NULL,
763f9ead4a5SGarrett D'Amore audioens_chinfo,
764f9ead4a5SGarrett D'Amore NULL,
76588447a05SGarrett D'Amore };
76688447a05SGarrett D'Amore
76788447a05SGarrett D'Amore void
audioens_init_hw(audioens_dev_t * dev)76888447a05SGarrett D'Amore audioens_init_hw(audioens_dev_t *dev)
76988447a05SGarrett D'Amore {
77088447a05SGarrett D'Amore int tmp;
77188447a05SGarrett D'Amore
77288447a05SGarrett D'Amore if ((dev->devid == ENSONIQ_ES5880) ||
77388447a05SGarrett D'Amore (dev->devid == ENSONIQ_ES5880A) ||
77488447a05SGarrett D'Amore (dev->devid == ENSONIQ_ES5880B) ||
77588447a05SGarrett D'Amore (dev->devid == 0x1371 && dev->revision == 7) ||
77688447a05SGarrett D'Amore (dev->devid == 0x1371 && dev->revision >= 9)) {
77788447a05SGarrett D'Amore
77888447a05SGarrett D'Amore /* Have a ES5880 so enable the codec manually */
77988447a05SGarrett D'Amore tmp = GET8(dev, CONC_bINTSUMM_OFF) & 0xff;
78088447a05SGarrett D'Amore tmp |= 0x20;
78188447a05SGarrett D'Amore PUT8(dev, CONC_bINTSUMM_OFF, tmp);
78288447a05SGarrett D'Amore for (int i = 0; i < 2000; i++)
78388447a05SGarrett D'Amore drv_usecwait(10);
78488447a05SGarrett D'Amore }
78588447a05SGarrett D'Amore
78688447a05SGarrett D'Amore SRCInit(dev);
78788447a05SGarrett D'Amore
78888447a05SGarrett D'Amore /*
78988447a05SGarrett D'Amore * Turn on CODEC (UART and joystick left disabled)
79088447a05SGarrett D'Amore */
79188447a05SGarrett D'Amore tmp = GET32(dev, CONC_bDEVCTL_OFF) & 0xff;
79288447a05SGarrett D'Amore tmp &= ~(CONC_DEVCTL_PCICLK_DS | CONC_DEVCTL_XTALCLK_DS);
79388447a05SGarrett D'Amore PUT8(dev, CONC_bDEVCTL_OFF, tmp);
79488447a05SGarrett D'Amore PUT8(dev, CONC_bUARTCSTAT_OFF, 0x00);
79588447a05SGarrett D'Amore
79688447a05SGarrett D'Amore /* Perform AC97 codec warm reset */
79788447a05SGarrett D'Amore tmp = GET8(dev, CONC_bMISCCTL_OFF) & 0xff;
79888447a05SGarrett D'Amore PUT8(dev, CONC_bMISCCTL_OFF, tmp | CONC_MISCCTL_SYNC_RES);
79988447a05SGarrett D'Amore drv_usecwait(200);
80088447a05SGarrett D'Amore PUT8(dev, CONC_bMISCCTL_OFF, tmp);
80188447a05SGarrett D'Amore drv_usecwait(200);
80288447a05SGarrett D'Amore
80388447a05SGarrett D'Amore if (dev->revision >= 4) {
80488447a05SGarrett D'Amore /* XXX: enable SPDIF - PCM only for now */
80588447a05SGarrett D'Amore if (audioens_spdif) {
80688447a05SGarrett D'Amore /* enable SPDIF */
80788447a05SGarrett D'Amore PUT32(dev, 0x04, GET32(dev, 0x04) | (1 << 18));
80888447a05SGarrett D'Amore /* SPDIF out = data from DAC */
80988447a05SGarrett D'Amore PUT32(dev, 0x00, GET32(dev, 0x00) | (1 << 26));
81088447a05SGarrett D'Amore CLR32(dev, CONC_dSPDIF_OFF, CONC_SPDIF_AC3);
81188447a05SGarrett D'Amore
81288447a05SGarrett D'Amore } else {
81388447a05SGarrett D'Amore /* disable spdif out */
81488447a05SGarrett D'Amore PUT32(dev, 0x04, GET32(dev, 0x04) & ~(1 << 18));
81588447a05SGarrett D'Amore PUT32(dev, 0x00, GET32(dev, 0x00) & ~(1 << 26));
81688447a05SGarrett D'Amore }
81788447a05SGarrett D'Amore
81888447a05SGarrett D'Amore /* we want to run each channel independently */
81988447a05SGarrett D'Amore CLR32(dev, CONC_dSTATUS_OFF, CONC_STATUS_ECHO);
82088447a05SGarrett D'Amore }
82188447a05SGarrett D'Amore }
82288447a05SGarrett D'Amore
82388447a05SGarrett D'Amore static int
audioens_init(audioens_dev_t * dev)82488447a05SGarrett D'Amore audioens_init(audioens_dev_t *dev)
82588447a05SGarrett D'Amore {
82688447a05SGarrett D'Amore
82788447a05SGarrett D'Amore audioens_init_hw(dev);
82888447a05SGarrett D'Amore
82988447a05SGarrett D'Amore /*
83088447a05SGarrett D'Amore * On this hardware, we want to disable the internal speaker by
83188447a05SGarrett D'Amore * default, if it exists. (We don't have a speakerphone on any
83288447a05SGarrett D'Amore * of these cards, and no SPARC hardware uses it either!)
83388447a05SGarrett D'Amore */
834505c7a69SGarrett D'Amore (void) ddi_prop_update_int(DDI_DEV_T_NONE, dev->dip, AC97_PROP_SPEAKER,
835505c7a69SGarrett D'Amore 0);
83688447a05SGarrett D'Amore
83788447a05SGarrett D'Amore /*
83888447a05SGarrett D'Amore * Init mixer
83988447a05SGarrett D'Amore */
84088447a05SGarrett D'Amore
84188447a05SGarrett D'Amore dev->ac97 = ac97_alloc(dev->dip, audioens_rd97, audioens_wr97, dev);
84288447a05SGarrett D'Amore if (dev->ac97 == NULL)
84388447a05SGarrett D'Amore return (DDI_FAILURE);
84488447a05SGarrett D'Amore
84588447a05SGarrett D'Amore if (ac97_init(dev->ac97, dev->osdev) != 0) {
84688447a05SGarrett D'Amore return (DDI_FAILURE);
84788447a05SGarrett D'Amore }
84888447a05SGarrett D'Amore
84988447a05SGarrett D'Amore for (int i = 0; i <= PORT_MAX; i++) {
85088447a05SGarrett D'Amore audioens_port_t *port;
85188447a05SGarrett D'Amore unsigned caps;
85288447a05SGarrett D'Amore unsigned dmaflags;
85388447a05SGarrett D'Amore size_t rlen;
85488447a05SGarrett D'Amore ddi_dma_cookie_t c;
85588447a05SGarrett D'Amore unsigned ccnt;
8565b162753SGarrett D'Amore size_t bufsz;
85788447a05SGarrett D'Amore
85888447a05SGarrett D'Amore port = &dev->port[i];
85988447a05SGarrett D'Amore port->dev = dev;
86088447a05SGarrett D'Amore
8615b162753SGarrett D'Amore /*
8625b162753SGarrett D'Amore * We have 48000Hz. At that rate, 128 frames will give
8635b162753SGarrett D'Amore * us an interrupt rate of 375Hz. 2048 frames buys about
8645b162753SGarrett D'Amore * 42ms of buffer. Note that interrupts are only enabled
8655b162753SGarrett D'Amore * for platforms which need them (i.e. VMWare).
8665b162753SGarrett D'Amore */
8675b162753SGarrett D'Amore
86888447a05SGarrett D'Amore switch (i) {
86988447a05SGarrett D'Amore case PORT_DAC:
87088447a05SGarrett D'Amore port->nchan = 4;
87188447a05SGarrett D'Amore port->speed = 48000;
8725b162753SGarrett D'Amore port->iframes = 128;
8735b162753SGarrett D'Amore port->nframes = 2048;
87488447a05SGarrett D'Amore caps = ENGINE_OUTPUT_CAP;
87588447a05SGarrett D'Amore dmaflags = DDI_DMA_WRITE | DDI_DMA_CONSISTENT;
87688447a05SGarrett D'Amore break;
87788447a05SGarrett D'Amore
87888447a05SGarrett D'Amore case PORT_ADC:
87988447a05SGarrett D'Amore port->nchan = 2;
88088447a05SGarrett D'Amore port->speed = 48000;
8815b162753SGarrett D'Amore port->iframes = 128;
8825b162753SGarrett D'Amore port->nframes = 2048;
88388447a05SGarrett D'Amore caps = ENGINE_INPUT_CAP;
88488447a05SGarrett D'Amore dmaflags = DDI_DMA_READ | DDI_DMA_CONSISTENT;
88588447a05SGarrett D'Amore break;
88688447a05SGarrett D'Amore }
88788447a05SGarrett D'Amore
88888447a05SGarrett D'Amore port->num = i;
8895b162753SGarrett D'Amore bufsz = port->nframes * port->nchan * sizeof (uint16_t);
89088447a05SGarrett D'Amore
89188447a05SGarrett D'Amore /*
89288447a05SGarrett D'Amore * Allocate DMA resources.
89388447a05SGarrett D'Amore */
89488447a05SGarrett D'Amore
89588447a05SGarrett D'Amore if (ddi_dma_alloc_handle(dev->dip, &dma_attr, DDI_DMA_SLEEP,
89688447a05SGarrett D'Amore NULL, &port->dmah) != DDI_SUCCESS) {
89788447a05SGarrett D'Amore audio_dev_warn(dev->osdev,
89888447a05SGarrett D'Amore "port %d: dma handle allocation failed", i);
89988447a05SGarrett D'Amore return (DDI_FAILURE);
90088447a05SGarrett D'Amore }
9015b162753SGarrett D'Amore if (ddi_dma_mem_alloc(port->dmah, bufsz, &buf_attr,
90288447a05SGarrett D'Amore DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &port->kaddr,
90388447a05SGarrett D'Amore &rlen, &port->acch) != DDI_SUCCESS) {
90488447a05SGarrett D'Amore audio_dev_warn(dev->osdev,
90588447a05SGarrett D'Amore "port %d: dma memory allocation failed", i);
90688447a05SGarrett D'Amore return (DDI_FAILURE);
90788447a05SGarrett D'Amore }
90888447a05SGarrett D'Amore /* ensure that the buffer is zeroed out properly */
90988447a05SGarrett D'Amore bzero(port->kaddr, rlen);
91088447a05SGarrett D'Amore if (ddi_dma_addr_bind_handle(port->dmah, NULL, port->kaddr,
9115b162753SGarrett D'Amore bufsz, dmaflags, DDI_DMA_SLEEP, NULL,
91288447a05SGarrett D'Amore &c, &ccnt) != DDI_DMA_MAPPED) {
91388447a05SGarrett D'Amore audio_dev_warn(dev->osdev,
91488447a05SGarrett D'Amore "port %d: dma binding failed", i);
91588447a05SGarrett D'Amore return (DDI_FAILURE);
91688447a05SGarrett D'Amore }
91788447a05SGarrett D'Amore port->paddr = c.dmac_address;
91888447a05SGarrett D'Amore
91988447a05SGarrett D'Amore /*
92088447a05SGarrett D'Amore * Allocate and configure audio engine.
92188447a05SGarrett D'Amore */
92288447a05SGarrett D'Amore port->engine = audio_engine_alloc(&audioens_engine_ops, caps);
92388447a05SGarrett D'Amore if (port->engine == NULL) {
92488447a05SGarrett D'Amore audio_dev_warn(dev->osdev,
92588447a05SGarrett D'Amore "port %d: audio_engine_alloc failed", i);
92688447a05SGarrett D'Amore return (DDI_FAILURE);
92788447a05SGarrett D'Amore }
92888447a05SGarrett D'Amore
92988447a05SGarrett D'Amore audio_engine_set_private(port->engine, port);
93088447a05SGarrett D'Amore audio_dev_add_engine(dev->osdev, port->engine);
93188447a05SGarrett D'Amore }
93288447a05SGarrett D'Amore
93388447a05SGarrett D'Amore if (audio_dev_register(dev->osdev) != DDI_SUCCESS) {
93488447a05SGarrett D'Amore audio_dev_warn(dev->osdev,
93588447a05SGarrett D'Amore "unable to register with audio framework");
93688447a05SGarrett D'Amore return (DDI_FAILURE);
93788447a05SGarrett D'Amore }
93888447a05SGarrett D'Amore
93988447a05SGarrett D'Amore return (DDI_SUCCESS);
94088447a05SGarrett D'Amore }
94188447a05SGarrett D'Amore
94288447a05SGarrett D'Amore void
audioens_destroy(audioens_dev_t * dev)94388447a05SGarrett D'Amore audioens_destroy(audioens_dev_t *dev)
94488447a05SGarrett D'Amore {
94588447a05SGarrett D'Amore int i;
94688447a05SGarrett D'Amore
9475b162753SGarrett D'Amore #ifdef __x86
9485b162753SGarrett D'Amore if (dev->useintr && dev->intrh != NULL) {
9495b162753SGarrett D'Amore (void) ddi_intr_disable(dev->intrh);
9505b162753SGarrett D'Amore (void) ddi_intr_remove_handler(dev->intrh);
9515b162753SGarrett D'Amore (void) ddi_intr_free(dev->intrh);
9525b162753SGarrett D'Amore dev->intrh = NULL;
9535b162753SGarrett D'Amore }
9545b162753SGarrett D'Amore #endif
9555b162753SGarrett D'Amore
95668c47f65SGarrett D'Amore mutex_destroy(&dev->mutex);
95788447a05SGarrett D'Amore
95888447a05SGarrett D'Amore /* free up ports, including DMA resources for ports */
95988447a05SGarrett D'Amore for (i = 0; i <= PORT_MAX; i++) {
96088447a05SGarrett D'Amore audioens_port_t *port = &dev->port[i];
96188447a05SGarrett D'Amore
96288447a05SGarrett D'Amore if (port->paddr != 0)
96388447a05SGarrett D'Amore (void) ddi_dma_unbind_handle(port->dmah);
96488447a05SGarrett D'Amore if (port->acch != NULL)
96588447a05SGarrett D'Amore ddi_dma_mem_free(&port->acch);
96688447a05SGarrett D'Amore if (port->dmah != NULL)
96788447a05SGarrett D'Amore ddi_dma_free_handle(&port->dmah);
96888447a05SGarrett D'Amore
96988447a05SGarrett D'Amore if (port->engine != NULL) {
97088447a05SGarrett D'Amore audio_dev_remove_engine(dev->osdev, port->engine);
97188447a05SGarrett D'Amore audio_engine_free(port->engine);
97288447a05SGarrett D'Amore }
97388447a05SGarrett D'Amore }
97488447a05SGarrett D'Amore
97588447a05SGarrett D'Amore if (dev->acch != NULL) {
97688447a05SGarrett D'Amore ddi_regs_map_free(&dev->acch);
97788447a05SGarrett D'Amore }
97888447a05SGarrett D'Amore
97988447a05SGarrett D'Amore if (dev->ac97) {
98088447a05SGarrett D'Amore ac97_free(dev->ac97);
98188447a05SGarrett D'Amore }
98288447a05SGarrett D'Amore
98388447a05SGarrett D'Amore if (dev->osdev != NULL) {
98488447a05SGarrett D'Amore audio_dev_free(dev->osdev);
98588447a05SGarrett D'Amore }
98688447a05SGarrett D'Amore
98788447a05SGarrett D'Amore kmem_free(dev, sizeof (*dev));
98888447a05SGarrett D'Amore }
98988447a05SGarrett D'Amore
99088447a05SGarrett D'Amore int
audioens_attach(dev_info_t * dip)99188447a05SGarrett D'Amore audioens_attach(dev_info_t *dip)
99288447a05SGarrett D'Amore {
99388447a05SGarrett D'Amore uint16_t pci_command, vendor, device;
99488447a05SGarrett D'Amore uint8_t revision;
99588447a05SGarrett D'Amore audioens_dev_t *dev;
99688447a05SGarrett D'Amore ddi_acc_handle_t pcih;
99788447a05SGarrett D'Amore const char *chip_name;
99888447a05SGarrett D'Amore const char *chip_vers;
99988447a05SGarrett D'Amore
100088447a05SGarrett D'Amore dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
100188447a05SGarrett D'Amore dev->dip = dip;
100288447a05SGarrett D'Amore ddi_set_driver_private(dip, dev);
100368c47f65SGarrett D'Amore mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL);
100488447a05SGarrett D'Amore
100588447a05SGarrett D'Amore if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
100688447a05SGarrett D'Amore audio_dev_warn(dev->osdev, "pci_config_setup failed");
10075b162753SGarrett D'Amore goto err_exit;
100888447a05SGarrett D'Amore }
100988447a05SGarrett D'Amore
101088447a05SGarrett D'Amore vendor = pci_config_get16(pcih, PCI_CONF_VENID);
101188447a05SGarrett D'Amore device = pci_config_get16(pcih, PCI_CONF_DEVID);
101288447a05SGarrett D'Amore revision = pci_config_get8(pcih, PCI_CONF_REVID);
101388447a05SGarrett D'Amore
101488447a05SGarrett D'Amore if ((vendor != ENSONIQ_VENDOR_ID && vendor != CREATIVE_VENDOR_ID) ||
101588447a05SGarrett D'Amore (device != ENSONIQ_ES1371 && device != ENSONIQ_ES5880 &&
101688447a05SGarrett D'Amore device != ENSONIQ_ES5880A && device != ECTIVA_ES1938 &&
10175b162753SGarrett D'Amore device != ENSONIQ_ES5880B)) {
10185b162753SGarrett D'Amore audio_dev_warn(dev->osdev, "unrecognized device");
101988447a05SGarrett D'Amore goto err_exit;
10205b162753SGarrett D'Amore }
102188447a05SGarrett D'Amore
102288447a05SGarrett D'Amore chip_name = "AudioPCI97";
102388447a05SGarrett D'Amore chip_vers = "unknown";
102488447a05SGarrett D'Amore
102588447a05SGarrett D'Amore switch (device) {
102688447a05SGarrett D'Amore case ENSONIQ_ES1371:
102788447a05SGarrett D'Amore chip_name = "AudioPCI97";
102888447a05SGarrett D'Amore switch (revision) {
102988447a05SGarrett D'Amore case 0x02:
103088447a05SGarrett D'Amore case 0x09:
103188447a05SGarrett D'Amore default:
103288447a05SGarrett D'Amore chip_vers = "ES1371";
103388447a05SGarrett D'Amore break;
103488447a05SGarrett D'Amore case 0x04:
103588447a05SGarrett D'Amore case 0x06:
103688447a05SGarrett D'Amore case 0x08:
103788447a05SGarrett D'Amore chip_vers = "ES1373";
103888447a05SGarrett D'Amore break;
103988447a05SGarrett D'Amore case 0x07:
104088447a05SGarrett D'Amore chip_vers = "ES5880";
104188447a05SGarrett D'Amore break;
104288447a05SGarrett D'Amore }
104388447a05SGarrett D'Amore break;
104488447a05SGarrett D'Amore
104588447a05SGarrett D'Amore case ENSONIQ_ES5880:
104688447a05SGarrett D'Amore chip_name = "SB PCI128";
104788447a05SGarrett D'Amore chip_vers = "ES5880";
104888447a05SGarrett D'Amore break;
104988447a05SGarrett D'Amore case ENSONIQ_ES5880A:
105088447a05SGarrett D'Amore chip_name = "SB PCI128";
105188447a05SGarrett D'Amore chip_vers = "ES5880A";
105288447a05SGarrett D'Amore break;
105388447a05SGarrett D'Amore case ENSONIQ_ES5880B:
105488447a05SGarrett D'Amore chip_name = "SB PCI128";
105588447a05SGarrett D'Amore chip_vers = "ES5880B";
105688447a05SGarrett D'Amore break;
105788447a05SGarrett D'Amore
105888447a05SGarrett D'Amore case ECTIVA_ES1938:
105988447a05SGarrett D'Amore chip_name = "AudioPCI";
106088447a05SGarrett D'Amore chip_vers = "ES1938";
106188447a05SGarrett D'Amore break;
106288447a05SGarrett D'Amore }
106388447a05SGarrett D'Amore
106488447a05SGarrett D'Amore dev->revision = revision;
106588447a05SGarrett D'Amore dev->devid = device;
106688447a05SGarrett D'Amore
106788447a05SGarrett D'Amore dev->osdev = audio_dev_alloc(dip, 0);
106888447a05SGarrett D'Amore if (dev->osdev == NULL) {
106988447a05SGarrett D'Amore goto err_exit;
107088447a05SGarrett D'Amore }
107188447a05SGarrett D'Amore
107288447a05SGarrett D'Amore audio_dev_set_description(dev->osdev, chip_name);
107388447a05SGarrett D'Amore audio_dev_set_version(dev->osdev, chip_vers);
107488447a05SGarrett D'Amore
107588447a05SGarrett D'Amore /* set the PCI latency */
107688447a05SGarrett D'Amore if ((audioens_latency == 32) || (audioens_latency == 64) ||
107788447a05SGarrett D'Amore (audioens_latency == 96))
107888447a05SGarrett D'Amore pci_config_put8(pcih, PCI_CONF_LATENCY_TIMER,
107988447a05SGarrett D'Amore audioens_latency);
108088447a05SGarrett D'Amore
108188447a05SGarrett D'Amore /* activate the device */
108288447a05SGarrett D'Amore pci_command = pci_config_get16(pcih, PCI_CONF_COMM);
108388447a05SGarrett D'Amore pci_command |= PCI_COMM_ME | PCI_COMM_IO;
108488447a05SGarrett D'Amore pci_config_put16(pcih, PCI_CONF_COMM, pci_command);
108588447a05SGarrett D'Amore
108688447a05SGarrett D'Amore /* map registers */
108788447a05SGarrett D'Amore if (ddi_regs_map_setup(dip, 1, &dev->regs, 0, 0, &acc_attr,
108888447a05SGarrett D'Amore &dev->acch) != DDI_SUCCESS) {
108988447a05SGarrett D'Amore audio_dev_warn(dev->osdev, "can't map registers");
109088447a05SGarrett D'Amore goto err_exit;
109188447a05SGarrett D'Amore }
109288447a05SGarrett D'Amore
10935b162753SGarrett D'Amore #ifdef __x86
10945b162753SGarrett D'Amore /*
10955b162753SGarrett D'Amore * Virtual platforms (mostly VMWare!) seem to need us to pulse
10965b162753SGarrett D'Amore * the interrupt enables to make progress. So enable (emulated)
10975b162753SGarrett D'Amore * hardware interrupts.
10985b162753SGarrett D'Amore */
10995b162753SGarrett D'Amore dev->useintr = B_FALSE;
11005b162753SGarrett D'Amore if (get_hwenv() & HW_VIRTUAL) {
11015b162753SGarrett D'Amore dev->useintr = B_TRUE;
11025b162753SGarrett D'Amore if (audioens_setup_intr(dev) != DDI_SUCCESS) {
11035b162753SGarrett D'Amore goto err_exit;
11045b162753SGarrett D'Amore }
11055b162753SGarrett D'Amore /* Reinitialize the mutex with interrupt priority. */
11065b162753SGarrett D'Amore mutex_destroy(&dev->mutex);
11075b162753SGarrett D'Amore mutex_init(&dev->mutex, NULL, MUTEX_DRIVER,
11085b162753SGarrett D'Amore DDI_INTR_PRI(dev->intrpri));
11095b162753SGarrett D'Amore }
11105b162753SGarrett D'Amore #endif
11115b162753SGarrett D'Amore
111288447a05SGarrett D'Amore /* This allocates and configures the engines */
111388447a05SGarrett D'Amore if (audioens_init(dev) != DDI_SUCCESS) {
111488447a05SGarrett D'Amore audio_dev_warn(dev->osdev, "can't init device");
111588447a05SGarrett D'Amore goto err_exit;
111688447a05SGarrett D'Amore }
111788447a05SGarrett D'Amore
11185b162753SGarrett D'Amore #ifdef __x86
11195b162753SGarrett D'Amore if (dev->useintr) {
11205b162753SGarrett D'Amore (void) ddi_intr_enable(dev->intrh);
11215b162753SGarrett D'Amore }
11225b162753SGarrett D'Amore #endif
112388447a05SGarrett D'Amore pci_config_teardown(&pcih);
112488447a05SGarrett D'Amore
112588447a05SGarrett D'Amore ddi_report_dev(dip);
112688447a05SGarrett D'Amore
112788447a05SGarrett D'Amore return (DDI_SUCCESS);
112888447a05SGarrett D'Amore
112988447a05SGarrett D'Amore err_exit:
113088447a05SGarrett D'Amore pci_config_teardown(&pcih);
113188447a05SGarrett D'Amore
113288447a05SGarrett D'Amore audioens_destroy(dev);
113388447a05SGarrett D'Amore
113488447a05SGarrett D'Amore return (DDI_FAILURE);
113588447a05SGarrett D'Amore }
113688447a05SGarrett D'Amore
113788447a05SGarrett D'Amore int
audioens_detach(audioens_dev_t * dev)113888447a05SGarrett D'Amore audioens_detach(audioens_dev_t *dev)
113988447a05SGarrett D'Amore {
114088447a05SGarrett D'Amore int tmp;
114188447a05SGarrett D'Amore
114288447a05SGarrett D'Amore /* first unregister us from the DDI framework, might be busy */
114388447a05SGarrett D'Amore if (audio_dev_unregister(dev->osdev) != DDI_SUCCESS)
114488447a05SGarrett D'Amore return (DDI_FAILURE);
114588447a05SGarrett D'Amore
114688447a05SGarrett D'Amore mutex_enter(&dev->mutex);
114788447a05SGarrett D'Amore
114888447a05SGarrett D'Amore tmp = GET8(dev, CONC_bSERCTL_OFF) &
114988447a05SGarrett D'Amore ~(CONC_SERCTL_DAC2IE | CONC_SERCTL_DAC1IE | CONC_SERCTL_ADCIE);
115088447a05SGarrett D'Amore PUT8(dev, CONC_bSERCTL_OFF, tmp);
115188447a05SGarrett D'Amore PUT8(dev, CONC_bSERCTL_OFF, tmp);
115288447a05SGarrett D'Amore PUT8(dev, CONC_bSERCTL_OFF, tmp);
115388447a05SGarrett D'Amore PUT8(dev, CONC_bSERCTL_OFF, tmp);
115488447a05SGarrett D'Amore
115588447a05SGarrett D'Amore tmp = GET8(dev, CONC_bDEVCTL_OFF) &
115688447a05SGarrett D'Amore ~(CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_DAC1_EN);
115788447a05SGarrett D'Amore PUT8(dev, CONC_bDEVCTL_OFF, tmp);
115888447a05SGarrett D'Amore PUT8(dev, CONC_bDEVCTL_OFF, tmp);
115988447a05SGarrett D'Amore PUT8(dev, CONC_bDEVCTL_OFF, tmp);
116088447a05SGarrett D'Amore PUT8(dev, CONC_bDEVCTL_OFF, tmp);
116188447a05SGarrett D'Amore
116288447a05SGarrett D'Amore mutex_exit(&dev->mutex);
116388447a05SGarrett D'Amore
116488447a05SGarrett D'Amore audioens_destroy(dev);
116588447a05SGarrett D'Amore
116688447a05SGarrett D'Amore return (DDI_SUCCESS);
116788447a05SGarrett D'Amore }
116888447a05SGarrett D'Amore
116988447a05SGarrett D'Amore static int
audioens_resume(audioens_dev_t * dev)117088447a05SGarrett D'Amore audioens_resume(audioens_dev_t *dev)
117188447a05SGarrett D'Amore {
11725b162753SGarrett D'Amore mutex_enter(&dev->mutex);
11735b162753SGarrett D'Amore dev->suspended = B_FALSE;
11745b162753SGarrett D'Amore mutex_exit(&dev->mutex);
11755b162753SGarrett D'Amore
117688447a05SGarrett D'Amore /* reinitialize hardware */
117788447a05SGarrett D'Amore audioens_init_hw(dev);
117888447a05SGarrett D'Amore
117988447a05SGarrett D'Amore /* restore AC97 state */
118068c47f65SGarrett D'Amore ac97_reset(dev->ac97);
118188447a05SGarrett D'Amore
118268c47f65SGarrett D'Amore audio_dev_resume(dev->osdev);
118388447a05SGarrett D'Amore
118488447a05SGarrett D'Amore return (DDI_SUCCESS);
118588447a05SGarrett D'Amore }
118688447a05SGarrett D'Amore
118788447a05SGarrett D'Amore static int
audioens_suspend(audioens_dev_t * dev)118888447a05SGarrett D'Amore audioens_suspend(audioens_dev_t *dev)
118988447a05SGarrett D'Amore {
119068c47f65SGarrett D'Amore audio_dev_suspend(dev->osdev);
119188447a05SGarrett D'Amore
11925b162753SGarrett D'Amore mutex_enter(&dev->mutex);
11935b162753SGarrett D'Amore CLR8(dev, CONC_bDEVCTL_OFF,
11945b162753SGarrett D'Amore CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_DAC1_EN | CONC_DEVCTL_ADC_EN);
11955b162753SGarrett D'Amore dev->suspended = B_TRUE;
11965b162753SGarrett D'Amore mutex_exit(&dev->mutex);
11975b162753SGarrett D'Amore
119888447a05SGarrett D'Amore return (DDI_SUCCESS);
119988447a05SGarrett D'Amore }
120088447a05SGarrett D'Amore
120188447a05SGarrett D'Amore static int
audioens_quiesce(dev_info_t * dip)120288447a05SGarrett D'Amore audioens_quiesce(dev_info_t *dip)
120388447a05SGarrett D'Amore {
120488447a05SGarrett D'Amore audioens_dev_t *dev;
120588447a05SGarrett D'Amore uint8_t tmp;
120688447a05SGarrett D'Amore
120788447a05SGarrett D'Amore if ((dev = ddi_get_driver_private(dip)) == NULL) {
120888447a05SGarrett D'Amore return (DDI_FAILURE);
120988447a05SGarrett D'Amore }
121088447a05SGarrett D'Amore
121188447a05SGarrett D'Amore /* This disables all DMA engines and interrupts */
121288447a05SGarrett D'Amore tmp = GET8(dev, CONC_bSERCTL_OFF) &
121388447a05SGarrett D'Amore ~(CONC_SERCTL_DAC2IE | CONC_SERCTL_DAC1IE | CONC_SERCTL_ADCIE);
121488447a05SGarrett D'Amore PUT8(dev, CONC_bSERCTL_OFF, tmp);
121588447a05SGarrett D'Amore PUT8(dev, CONC_bSERCTL_OFF, tmp);
121688447a05SGarrett D'Amore PUT8(dev, CONC_bSERCTL_OFF, tmp);
121788447a05SGarrett D'Amore PUT8(dev, CONC_bSERCTL_OFF, tmp);
121888447a05SGarrett D'Amore
121988447a05SGarrett D'Amore tmp = GET8(dev, CONC_bDEVCTL_OFF) &
122088447a05SGarrett D'Amore ~(CONC_DEVCTL_DAC2_EN | CONC_DEVCTL_ADC_EN | CONC_DEVCTL_DAC1_EN);
122188447a05SGarrett D'Amore PUT8(dev, CONC_bDEVCTL_OFF, tmp);
122288447a05SGarrett D'Amore PUT8(dev, CONC_bDEVCTL_OFF, tmp);
122388447a05SGarrett D'Amore PUT8(dev, CONC_bDEVCTL_OFF, tmp);
122488447a05SGarrett D'Amore PUT8(dev, CONC_bDEVCTL_OFF, tmp);
122588447a05SGarrett D'Amore
122688447a05SGarrett D'Amore return (DDI_SUCCESS);
122788447a05SGarrett D'Amore }
122888447a05SGarrett D'Amore
122988447a05SGarrett D'Amore
123088447a05SGarrett D'Amore static int
audioens_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)123188447a05SGarrett D'Amore audioens_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
123288447a05SGarrett D'Amore {
123388447a05SGarrett D'Amore audioens_dev_t *dev;
123488447a05SGarrett D'Amore
123588447a05SGarrett D'Amore switch (cmd) {
123688447a05SGarrett D'Amore case DDI_ATTACH:
123788447a05SGarrett D'Amore return (audioens_attach(dip));
123888447a05SGarrett D'Amore
123988447a05SGarrett D'Amore case DDI_RESUME:
124088447a05SGarrett D'Amore if ((dev = ddi_get_driver_private(dip)) == NULL) {
124188447a05SGarrett D'Amore return (DDI_FAILURE);
124288447a05SGarrett D'Amore }
124388447a05SGarrett D'Amore return (audioens_resume(dev));
124488447a05SGarrett D'Amore
124588447a05SGarrett D'Amore default:
124688447a05SGarrett D'Amore return (DDI_FAILURE);
124788447a05SGarrett D'Amore }
124888447a05SGarrett D'Amore }
124988447a05SGarrett D'Amore
125088447a05SGarrett D'Amore static int
audioens_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)125188447a05SGarrett D'Amore audioens_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
125288447a05SGarrett D'Amore {
125388447a05SGarrett D'Amore audioens_dev_t *dev;
125488447a05SGarrett D'Amore
125588447a05SGarrett D'Amore if ((dev = ddi_get_driver_private(dip)) == NULL) {
125688447a05SGarrett D'Amore return (DDI_FAILURE);
125788447a05SGarrett D'Amore }
125888447a05SGarrett D'Amore
125988447a05SGarrett D'Amore switch (cmd) {
126088447a05SGarrett D'Amore case DDI_DETACH:
126188447a05SGarrett D'Amore return (audioens_detach(dev));
126288447a05SGarrett D'Amore
126388447a05SGarrett D'Amore case DDI_SUSPEND:
126488447a05SGarrett D'Amore return (audioens_suspend(dev));
126588447a05SGarrett D'Amore default:
126688447a05SGarrett D'Amore return (DDI_FAILURE);
126788447a05SGarrett D'Amore }
126888447a05SGarrett D'Amore }
126988447a05SGarrett D'Amore
127088447a05SGarrett D'Amore static int audioens_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
127188447a05SGarrett D'Amore static int audioens_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
127288447a05SGarrett D'Amore
127388447a05SGarrett D'Amore static struct dev_ops audioens_dev_ops = {
127488447a05SGarrett D'Amore DEVO_REV, /* rev */
127588447a05SGarrett D'Amore 0, /* refcnt */
127688447a05SGarrett D'Amore NULL, /* getinfo */
127788447a05SGarrett D'Amore nulldev, /* identify */
127888447a05SGarrett D'Amore nulldev, /* probe */
127988447a05SGarrett D'Amore audioens_ddi_attach, /* attach */
128088447a05SGarrett D'Amore audioens_ddi_detach, /* detach */
128188447a05SGarrett D'Amore nodev, /* reset */
128288447a05SGarrett D'Amore NULL, /* cb_ops */
128388447a05SGarrett D'Amore NULL, /* bus_ops */
128488447a05SGarrett D'Amore NULL, /* power */
128588447a05SGarrett D'Amore audioens_quiesce, /* quiesce */
128688447a05SGarrett D'Amore };
128788447a05SGarrett D'Amore
128888447a05SGarrett D'Amore static struct modldrv audioens_modldrv = {
128988447a05SGarrett D'Amore &mod_driverops, /* drv_modops */
129088447a05SGarrett D'Amore "Ensoniq 1371/1373 Audio", /* linkinfo */
129188447a05SGarrett D'Amore &audioens_dev_ops, /* dev_ops */
129288447a05SGarrett D'Amore };
129388447a05SGarrett D'Amore
129488447a05SGarrett D'Amore static struct modlinkage modlinkage = {
129588447a05SGarrett D'Amore MODREV_1,
129688447a05SGarrett D'Amore { &audioens_modldrv, NULL }
129788447a05SGarrett D'Amore };
129888447a05SGarrett D'Amore
129988447a05SGarrett D'Amore int
_init(void)130088447a05SGarrett D'Amore _init(void)
130188447a05SGarrett D'Amore {
130288447a05SGarrett D'Amore int rv;
130388447a05SGarrett D'Amore
130488447a05SGarrett D'Amore audio_init_ops(&audioens_dev_ops, DRVNAME);
130588447a05SGarrett D'Amore if ((rv = mod_install(&modlinkage)) != 0) {
130688447a05SGarrett D'Amore audio_fini_ops(&audioens_dev_ops);
130788447a05SGarrett D'Amore }
130888447a05SGarrett D'Amore return (rv);
130988447a05SGarrett D'Amore }
131088447a05SGarrett D'Amore
131188447a05SGarrett D'Amore int
_fini(void)131288447a05SGarrett D'Amore _fini(void)
131388447a05SGarrett D'Amore {
131488447a05SGarrett D'Amore int rv;
131588447a05SGarrett D'Amore
131688447a05SGarrett D'Amore if ((rv = mod_remove(&modlinkage)) == 0) {
131788447a05SGarrett D'Amore audio_fini_ops(&audioens_dev_ops);
131888447a05SGarrett D'Amore }
131988447a05SGarrett D'Amore return (rv);
132088447a05SGarrett D'Amore }
132188447a05SGarrett D'Amore
132288447a05SGarrett D'Amore int
_info(struct modinfo * modinfop)132388447a05SGarrett D'Amore _info(struct modinfo *modinfop)
132488447a05SGarrett D'Amore {
132588447a05SGarrett D'Amore return (mod_info(&modlinkage, modinfop));
132688447a05SGarrett D'Amore }
1327