148722b5fSGarrett D'Amore /*
248722b5fSGarrett D'Amore * CDDL HEADER START
348722b5fSGarrett D'Amore *
448722b5fSGarrett D'Amore * The contents of this file are subject to the terms of the
548722b5fSGarrett D'Amore * Common Development and Distribution License (the "License").
648722b5fSGarrett D'Amore * You may not use this file except in compliance with the License.
748722b5fSGarrett D'Amore *
848722b5fSGarrett D'Amore * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
948722b5fSGarrett D'Amore * or http://www.opensolaris.org/os/licensing.
1048722b5fSGarrett D'Amore * See the License for the specific language governing permissions
1148722b5fSGarrett D'Amore * and limitations under the License.
1248722b5fSGarrett D'Amore *
1348722b5fSGarrett D'Amore * When distributing Covered Code, include this CDDL HEADER in each
1448722b5fSGarrett D'Amore * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1548722b5fSGarrett D'Amore * If applicable, add the following below this CDDL HEADER, with the
1648722b5fSGarrett D'Amore * fields enclosed by brackets "[]" replaced with your own identifying
1748722b5fSGarrett D'Amore * information: Portions Copyright [yyyy] [name of copyright owner]
1848722b5fSGarrett D'Amore *
1948722b5fSGarrett D'Amore * CDDL HEADER END
2048722b5fSGarrett D'Amore */
2148722b5fSGarrett D'Amore
2248722b5fSGarrett D'Amore /*
232c30fa45SGarrett D'Amore * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2448722b5fSGarrett D'Amore */
2548722b5fSGarrett D'Amore
2648722b5fSGarrett D'Amore /*
2748722b5fSGarrett D'Amore * Purpose: Driver for the Creative Audigy LS sound card
2848722b5fSGarrett D'Amore */
2948722b5fSGarrett D'Amore /*
3048722b5fSGarrett D'Amore * Copyright (C) 4Front Technologies 1996-2009.
3148722b5fSGarrett D'Amore */
3248722b5fSGarrett D'Amore
3348722b5fSGarrett D'Amore #include <sys/types.h>
3448722b5fSGarrett D'Amore #include <sys/modctl.h>
3548722b5fSGarrett D'Amore #include <sys/kmem.h>
3648722b5fSGarrett D'Amore #include <sys/conf.h>
3748722b5fSGarrett D'Amore #include <sys/ddi.h>
3848722b5fSGarrett D'Amore #include <sys/sunddi.h>
3948722b5fSGarrett D'Amore #include <sys/pci.h>
4048722b5fSGarrett D'Amore #include <sys/note.h>
4148722b5fSGarrett D'Amore #include <sys/audio/audio_driver.h>
4248722b5fSGarrett D'Amore #include <sys/audio/ac97.h>
4348722b5fSGarrett D'Amore
4448722b5fSGarrett D'Amore #include "audiols.h"
4548722b5fSGarrett D'Amore
4648722b5fSGarrett D'Amore static struct ddi_device_acc_attr dev_attr = {
4748722b5fSGarrett D'Amore DDI_DEVICE_ATTR_V0,
4848722b5fSGarrett D'Amore DDI_STRUCTURE_LE_ACC,
4948722b5fSGarrett D'Amore DDI_STRICTORDER_ACC
5048722b5fSGarrett D'Amore };
5148722b5fSGarrett D'Amore
5248722b5fSGarrett D'Amore static struct ddi_device_acc_attr buf_attr = {
5348722b5fSGarrett D'Amore DDI_DEVICE_ATTR_V0,
5448722b5fSGarrett D'Amore DDI_NEVERSWAP_ACC,
5548722b5fSGarrett D'Amore DDI_STRICTORDER_ACC
5648722b5fSGarrett D'Amore };
5748722b5fSGarrett D'Amore
5848722b5fSGarrett D'Amore static ddi_dma_attr_t dma_attr_buf = {
5948722b5fSGarrett D'Amore DMA_ATTR_V0, /* version number */
6048722b5fSGarrett D'Amore 0x00000000, /* low DMA address range */
6148722b5fSGarrett D'Amore 0xffffffff, /* high DMA address range */
6248722b5fSGarrett D'Amore 0x000fffff, /* DMA counter (16 bits only in Audigy LS) */
6348722b5fSGarrett D'Amore 4, /* DMA address alignment */
6448722b5fSGarrett D'Amore 0x3c, /* DMA burstsizes */
6548722b5fSGarrett D'Amore 4, /* min effective DMA size */
6648722b5fSGarrett D'Amore 0xffffffff, /* max DMA xfer size */
6748722b5fSGarrett D'Amore 0xffffffff, /* segment boundary */
6848722b5fSGarrett D'Amore 1, /* s/g length */
6948722b5fSGarrett D'Amore 4, /* granularity of device */
7048722b5fSGarrett D'Amore 0 /* Bus specific DMA flags */
7148722b5fSGarrett D'Amore };
7248722b5fSGarrett D'Amore
7348722b5fSGarrett D'Amore static int audigyls_attach(dev_info_t *);
7448722b5fSGarrett D'Amore static int audigyls_resume(dev_info_t *);
7548722b5fSGarrett D'Amore static int audigyls_detach(audigyls_dev_t *);
7648722b5fSGarrett D'Amore static int audigyls_suspend(audigyls_dev_t *);
7748722b5fSGarrett D'Amore
7868c47f65SGarrett D'Amore static int audigyls_open(void *, int, unsigned *, caddr_t *);
7948722b5fSGarrett D'Amore static void audigyls_close(void *);
8048722b5fSGarrett D'Amore static int audigyls_start(void *);
8148722b5fSGarrett D'Amore static void audigyls_stop(void *);
8248722b5fSGarrett D'Amore static int audigyls_format(void *);
8348722b5fSGarrett D'Amore static int audigyls_channels(void *);
8448722b5fSGarrett D'Amore static int audigyls_rate(void *);
8548722b5fSGarrett D'Amore static uint64_t audigyls_count(void *);
8648722b5fSGarrett D'Amore static void audigyls_sync(void *, unsigned);
8748722b5fSGarrett D'Amore static void audigyls_chinfo(void *, int, unsigned *, unsigned *);
8848722b5fSGarrett D'Amore
8948722b5fSGarrett D'Amore
9048722b5fSGarrett D'Amore static uint16_t audigyls_read_ac97(void *, uint8_t);
9148722b5fSGarrett D'Amore static void audigyls_write_ac97(void *, uint8_t, uint16_t);
9248722b5fSGarrett D'Amore static int audigyls_alloc_port(audigyls_dev_t *, int);
9348722b5fSGarrett D'Amore static void audigyls_destroy(audigyls_dev_t *);
9448722b5fSGarrett D'Amore static void audigyls_hwinit(audigyls_dev_t *);
9548722b5fSGarrett D'Amore static void audigyls_configure_mixer(audigyls_dev_t *dev);
9648722b5fSGarrett D'Amore
9748722b5fSGarrett D'Amore static audio_engine_ops_t audigyls_engine_ops = {
9848722b5fSGarrett D'Amore AUDIO_ENGINE_VERSION,
9948722b5fSGarrett D'Amore audigyls_open,
10048722b5fSGarrett D'Amore audigyls_close,
10148722b5fSGarrett D'Amore audigyls_start,
10248722b5fSGarrett D'Amore audigyls_stop,
10348722b5fSGarrett D'Amore audigyls_count,
10448722b5fSGarrett D'Amore audigyls_format,
10548722b5fSGarrett D'Amore audigyls_channels,
10648722b5fSGarrett D'Amore audigyls_rate,
10748722b5fSGarrett D'Amore audigyls_sync,
108f9ead4a5SGarrett D'Amore NULL,
109f9ead4a5SGarrett D'Amore audigyls_chinfo,
110f9ead4a5SGarrett D'Amore NULL
11148722b5fSGarrett D'Amore };
11248722b5fSGarrett D'Amore
11348722b5fSGarrett D'Amore /*
11448722b5fSGarrett D'Amore * Audigy LS uses AC'97 strictly for the recording side of things.
11548722b5fSGarrett D'Amore * While the chip can supposedly route output to AC'97 for playback,
11648722b5fSGarrett D'Amore * the PCI devices use a separate I2S DAC instead. As a result we
11748722b5fSGarrett D'Amore * need to suppress controls that the AC'97 codec registers.
11848722b5fSGarrett D'Amore *
11948722b5fSGarrett D'Amore * Furthermore, even then the AC'97 codec offers inputs that we just
12048722b5fSGarrett D'Amore * aren't interested in.
12148722b5fSGarrett D'Amore */
12248722b5fSGarrett D'Amore const char *audigyls_remove_ac97[] = {
12348722b5fSGarrett D'Amore AUDIO_CTRL_ID_VOLUME,
12448722b5fSGarrett D'Amore AUDIO_CTRL_ID_LINEOUT,
12548722b5fSGarrett D'Amore AUDIO_CTRL_ID_HEADPHONE,
12648722b5fSGarrett D'Amore AUDIO_CTRL_ID_CD,
12748722b5fSGarrett D'Amore AUDIO_CTRL_ID_VIDEO,
12848722b5fSGarrett D'Amore AUDIO_CTRL_ID_3DDEPTH,
12948722b5fSGarrett D'Amore AUDIO_CTRL_ID_3DENHANCE,
13048722b5fSGarrett D'Amore AUDIO_CTRL_ID_BEEP,
13148722b5fSGarrett D'Amore AUDIO_CTRL_ID_RECGAIN,
13248722b5fSGarrett D'Amore AUDIO_CTRL_ID_RECSRC,
13348722b5fSGarrett D'Amore AUDIO_CTRL_ID_LOOPBACK,
13448722b5fSGarrett D'Amore NULL,
13548722b5fSGarrett D'Amore };
13648722b5fSGarrett D'Amore
13748722b5fSGarrett D'Amore /*
13848722b5fSGarrett D'Amore * AC'97 sources we don't want to expose.
13948722b5fSGarrett D'Amore */
14048722b5fSGarrett D'Amore const char *audigyls_badsrcs[] = {
14148722b5fSGarrett D'Amore AUDIO_PORT_VIDEO,
14248722b5fSGarrett D'Amore AUDIO_PORT_CD,
14348722b5fSGarrett D'Amore AUDIO_PORT_STEREOMIX,
14448722b5fSGarrett D'Amore AUDIO_PORT_MONOMIX,
14548722b5fSGarrett D'Amore NULL,
14648722b5fSGarrett D'Amore };
14748722b5fSGarrett D'Amore
14848722b5fSGarrett D'Amore static unsigned int
read_chan(audigyls_dev_t * dev,int reg,int chn)14948722b5fSGarrett D'Amore read_chan(audigyls_dev_t *dev, int reg, int chn)
15048722b5fSGarrett D'Amore {
15148722b5fSGarrett D'Amore uint32_t val;
15248722b5fSGarrett D'Amore
15348722b5fSGarrett D'Amore mutex_enter(&dev->low_mutex);
15448722b5fSGarrett D'Amore /* Pointer */
15548722b5fSGarrett D'Amore OUTL(dev, PR, (reg << 16) | (chn & 0xffff));
15648722b5fSGarrett D'Amore /* Data */
15748722b5fSGarrett D'Amore val = INL(dev, DR);
15848722b5fSGarrett D'Amore mutex_exit(&dev->low_mutex);
15948722b5fSGarrett D'Amore
16048722b5fSGarrett D'Amore return (val);
16148722b5fSGarrett D'Amore }
16248722b5fSGarrett D'Amore
16348722b5fSGarrett D'Amore static void
write_chan(audigyls_dev_t * dev,int reg,int chn,uint32_t value)16448722b5fSGarrett D'Amore write_chan(audigyls_dev_t *dev, int reg, int chn, uint32_t value)
16548722b5fSGarrett D'Amore {
16648722b5fSGarrett D'Amore mutex_enter(&dev->low_mutex);
16748722b5fSGarrett D'Amore /* Pointer */
16848722b5fSGarrett D'Amore OUTL(dev, PR, (reg << 16) | (chn & 0x7));
16948722b5fSGarrett D'Amore /* Data */
17048722b5fSGarrett D'Amore OUTL(dev, DR, value);
17148722b5fSGarrett D'Amore mutex_exit(&dev->low_mutex);
17248722b5fSGarrett D'Amore }
17348722b5fSGarrett D'Amore
17448722b5fSGarrett D'Amore static unsigned int
read_reg(audigyls_dev_t * dev,int reg)17548722b5fSGarrett D'Amore read_reg(audigyls_dev_t *dev, int reg)
17648722b5fSGarrett D'Amore {
17748722b5fSGarrett D'Amore return (read_chan(dev, reg, 0));
17848722b5fSGarrett D'Amore }
17948722b5fSGarrett D'Amore
18048722b5fSGarrett D'Amore static void
write_reg(audigyls_dev_t * dev,int reg,uint32_t value)18148722b5fSGarrett D'Amore write_reg(audigyls_dev_t *dev, int reg, uint32_t value)
18248722b5fSGarrett D'Amore {
18348722b5fSGarrett D'Amore write_chan(dev, reg, 0, value);
18448722b5fSGarrett D'Amore }
18548722b5fSGarrett D'Amore
18648722b5fSGarrett D'Amore
18748722b5fSGarrett D'Amore static uint16_t
audigyls_read_ac97(void * arg,uint8_t index)18848722b5fSGarrett D'Amore audigyls_read_ac97(void *arg, uint8_t index)
18948722b5fSGarrett D'Amore {
19048722b5fSGarrett D'Amore audigyls_dev_t *dev = arg;
19148722b5fSGarrett D'Amore uint16_t dtemp = 0;
19248722b5fSGarrett D'Amore int i;
19348722b5fSGarrett D'Amore
19448722b5fSGarrett D'Amore mutex_enter(&dev->low_mutex);
19548722b5fSGarrett D'Amore OUTB(dev, AC97A, index);
19648722b5fSGarrett D'Amore for (i = 0; i < 10000; i++) {
19748722b5fSGarrett D'Amore if (INB(dev, AC97A) & 0x80)
19848722b5fSGarrett D'Amore break;
19948722b5fSGarrett D'Amore }
20048722b5fSGarrett D'Amore if (i == 10000) { /* Timeout */
20148722b5fSGarrett D'Amore mutex_exit(&dev->low_mutex);
20248722b5fSGarrett D'Amore return (0xffff);
20348722b5fSGarrett D'Amore }
20448722b5fSGarrett D'Amore dtemp = INW(dev, AC97D);
20548722b5fSGarrett D'Amore mutex_exit(&dev->low_mutex);
20648722b5fSGarrett D'Amore
20748722b5fSGarrett D'Amore return (dtemp);
20848722b5fSGarrett D'Amore }
20948722b5fSGarrett D'Amore
21048722b5fSGarrett D'Amore static void
audigyls_write_ac97(void * arg,uint8_t index,uint16_t data)21148722b5fSGarrett D'Amore audigyls_write_ac97(void *arg, uint8_t index, uint16_t data)
21248722b5fSGarrett D'Amore {
21348722b5fSGarrett D'Amore audigyls_dev_t *dev = arg;
21448722b5fSGarrett D'Amore int i;
21548722b5fSGarrett D'Amore
21648722b5fSGarrett D'Amore mutex_enter(&dev->low_mutex);
21748722b5fSGarrett D'Amore OUTB(dev, AC97A, index);
21848722b5fSGarrett D'Amore for (i = 0; i < 50000; i++) {
21948722b5fSGarrett D'Amore if (INB(dev, AC97A) & 0x80)
22048722b5fSGarrett D'Amore break;
22148722b5fSGarrett D'Amore }
22248722b5fSGarrett D'Amore if (i == 50000) {
22348722b5fSGarrett D'Amore mutex_exit(&dev->low_mutex);
22448722b5fSGarrett D'Amore return;
22548722b5fSGarrett D'Amore }
22648722b5fSGarrett D'Amore OUTW(dev, AC97D, data);
22748722b5fSGarrett D'Amore mutex_exit(&dev->low_mutex);
22848722b5fSGarrett D'Amore }
22948722b5fSGarrett D'Amore
23048722b5fSGarrett D'Amore static void
select_digital_enable(audigyls_dev_t * dev,int mode)23148722b5fSGarrett D'Amore select_digital_enable(audigyls_dev_t *dev, int mode)
23248722b5fSGarrett D'Amore {
23348722b5fSGarrett D'Amore /*
23448722b5fSGarrett D'Amore * Set the out3/spdif combo jack format.
23548722b5fSGarrett D'Amore * mode0=analog rear/center, 1=spdif
23648722b5fSGarrett D'Amore */
23748722b5fSGarrett D'Amore
23848722b5fSGarrett D'Amore if (mode == 0) {
23948722b5fSGarrett D'Amore write_reg(dev, SPC, 0x00000f00);
24048722b5fSGarrett D'Amore } else {
24148722b5fSGarrett D'Amore write_reg(dev, SPC, 0x0000000f);
24248722b5fSGarrett D'Amore }
24348722b5fSGarrett D'Amore }
24448722b5fSGarrett D'Amore
24548722b5fSGarrett D'Amore /* only for SBLive 7.1 */
246a60cc674SGarrett D'Amore void
audigyls_i2c_write(audigyls_dev_t * dev,int reg,int data)24748722b5fSGarrett D'Amore audigyls_i2c_write(audigyls_dev_t *dev, int reg, int data)
24848722b5fSGarrett D'Amore {
24948722b5fSGarrett D'Amore int i, timeout, tmp;
25048722b5fSGarrett D'Amore
25148722b5fSGarrett D'Amore tmp = (reg << 9 | data) << 16; /* set the upper 16 bits */
25248722b5fSGarrett D'Amore /* first write the command to the data reg */
25348722b5fSGarrett D'Amore write_reg(dev, I2C_1, tmp);
25448722b5fSGarrett D'Amore for (i = 0; i < 20; i++) {
25548722b5fSGarrett D'Amore tmp = read_reg(dev, I2C_A) & ~0x6fe;
25648722b5fSGarrett D'Amore /* see audigyls.pdf for bits */
25748722b5fSGarrett D'Amore tmp |= 0x400 | 0x100 | 0x34;
25848722b5fSGarrett D'Amore write_reg(dev, I2C_A, tmp);
25948722b5fSGarrett D'Amore /* now wait till controller sets valid bit (0x100) to 0 */
26048722b5fSGarrett D'Amore timeout = 0;
26148722b5fSGarrett D'Amore for (;;) {
26248722b5fSGarrett D'Amore tmp = read_reg(dev, I2C_A);
26348722b5fSGarrett D'Amore if ((tmp & 0x100) == 0)
26448722b5fSGarrett D'Amore break;
26548722b5fSGarrett D'Amore
26648722b5fSGarrett D'Amore if (timeout > 100)
26748722b5fSGarrett D'Amore break;
26848722b5fSGarrett D'Amore
26948722b5fSGarrett D'Amore timeout++;
27048722b5fSGarrett D'Amore }
27148722b5fSGarrett D'Amore
27248722b5fSGarrett D'Amore /* transaction aborted */
27348722b5fSGarrett D'Amore if (tmp & 0x200)
274a60cc674SGarrett D'Amore break;
27548722b5fSGarrett D'Amore }
27648722b5fSGarrett D'Amore }
27748722b5fSGarrett D'Amore
27848722b5fSGarrett D'Amore int
audigyls_spi_write(audigyls_dev_t * dev,int data)27948722b5fSGarrett D'Amore audigyls_spi_write(audigyls_dev_t *dev, int data)
28048722b5fSGarrett D'Amore {
28148722b5fSGarrett D'Amore unsigned int orig;
28248722b5fSGarrett D'Amore unsigned int tmp;
28348722b5fSGarrett D'Amore int i, valid;
28448722b5fSGarrett D'Amore
28548722b5fSGarrett D'Amore tmp = read_reg(dev, SPI);
28648722b5fSGarrett D'Amore orig = (tmp & ~0x3ffff) | 0x30000;
28748722b5fSGarrett D'Amore write_reg(dev, SPI, orig | data);
28848722b5fSGarrett D'Amore valid = 0;
28948722b5fSGarrett D'Amore /* Wait for status bit to return to 0 */
29048722b5fSGarrett D'Amore for (i = 0; i < 1000; i++) {
29148722b5fSGarrett D'Amore drv_usecwait(100);
29248722b5fSGarrett D'Amore tmp = read_reg(dev, SPI);
29348722b5fSGarrett D'Amore if (!(tmp & 0x10000)) {
29448722b5fSGarrett D'Amore valid = 1;
29548722b5fSGarrett D'Amore break;
29648722b5fSGarrett D'Amore }
29748722b5fSGarrett D'Amore }
29848722b5fSGarrett D'Amore if (!valid) /* Timed out */
29948722b5fSGarrett D'Amore return (0);
30048722b5fSGarrett D'Amore
30148722b5fSGarrett D'Amore return (1);
30248722b5fSGarrett D'Amore }
30348722b5fSGarrett D'Amore
30448722b5fSGarrett D'Amore /*
30548722b5fSGarrett D'Amore * Audio routines
30648722b5fSGarrett D'Amore */
30748722b5fSGarrett D'Amore
30848722b5fSGarrett D'Amore int
audigyls_open(void * arg,int flag,unsigned * nframesp,caddr_t * bufp)30968c47f65SGarrett D'Amore audigyls_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp)
31048722b5fSGarrett D'Amore {
31148722b5fSGarrett D'Amore audigyls_port_t *port = arg;
31248722b5fSGarrett D'Amore audigyls_dev_t *dev = port->dev;
31348722b5fSGarrett D'Amore
31448722b5fSGarrett D'Amore _NOTE(ARGUNUSED(flag));
31548722b5fSGarrett D'Amore
31648722b5fSGarrett D'Amore mutex_enter(&dev->mutex);
31748722b5fSGarrett D'Amore
31848722b5fSGarrett D'Amore port->count = 0;
31968c47f65SGarrett D'Amore *nframesp = port->buf_frames;
32048722b5fSGarrett D'Amore *bufp = port->buf_kaddr;
32148722b5fSGarrett D'Amore mutex_exit(&dev->mutex);
32248722b5fSGarrett D'Amore
32348722b5fSGarrett D'Amore return (0);
32448722b5fSGarrett D'Amore }
32548722b5fSGarrett D'Amore
32648722b5fSGarrett D'Amore void
audigyls_close(void * arg)32748722b5fSGarrett D'Amore audigyls_close(void *arg)
32848722b5fSGarrett D'Amore {
32968c47f65SGarrett D'Amore _NOTE(ARGUNUSED(arg));
33048722b5fSGarrett D'Amore }
33148722b5fSGarrett D'Amore
33248722b5fSGarrett D'Amore int
audigyls_start(void * arg)33348722b5fSGarrett D'Amore audigyls_start(void *arg)
33448722b5fSGarrett D'Amore {
33548722b5fSGarrett D'Amore audigyls_port_t *port = arg;
33648722b5fSGarrett D'Amore audigyls_dev_t *dev = port->dev;
33768c47f65SGarrett D'Amore uint32_t tmp;
33848722b5fSGarrett D'Amore
33948722b5fSGarrett D'Amore mutex_enter(&dev->mutex);
34068c47f65SGarrett D'Amore
34168c47f65SGarrett D'Amore port->offset = 0;
34268c47f65SGarrett D'Amore
34368c47f65SGarrett D'Amore switch (port->direction) {
34468c47f65SGarrett D'Amore case AUDIGYLS_PLAY_PORT:
34568c47f65SGarrett D'Amore write_chan(dev, PTCA, 0, 0);
34668c47f65SGarrett D'Amore write_chan(dev, CPFA, 0, 0);
34768c47f65SGarrett D'Amore write_chan(dev, CPCAV, 0, 0);
34868c47f65SGarrett D'Amore write_chan(dev, PTCA, 1, 0);
34968c47f65SGarrett D'Amore write_chan(dev, CPFA, 1, 0);
35068c47f65SGarrett D'Amore write_chan(dev, CPCAV, 1, 0);
35168c47f65SGarrett D'Amore write_chan(dev, PTCA, 3, 0);
35268c47f65SGarrett D'Amore write_chan(dev, CPFA, 3, 0);
35368c47f65SGarrett D'Amore write_chan(dev, CPCAV, 3, 0);
35468c47f65SGarrett D'Amore
35568c47f65SGarrett D'Amore tmp = read_reg(dev, SA);
35668c47f65SGarrett D'Amore tmp |= SA_SPA(0);
35768c47f65SGarrett D'Amore tmp |= SA_SPA(1);
35868c47f65SGarrett D'Amore tmp |= SA_SPA(3);
35968c47f65SGarrett D'Amore write_reg(dev, SA, tmp);
36068c47f65SGarrett D'Amore break;
36168c47f65SGarrett D'Amore
36268c47f65SGarrett D'Amore case AUDIGYLS_REC_PORT:
36368c47f65SGarrett D'Amore write_chan(dev, CRFA, 2, 0);
36468c47f65SGarrett D'Amore write_chan(dev, CRCAV, 2, 0);
36568c47f65SGarrett D'Amore
36668c47f65SGarrett D'Amore tmp = read_reg(dev, SA);
36768c47f65SGarrett D'Amore tmp |= SA_SRA(2);
36868c47f65SGarrett D'Amore write_reg(dev, SA, tmp);
36968c47f65SGarrett D'Amore break;
37048722b5fSGarrett D'Amore }
37168c47f65SGarrett D'Amore
37248722b5fSGarrett D'Amore mutex_exit(&dev->mutex);
37348722b5fSGarrett D'Amore return (0);
37448722b5fSGarrett D'Amore }
37548722b5fSGarrett D'Amore
37648722b5fSGarrett D'Amore void
audigyls_stop(void * arg)37748722b5fSGarrett D'Amore audigyls_stop(void *arg)
37848722b5fSGarrett D'Amore {
37948722b5fSGarrett D'Amore audigyls_port_t *port = arg;
38048722b5fSGarrett D'Amore audigyls_dev_t *dev = port->dev;
38168c47f65SGarrett D'Amore uint32_t tmp;
38248722b5fSGarrett D'Amore
38348722b5fSGarrett D'Amore mutex_enter(&dev->mutex);
38468c47f65SGarrett D'Amore
38568c47f65SGarrett D'Amore switch (port->direction) {
38668c47f65SGarrett D'Amore case AUDIGYLS_PLAY_PORT:
38768c47f65SGarrett D'Amore tmp = read_reg(dev, SA);
38868c47f65SGarrett D'Amore tmp &= ~SA_SPA(0);
38968c47f65SGarrett D'Amore tmp &= ~SA_SPA(1);
39068c47f65SGarrett D'Amore tmp &= ~SA_SPA(3);
39168c47f65SGarrett D'Amore write_reg(dev, SA, tmp);
39268c47f65SGarrett D'Amore break;
39368c47f65SGarrett D'Amore
39468c47f65SGarrett D'Amore case AUDIGYLS_REC_PORT:
39568c47f65SGarrett D'Amore tmp = read_reg(dev, SA);
39668c47f65SGarrett D'Amore tmp &= ~SA_SRA(2);
39768c47f65SGarrett D'Amore write_reg(dev, SA, tmp);
39868c47f65SGarrett D'Amore break;
39948722b5fSGarrett D'Amore }
40068c47f65SGarrett D'Amore
40148722b5fSGarrett D'Amore mutex_exit(&dev->mutex);
40248722b5fSGarrett D'Amore }
40348722b5fSGarrett D'Amore
40448722b5fSGarrett D'Amore int
audigyls_format(void * arg)40548722b5fSGarrett D'Amore audigyls_format(void *arg)
40648722b5fSGarrett D'Amore {
40748722b5fSGarrett D'Amore _NOTE(ARGUNUSED(arg));
40848722b5fSGarrett D'Amore
40948722b5fSGarrett D'Amore return (AUDIO_FORMAT_S16_LE);
41048722b5fSGarrett D'Amore }
41148722b5fSGarrett D'Amore
41248722b5fSGarrett D'Amore int
audigyls_channels(void * arg)41348722b5fSGarrett D'Amore audigyls_channels(void *arg)
41448722b5fSGarrett D'Amore {
41548722b5fSGarrett D'Amore audigyls_port_t *port = arg;
41648722b5fSGarrett D'Amore
41748722b5fSGarrett D'Amore return (port->nchan);
41848722b5fSGarrett D'Amore }
41948722b5fSGarrett D'Amore
42048722b5fSGarrett D'Amore int
audigyls_rate(void * arg)42148722b5fSGarrett D'Amore audigyls_rate(void *arg)
42248722b5fSGarrett D'Amore {
42348722b5fSGarrett D'Amore _NOTE(ARGUNUSED(arg));
42448722b5fSGarrett D'Amore
42548722b5fSGarrett D'Amore return (48000);
42648722b5fSGarrett D'Amore }
42748722b5fSGarrett D'Amore
42848722b5fSGarrett D'Amore void
audigyls_sync(void * arg,unsigned nframes)42948722b5fSGarrett D'Amore audigyls_sync(void *arg, unsigned nframes)
43048722b5fSGarrett D'Amore {
43148722b5fSGarrett D'Amore audigyls_port_t *port = arg;
43248722b5fSGarrett D'Amore _NOTE(ARGUNUSED(nframes));
43348722b5fSGarrett D'Amore
43448722b5fSGarrett D'Amore (void) ddi_dma_sync(port->buf_dmah, 0, 0, port->syncdir);
43548722b5fSGarrett D'Amore }
43648722b5fSGarrett D'Amore
43748722b5fSGarrett D'Amore uint64_t
audigyls_count(void * arg)43848722b5fSGarrett D'Amore audigyls_count(void *arg)
43948722b5fSGarrett D'Amore {
44048722b5fSGarrett D'Amore audigyls_port_t *port = arg;
44148722b5fSGarrett D'Amore audigyls_dev_t *dev = port->dev;
44248722b5fSGarrett D'Amore uint64_t count;
44368c47f65SGarrett D'Amore uint32_t offset, n;
44448722b5fSGarrett D'Amore
44548722b5fSGarrett D'Amore mutex_enter(&dev->mutex);
44668c47f65SGarrett D'Amore
44768c47f65SGarrett D'Amore if (port->direction == AUDIGYLS_PLAY_PORT) {
44868c47f65SGarrett D'Amore offset = read_chan(dev, CPFA, 0);
44968c47f65SGarrett D'Amore } else {
45068c47f65SGarrett D'Amore offset = read_chan(dev, CRFA, 2);
45168c47f65SGarrett D'Amore }
45268c47f65SGarrett D'Amore
45368c47f65SGarrett D'Amore /* get the offset, and switch to frames */
45468c47f65SGarrett D'Amore offset /= (2 * sizeof (uint16_t));
45568c47f65SGarrett D'Amore
45668c47f65SGarrett D'Amore if (offset >= port->offset) {
45768c47f65SGarrett D'Amore n = offset - port->offset;
45868c47f65SGarrett D'Amore } else {
45968c47f65SGarrett D'Amore n = offset + (port->buf_frames - port->offset);
46068c47f65SGarrett D'Amore }
46168c47f65SGarrett D'Amore port->offset = offset;
46268c47f65SGarrett D'Amore port->count += n;
46368c47f65SGarrett D'Amore
46448722b5fSGarrett D'Amore count = port->count;
46548722b5fSGarrett D'Amore mutex_exit(&dev->mutex);
46648722b5fSGarrett D'Amore return (count);
46748722b5fSGarrett D'Amore }
46848722b5fSGarrett D'Amore
46948722b5fSGarrett D'Amore static void
audigyls_chinfo(void * arg,int chan,unsigned * offset,unsigned * incr)47048722b5fSGarrett D'Amore audigyls_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
47148722b5fSGarrett D'Amore {
47248722b5fSGarrett D'Amore audigyls_port_t *port = arg;
47348722b5fSGarrett D'Amore
47448722b5fSGarrett D'Amore if (port->direction == AUDIGYLS_PLAY_PORT) {
47548722b5fSGarrett D'Amore *offset = (port->buf_frames * 2 * (chan / 2)) + (chan % 2);
47648722b5fSGarrett D'Amore *incr = 2;
47748722b5fSGarrett D'Amore } else {
47848722b5fSGarrett D'Amore *offset = chan;
47948722b5fSGarrett D'Amore *incr = 2;
48048722b5fSGarrett D'Amore }
48148722b5fSGarrett D'Amore }
48248722b5fSGarrett D'Amore
48348722b5fSGarrett D'Amore /* private implementation bits */
48448722b5fSGarrett D'Amore
48548722b5fSGarrett D'Amore int
audigyls_alloc_port(audigyls_dev_t * dev,int num)48648722b5fSGarrett D'Amore audigyls_alloc_port(audigyls_dev_t *dev, int num)
48748722b5fSGarrett D'Amore {
48848722b5fSGarrett D'Amore audigyls_port_t *port;
48948722b5fSGarrett D'Amore size_t len;
49048722b5fSGarrett D'Amore ddi_dma_cookie_t cookie;
49148722b5fSGarrett D'Amore uint_t count;
49248722b5fSGarrett D'Amore int dir;
49348722b5fSGarrett D'Amore unsigned caps;
49448722b5fSGarrett D'Amore audio_dev_t *adev;
49548722b5fSGarrett D'Amore
49648722b5fSGarrett D'Amore adev = dev->adev;
49748722b5fSGarrett D'Amore port = kmem_zalloc(sizeof (*port), KM_SLEEP);
49848722b5fSGarrett D'Amore dev->port[num] = port;
49948722b5fSGarrett D'Amore port->dev = dev;
50048722b5fSGarrett D'Amore port->direction = num;
50148722b5fSGarrett D'Amore
50248722b5fSGarrett D'Amore switch (num) {
50348722b5fSGarrett D'Amore case AUDIGYLS_REC_PORT:
50448722b5fSGarrett D'Amore port->syncdir = DDI_DMA_SYNC_FORKERNEL;
50548722b5fSGarrett D'Amore caps = ENGINE_INPUT_CAP;
50648722b5fSGarrett D'Amore dir = DDI_DMA_READ;
50748722b5fSGarrett D'Amore port->nchan = 2;
50848722b5fSGarrett D'Amore break;
50948722b5fSGarrett D'Amore case AUDIGYLS_PLAY_PORT:
51048722b5fSGarrett D'Amore port->syncdir = DDI_DMA_SYNC_FORDEV;
51148722b5fSGarrett D'Amore caps = ENGINE_OUTPUT_CAP;
51248722b5fSGarrett D'Amore dir = DDI_DMA_WRITE;
51348722b5fSGarrett D'Amore port->nchan = 6;
51448722b5fSGarrett D'Amore break;
51548722b5fSGarrett D'Amore default:
51648722b5fSGarrett D'Amore return (DDI_FAILURE);
51748722b5fSGarrett D'Amore }
51848722b5fSGarrett D'Amore
51968c47f65SGarrett D'Amore port->buf_frames = 2048;
52068c47f65SGarrett D'Amore port->buf_size = port->buf_frames * port->nchan * sizeof (int16_t);
52148722b5fSGarrett D'Amore
52248722b5fSGarrett D'Amore /* Alloc buffers */
52348722b5fSGarrett D'Amore if (ddi_dma_alloc_handle(dev->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
52448722b5fSGarrett D'Amore &port->buf_dmah) != DDI_SUCCESS) {
52548722b5fSGarrett D'Amore audio_dev_warn(adev, "failed to allocate BUF handle");
52648722b5fSGarrett D'Amore return (DDI_FAILURE);
52748722b5fSGarrett D'Amore }
52848722b5fSGarrett D'Amore
52948722b5fSGarrett D'Amore if (ddi_dma_mem_alloc(port->buf_dmah, port->buf_size,
53048722b5fSGarrett D'Amore &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
53148722b5fSGarrett D'Amore &port->buf_kaddr, &len, &port->buf_acch) != DDI_SUCCESS) {
53248722b5fSGarrett D'Amore audio_dev_warn(adev, "failed to allocate BUF memory");
53348722b5fSGarrett D'Amore return (DDI_FAILURE);
53448722b5fSGarrett D'Amore }
53548722b5fSGarrett D'Amore
53648722b5fSGarrett D'Amore if (ddi_dma_addr_bind_handle(port->buf_dmah, NULL, port->buf_kaddr,
53748722b5fSGarrett D'Amore len, DDI_DMA_CONSISTENT | dir, DDI_DMA_SLEEP, NULL, &cookie,
53848722b5fSGarrett D'Amore &count) != DDI_SUCCESS) {
53948722b5fSGarrett D'Amore audio_dev_warn(adev, "failed binding BUF DMA handle");
54048722b5fSGarrett D'Amore return (DDI_FAILURE);
54148722b5fSGarrett D'Amore }
54248722b5fSGarrett D'Amore port->buf_paddr = cookie.dmac_address;
54348722b5fSGarrett D'Amore
54448722b5fSGarrett D'Amore port->engine = audio_engine_alloc(&audigyls_engine_ops, caps);
54548722b5fSGarrett D'Amore if (port->engine == NULL) {
54648722b5fSGarrett D'Amore audio_dev_warn(adev, "audio_engine_alloc failed");
54748722b5fSGarrett D'Amore return (DDI_FAILURE);
54848722b5fSGarrett D'Amore }
54948722b5fSGarrett D'Amore
55048722b5fSGarrett D'Amore audio_engine_set_private(port->engine, port);
55148722b5fSGarrett D'Amore audio_dev_add_engine(adev, port->engine);
55248722b5fSGarrett D'Amore
55348722b5fSGarrett D'Amore return (DDI_SUCCESS);
55448722b5fSGarrett D'Amore }
55548722b5fSGarrett D'Amore
5564a20ca87SGarrett D'Amore void
audigyls_del_controls(audigyls_dev_t * dev)5574a20ca87SGarrett D'Amore audigyls_del_controls(audigyls_dev_t *dev)
5584a20ca87SGarrett D'Amore {
5594a20ca87SGarrett D'Amore for (int i = 0; i < CTL_NUM; i++) {
5604a20ca87SGarrett D'Amore if (dev->controls[i].ctrl) {
5614a20ca87SGarrett D'Amore audio_dev_del_control(dev->controls[i].ctrl);
5624a20ca87SGarrett D'Amore dev->controls[i].ctrl = NULL;
5634a20ca87SGarrett D'Amore }
5644a20ca87SGarrett D'Amore }
5654a20ca87SGarrett D'Amore }
5664a20ca87SGarrett D'Amore
56748722b5fSGarrett D'Amore void
audigyls_destroy(audigyls_dev_t * dev)56848722b5fSGarrett D'Amore audigyls_destroy(audigyls_dev_t *dev)
56948722b5fSGarrett D'Amore {
57068c47f65SGarrett D'Amore mutex_destroy(&dev->mutex);
57168c47f65SGarrett D'Amore mutex_destroy(&dev->low_mutex);
57248722b5fSGarrett D'Amore
57348722b5fSGarrett D'Amore for (int i = 0; i < AUDIGYLS_NUM_PORT; i++) {
57448722b5fSGarrett D'Amore audigyls_port_t *port = dev->port[i];
57548722b5fSGarrett D'Amore if (!port)
57648722b5fSGarrett D'Amore continue;
57748722b5fSGarrett D'Amore if (port->engine) {
57848722b5fSGarrett D'Amore audio_dev_remove_engine(dev->adev, port->engine);
57948722b5fSGarrett D'Amore audio_engine_free(port->engine);
58048722b5fSGarrett D'Amore }
58148722b5fSGarrett D'Amore if (port->buf_paddr) {
58248722b5fSGarrett D'Amore (void) ddi_dma_unbind_handle(port->buf_dmah);
58348722b5fSGarrett D'Amore }
58448722b5fSGarrett D'Amore if (port->buf_acch) {
58548722b5fSGarrett D'Amore ddi_dma_mem_free(&port->buf_acch);
58648722b5fSGarrett D'Amore }
58748722b5fSGarrett D'Amore if (port->buf_dmah) {
58848722b5fSGarrett D'Amore ddi_dma_free_handle(&port->buf_dmah);
58948722b5fSGarrett D'Amore }
59048722b5fSGarrett D'Amore kmem_free(port, sizeof (*port));
59148722b5fSGarrett D'Amore }
59248722b5fSGarrett D'Amore
59348722b5fSGarrett D'Amore if (dev->ac97 != NULL) {
59448722b5fSGarrett D'Amore ac97_free(dev->ac97);
59548722b5fSGarrett D'Amore }
5964a20ca87SGarrett D'Amore
5974a20ca87SGarrett D'Amore audigyls_del_controls(dev);
5984a20ca87SGarrett D'Amore
59948722b5fSGarrett D'Amore if (dev->adev != NULL) {
60048722b5fSGarrett D'Amore audio_dev_free(dev->adev);
60148722b5fSGarrett D'Amore }
60248722b5fSGarrett D'Amore if (dev->regsh != NULL) {
60348722b5fSGarrett D'Amore ddi_regs_map_free(&dev->regsh);
60448722b5fSGarrett D'Amore }
60548722b5fSGarrett D'Amore if (dev->pcih != NULL) {
60648722b5fSGarrett D'Amore pci_config_teardown(&dev->pcih);
60748722b5fSGarrett D'Amore }
60848722b5fSGarrett D'Amore kmem_free(dev, sizeof (*dev));
60948722b5fSGarrett D'Amore }
61048722b5fSGarrett D'Amore
61148722b5fSGarrett D'Amore void
audigyls_hwinit(audigyls_dev_t * dev)61248722b5fSGarrett D'Amore audigyls_hwinit(audigyls_dev_t *dev)
61348722b5fSGarrett D'Amore {
61448722b5fSGarrett D'Amore static unsigned int spi_dac[] = {
61548722b5fSGarrett D'Amore 0x00ff, 0x02ff, 0x0400, 0x520, 0x0620, 0x08ff, 0x0aff, 0x0cff,
61648722b5fSGarrett D'Amore 0x0eff, 0x10ff, 0x1200, 0x1400, 0x1800, 0x1aff, 0x1cff,
61748722b5fSGarrett D'Amore 0x1e00, 0x0530, 0x0602, 0x0622, 0x1400,
61848722b5fSGarrett D'Amore };
61948722b5fSGarrett D'Amore
62048722b5fSGarrett D'Amore uint32_t tmp;
62148722b5fSGarrett D'Amore int i, tries;
62248722b5fSGarrett D'Amore uint32_t paddr;
62348722b5fSGarrett D'Amore uint32_t chunksz;
62448722b5fSGarrett D'Amore audigyls_port_t *port;
62548722b5fSGarrett D'Amore
62648722b5fSGarrett D'Amore
62748722b5fSGarrett D'Amore /* Set the orange jack to be analog out or S/PDIF */
62848722b5fSGarrett D'Amore select_digital_enable(dev, dev->digital_enable);
62948722b5fSGarrett D'Amore
63048722b5fSGarrett D'Amore /*
63148722b5fSGarrett D'Amore * In P17, there's 8 GPIO pins.
63248722b5fSGarrett D'Amore * GPIO register: 0x00XXYYZZ
63348722b5fSGarrett D'Amore * XX: Configure GPIO to be either GPI (0) or GPO (1).
63448722b5fSGarrett D'Amore * YY: GPO values, applicable if the pin is configure to be GPO.
63548722b5fSGarrett D'Amore * ZZ: GPI values, applicable if the pin is configure to be GPI.
63648722b5fSGarrett D'Amore *
63748722b5fSGarrett D'Amore * in SB570, pin 0-4 and 6 is used as GPO and pin 5 and 7 is
63848722b5fSGarrett D'Amore * used as GPI.
63948722b5fSGarrett D'Amore *
64048722b5fSGarrett D'Amore * GPO0:
64148722b5fSGarrett D'Amore * 1 ==> Analog output
64248722b5fSGarrett D'Amore * 0 ==> Digital output
64348722b5fSGarrett D'Amore * GPO1:
64448722b5fSGarrett D'Amore * 1 ==> Enable output on card
64548722b5fSGarrett D'Amore * 0 ==> Disable output on card
64648722b5fSGarrett D'Amore * GPO2:
64748722b5fSGarrett D'Amore * 1 ==> Enable Mic Bias and Mic Path
64848722b5fSGarrett D'Amore * 0 ==> Disable Mic Bias and Mic Path
64948722b5fSGarrett D'Amore * GPO3:
65048722b5fSGarrett D'Amore * 1 ==> Disable SPDIF-IO output
65148722b5fSGarrett D'Amore * 0 ==> Enable SPDIF-IO output
65248722b5fSGarrett D'Amore * GPO4 and GPO6:
65348722b5fSGarrett D'Amore * DAC sampling rate selection:
65448722b5fSGarrett D'Amore * Not applicable to SB570 since DAC is controlled through SPI
65548722b5fSGarrett D'Amore * GPI5:
65648722b5fSGarrett D'Amore * 1 ==> Front Panel is not connected
65748722b5fSGarrett D'Amore * 0 ==> Front Panel is connected
65848722b5fSGarrett D'Amore * GPI7:
65948722b5fSGarrett D'Amore * 1 ==> Front Panel Headphone is not connected
66048722b5fSGarrett D'Amore * 0 ==> Front Panel Headphone is connected
66148722b5fSGarrett D'Amore */
66248722b5fSGarrett D'Amore if (dev->ac97)
66348722b5fSGarrett D'Amore OUTL(dev, GPIO, 0x005f03a3);
66448722b5fSGarrett D'Amore else {
66548722b5fSGarrett D'Amore /* for SBLive 7.1 */
66648722b5fSGarrett D'Amore OUTL(dev, GPIO, 0x005f4301);
66748722b5fSGarrett D'Amore
66848722b5fSGarrett D'Amore audigyls_i2c_write(dev, 0x15, 0x2);
66948722b5fSGarrett D'Amore tries = 0;
67048722b5fSGarrett D'Amore again:
671*2a7bf89eSGarrett D'Amore for (i = 0; i < (sizeof (spi_dac) / sizeof (spi_dac[0])); i++) {
67248722b5fSGarrett D'Amore if (!audigyls_spi_write(dev, spi_dac[i]) &&
67348722b5fSGarrett D'Amore tries < 100) {
67448722b5fSGarrett D'Amore tries++;
67548722b5fSGarrett D'Amore goto again;
67648722b5fSGarrett D'Amore }
67748722b5fSGarrett D'Amore }
67848722b5fSGarrett D'Amore }
67948722b5fSGarrett D'Amore
68068c47f65SGarrett D'Amore OUTL(dev, IER, 0);
68148722b5fSGarrett D'Amore OUTL(dev, HC, 0x00000009); /* Enable audio, use 48 kHz */
68248722b5fSGarrett D'Amore
68348722b5fSGarrett D'Amore tmp = read_chan(dev, SRCTL, 0);
68448722b5fSGarrett D'Amore if (dev->ac97)
68548722b5fSGarrett D'Amore tmp |= 0xf0c81000; /* Record src0/src1 from ac97 */
68648722b5fSGarrett D'Amore else
68748722b5fSGarrett D'Amore tmp |= 0x50c81000; /* Record src0/src1 from I2SIN */
68848722b5fSGarrett D'Amore tmp &= ~0x0303c00f; /* Set sample rates to 48 kHz */
68948722b5fSGarrett D'Amore write_chan(dev, SRCTL, 0, tmp);
69048722b5fSGarrett D'Amore
69148722b5fSGarrett D'Amore write_reg(dev, HMIXMAP_I2S, 0x76543210); /* Default out route */
69248722b5fSGarrett D'Amore write_reg(dev, AUDCTL, 0x0f0f003f); /* Enable all outputs */
69348722b5fSGarrett D'Amore
69448722b5fSGarrett D'Amore /* All audio stopped! */
69548722b5fSGarrett D'Amore write_reg(dev, SA, 0);
69648722b5fSGarrett D'Amore
69748722b5fSGarrett D'Amore for (i = 0; i < 4; i++) {
69848722b5fSGarrett D'Amore /*
69948722b5fSGarrett D'Amore * Reset DMA pointers and counters. Note that we do
70048722b5fSGarrett D'Amore * not use scatter/gather.
70148722b5fSGarrett D'Amore */
70248722b5fSGarrett D'Amore write_chan(dev, PTBA, i, 0);
70348722b5fSGarrett D'Amore write_chan(dev, PTBS, i, 0);
70448722b5fSGarrett D'Amore write_chan(dev, PTCA, i, 0);
70548722b5fSGarrett D'Amore
70648722b5fSGarrett D'Amore write_chan(dev, CPFA, i, 0);
70748722b5fSGarrett D'Amore write_chan(dev, PFEA, i, 0);
70848722b5fSGarrett D'Amore write_chan(dev, CPCAV, i, 0);
70948722b5fSGarrett D'Amore
71048722b5fSGarrett D'Amore write_chan(dev, CRFA, i, 0);
71148722b5fSGarrett D'Amore write_chan(dev, CRCAV, i, 0);
71248722b5fSGarrett D'Amore }
71348722b5fSGarrett D'Amore
71448722b5fSGarrett D'Amore /*
71548722b5fSGarrett D'Amore * The 5.1 play port made up channels 0, 1, and 3. The record
71648722b5fSGarrett D'Amore * port is channel 2.
71748722b5fSGarrett D'Amore */
71848722b5fSGarrett D'Amore port = dev->port[AUDIGYLS_PLAY_PORT];
71948722b5fSGarrett D'Amore paddr = port->buf_paddr;
72048722b5fSGarrett D'Amore chunksz = port->buf_frames * 4;
72148722b5fSGarrett D'Amore write_chan(dev, PFBA, 0, paddr);
72248722b5fSGarrett D'Amore write_chan(dev, PFBS, 0, chunksz << 16);
72348722b5fSGarrett D'Amore paddr += chunksz;
72448722b5fSGarrett D'Amore write_chan(dev, PFBA, 1, paddr);
72548722b5fSGarrett D'Amore write_chan(dev, PFBS, 1, chunksz << 16);
72648722b5fSGarrett D'Amore paddr += chunksz;
72748722b5fSGarrett D'Amore write_chan(dev, PFBA, 3, paddr);
72848722b5fSGarrett D'Amore write_chan(dev, PFBS, 3, chunksz << 16);
72948722b5fSGarrett D'Amore
73048722b5fSGarrett D'Amore /* Record */
73148722b5fSGarrett D'Amore port = dev->port[AUDIGYLS_REC_PORT];
73248722b5fSGarrett D'Amore paddr = port->buf_paddr;
73348722b5fSGarrett D'Amore chunksz = port->buf_frames * 4;
73448722b5fSGarrett D'Amore write_chan(dev, RFBA, 2, paddr);
73548722b5fSGarrett D'Amore write_chan(dev, RFBS, 2, chunksz << 16);
73648722b5fSGarrett D'Amore
73748722b5fSGarrett D'Amore /* Set sample rates to 48 kHz. */
73848722b5fSGarrett D'Amore tmp = read_chan(dev, SRCTL, 0) & ~0x0303c00f;
73948722b5fSGarrett D'Amore write_chan(dev, SRCTL, 0, tmp);
74048722b5fSGarrett D'Amore
74148722b5fSGarrett D'Amore write_reg(dev, SCS0, 0x02108004); /* Audio */
74248722b5fSGarrett D'Amore write_reg(dev, SCS1, 0x02108004); /* Audio */
74348722b5fSGarrett D'Amore write_reg(dev, SCS2, 0x02108004); /* Audio */
74448722b5fSGarrett D'Amore write_reg(dev, SCS3, 0x02108004); /* Audio */
74548722b5fSGarrett D'Amore }
74648722b5fSGarrett D'Amore
74748722b5fSGarrett D'Amore #define PLAYCTL (AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY)
74848722b5fSGarrett D'Amore #define RECCTL (AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_REC)
74948722b5fSGarrett D'Amore #define MONCTL (AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_MONITOR)
75048722b5fSGarrett D'Amore #define PCMVOL (PLAYCTL | AUDIO_CTRL_FLAG_PCMVOL)
75148722b5fSGarrett D'Amore #define MAINVOL (PLAYCTL | AUDIO_CTRL_FLAG_MAINVOL)
75248722b5fSGarrett D'Amore #define RECVOL (RECCTL | AUDIO_CTRL_FLAG_RECVOL)
75348722b5fSGarrett D'Amore #define MONVOL (MONCTL | AUDIO_CTRL_FLAG_MONVOL)
75448722b5fSGarrett D'Amore
75548722b5fSGarrett D'Amore #define MASK(nbits) ((1 << (nbits)) - 1)
75648722b5fSGarrett D'Amore #define SCALE(val, nbits) \
75748722b5fSGarrett D'Amore ((uint8_t)((((val) * MASK(nbits)) / 100)) << (8 - (nbits)))
75848722b5fSGarrett D'Amore
75948722b5fSGarrett D'Amore static uint32_t
audigyls_stereo_scale(uint32_t value,uint8_t bits)76048722b5fSGarrett D'Amore audigyls_stereo_scale(uint32_t value, uint8_t bits)
76148722b5fSGarrett D'Amore {
76248722b5fSGarrett D'Amore uint8_t left, right;
76348722b5fSGarrett D'Amore uint32_t val;
76448722b5fSGarrett D'Amore
76548722b5fSGarrett D'Amore left = (value >> 8) & 0xff;
76648722b5fSGarrett D'Amore right = value & 0xff;
76748722b5fSGarrett D'Amore
76848722b5fSGarrett D'Amore val = (((left * ((1 << bits) - 1) / 100) << 8) |
76948722b5fSGarrett D'Amore (right * ((1 << bits) - 1) / 100));
77048722b5fSGarrett D'Amore return (val);
77148722b5fSGarrett D'Amore }
77248722b5fSGarrett D'Amore
77348722b5fSGarrett D'Amore static void
audigyls_configure_mixer(audigyls_dev_t * dev)77448722b5fSGarrett D'Amore audigyls_configure_mixer(audigyls_dev_t *dev)
77548722b5fSGarrett D'Amore {
77648722b5fSGarrett D'Amore unsigned int r, v1, v2;
77748722b5fSGarrett D'Amore
77848722b5fSGarrett D'Amore /* output items */
77948722b5fSGarrett D'Amore /* front */
78048722b5fSGarrett D'Amore r = 0xffff - audigyls_stereo_scale(dev->controls[CTL_FRONT].val, 8);
78148722b5fSGarrett D'Amore r = (r << 16) | r;
78248722b5fSGarrett D'Amore write_chan(dev, MIXVOL_I2S, 0, r);
78348722b5fSGarrett D'Amore
78448722b5fSGarrett D'Amore /* surround */
78548722b5fSGarrett D'Amore r = 0xffff - audigyls_stereo_scale(dev->controls[CTL_SURROUND].val, 8);
78648722b5fSGarrett D'Amore r = (r << 16) | r;
78748722b5fSGarrett D'Amore write_chan(dev, MIXVOL_I2S, 3, r);
78848722b5fSGarrett D'Amore
78948722b5fSGarrett D'Amore /* center/lfe */
79048722b5fSGarrett D'Amore v1 = 255 - SCALE(dev->controls[CTL_CENTER].val, 8);
79148722b5fSGarrett D'Amore v2 = 255 - SCALE(dev->controls[CTL_LFE].val, 8);
79248722b5fSGarrett D'Amore r = (v1 << 8) | v2;
79348722b5fSGarrett D'Amore r = (r << 16) | r;
79448722b5fSGarrett D'Amore write_chan(dev, MIXVOL_I2S, 1, r);
79548722b5fSGarrett D'Amore
79648722b5fSGarrett D'Amore /* spread */
79748722b5fSGarrett D'Amore r = dev->controls[CTL_SPREAD].val ? 0x10101010 : 0x76543210;
79848722b5fSGarrett D'Amore write_reg(dev, HMIXMAP_I2S, r);
79948722b5fSGarrett D'Amore
80048722b5fSGarrett D'Amore /* input items */
80148722b5fSGarrett D'Amore
80248722b5fSGarrett D'Amore /* recgain */
80348722b5fSGarrett D'Amore v1 = dev->controls[CTL_RECORDVOL].val;
80448722b5fSGarrett D'Amore if (dev->ac97_recgain && !dev->controls[CTL_LOOP].val) {
80548722b5fSGarrett D'Amore /*
80648722b5fSGarrett D'Amore * For AC'97, we use the AC'97 record gain, unless we are
80748722b5fSGarrett D'Amore * in loopback.
80848722b5fSGarrett D'Amore */
809a60cc674SGarrett D'Amore (void) ac97_control_set(dev->ac97_recgain, v1);
81048722b5fSGarrett D'Amore write_reg(dev, P17RECVOLL, 0x30303030);
81148722b5fSGarrett D'Amore write_reg(dev, P17RECVOLH, 0x30303030);
81248722b5fSGarrett D'Amore } else {
81348722b5fSGarrett D'Amore /*
81448722b5fSGarrett D'Amore * Otherwise we set the P17 gain.
81548722b5fSGarrett D'Amore */
81648722b5fSGarrett D'Amore r = 0xffff - audigyls_stereo_scale(v1, 8);
81748722b5fSGarrett D'Amore r = r << 16 | r;
81848722b5fSGarrett D'Amore write_reg(dev, P17RECVOLL, r);
81948722b5fSGarrett D'Amore write_reg(dev, P17RECVOLH, r);
82048722b5fSGarrett D'Amore }
82148722b5fSGarrett D'Amore
82248722b5fSGarrett D'Amore /* monitor gain */
82348722b5fSGarrett D'Amore if (dev->ac97) {
82448722b5fSGarrett D'Amore /* AC'97 monitor gain is done by the AC'97 codec */
82548722b5fSGarrett D'Amore write_chan(dev, SRCTL, 1, 0x30303030);
82648722b5fSGarrett D'Amore write_reg(dev, SMIXMAP_I2S, 0x10101076);
82748722b5fSGarrett D'Amore } else {
82848722b5fSGarrett D'Amore /* For non-AC'97 devices, just a single master monitor gain */
82948722b5fSGarrett D'Amore r = 255 - SCALE(dev->controls[CTL_MONGAIN].val, 8);
83048722b5fSGarrett D'Amore write_chan(dev, SRCTL, 1, 0xffff0000 | r << 8 | r);
83148722b5fSGarrett D'Amore if (r != 0xff) {
83248722b5fSGarrett D'Amore write_reg(dev, SMIXMAP_I2S, 0x10101076);
83348722b5fSGarrett D'Amore } else {
83448722b5fSGarrett D'Amore write_reg(dev, SMIXMAP_I2S, 0x10101010);
83548722b5fSGarrett D'Amore }
83648722b5fSGarrett D'Amore }
83748722b5fSGarrett D'Amore
83848722b5fSGarrett D'Amore /* record source */
83948722b5fSGarrett D'Amore if (dev->ac97_recsrc != NULL) {
840a60cc674SGarrett D'Amore (void) ac97_control_set(dev->ac97_recsrc,
84148722b5fSGarrett D'Amore dev->controls[CTL_RECSRC].val);
84248722b5fSGarrett D'Amore v1 = RECSEL_AC97; /* Audigy LS */
84348722b5fSGarrett D'Amore } else {
84448722b5fSGarrett D'Amore switch (dev->controls[CTL_RECSRC].val) {
84548722b5fSGarrett D'Amore case 1:
84648722b5fSGarrett D'Amore audigyls_i2c_write(dev, 0x15, 0x2); /* Mic */
84748722b5fSGarrett D'Amore OUTL(dev, GPIO, INL(dev, GPIO) | 0x400);
84848722b5fSGarrett D'Amore break;
84948722b5fSGarrett D'Amore
85048722b5fSGarrett D'Amore case 2:
85148722b5fSGarrett D'Amore audigyls_i2c_write(dev, 0x15, 0x4); /* Line */
85248722b5fSGarrett D'Amore OUTL(dev, GPIO, INL(dev, GPIO) & ~0x400);
85348722b5fSGarrett D'Amore break;
85448722b5fSGarrett D'Amore }
85548722b5fSGarrett D'Amore v1 = RECSEL_I2SIN; /* SB 7.1 value */
85648722b5fSGarrett D'Amore }
85748722b5fSGarrett D'Amore
85848722b5fSGarrett D'Amore /* If loopback, record what you hear instead */
85948722b5fSGarrett D'Amore
86048722b5fSGarrett D'Amore if (dev->controls[CTL_LOOP].val) {
86148722b5fSGarrett D'Amore r = 0;
86248722b5fSGarrett D'Amore v1 = RECSEL_I2SOUT;
86368c47f65SGarrett D'Amore r |= (v1 << 28) | (v1 << 24) | (v1 << 20) | (v1 << 16) | v1;
86448722b5fSGarrett D'Amore } else {
86548722b5fSGarrett D'Amore /*
86648722b5fSGarrett D'Amore * You'd think this would be the same as the logic
86748722b5fSGarrett D'Amore * above, but experience shows that what you need for
86848722b5fSGarrett D'Amore * loopback is different. This whole thing looks
86948722b5fSGarrett D'Amore * particularly fishy to me. I suspect someone has
87048722b5fSGarrett D'Amore * made a mistake somewhere. But I can't seem to
87148722b5fSGarrett D'Amore * figure out where it lies.
87248722b5fSGarrett D'Amore */
873e7437094SGarrett D'Amore if (dev->ac97_recsrc != NULL) {
874e7437094SGarrett D'Amore r = 0xe4;
875e7437094SGarrett D'Amore for (int i = 0; i < 4; i++)
876e7437094SGarrett D'Amore r |= v1 << (16 + i * 3); /* Select input */
877e7437094SGarrett D'Amore } else {
878e7437094SGarrett D'Amore r = (v1 << 28) | (v1 << 24) | (v1 << 20) | (v1 << 16) |
879e7437094SGarrett D'Amore v1;
880e7437094SGarrett D'Amore }
88148722b5fSGarrett D'Amore }
88248722b5fSGarrett D'Amore
88348722b5fSGarrett D'Amore write_reg(dev, P17RECSEL, r);
88448722b5fSGarrett D'Amore }
88548722b5fSGarrett D'Amore
88648722b5fSGarrett D'Amore static int
audigyls_set_control(void * arg,uint64_t val)88748722b5fSGarrett D'Amore audigyls_set_control(void *arg, uint64_t val)
88848722b5fSGarrett D'Amore {
88948722b5fSGarrett D'Amore audigyls_ctrl_t *pc = arg;
89048722b5fSGarrett D'Amore audigyls_dev_t *dev = pc->dev;
89148722b5fSGarrett D'Amore
89248722b5fSGarrett D'Amore switch (pc->num) {
89348722b5fSGarrett D'Amore
89448722b5fSGarrett D'Amore case CTL_FRONT:
89548722b5fSGarrett D'Amore case CTL_SURROUND:
89648722b5fSGarrett D'Amore case CTL_RECORDVOL:
89748722b5fSGarrett D'Amore if (((val & 0xff) > 100) ||
89848722b5fSGarrett D'Amore (((val & 0xff00) >> 8) > 100) ||
89948722b5fSGarrett D'Amore ((val & ~0xffff) != 0)) {
90048722b5fSGarrett D'Amore return (EINVAL);
90148722b5fSGarrett D'Amore }
90248722b5fSGarrett D'Amore break;
90348722b5fSGarrett D'Amore
90448722b5fSGarrett D'Amore case CTL_CENTER:
90548722b5fSGarrett D'Amore case CTL_LFE:
90648722b5fSGarrett D'Amore case CTL_MONGAIN:
90748722b5fSGarrett D'Amore if (val > 100) {
90848722b5fSGarrett D'Amore return (EINVAL);
90948722b5fSGarrett D'Amore }
91048722b5fSGarrett D'Amore break;
91148722b5fSGarrett D'Amore
91248722b5fSGarrett D'Amore case CTL_RECSRC:
91348722b5fSGarrett D'Amore if (((1U << val) & (dev->recmask)) == 0) {
91448722b5fSGarrett D'Amore return (EINVAL);
91548722b5fSGarrett D'Amore }
91648722b5fSGarrett D'Amore break;
91748722b5fSGarrett D'Amore
91848722b5fSGarrett D'Amore case CTL_SPREAD:
91948722b5fSGarrett D'Amore case CTL_LOOP:
92048722b5fSGarrett D'Amore switch (val) {
92148722b5fSGarrett D'Amore case 0:
92248722b5fSGarrett D'Amore case 1:
92348722b5fSGarrett D'Amore break;
92448722b5fSGarrett D'Amore default:
92548722b5fSGarrett D'Amore return (EINVAL);
92648722b5fSGarrett D'Amore }
92748722b5fSGarrett D'Amore }
92848722b5fSGarrett D'Amore
92948722b5fSGarrett D'Amore mutex_enter(&dev->mutex);
93048722b5fSGarrett D'Amore pc->val = val;
93168c47f65SGarrett D'Amore audigyls_configure_mixer(dev);
93268c47f65SGarrett D'Amore
93348722b5fSGarrett D'Amore mutex_exit(&dev->mutex);
93448722b5fSGarrett D'Amore
93548722b5fSGarrett D'Amore return (0);
93648722b5fSGarrett D'Amore }
93748722b5fSGarrett D'Amore
93848722b5fSGarrett D'Amore static int
audigyls_get_control(void * arg,uint64_t * val)93948722b5fSGarrett D'Amore audigyls_get_control(void *arg, uint64_t *val)
94048722b5fSGarrett D'Amore {
94148722b5fSGarrett D'Amore audigyls_ctrl_t *pc = arg;
94248722b5fSGarrett D'Amore audigyls_dev_t *dev = pc->dev;
94348722b5fSGarrett D'Amore
94448722b5fSGarrett D'Amore mutex_enter(&dev->mutex);
94548722b5fSGarrett D'Amore *val = pc->val;
94648722b5fSGarrett D'Amore mutex_exit(&dev->mutex);
94748722b5fSGarrett D'Amore return (0);
94848722b5fSGarrett D'Amore }
94948722b5fSGarrett D'Amore
95048722b5fSGarrett D'Amore static void
audigyls_alloc_ctrl(audigyls_dev_t * dev,uint32_t num,uint64_t val)95148722b5fSGarrett D'Amore audigyls_alloc_ctrl(audigyls_dev_t *dev, uint32_t num, uint64_t val)
95248722b5fSGarrett D'Amore {
95348722b5fSGarrett D'Amore audio_ctrl_desc_t desc;
95448722b5fSGarrett D'Amore audigyls_ctrl_t *pc;
95548722b5fSGarrett D'Amore
95648722b5fSGarrett D'Amore bzero(&desc, sizeof (desc));
95748722b5fSGarrett D'Amore
95848722b5fSGarrett D'Amore pc = &dev->controls[num];
95948722b5fSGarrett D'Amore pc->num = num;
96048722b5fSGarrett D'Amore pc->dev = dev;
96148722b5fSGarrett D'Amore
96248722b5fSGarrett D'Amore
96348722b5fSGarrett D'Amore switch (num) {
96448722b5fSGarrett D'Amore case CTL_FRONT:
96548722b5fSGarrett D'Amore desc.acd_name = AUDIO_CTRL_ID_FRONT;
96648722b5fSGarrett D'Amore desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
96748722b5fSGarrett D'Amore desc.acd_minvalue = 0;
96848722b5fSGarrett D'Amore desc.acd_maxvalue = 100;
96948722b5fSGarrett D'Amore desc.acd_flags = MAINVOL;
97048722b5fSGarrett D'Amore break;
97148722b5fSGarrett D'Amore
97248722b5fSGarrett D'Amore case CTL_SURROUND:
97348722b5fSGarrett D'Amore desc.acd_name = AUDIO_CTRL_ID_SURROUND;
97448722b5fSGarrett D'Amore desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
97548722b5fSGarrett D'Amore desc.acd_minvalue = 0;
97648722b5fSGarrett D'Amore desc.acd_maxvalue = 100;
97748722b5fSGarrett D'Amore desc.acd_flags = MAINVOL;
97848722b5fSGarrett D'Amore break;
97948722b5fSGarrett D'Amore
98048722b5fSGarrett D'Amore case CTL_CENTER:
98148722b5fSGarrett D'Amore desc.acd_name = AUDIO_CTRL_ID_CENTER;
98248722b5fSGarrett D'Amore desc.acd_type = AUDIO_CTRL_TYPE_MONO;
98348722b5fSGarrett D'Amore desc.acd_minvalue = 0;
98448722b5fSGarrett D'Amore desc.acd_maxvalue = 100;
98548722b5fSGarrett D'Amore desc.acd_flags = MAINVOL;
98648722b5fSGarrett D'Amore break;
98748722b5fSGarrett D'Amore
98848722b5fSGarrett D'Amore case CTL_LFE:
98948722b5fSGarrett D'Amore desc.acd_name = AUDIO_CTRL_ID_LFE;
99048722b5fSGarrett D'Amore desc.acd_type = AUDIO_CTRL_TYPE_MONO;
99148722b5fSGarrett D'Amore desc.acd_minvalue = 0;
99248722b5fSGarrett D'Amore desc.acd_maxvalue = 100;
99348722b5fSGarrett D'Amore desc.acd_flags = MAINVOL;
99448722b5fSGarrett D'Amore break;
99548722b5fSGarrett D'Amore
99648722b5fSGarrett D'Amore case CTL_RECORDVOL:
99748722b5fSGarrett D'Amore desc.acd_name = AUDIO_CTRL_ID_RECGAIN;
99848722b5fSGarrett D'Amore desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
99948722b5fSGarrett D'Amore desc.acd_minvalue = 0;
100048722b5fSGarrett D'Amore desc.acd_maxvalue = 100;
100148722b5fSGarrett D'Amore desc.acd_flags = RECVOL;
100248722b5fSGarrett D'Amore break;
100348722b5fSGarrett D'Amore
100448722b5fSGarrett D'Amore case CTL_RECSRC:
100548722b5fSGarrett D'Amore desc.acd_name = AUDIO_CTRL_ID_RECSRC;
100648722b5fSGarrett D'Amore desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
100748722b5fSGarrett D'Amore desc.acd_flags = RECCTL;
100848722b5fSGarrett D'Amore
100948722b5fSGarrett D'Amore /*
101048722b5fSGarrett D'Amore * For AC'97 devices, we want to expose the reasonable
101148722b5fSGarrett D'Amore * AC'97 input sources, but suppress the stereomix,
101248722b5fSGarrett D'Amore * because we use loopback instead.
101348722b5fSGarrett D'Amore */
101448722b5fSGarrett D'Amore if (dev->ac97_recsrc) {
101548722b5fSGarrett D'Amore int i, j;
101648722b5fSGarrett D'Amore const char *n;
101748722b5fSGarrett D'Amore const audio_ctrl_desc_t *adp;
101848722b5fSGarrett D'Amore
101948722b5fSGarrett D'Amore adp = ac97_control_desc(dev->ac97_recsrc);
102048722b5fSGarrett D'Amore for (i = 0; i < 64; i++) {
102148722b5fSGarrett D'Amore n = adp->acd_enum[i];
102248722b5fSGarrett D'Amore
102348722b5fSGarrett D'Amore if (((adp->acd_minvalue & (1 << i)) == 0) ||
102448722b5fSGarrett D'Amore (n == NULL)) {
102548722b5fSGarrett D'Amore continue;
102648722b5fSGarrett D'Amore }
102748722b5fSGarrett D'Amore for (j = 0; audigyls_badsrcs[j]; j++) {
102848722b5fSGarrett D'Amore if (strcmp(n, audigyls_badsrcs[j])
102948722b5fSGarrett D'Amore == 0) {
103048722b5fSGarrett D'Amore n = NULL;
103148722b5fSGarrett D'Amore break;
103248722b5fSGarrett D'Amore }
103348722b5fSGarrett D'Amore }
103448722b5fSGarrett D'Amore if (n) {
103548722b5fSGarrett D'Amore desc.acd_enum[i] = n;
103648722b5fSGarrett D'Amore dev->recmask |= (1 << i);
103748722b5fSGarrett D'Amore }
103848722b5fSGarrett D'Amore }
103948722b5fSGarrett D'Amore desc.acd_minvalue = desc.acd_maxvalue = dev->recmask;
104048722b5fSGarrett D'Amore } else {
104148722b5fSGarrett D'Amore dev->recmask = 3;
104248722b5fSGarrett D'Amore desc.acd_minvalue = 3;
104348722b5fSGarrett D'Amore desc.acd_maxvalue = 3;
104448722b5fSGarrett D'Amore desc.acd_enum[0] = AUDIO_PORT_MIC;
104548722b5fSGarrett D'Amore desc.acd_enum[1] = AUDIO_PORT_LINEIN;
104648722b5fSGarrett D'Amore }
104748722b5fSGarrett D'Amore break;
104848722b5fSGarrett D'Amore
104948722b5fSGarrett D'Amore case CTL_MONGAIN:
105048722b5fSGarrett D'Amore ASSERT(!dev->ac97);
105148722b5fSGarrett D'Amore desc.acd_name = AUDIO_CTRL_ID_MONGAIN;
105248722b5fSGarrett D'Amore desc.acd_type = AUDIO_CTRL_TYPE_MONO;
105348722b5fSGarrett D'Amore desc.acd_minvalue = 0;
105448722b5fSGarrett D'Amore desc.acd_maxvalue = 100;
105548722b5fSGarrett D'Amore desc.acd_flags = MONVOL;
105648722b5fSGarrett D'Amore break;
105748722b5fSGarrett D'Amore
105848722b5fSGarrett D'Amore case CTL_SPREAD:
105948722b5fSGarrett D'Amore desc.acd_name = AUDIO_CTRL_ID_SPREAD;
106048722b5fSGarrett D'Amore desc.acd_type = AUDIO_CTRL_TYPE_BOOLEAN;
106148722b5fSGarrett D'Amore desc.acd_minvalue = 0;
106248722b5fSGarrett D'Amore desc.acd_maxvalue = 1;
106348722b5fSGarrett D'Amore desc.acd_flags = PLAYCTL;
106448722b5fSGarrett D'Amore break;
106548722b5fSGarrett D'Amore
106648722b5fSGarrett D'Amore case CTL_LOOP:
106748722b5fSGarrett D'Amore desc.acd_name = AUDIO_CTRL_ID_LOOPBACK;
106848722b5fSGarrett D'Amore desc.acd_type = AUDIO_CTRL_TYPE_BOOLEAN;
106948722b5fSGarrett D'Amore desc.acd_minvalue = 0;
107048722b5fSGarrett D'Amore desc.acd_maxvalue = 1;
107148722b5fSGarrett D'Amore desc.acd_flags = RECCTL;
107248722b5fSGarrett D'Amore break;
107348722b5fSGarrett D'Amore }
107448722b5fSGarrett D'Amore
107548722b5fSGarrett D'Amore pc->val = val;
107648722b5fSGarrett D'Amore pc->ctrl = audio_dev_add_control(dev->adev, &desc,
107748722b5fSGarrett D'Amore audigyls_get_control, audigyls_set_control, pc);
107848722b5fSGarrett D'Amore }
107948722b5fSGarrett D'Amore
1080a60cc674SGarrett D'Amore static void
audigyls_add_controls(audigyls_dev_t * dev)108148722b5fSGarrett D'Amore audigyls_add_controls(audigyls_dev_t *dev)
108248722b5fSGarrett D'Amore {
10832c30fa45SGarrett D'Amore audio_dev_add_soft_volume(dev->adev);
108448722b5fSGarrett D'Amore
108548722b5fSGarrett D'Amore audigyls_alloc_ctrl(dev, CTL_FRONT, 75 | (75 << 8));
108648722b5fSGarrett D'Amore audigyls_alloc_ctrl(dev, CTL_SURROUND, 75 | (75 << 8));
108748722b5fSGarrett D'Amore audigyls_alloc_ctrl(dev, CTL_CENTER, 75);
108848722b5fSGarrett D'Amore audigyls_alloc_ctrl(dev, CTL_LFE, 75);
108948722b5fSGarrett D'Amore audigyls_alloc_ctrl(dev, CTL_RECORDVOL, 75 | (75 << 8));
109048722b5fSGarrett D'Amore audigyls_alloc_ctrl(dev, CTL_RECSRC, 1);
109148722b5fSGarrett D'Amore audigyls_alloc_ctrl(dev, CTL_SPREAD, 0);
109248722b5fSGarrett D'Amore audigyls_alloc_ctrl(dev, CTL_LOOP, 0);
109348722b5fSGarrett D'Amore if (!dev->ac97) {
109448722b5fSGarrett D'Amore audigyls_alloc_ctrl(dev, CTL_MONGAIN, 0);
109548722b5fSGarrett D'Amore }
109648722b5fSGarrett D'Amore }
109748722b5fSGarrett D'Amore
109848722b5fSGarrett D'Amore int
audigyls_attach(dev_info_t * dip)109948722b5fSGarrett D'Amore audigyls_attach(dev_info_t *dip)
110048722b5fSGarrett D'Amore {
110148722b5fSGarrett D'Amore uint16_t pci_command, vendor, device;
110248722b5fSGarrett D'Amore uint32_t subdevice;
110348722b5fSGarrett D'Amore audigyls_dev_t *dev;
110448722b5fSGarrett D'Amore ddi_acc_handle_t pcih;
110548722b5fSGarrett D'Amore const char *name, *version;
110648722b5fSGarrett D'Amore boolean_t ac97 = B_FALSE;
110748722b5fSGarrett D'Amore
110848722b5fSGarrett D'Amore dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
110948722b5fSGarrett D'Amore dev->dip = dip;
111048722b5fSGarrett D'Amore ddi_set_driver_private(dip, dev);
111168c47f65SGarrett D'Amore mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL);
111268c47f65SGarrett D'Amore mutex_init(&dev->low_mutex, NULL, MUTEX_DRIVER, NULL);
111348722b5fSGarrett D'Amore
111448722b5fSGarrett D'Amore if ((dev->adev = audio_dev_alloc(dip, 0)) == NULL) {
111548722b5fSGarrett D'Amore cmn_err(CE_WARN, "audio_dev_alloc failed");
111648722b5fSGarrett D'Amore goto error;
111748722b5fSGarrett D'Amore }
111848722b5fSGarrett D'Amore
111948722b5fSGarrett D'Amore if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
112048722b5fSGarrett D'Amore audio_dev_warn(dev->adev, "pci_config_setup failed");
112148722b5fSGarrett D'Amore goto error;
112248722b5fSGarrett D'Amore }
112348722b5fSGarrett D'Amore dev->pcih = pcih;
112448722b5fSGarrett D'Amore
112548722b5fSGarrett D'Amore vendor = pci_config_get16(pcih, PCI_CONF_VENID);
112648722b5fSGarrett D'Amore device = pci_config_get16(pcih, PCI_CONF_DEVID);
112748722b5fSGarrett D'Amore subdevice = pci_config_get16(pcih, PCI_CONF_SUBVENID);
112848722b5fSGarrett D'Amore subdevice <<= 16;
112948722b5fSGarrett D'Amore subdevice |= pci_config_get16(pcih, PCI_CONF_SUBSYSID);
113048722b5fSGarrett D'Amore if (vendor != PCI_VENDOR_ID_CREATIVE ||
113148722b5fSGarrett D'Amore device != PCI_DEVICE_ID_CREATIVE_AUDIGYLS) {
113248722b5fSGarrett D'Amore audio_dev_warn(dev->adev, "Hardware not recognized "
113348722b5fSGarrett D'Amore "(vendor=%x, dev=%x)", vendor, device);
113448722b5fSGarrett D'Amore goto error;
113548722b5fSGarrett D'Amore }
113648722b5fSGarrett D'Amore
113748722b5fSGarrett D'Amore pci_command = pci_config_get16(pcih, PCI_CONF_COMM);
113848722b5fSGarrett D'Amore pci_command |= PCI_COMM_ME | PCI_COMM_IO;
113948722b5fSGarrett D'Amore pci_config_put16(pcih, PCI_CONF_COMM, pci_command);
114048722b5fSGarrett D'Amore
114148722b5fSGarrett D'Amore if ((ddi_regs_map_setup(dip, 1, &dev->base, 0, 0, &dev_attr,
114248722b5fSGarrett D'Amore &dev->regsh)) != DDI_SUCCESS) {
114348722b5fSGarrett D'Amore audio_dev_warn(dev->adev, "failed to map registers");
114448722b5fSGarrett D'Amore goto error;
114548722b5fSGarrett D'Amore }
114648722b5fSGarrett D'Amore
114748722b5fSGarrett D'Amore /* Function of the orange jack: 0=analog, 1=digital */
114848722b5fSGarrett D'Amore dev->digital_enable = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip,
114948722b5fSGarrett D'Amore DDI_PROP_DONTPASS, "digital-enable", 0);
115048722b5fSGarrett D'Amore
115148722b5fSGarrett D'Amore switch (subdevice) {
115248722b5fSGarrett D'Amore case 0x11021001: /* SB0310 */
115348722b5fSGarrett D'Amore case 0x11021002: /* SB0310 */
115448722b5fSGarrett D'Amore case 0x11021005: /* SB0310b */
115548722b5fSGarrett D'Amore name = "Creative Audigy LS";
115648722b5fSGarrett D'Amore version = "SB0310"; /* could also be SB0312 */
115748722b5fSGarrett D'Amore ac97 = B_TRUE;
115848722b5fSGarrett D'Amore break;
115948722b5fSGarrett D'Amore case 0x11021006:
116048722b5fSGarrett D'Amore name = "Creative Sound Blaster Live! 24 bit";
116148722b5fSGarrett D'Amore version = "SB0410";
116248722b5fSGarrett D'Amore break;
116348722b5fSGarrett D'Amore case 0x11021007: /* Dell OEM version */
116448722b5fSGarrett D'Amore name = "Creative Sound Blaster Live! 24 bit";
116548722b5fSGarrett D'Amore version = "SB0413";
116648722b5fSGarrett D'Amore break;
116748722b5fSGarrett D'Amore case 0x1102100a:
116848722b5fSGarrett D'Amore name = "Creative Audigy SE";
116948722b5fSGarrett D'Amore version = "SB0570";
117048722b5fSGarrett D'Amore break;
117148722b5fSGarrett D'Amore case 0x11021011:
117248722b5fSGarrett D'Amore name = "Creative Audigy SE OEM";
117348722b5fSGarrett D'Amore version = "SB0570a";
117448722b5fSGarrett D'Amore break;
117548722b5fSGarrett D'Amore case 0x11021012:
117648722b5fSGarrett D'Amore name = "Creative X-Fi Extreme Audio";
117748722b5fSGarrett D'Amore version = "SB0790";
117848722b5fSGarrett D'Amore break;
117948722b5fSGarrett D'Amore case 0x14621009:
118048722b5fSGarrett D'Amore name = "MSI K8N Diamond MB";
118148722b5fSGarrett D'Amore version = "SB0438";
118248722b5fSGarrett D'Amore break;
118348722b5fSGarrett D'Amore case 0x12973038:
118448722b5fSGarrett D'Amore name = "Shuttle XPC SD31P";
118548722b5fSGarrett D'Amore version = "SD31P";
118648722b5fSGarrett D'Amore break;
118748722b5fSGarrett D'Amore case 0x12973041:
118848722b5fSGarrett D'Amore name = "Shuttle XPC SD11G5";
118948722b5fSGarrett D'Amore version = "SD11G5";
119048722b5fSGarrett D'Amore break;
119148722b5fSGarrett D'Amore default:
119248722b5fSGarrett D'Amore name = "Creative Audigy LS";
119348722b5fSGarrett D'Amore version = NULL;
119448722b5fSGarrett D'Amore break;
119548722b5fSGarrett D'Amore }
119648722b5fSGarrett D'Amore
119748722b5fSGarrett D'Amore audio_dev_set_description(dev->adev, name);
119848722b5fSGarrett D'Amore if (version)
119948722b5fSGarrett D'Amore audio_dev_set_version(dev->adev, version);
120048722b5fSGarrett D'Amore
120148722b5fSGarrett D'Amore if (ac97) {
120248722b5fSGarrett D'Amore ac97_ctrl_t *ctrl;
120348722b5fSGarrett D'Amore
120448722b5fSGarrett D'Amore /* Original Audigy LS revision (AC97 based) */
120548722b5fSGarrett D'Amore dev->ac97 = ac97_allocate(dev->adev, dip,
120648722b5fSGarrett D'Amore audigyls_read_ac97, audigyls_write_ac97, dev);
120748722b5fSGarrett D'Amore if (dev->ac97 == NULL) {
120848722b5fSGarrett D'Amore audio_dev_warn(dev->adev,
120948722b5fSGarrett D'Amore "failed to allocate ac97 handle");
121048722b5fSGarrett D'Amore goto error;
121148722b5fSGarrett D'Amore }
121248722b5fSGarrett D'Amore
121348722b5fSGarrett D'Amore ac97_probe_controls(dev->ac97);
121448722b5fSGarrett D'Amore
121548722b5fSGarrett D'Amore /* remove the AC'97 controls we don't want to expose */
121648722b5fSGarrett D'Amore for (int i = 0; audigyls_remove_ac97[i]; i++) {
121748722b5fSGarrett D'Amore ctrl = ac97_control_find(dev->ac97,
121848722b5fSGarrett D'Amore audigyls_remove_ac97[i]);
121948722b5fSGarrett D'Amore if (ctrl != NULL) {
122048722b5fSGarrett D'Amore ac97_control_unregister(ctrl);
122148722b5fSGarrett D'Amore }
122248722b5fSGarrett D'Amore }
122348722b5fSGarrett D'Amore
122448722b5fSGarrett D'Amore dev->ac97_recgain = ac97_control_find(dev->ac97,
122548722b5fSGarrett D'Amore AUDIO_CTRL_ID_RECGAIN);
122648722b5fSGarrett D'Amore dev->ac97_recsrc = ac97_control_find(dev->ac97,
122748722b5fSGarrett D'Amore AUDIO_CTRL_ID_RECSRC);
122848722b5fSGarrett D'Amore }
122948722b5fSGarrett D'Amore
123048722b5fSGarrett D'Amore audigyls_add_controls(dev);
123148722b5fSGarrett D'Amore
123248722b5fSGarrett D'Amore if (dev->ac97) {
123348722b5fSGarrett D'Amore ac97_register_controls(dev->ac97);
123448722b5fSGarrett D'Amore }
123548722b5fSGarrett D'Amore
123648722b5fSGarrett D'Amore if (audigyls_alloc_port(dev, AUDIGYLS_PLAY_PORT) != DDI_SUCCESS)
123748722b5fSGarrett D'Amore goto error;
123848722b5fSGarrett D'Amore if (audigyls_alloc_port(dev, AUDIGYLS_REC_PORT) != DDI_SUCCESS)
123948722b5fSGarrett D'Amore goto error;
124048722b5fSGarrett D'Amore
124148722b5fSGarrett D'Amore audigyls_hwinit(dev);
124248722b5fSGarrett D'Amore
124348722b5fSGarrett D'Amore audigyls_configure_mixer(dev);
124448722b5fSGarrett D'Amore
124548722b5fSGarrett D'Amore if (audio_dev_register(dev->adev) != DDI_SUCCESS) {
124648722b5fSGarrett D'Amore audio_dev_warn(dev->adev, "unable to register with framework");
124748722b5fSGarrett D'Amore goto error;
124848722b5fSGarrett D'Amore }
124948722b5fSGarrett D'Amore
125048722b5fSGarrett D'Amore ddi_report_dev(dip);
125148722b5fSGarrett D'Amore
125248722b5fSGarrett D'Amore return (DDI_SUCCESS);
125348722b5fSGarrett D'Amore
125448722b5fSGarrett D'Amore error:
125548722b5fSGarrett D'Amore audigyls_destroy(dev);
125648722b5fSGarrett D'Amore return (DDI_FAILURE);
125748722b5fSGarrett D'Amore }
125848722b5fSGarrett D'Amore
125948722b5fSGarrett D'Amore int
audigyls_resume(dev_info_t * dip)126048722b5fSGarrett D'Amore audigyls_resume(dev_info_t *dip)
126148722b5fSGarrett D'Amore {
126248722b5fSGarrett D'Amore audigyls_dev_t *dev;
126348722b5fSGarrett D'Amore
126448722b5fSGarrett D'Amore dev = ddi_get_driver_private(dip);
126548722b5fSGarrett D'Amore
126648722b5fSGarrett D'Amore audigyls_hwinit(dev);
126748722b5fSGarrett D'Amore
126848722b5fSGarrett D'Amore /* allow ac97 operations again */
126948722b5fSGarrett D'Amore if (dev->ac97)
127068c47f65SGarrett D'Amore ac97_reset(dev->ac97);
127148722b5fSGarrett D'Amore
127268c47f65SGarrett D'Amore audio_dev_resume(dev->adev);
127348722b5fSGarrett D'Amore
127448722b5fSGarrett D'Amore return (DDI_SUCCESS);
127548722b5fSGarrett D'Amore }
127648722b5fSGarrett D'Amore
127748722b5fSGarrett D'Amore int
audigyls_detach(audigyls_dev_t * dev)127848722b5fSGarrett D'Amore audigyls_detach(audigyls_dev_t *dev)
127948722b5fSGarrett D'Amore {
128048722b5fSGarrett D'Amore if (audio_dev_unregister(dev->adev) != DDI_SUCCESS)
128148722b5fSGarrett D'Amore return (DDI_FAILURE);
128248722b5fSGarrett D'Amore
128348722b5fSGarrett D'Amore audigyls_destroy(dev);
128448722b5fSGarrett D'Amore return (DDI_SUCCESS);
128548722b5fSGarrett D'Amore }
128648722b5fSGarrett D'Amore
128748722b5fSGarrett D'Amore int
audigyls_suspend(audigyls_dev_t * dev)128848722b5fSGarrett D'Amore audigyls_suspend(audigyls_dev_t *dev)
128948722b5fSGarrett D'Amore {
129068c47f65SGarrett D'Amore audio_dev_suspend(dev->adev);
129148722b5fSGarrett D'Amore
129248722b5fSGarrett D'Amore return (DDI_SUCCESS);
129348722b5fSGarrett D'Amore }
129448722b5fSGarrett D'Amore
129548722b5fSGarrett D'Amore static int audigyls_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
129648722b5fSGarrett D'Amore static int audigyls_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
129748722b5fSGarrett D'Amore static int audigyls_ddi_quiesce(dev_info_t *);
129848722b5fSGarrett D'Amore
129948722b5fSGarrett D'Amore static struct dev_ops audigyls_dev_ops = {
130048722b5fSGarrett D'Amore DEVO_REV, /* rev */
130148722b5fSGarrett D'Amore 0, /* refcnt */
130248722b5fSGarrett D'Amore NULL, /* getinfo */
130348722b5fSGarrett D'Amore nulldev, /* identify */
130448722b5fSGarrett D'Amore nulldev, /* probe */
130548722b5fSGarrett D'Amore audigyls_ddi_attach, /* attach */
130648722b5fSGarrett D'Amore audigyls_ddi_detach, /* detach */
130748722b5fSGarrett D'Amore nodev, /* reset */
130848722b5fSGarrett D'Amore NULL, /* cb_ops */
130948722b5fSGarrett D'Amore NULL, /* bus_ops */
131048722b5fSGarrett D'Amore NULL, /* power */
131148722b5fSGarrett D'Amore audigyls_ddi_quiesce, /* quiesce */
131248722b5fSGarrett D'Amore };
131348722b5fSGarrett D'Amore
131448722b5fSGarrett D'Amore static struct modldrv audigyls_modldrv = {
131548722b5fSGarrett D'Amore &mod_driverops, /* drv_modops */
131648722b5fSGarrett D'Amore "Creative Audigy LS Audio", /* linkinfo */
131748722b5fSGarrett D'Amore &audigyls_dev_ops, /* dev_ops */
131848722b5fSGarrett D'Amore };
131948722b5fSGarrett D'Amore
132048722b5fSGarrett D'Amore static struct modlinkage modlinkage = {
132148722b5fSGarrett D'Amore MODREV_1,
132248722b5fSGarrett D'Amore { &audigyls_modldrv, NULL }
132348722b5fSGarrett D'Amore };
132448722b5fSGarrett D'Amore
132548722b5fSGarrett D'Amore int
_init(void)132648722b5fSGarrett D'Amore _init(void)
132748722b5fSGarrett D'Amore {
132848722b5fSGarrett D'Amore int rv;
132948722b5fSGarrett D'Amore
133048722b5fSGarrett D'Amore audio_init_ops(&audigyls_dev_ops, AUDIGYLS_NAME);
133148722b5fSGarrett D'Amore if ((rv = mod_install(&modlinkage)) != 0) {
133248722b5fSGarrett D'Amore audio_fini_ops(&audigyls_dev_ops);
133348722b5fSGarrett D'Amore }
133448722b5fSGarrett D'Amore return (rv);
133548722b5fSGarrett D'Amore }
133648722b5fSGarrett D'Amore
133748722b5fSGarrett D'Amore int
_fini(void)133848722b5fSGarrett D'Amore _fini(void)
133948722b5fSGarrett D'Amore {
134048722b5fSGarrett D'Amore int rv;
134148722b5fSGarrett D'Amore
134248722b5fSGarrett D'Amore if ((rv = mod_remove(&modlinkage)) == 0) {
134348722b5fSGarrett D'Amore audio_fini_ops(&audigyls_dev_ops);
134448722b5fSGarrett D'Amore }
134548722b5fSGarrett D'Amore return (rv);
134648722b5fSGarrett D'Amore }
134748722b5fSGarrett D'Amore
134848722b5fSGarrett D'Amore int
_info(struct modinfo * modinfop)134948722b5fSGarrett D'Amore _info(struct modinfo *modinfop)
135048722b5fSGarrett D'Amore {
135148722b5fSGarrett D'Amore return (mod_info(&modlinkage, modinfop));
135248722b5fSGarrett D'Amore }
135348722b5fSGarrett D'Amore
135448722b5fSGarrett D'Amore int
audigyls_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)135548722b5fSGarrett D'Amore audigyls_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
135648722b5fSGarrett D'Amore {
135748722b5fSGarrett D'Amore switch (cmd) {
135848722b5fSGarrett D'Amore case DDI_ATTACH:
135948722b5fSGarrett D'Amore return (audigyls_attach(dip));
136048722b5fSGarrett D'Amore
136148722b5fSGarrett D'Amore case DDI_RESUME:
136248722b5fSGarrett D'Amore return (audigyls_resume(dip));
136348722b5fSGarrett D'Amore
136448722b5fSGarrett D'Amore default:
136548722b5fSGarrett D'Amore return (DDI_FAILURE);
136648722b5fSGarrett D'Amore }
136748722b5fSGarrett D'Amore }
136848722b5fSGarrett D'Amore
136948722b5fSGarrett D'Amore int
audigyls_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)137048722b5fSGarrett D'Amore audigyls_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
137148722b5fSGarrett D'Amore {
137248722b5fSGarrett D'Amore audigyls_dev_t *dev;
137348722b5fSGarrett D'Amore
137448722b5fSGarrett D'Amore dev = ddi_get_driver_private(dip);
137548722b5fSGarrett D'Amore
137648722b5fSGarrett D'Amore switch (cmd) {
137748722b5fSGarrett D'Amore case DDI_DETACH:
137848722b5fSGarrett D'Amore return (audigyls_detach(dev));
137948722b5fSGarrett D'Amore
138048722b5fSGarrett D'Amore case DDI_SUSPEND:
138148722b5fSGarrett D'Amore return (audigyls_suspend(dev));
138248722b5fSGarrett D'Amore
138348722b5fSGarrett D'Amore default:
138448722b5fSGarrett D'Amore return (DDI_FAILURE);
138548722b5fSGarrett D'Amore }
138648722b5fSGarrett D'Amore }
138748722b5fSGarrett D'Amore
138848722b5fSGarrett D'Amore int
audigyls_ddi_quiesce(dev_info_t * dip)138948722b5fSGarrett D'Amore audigyls_ddi_quiesce(dev_info_t *dip)
139048722b5fSGarrett D'Amore {
139148722b5fSGarrett D'Amore audigyls_dev_t *dev;
139248722b5fSGarrett D'Amore uint32_t status;
139348722b5fSGarrett D'Amore
139448722b5fSGarrett D'Amore /*
139548722b5fSGarrett D'Amore * Turn off the hardware
139648722b5fSGarrett D'Amore */
139768c47f65SGarrett D'Amore dev = ddi_get_driver_private(dip);
139848722b5fSGarrett D'Amore
139948722b5fSGarrett D'Amore write_reg(dev, SA, 0);
140048722b5fSGarrett D'Amore OUTL(dev, IER, 0); /* Interrupt disable */
140148722b5fSGarrett D'Amore write_reg(dev, AIE, 0); /* Disable audio interrupts */
140248722b5fSGarrett D'Amore status = INL(dev, IPR);
140348722b5fSGarrett D'Amore OUTL(dev, IPR, status); /* Acknowledge */
140448722b5fSGarrett D'Amore return (DDI_SUCCESS);
140548722b5fSGarrett D'Amore }
1406