xref: /onnv-gate/usr/src/uts/common/io/audio/drv/audiop16x/audiop16x.c (revision 11936:54dc8a89ba0d)
110463SGarrett.Damore@Sun.COM /*
210463SGarrett.Damore@Sun.COM  * CDDL HEADER START
310463SGarrett.Damore@Sun.COM  *
410463SGarrett.Damore@Sun.COM  * The contents of this file are subject to the terms of the
510463SGarrett.Damore@Sun.COM  * Common Development and Distribution License (the "License").
610463SGarrett.Damore@Sun.COM  * You may not use this file except in compliance with the License.
710463SGarrett.Damore@Sun.COM  *
810463SGarrett.Damore@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910463SGarrett.Damore@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010463SGarrett.Damore@Sun.COM  * See the License for the specific language governing permissions
1110463SGarrett.Damore@Sun.COM  * and limitations under the License.
1210463SGarrett.Damore@Sun.COM  *
1310463SGarrett.Damore@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410463SGarrett.Damore@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510463SGarrett.Damore@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610463SGarrett.Damore@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710463SGarrett.Damore@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810463SGarrett.Damore@Sun.COM  *
1910463SGarrett.Damore@Sun.COM  * CDDL HEADER END
2010463SGarrett.Damore@Sun.COM  */
2110463SGarrett.Damore@Sun.COM 
2210463SGarrett.Damore@Sun.COM /*
23*11936Sgdamore@opensolaris.org  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2410463SGarrett.Damore@Sun.COM  * Use is subject to license terms.
2510463SGarrett.Damore@Sun.COM  */
2610463SGarrett.Damore@Sun.COM 
2710463SGarrett.Damore@Sun.COM /*
2810463SGarrett.Damore@Sun.COM  * Purpose: Driver for the Creative P16X AC97 audio controller
2910463SGarrett.Damore@Sun.COM  */
3010463SGarrett.Damore@Sun.COM /*
3110463SGarrett.Damore@Sun.COM  *
3210463SGarrett.Damore@Sun.COM  * Copyright (C) 4Front Technologies 1996-2009.
3310463SGarrett.Damore@Sun.COM  *
3410463SGarrett.Damore@Sun.COM  * This software is released under CDDL 1.0 source license.
3510463SGarrett.Damore@Sun.COM  * See the COPYING file included in the main directory of this source
3610463SGarrett.Damore@Sun.COM  * distribution for the license terms and conditions.
3710463SGarrett.Damore@Sun.COM  */
3810463SGarrett.Damore@Sun.COM 
3910463SGarrett.Damore@Sun.COM #include <sys/types.h>
4010463SGarrett.Damore@Sun.COM #include <sys/modctl.h>
4110463SGarrett.Damore@Sun.COM #include <sys/kmem.h>
4210463SGarrett.Damore@Sun.COM #include <sys/conf.h>
4310463SGarrett.Damore@Sun.COM #include <sys/ddi.h>
4410463SGarrett.Damore@Sun.COM #include <sys/sunddi.h>
4510463SGarrett.Damore@Sun.COM #include <sys/pci.h>
4610463SGarrett.Damore@Sun.COM #include <sys/note.h>
4710463SGarrett.Damore@Sun.COM #include <sys/audio/audio_driver.h>
4810463SGarrett.Damore@Sun.COM #include <sys/audio/ac97.h>
4910463SGarrett.Damore@Sun.COM 
5010463SGarrett.Damore@Sun.COM #include "audiop16x.h"
5110463SGarrett.Damore@Sun.COM 
5210463SGarrett.Damore@Sun.COM /*
5310463SGarrett.Damore@Sun.COM  * These boards use an AC'97 codec, but don't have all of the
5410463SGarrett.Damore@Sun.COM  * various outputs that the AC'97 codec can offer.  We just
5510463SGarrett.Damore@Sun.COM  * suppress them for now.
5610463SGarrett.Damore@Sun.COM  */
5710463SGarrett.Damore@Sun.COM static char *p16x_remove_ac97[] = {
5810463SGarrett.Damore@Sun.COM 	AUDIO_CTRL_ID_BEEP,
5910463SGarrett.Damore@Sun.COM 	AUDIO_CTRL_ID_VIDEO,
6010463SGarrett.Damore@Sun.COM 	AUDIO_CTRL_ID_MICSRC,
6110463SGarrett.Damore@Sun.COM 	AUDIO_CTRL_ID_SPEAKER,
6210463SGarrett.Damore@Sun.COM 	AUDIO_CTRL_ID_SPKSRC,
6310463SGarrett.Damore@Sun.COM 	NULL
6410463SGarrett.Damore@Sun.COM };
6510463SGarrett.Damore@Sun.COM 
6610463SGarrett.Damore@Sun.COM static struct ddi_device_acc_attr dev_attr = {
6710463SGarrett.Damore@Sun.COM 	DDI_DEVICE_ATTR_V0,
6810463SGarrett.Damore@Sun.COM 	DDI_STRUCTURE_LE_ACC,
6910463SGarrett.Damore@Sun.COM 	DDI_STRICTORDER_ACC
7010463SGarrett.Damore@Sun.COM };
7110463SGarrett.Damore@Sun.COM 
7210463SGarrett.Damore@Sun.COM static struct ddi_device_acc_attr buf_attr = {
7310463SGarrett.Damore@Sun.COM 	DDI_DEVICE_ATTR_V0,
7410463SGarrett.Damore@Sun.COM 	DDI_NEVERSWAP_ACC,
7510463SGarrett.Damore@Sun.COM 	DDI_STRICTORDER_ACC
7610463SGarrett.Damore@Sun.COM };
7710463SGarrett.Damore@Sun.COM 
7810463SGarrett.Damore@Sun.COM static ddi_dma_attr_t dma_attr_buf = {
7910463SGarrett.Damore@Sun.COM 	DMA_ATTR_V0,		/* version number */
8010463SGarrett.Damore@Sun.COM 	0x00000000,		/* low DMA address range */
8110463SGarrett.Damore@Sun.COM 	0xffffffff,		/* high DMA address range */
8210463SGarrett.Damore@Sun.COM 	0xfffffffe,		/* DMA counter register */
8310463SGarrett.Damore@Sun.COM 	4,			/* DMA address alignment */
8410463SGarrett.Damore@Sun.COM 	0x3c,			/* DMA burstsizes */
8510463SGarrett.Damore@Sun.COM 	4,			/* min effective DMA size */
8610463SGarrett.Damore@Sun.COM 	0xffffffff,		/* max DMA xfer size */
8710463SGarrett.Damore@Sun.COM 	0xffffffff,		/* segment boundary */
8810463SGarrett.Damore@Sun.COM 	1,			/* s/g length */
8910463SGarrett.Damore@Sun.COM 	4,			/* granularity of device */
9010463SGarrett.Damore@Sun.COM 	0			/* Bus specific DMA flags */
9110463SGarrett.Damore@Sun.COM };
9210463SGarrett.Damore@Sun.COM 
9310463SGarrett.Damore@Sun.COM static int p16x_attach(dev_info_t *);
9410463SGarrett.Damore@Sun.COM static int p16x_resume(dev_info_t *);
9510463SGarrett.Damore@Sun.COM static int p16x_detach(p16x_dev_t *);
9610463SGarrett.Damore@Sun.COM static int p16x_suspend(p16x_dev_t *);
9710463SGarrett.Damore@Sun.COM 
98*11936Sgdamore@opensolaris.org static int p16x_open(void *, int, unsigned *, caddr_t *);
9910463SGarrett.Damore@Sun.COM static void p16x_close(void *);
10010463SGarrett.Damore@Sun.COM static int p16x_start(void *);
10110463SGarrett.Damore@Sun.COM static void p16x_stop(void *);
10210463SGarrett.Damore@Sun.COM static int p16x_format(void *);
10310463SGarrett.Damore@Sun.COM static int p16x_channels(void *);
10410463SGarrett.Damore@Sun.COM static int p16x_rate(void *);
10510463SGarrett.Damore@Sun.COM static uint64_t p16x_count(void *);
10610463SGarrett.Damore@Sun.COM static void p16x_sync(void *, unsigned);
10710463SGarrett.Damore@Sun.COM static void p16x_chinfo(void *, int, unsigned *, unsigned *);
10810463SGarrett.Damore@Sun.COM 
10910463SGarrett.Damore@Sun.COM static uint16_t p16x_read_ac97(void *, uint8_t);
11010463SGarrett.Damore@Sun.COM static void p16x_write_ac97(void *, uint8_t, uint16_t);
11110463SGarrett.Damore@Sun.COM static int p16x_alloc_port(p16x_dev_t *, int);
11210463SGarrett.Damore@Sun.COM static void p16x_destroy(p16x_dev_t *);
11310463SGarrett.Damore@Sun.COM static void p16x_hwinit(p16x_dev_t *);
11410463SGarrett.Damore@Sun.COM 
11510463SGarrett.Damore@Sun.COM static audio_engine_ops_t p16x_engine_ops = {
11610463SGarrett.Damore@Sun.COM 	AUDIO_ENGINE_VERSION,
11710463SGarrett.Damore@Sun.COM 	p16x_open,
11810463SGarrett.Damore@Sun.COM 	p16x_close,
11910463SGarrett.Damore@Sun.COM 	p16x_start,
12010463SGarrett.Damore@Sun.COM 	p16x_stop,
12110463SGarrett.Damore@Sun.COM 	p16x_count,
12210463SGarrett.Damore@Sun.COM 	p16x_format,
12310463SGarrett.Damore@Sun.COM 	p16x_channels,
12410463SGarrett.Damore@Sun.COM 	p16x_rate,
12510463SGarrett.Damore@Sun.COM 	p16x_sync,
12611213Sgdamore@opensolaris.org 	NULL,
12710463SGarrett.Damore@Sun.COM 	p16x_chinfo,
12811213Sgdamore@opensolaris.org 	NULL
12910463SGarrett.Damore@Sun.COM };
13010463SGarrett.Damore@Sun.COM 
13110463SGarrett.Damore@Sun.COM static unsigned int
read_reg(p16x_dev_t * dev,int reg,int chn)13210463SGarrett.Damore@Sun.COM read_reg(p16x_dev_t *dev, int reg, int chn)
13310463SGarrett.Damore@Sun.COM {
13410463SGarrett.Damore@Sun.COM 	unsigned int val;
13510463SGarrett.Damore@Sun.COM 
136*11936Sgdamore@opensolaris.org 	mutex_enter(&dev->mutex);
13710463SGarrett.Damore@Sun.COM 	OUTL(dev, (reg << 16) | (chn & 0xffff), PTR);	/* Pointer */
13810463SGarrett.Damore@Sun.COM 	val = INL(dev, DR);	/* Data */
139*11936Sgdamore@opensolaris.org 	mutex_exit(&dev->mutex);
14010463SGarrett.Damore@Sun.COM 
14110463SGarrett.Damore@Sun.COM 	return (val);
14210463SGarrett.Damore@Sun.COM }
14310463SGarrett.Damore@Sun.COM 
14410463SGarrett.Damore@Sun.COM static void
write_reg(p16x_dev_t * dev,int reg,int chn,unsigned int value)14510463SGarrett.Damore@Sun.COM write_reg(p16x_dev_t *dev, int reg, int chn, unsigned int value)
14610463SGarrett.Damore@Sun.COM {
14710463SGarrett.Damore@Sun.COM 
148*11936Sgdamore@opensolaris.org 	mutex_enter(&dev->mutex);
14910463SGarrett.Damore@Sun.COM 	OUTL(dev, (reg << 16) | (chn & 0xffff), PTR);	/* Pointer */
15010463SGarrett.Damore@Sun.COM 	OUTL(dev, value, DR);	/* Data */
151*11936Sgdamore@opensolaris.org 	mutex_exit(&dev->mutex);
152*11936Sgdamore@opensolaris.org }
153*11936Sgdamore@opensolaris.org 
154*11936Sgdamore@opensolaris.org static void
set_reg_bits(p16x_dev_t * dev,int reg,int chn,unsigned int mask)155*11936Sgdamore@opensolaris.org set_reg_bits(p16x_dev_t *dev, int reg, int chn, unsigned int mask)
156*11936Sgdamore@opensolaris.org {
157*11936Sgdamore@opensolaris.org 	unsigned int	val;
158*11936Sgdamore@opensolaris.org 	mutex_enter(&dev->mutex);
159*11936Sgdamore@opensolaris.org 	OUTL(dev, (reg << 16) | (chn & 0xffff), PTR);	/* Pointer */
160*11936Sgdamore@opensolaris.org 	val = INL(dev, DR);	/* Data */
161*11936Sgdamore@opensolaris.org 	val |= mask;
162*11936Sgdamore@opensolaris.org 	OUTL(dev, val, DR);	/* Data */
163*11936Sgdamore@opensolaris.org 	mutex_exit(&dev->mutex);
164*11936Sgdamore@opensolaris.org }
165*11936Sgdamore@opensolaris.org 
166*11936Sgdamore@opensolaris.org static void
clear_reg_bits(p16x_dev_t * dev,int reg,int chn,unsigned int mask)167*11936Sgdamore@opensolaris.org clear_reg_bits(p16x_dev_t *dev, int reg, int chn, unsigned int mask)
168*11936Sgdamore@opensolaris.org {
169*11936Sgdamore@opensolaris.org 	unsigned int	val;
170*11936Sgdamore@opensolaris.org 	mutex_enter(&dev->mutex);
171*11936Sgdamore@opensolaris.org 	OUTL(dev, (reg << 16) | (chn & 0xffff), PTR);	/* Pointer */
172*11936Sgdamore@opensolaris.org 	val = INL(dev, DR);	/* Data */
173*11936Sgdamore@opensolaris.org 	val &= ~(mask);
174*11936Sgdamore@opensolaris.org 	OUTL(dev, val, DR);	/* Data */
175*11936Sgdamore@opensolaris.org 	mutex_exit(&dev->mutex);
17610463SGarrett.Damore@Sun.COM }
17710463SGarrett.Damore@Sun.COM 
17810463SGarrett.Damore@Sun.COM static uint16_t
p16x_read_ac97(void * arg,uint8_t index)17910463SGarrett.Damore@Sun.COM p16x_read_ac97(void *arg, uint8_t index)
18010463SGarrett.Damore@Sun.COM {
18110463SGarrett.Damore@Sun.COM 	p16x_dev_t *dev = arg;
18210463SGarrett.Damore@Sun.COM 	uint16_t value;
18310463SGarrett.Damore@Sun.COM 	int i;
18410463SGarrett.Damore@Sun.COM 
18510463SGarrett.Damore@Sun.COM 	OUTB(dev, index, AC97A);
18610463SGarrett.Damore@Sun.COM 	for (i = 0; i < 10000; i++)
18710463SGarrett.Damore@Sun.COM 		if (INB(dev, AC97A) & 0x80)
18810463SGarrett.Damore@Sun.COM 			break;
18910463SGarrett.Damore@Sun.COM 	value = INW(dev, AC97D);
19010463SGarrett.Damore@Sun.COM 	return (value);
19110463SGarrett.Damore@Sun.COM }
19210463SGarrett.Damore@Sun.COM 
19310463SGarrett.Damore@Sun.COM static void
p16x_write_ac97(void * arg,uint8_t index,uint16_t data)19410463SGarrett.Damore@Sun.COM p16x_write_ac97(void *arg, uint8_t index, uint16_t data)
19510463SGarrett.Damore@Sun.COM {
19610463SGarrett.Damore@Sun.COM 	p16x_dev_t *dev = arg;
19710463SGarrett.Damore@Sun.COM 	unsigned int i;
19810463SGarrett.Damore@Sun.COM 
19910463SGarrett.Damore@Sun.COM 	OUTB(dev, index, AC97A);
20010463SGarrett.Damore@Sun.COM 	for (i = 0; i < 10000; i++)
20110463SGarrett.Damore@Sun.COM 		if (INB(dev, AC97A) & 0x80)
20210463SGarrett.Damore@Sun.COM 			break;
20310463SGarrett.Damore@Sun.COM 	OUTW(dev, data, AC97D);
20410463SGarrett.Damore@Sun.COM }
20510463SGarrett.Damore@Sun.COM 
20610463SGarrett.Damore@Sun.COM /*
20710463SGarrett.Damore@Sun.COM  * Audio routines
20810463SGarrett.Damore@Sun.COM  */
20910463SGarrett.Damore@Sun.COM 
210*11936Sgdamore@opensolaris.org int
p16x_open(void * arg,int flag,uint_t * nframes,caddr_t * bufp)211*11936Sgdamore@opensolaris.org p16x_open(void *arg, int flag, uint_t *nframes, caddr_t *bufp)
21210463SGarrett.Damore@Sun.COM {
213*11936Sgdamore@opensolaris.org 	p16x_port_t	*port = arg;
214*11936Sgdamore@opensolaris.org 
215*11936Sgdamore@opensolaris.org 	_NOTE(ARGUNUSED(flag));
216*11936Sgdamore@opensolaris.org 
217*11936Sgdamore@opensolaris.org 	port->count = 0;
218*11936Sgdamore@opensolaris.org 	*nframes = port->buf_frames;
219*11936Sgdamore@opensolaris.org 	*bufp = port->buf_kaddr;
220*11936Sgdamore@opensolaris.org 
221*11936Sgdamore@opensolaris.org 	return (0);
222*11936Sgdamore@opensolaris.org }
223*11936Sgdamore@opensolaris.org 
224*11936Sgdamore@opensolaris.org void
p16x_close(void * arg)225*11936Sgdamore@opensolaris.org p16x_close(void *arg)
226*11936Sgdamore@opensolaris.org {
227*11936Sgdamore@opensolaris.org 	_NOTE(ARGUNUSED(arg));
228*11936Sgdamore@opensolaris.org }
229*11936Sgdamore@opensolaris.org 
230*11936Sgdamore@opensolaris.org int
p16x_start(void * arg)231*11936Sgdamore@opensolaris.org p16x_start(void *arg)
232*11936Sgdamore@opensolaris.org {
233*11936Sgdamore@opensolaris.org 	p16x_port_t	*port = arg;
23410463SGarrett.Damore@Sun.COM 	p16x_dev_t	*dev = port->dev;
23510463SGarrett.Damore@Sun.COM 
236*11936Sgdamore@opensolaris.org 	port->offset = 0;
23710463SGarrett.Damore@Sun.COM 
23810463SGarrett.Damore@Sun.COM 	if (port->port_num == P16X_REC) {
23910463SGarrett.Damore@Sun.COM 		write_reg(dev, CRFA, 0, 0);
24010463SGarrett.Damore@Sun.COM 		write_reg(dev, CRCAV, 0, 0);
24110463SGarrett.Damore@Sun.COM 
242*11936Sgdamore@opensolaris.org 		/* Enable rec channel */
243*11936Sgdamore@opensolaris.org 		set_reg_bits(dev, SA, 0, 0x100);
24410463SGarrett.Damore@Sun.COM 	} else {
24510463SGarrett.Damore@Sun.COM 		for (int i = 0; i < 3; i++) {
24610463SGarrett.Damore@Sun.COM 			write_reg(dev, PTBA, i, 0);
24710463SGarrett.Damore@Sun.COM 			write_reg(dev, PTBS, i, 0);
24810463SGarrett.Damore@Sun.COM 			write_reg(dev, PTCA, i, 0);
24910463SGarrett.Damore@Sun.COM 			write_reg(dev, PFEA, i, 0);
25010463SGarrett.Damore@Sun.COM 			write_reg(dev, CPFA, i, 0);
25110463SGarrett.Damore@Sun.COM 			write_reg(dev, CPCAV, i, 0);
25210463SGarrett.Damore@Sun.COM 		}
25310463SGarrett.Damore@Sun.COM 
254*11936Sgdamore@opensolaris.org 		/* Enable play channel */
255*11936Sgdamore@opensolaris.org 		set_reg_bits(dev, SA, 0, 0x7);
25610463SGarrett.Damore@Sun.COM 	}
25710463SGarrett.Damore@Sun.COM 
25810463SGarrett.Damore@Sun.COM 	return (0);
25910463SGarrett.Damore@Sun.COM }
26010463SGarrett.Damore@Sun.COM 
26110463SGarrett.Damore@Sun.COM void
p16x_stop(void * arg)26210463SGarrett.Damore@Sun.COM p16x_stop(void *arg)
26310463SGarrett.Damore@Sun.COM {
26410463SGarrett.Damore@Sun.COM 	p16x_port_t	*port = arg;
26510463SGarrett.Damore@Sun.COM 	p16x_dev_t	*dev = port->dev;
26610463SGarrett.Damore@Sun.COM 
267*11936Sgdamore@opensolaris.org 	if (port->port_num == P16X_REC) {
268*11936Sgdamore@opensolaris.org 		/* Disable rec channel */
269*11936Sgdamore@opensolaris.org 		clear_reg_bits(dev, SA, 0, 0x100);
270*11936Sgdamore@opensolaris.org 
271*11936Sgdamore@opensolaris.org 	} else {
272*11936Sgdamore@opensolaris.org 		/* Disable Play channel */
273*11936Sgdamore@opensolaris.org 		clear_reg_bits(dev, SA, 0, 0x7);
27410463SGarrett.Damore@Sun.COM 	}
27510463SGarrett.Damore@Sun.COM }
27610463SGarrett.Damore@Sun.COM 
27710463SGarrett.Damore@Sun.COM int
p16x_format(void * arg)27810463SGarrett.Damore@Sun.COM p16x_format(void *arg)
27910463SGarrett.Damore@Sun.COM {
28010463SGarrett.Damore@Sun.COM 	_NOTE(ARGUNUSED(arg));
28110463SGarrett.Damore@Sun.COM 
28210463SGarrett.Damore@Sun.COM 	return (AUDIO_FORMAT_S16_LE);
28310463SGarrett.Damore@Sun.COM }
28410463SGarrett.Damore@Sun.COM 
28510463SGarrett.Damore@Sun.COM int
p16x_channels(void * arg)28610463SGarrett.Damore@Sun.COM p16x_channels(void *arg)
28710463SGarrett.Damore@Sun.COM {
28810463SGarrett.Damore@Sun.COM 	p16x_port_t *port = arg;
28910463SGarrett.Damore@Sun.COM 
29010463SGarrett.Damore@Sun.COM 	return (port->nchan);
29110463SGarrett.Damore@Sun.COM }
29210463SGarrett.Damore@Sun.COM 
29310463SGarrett.Damore@Sun.COM int
p16x_rate(void * arg)29410463SGarrett.Damore@Sun.COM p16x_rate(void *arg)
29510463SGarrett.Damore@Sun.COM {
29610463SGarrett.Damore@Sun.COM 	_NOTE(ARGUNUSED(arg));
29710463SGarrett.Damore@Sun.COM 
29810463SGarrett.Damore@Sun.COM 	return (48000);
29910463SGarrett.Damore@Sun.COM }
30010463SGarrett.Damore@Sun.COM 
30110463SGarrett.Damore@Sun.COM void
p16x_sync(void * arg,unsigned nframes)30210463SGarrett.Damore@Sun.COM p16x_sync(void *arg, unsigned nframes)
30310463SGarrett.Damore@Sun.COM {
30410463SGarrett.Damore@Sun.COM 	p16x_port_t *port = arg;
30510463SGarrett.Damore@Sun.COM 	_NOTE(ARGUNUSED(nframes));
30610463SGarrett.Damore@Sun.COM 
30710463SGarrett.Damore@Sun.COM 	(void) ddi_dma_sync(port->buf_dmah, 0, 0, port->syncdir);
30810463SGarrett.Damore@Sun.COM }
30910463SGarrett.Damore@Sun.COM 
31010463SGarrett.Damore@Sun.COM uint64_t
p16x_count(void * arg)31110463SGarrett.Damore@Sun.COM p16x_count(void *arg)
31210463SGarrett.Damore@Sun.COM {
31310463SGarrett.Damore@Sun.COM 	p16x_port_t	*port = arg;
31410463SGarrett.Damore@Sun.COM 	p16x_dev_t	*dev = port->dev;
31510463SGarrett.Damore@Sun.COM 	uint64_t	val;
316*11936Sgdamore@opensolaris.org 	uint32_t	offset, n;
31710463SGarrett.Damore@Sun.COM 
318*11936Sgdamore@opensolaris.org 	if (port->port_num == P16X_PLAY) {
319*11936Sgdamore@opensolaris.org 		offset = read_reg(dev, CPFA, 0);
320*11936Sgdamore@opensolaris.org 	} else {
321*11936Sgdamore@opensolaris.org 		offset = read_reg(dev, CRFA, 0);
322*11936Sgdamore@opensolaris.org 	}
323*11936Sgdamore@opensolaris.org 
324*11936Sgdamore@opensolaris.org 	/* get the offset, and switch to frames */
325*11936Sgdamore@opensolaris.org 	offset /= (2 * sizeof (uint16_t));
326*11936Sgdamore@opensolaris.org 
327*11936Sgdamore@opensolaris.org 	if (offset >= port->offset) {
328*11936Sgdamore@opensolaris.org 		n = offset - port->offset;
329*11936Sgdamore@opensolaris.org 	} else {
330*11936Sgdamore@opensolaris.org 		n = offset + (port->buf_frames - port->offset);
331*11936Sgdamore@opensolaris.org 	}
332*11936Sgdamore@opensolaris.org 	port->offset = offset;
333*11936Sgdamore@opensolaris.org 	port->count += n;
33410463SGarrett.Damore@Sun.COM 	val = port->count;
33510463SGarrett.Damore@Sun.COM 
33610463SGarrett.Damore@Sun.COM 	return (val);
33710463SGarrett.Damore@Sun.COM }
33810463SGarrett.Damore@Sun.COM 
33910463SGarrett.Damore@Sun.COM static void
p16x_chinfo(void * arg,int chan,unsigned * offset,unsigned * incr)34010463SGarrett.Damore@Sun.COM p16x_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
34110463SGarrett.Damore@Sun.COM {
34210463SGarrett.Damore@Sun.COM 	p16x_port_t *port = arg;
34310463SGarrett.Damore@Sun.COM 	unsigned mult;
34410463SGarrett.Damore@Sun.COM 
34510463SGarrett.Damore@Sun.COM 	if (port->port_num == P16X_PLAY) {
34610463SGarrett.Damore@Sun.COM 		switch (chan) {
34710463SGarrett.Damore@Sun.COM 		case 0:	/* left front */
34810463SGarrett.Damore@Sun.COM 		case 1:	/* right front */
34910463SGarrett.Damore@Sun.COM 			mult = 0;
35010463SGarrett.Damore@Sun.COM 			break;
35110463SGarrett.Damore@Sun.COM 		case 2:	/* center */
35210463SGarrett.Damore@Sun.COM 		case 3:	/* lfe */
35310463SGarrett.Damore@Sun.COM 			mult = 2;
35410463SGarrett.Damore@Sun.COM 			break;
35510463SGarrett.Damore@Sun.COM 		case 4:	/* left surround */
35610463SGarrett.Damore@Sun.COM 		case 5:	/* right surround */
35710463SGarrett.Damore@Sun.COM 			mult = 1;
35810463SGarrett.Damore@Sun.COM 			break;
35910463SGarrett.Damore@Sun.COM 		}
36010463SGarrett.Damore@Sun.COM 		*offset = (port->buf_frames * 2 * mult) + (chan % 2);
36110463SGarrett.Damore@Sun.COM 		*incr = 2;
36210463SGarrett.Damore@Sun.COM 	} else {
36310463SGarrett.Damore@Sun.COM 		*offset = chan;
36410463SGarrett.Damore@Sun.COM 		*incr = 2;
36510463SGarrett.Damore@Sun.COM 	}
36610463SGarrett.Damore@Sun.COM }
36710463SGarrett.Damore@Sun.COM 
36810463SGarrett.Damore@Sun.COM /* private implementation bits */
36910463SGarrett.Damore@Sun.COM 
37010463SGarrett.Damore@Sun.COM int
p16x_alloc_port(p16x_dev_t * dev,int num)37110463SGarrett.Damore@Sun.COM p16x_alloc_port(p16x_dev_t *dev, int num)
37210463SGarrett.Damore@Sun.COM {
37310463SGarrett.Damore@Sun.COM 	p16x_port_t		*port;
37410463SGarrett.Damore@Sun.COM 	size_t			len;
37510463SGarrett.Damore@Sun.COM 	ddi_dma_cookie_t	cookie;
37610463SGarrett.Damore@Sun.COM 	uint_t			count;
37710463SGarrett.Damore@Sun.COM 	int			dir;
37810463SGarrett.Damore@Sun.COM 	unsigned		caps;
37910463SGarrett.Damore@Sun.COM 	audio_dev_t		*adev;
38010463SGarrett.Damore@Sun.COM 
38110463SGarrett.Damore@Sun.COM 	adev = dev->adev;
38210463SGarrett.Damore@Sun.COM 	port = kmem_zalloc(sizeof (*port), KM_SLEEP);
38310463SGarrett.Damore@Sun.COM 	dev->port[num] = port;
38410463SGarrett.Damore@Sun.COM 	port->dev = dev;
38510463SGarrett.Damore@Sun.COM 
38610463SGarrett.Damore@Sun.COM 	switch (num) {
38710463SGarrett.Damore@Sun.COM 	case P16X_REC:
38810463SGarrett.Damore@Sun.COM 		port->syncdir = DDI_DMA_SYNC_FORKERNEL;
38910463SGarrett.Damore@Sun.COM 		caps = ENGINE_INPUT_CAP;
39010463SGarrett.Damore@Sun.COM 		dir = DDI_DMA_READ;
39110463SGarrett.Damore@Sun.COM 		port->port_num = P16X_REC;
39210463SGarrett.Damore@Sun.COM 		port->nchan = 2;
39310463SGarrett.Damore@Sun.COM 		break;
39410463SGarrett.Damore@Sun.COM 	case P16X_PLAY:
39510463SGarrett.Damore@Sun.COM 		port->syncdir = DDI_DMA_SYNC_FORDEV;
39610463SGarrett.Damore@Sun.COM 		caps = ENGINE_OUTPUT_CAP;
39710463SGarrett.Damore@Sun.COM 		dir = DDI_DMA_WRITE;
39810463SGarrett.Damore@Sun.COM 		port->port_num = P16X_PLAY;
39910463SGarrett.Damore@Sun.COM 		port->nchan = 6;
40010463SGarrett.Damore@Sun.COM 		break;
40110463SGarrett.Damore@Sun.COM 	default:
40210463SGarrett.Damore@Sun.COM 		return (DDI_FAILURE);
40310463SGarrett.Damore@Sun.COM 	}
40410463SGarrett.Damore@Sun.COM 
40510463SGarrett.Damore@Sun.COM 	/*
406*11936Sgdamore@opensolaris.org 	 * NB: The device operates in pairs of dwords at a time, for
407*11936Sgdamore@opensolaris.org 	 * performance reasons.  So make sure that our buffer is
408*11936Sgdamore@opensolaris.org 	 * arranged as a whole number of these.  The value below gives
409*11936Sgdamore@opensolaris.org 	 * a reasonably large buffer so we can support a deep
410*11936Sgdamore@opensolaris.org 	 * playahead if we need to (and we should avoid input
411*11936Sgdamore@opensolaris.org 	 * overruns.)
41210463SGarrett.Damore@Sun.COM 	 */
413*11936Sgdamore@opensolaris.org 	port->buf_frames = 4096;
414*11936Sgdamore@opensolaris.org 	port->buf_size = port->buf_frames * port->nchan * sizeof (uint16_t);
41510463SGarrett.Damore@Sun.COM 
41610463SGarrett.Damore@Sun.COM 	/* now allocate buffers */
41710463SGarrett.Damore@Sun.COM 	if (ddi_dma_alloc_handle(dev->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
41810463SGarrett.Damore@Sun.COM 	    &port->buf_dmah) != DDI_SUCCESS) {
41910463SGarrett.Damore@Sun.COM 		audio_dev_warn(adev, "failed to allocate BUF handle");
42010463SGarrett.Damore@Sun.COM 		return (DDI_FAILURE);
42110463SGarrett.Damore@Sun.COM 	}
42210463SGarrett.Damore@Sun.COM 
42310463SGarrett.Damore@Sun.COM 	if (ddi_dma_mem_alloc(port->buf_dmah, port->buf_size,
42410463SGarrett.Damore@Sun.COM 	    &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
42510463SGarrett.Damore@Sun.COM 	    &port->buf_kaddr, &len, &port->buf_acch) != DDI_SUCCESS) {
42610463SGarrett.Damore@Sun.COM 		audio_dev_warn(adev, "failed to allocate BUF memory");
42710463SGarrett.Damore@Sun.COM 		return (DDI_FAILURE);
42810463SGarrett.Damore@Sun.COM 	}
42910463SGarrett.Damore@Sun.COM 
43010463SGarrett.Damore@Sun.COM 	if (ddi_dma_addr_bind_handle(port->buf_dmah, NULL, port->buf_kaddr,
43110463SGarrett.Damore@Sun.COM 	    len, DDI_DMA_CONSISTENT | dir, DDI_DMA_SLEEP, NULL, &cookie,
43210463SGarrett.Damore@Sun.COM 	    &count) != DDI_SUCCESS) {
43310463SGarrett.Damore@Sun.COM 		audio_dev_warn(adev, "failed binding BUF DMA handle");
43410463SGarrett.Damore@Sun.COM 		return (DDI_FAILURE);
43510463SGarrett.Damore@Sun.COM 	}
43610463SGarrett.Damore@Sun.COM 	port->buf_paddr = cookie.dmac_address;
43710463SGarrett.Damore@Sun.COM 
43810463SGarrett.Damore@Sun.COM 	port->engine = audio_engine_alloc(&p16x_engine_ops, caps);
43910463SGarrett.Damore@Sun.COM 	if (port->engine == NULL) {
44010463SGarrett.Damore@Sun.COM 		audio_dev_warn(adev, "audio_engine_alloc failed");
44110463SGarrett.Damore@Sun.COM 		return (DDI_FAILURE);
44210463SGarrett.Damore@Sun.COM 	}
44310463SGarrett.Damore@Sun.COM 
44410463SGarrett.Damore@Sun.COM 	audio_engine_set_private(port->engine, port);
44510463SGarrett.Damore@Sun.COM 	audio_dev_add_engine(adev, port->engine);
44610463SGarrett.Damore@Sun.COM 
44710463SGarrett.Damore@Sun.COM 	return (DDI_SUCCESS);
44810463SGarrett.Damore@Sun.COM }
44910463SGarrett.Damore@Sun.COM 
45010463SGarrett.Damore@Sun.COM void
p16x_destroy(p16x_dev_t * dev)45110463SGarrett.Damore@Sun.COM p16x_destroy(p16x_dev_t *dev)
45210463SGarrett.Damore@Sun.COM {
453*11936Sgdamore@opensolaris.org 	mutex_destroy(&dev->mutex);
45410463SGarrett.Damore@Sun.COM 
45510463SGarrett.Damore@Sun.COM 	for (int i = 0; i < P16X_NUM_PORT; i++) {
45610463SGarrett.Damore@Sun.COM 		p16x_port_t *port = dev->port[i];
45710463SGarrett.Damore@Sun.COM 		if (!port)
45810463SGarrett.Damore@Sun.COM 			continue;
45910463SGarrett.Damore@Sun.COM 		if (port->engine) {
46010463SGarrett.Damore@Sun.COM 			audio_dev_remove_engine(dev->adev, port->engine);
46110463SGarrett.Damore@Sun.COM 			audio_engine_free(port->engine);
46210463SGarrett.Damore@Sun.COM 		}
46310463SGarrett.Damore@Sun.COM 		if (port->buf_paddr) {
46410463SGarrett.Damore@Sun.COM 			(void) ddi_dma_unbind_handle(port->buf_dmah);
46510463SGarrett.Damore@Sun.COM 		}
46610463SGarrett.Damore@Sun.COM 		if (port->buf_acch) {
46710463SGarrett.Damore@Sun.COM 			ddi_dma_mem_free(&port->buf_acch);
46810463SGarrett.Damore@Sun.COM 		}
46910463SGarrett.Damore@Sun.COM 		if (port->buf_dmah) {
47010463SGarrett.Damore@Sun.COM 			ddi_dma_free_handle(&port->buf_dmah);
47110463SGarrett.Damore@Sun.COM 		}
47210463SGarrett.Damore@Sun.COM 		kmem_free(port, sizeof (*port));
47310463SGarrett.Damore@Sun.COM 	}
47410463SGarrett.Damore@Sun.COM 
47510463SGarrett.Damore@Sun.COM 	if (dev->ac97 != NULL) {
47610463SGarrett.Damore@Sun.COM 		ac97_free(dev->ac97);
47710463SGarrett.Damore@Sun.COM 	}
47810463SGarrett.Damore@Sun.COM 	if (dev->adev != NULL) {
47910463SGarrett.Damore@Sun.COM 		audio_dev_free(dev->adev);
48010463SGarrett.Damore@Sun.COM 	}
48110463SGarrett.Damore@Sun.COM 	if (dev->regsh != NULL) {
48210463SGarrett.Damore@Sun.COM 		ddi_regs_map_free(&dev->regsh);
48310463SGarrett.Damore@Sun.COM 	}
48410463SGarrett.Damore@Sun.COM 	if (dev->pcih != NULL) {
48510463SGarrett.Damore@Sun.COM 		pci_config_teardown(&dev->pcih);
48610463SGarrett.Damore@Sun.COM 	}
48710463SGarrett.Damore@Sun.COM 	kmem_free(dev, sizeof (*dev));
48810463SGarrett.Damore@Sun.COM }
48910463SGarrett.Damore@Sun.COM 
49010463SGarrett.Damore@Sun.COM void
p16x_hwinit(p16x_dev_t * dev)49110463SGarrett.Damore@Sun.COM p16x_hwinit(p16x_dev_t *dev)
49210463SGarrett.Damore@Sun.COM {
49310463SGarrett.Damore@Sun.COM 	p16x_port_t		*port;
49410463SGarrett.Damore@Sun.COM 	uint32_t		paddr;
49510463SGarrett.Damore@Sun.COM 	uint32_t		chunksz;
49610463SGarrett.Damore@Sun.COM 	int i;
49710463SGarrett.Damore@Sun.COM 
49810463SGarrett.Damore@Sun.COM 	for (i = 0; i < 3; i++) {
49910463SGarrett.Damore@Sun.COM 		write_reg(dev, PTBA, i, 0);
50010463SGarrett.Damore@Sun.COM 		write_reg(dev, PTBS, i, 0);
50110463SGarrett.Damore@Sun.COM 		write_reg(dev, PTCA, i, 0);
50210463SGarrett.Damore@Sun.COM 		write_reg(dev, PFEA, i, 0);
50310463SGarrett.Damore@Sun.COM 		write_reg(dev, CPFA, i, 0);
50410463SGarrett.Damore@Sun.COM 		write_reg(dev, CPCAV, i, 0);
50510463SGarrett.Damore@Sun.COM 		write_reg(dev, CRFA, i, 0);
50610463SGarrett.Damore@Sun.COM 		write_reg(dev, CRCAV, i, 0);
50710463SGarrett.Damore@Sun.COM 	}
50810463SGarrett.Damore@Sun.COM 	write_reg(dev, SCS0, 0, 0x02108504);
50910463SGarrett.Damore@Sun.COM 	write_reg(dev, SCS1, 0, 0x02108504);
51010463SGarrett.Damore@Sun.COM 	write_reg(dev, SCS2, 0, 0x02108504);
51110463SGarrett.Damore@Sun.COM 
51210463SGarrett.Damore@Sun.COM 	/* set the spdif/analog combo jack to analog out */
51310463SGarrett.Damore@Sun.COM 	write_reg(dev, SPC, 0, 0x00000700);
51410463SGarrett.Damore@Sun.COM 	write_reg(dev, EA_aux, 0, 0x0001003f);
51510463SGarrett.Damore@Sun.COM 
51610463SGarrett.Damore@Sun.COM 	port = dev->port[P16X_REC];
51710463SGarrett.Damore@Sun.COM 	/* Set physical address of the DMA buffer */
51810463SGarrett.Damore@Sun.COM 	write_reg(dev, RFBA, 0, port->buf_paddr);
51910463SGarrett.Damore@Sun.COM 	write_reg(dev, RFBS, 0, (port->buf_size) << 16);
52010463SGarrett.Damore@Sun.COM 
52110463SGarrett.Damore@Sun.COM 	/* Set physical address of the DMA buffer */
52210463SGarrett.Damore@Sun.COM 	port = dev->port[P16X_PLAY];
52310463SGarrett.Damore@Sun.COM 	paddr = port->buf_paddr;
52410463SGarrett.Damore@Sun.COM 	chunksz = port->buf_frames * 4;
52510463SGarrett.Damore@Sun.COM 	write_reg(dev, PFBA, 0, paddr);
52610463SGarrett.Damore@Sun.COM 	write_reg(dev, PFBS, 0, chunksz << 16);
52710463SGarrett.Damore@Sun.COM 	paddr += chunksz;
52810463SGarrett.Damore@Sun.COM 	write_reg(dev, PFBA, 1, paddr);
52910463SGarrett.Damore@Sun.COM 	write_reg(dev, PFBS, 1, chunksz << 16);
53010463SGarrett.Damore@Sun.COM 	paddr += chunksz;
53110463SGarrett.Damore@Sun.COM 	write_reg(dev, PFBA, 2, paddr);
53210463SGarrett.Damore@Sun.COM 	write_reg(dev, PFBS, 2, chunksz << 16);
53310463SGarrett.Damore@Sun.COM 
53410463SGarrett.Damore@Sun.COM 	OUTL(dev, 0x1080, GPIO);	/* GPIO */
53510463SGarrett.Damore@Sun.COM 	/* Clear any pending interrupts */
53610463SGarrett.Damore@Sun.COM 	OUTL(dev, INTR_ALL, IP);
53710463SGarrett.Damore@Sun.COM 	OUTL(dev, 0, IE);
53810463SGarrett.Damore@Sun.COM 	OUTL(dev, 0x9, HC);	/* Enable audio */
53910463SGarrett.Damore@Sun.COM }
54010463SGarrett.Damore@Sun.COM 
54110463SGarrett.Damore@Sun.COM int
p16x_attach(dev_info_t * dip)54210463SGarrett.Damore@Sun.COM p16x_attach(dev_info_t *dip)
54310463SGarrett.Damore@Sun.COM {
54410463SGarrett.Damore@Sun.COM 	uint16_t	vendor, device;
54510463SGarrett.Damore@Sun.COM 	p16x_dev_t	*dev;
54610463SGarrett.Damore@Sun.COM 	ddi_acc_handle_t pcih;
54710463SGarrett.Damore@Sun.COM 
54810463SGarrett.Damore@Sun.COM 	dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
54910463SGarrett.Damore@Sun.COM 	dev->dip = dip;
55010463SGarrett.Damore@Sun.COM 	ddi_set_driver_private(dip, dev);
55110463SGarrett.Damore@Sun.COM 
552*11936Sgdamore@opensolaris.org 	mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL);
55310463SGarrett.Damore@Sun.COM 
55410463SGarrett.Damore@Sun.COM 	if ((dev->adev = audio_dev_alloc(dip, 0)) == NULL) {
55510463SGarrett.Damore@Sun.COM 		cmn_err(CE_WARN, "audio_dev_alloc failed");
55610463SGarrett.Damore@Sun.COM 		goto error;
55710463SGarrett.Damore@Sun.COM 	}
55810463SGarrett.Damore@Sun.COM 
55910463SGarrett.Damore@Sun.COM 	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
56010463SGarrett.Damore@Sun.COM 		audio_dev_warn(dev->adev, "pci_config_setup failed");
56110463SGarrett.Damore@Sun.COM 		goto error;
56210463SGarrett.Damore@Sun.COM 	}
56310463SGarrett.Damore@Sun.COM 	dev->pcih = pcih;
56410463SGarrett.Damore@Sun.COM 
56510463SGarrett.Damore@Sun.COM 	vendor = pci_config_get16(pcih, PCI_CONF_VENID);
56610463SGarrett.Damore@Sun.COM 	device = pci_config_get16(pcih, PCI_CONF_DEVID);
56710463SGarrett.Damore@Sun.COM 	if (vendor != CREATIVE_VENDOR_ID ||
56810463SGarrett.Damore@Sun.COM 	    device != SB_P16X_ID) {
56910463SGarrett.Damore@Sun.COM 		audio_dev_warn(dev->adev, "Hardware not recognized "
57010463SGarrett.Damore@Sun.COM 		    "(vendor=%x, dev=%x)", vendor, device);
57110463SGarrett.Damore@Sun.COM 		goto error;
57210463SGarrett.Damore@Sun.COM 	}
57310463SGarrett.Damore@Sun.COM 
57410463SGarrett.Damore@Sun.COM 	/* set PCI command register */
57510463SGarrett.Damore@Sun.COM 	pci_config_put16(pcih, PCI_CONF_COMM,
57610463SGarrett.Damore@Sun.COM 	    pci_config_get16(pcih, PCI_CONF_COMM) |
57710463SGarrett.Damore@Sun.COM 	    PCI_COMM_MAE | PCI_COMM_IO);
57810463SGarrett.Damore@Sun.COM 
57910463SGarrett.Damore@Sun.COM 
58010463SGarrett.Damore@Sun.COM 	if ((ddi_regs_map_setup(dip, 1, &dev->base, 0, 0, &dev_attr,
58110463SGarrett.Damore@Sun.COM 	    &dev->regsh)) != DDI_SUCCESS) {
58210463SGarrett.Damore@Sun.COM 		audio_dev_warn(dev->adev, "failed to map registers");
58310463SGarrett.Damore@Sun.COM 		goto error;
58410463SGarrett.Damore@Sun.COM 	}
58510463SGarrett.Damore@Sun.COM 
58610463SGarrett.Damore@Sun.COM 	audio_dev_set_description(dev->adev, "Creative Sound Blaster Live!");
58710463SGarrett.Damore@Sun.COM 	audio_dev_set_version(dev->adev, "SBO200");
58810463SGarrett.Damore@Sun.COM 
58910463SGarrett.Damore@Sun.COM 	if ((p16x_alloc_port(dev, P16X_PLAY) != DDI_SUCCESS) ||
59010463SGarrett.Damore@Sun.COM 	    (p16x_alloc_port(dev, P16X_REC) != DDI_SUCCESS)) {
59110463SGarrett.Damore@Sun.COM 		goto error;
59210463SGarrett.Damore@Sun.COM 	}
59310463SGarrett.Damore@Sun.COM 
59410463SGarrett.Damore@Sun.COM 	p16x_hwinit(dev);
59510463SGarrett.Damore@Sun.COM 
59610463SGarrett.Damore@Sun.COM 	dev->ac97 = ac97_allocate(dev->adev, dip,
59710463SGarrett.Damore@Sun.COM 	    p16x_read_ac97, p16x_write_ac97, dev);
59810463SGarrett.Damore@Sun.COM 	if (dev->ac97 == NULL) {
59910463SGarrett.Damore@Sun.COM 		audio_dev_warn(dev->adev, "failed to allocate ac97 handle");
60010463SGarrett.Damore@Sun.COM 		goto error;
60110463SGarrett.Damore@Sun.COM 	}
60210463SGarrett.Damore@Sun.COM 
60310463SGarrett.Damore@Sun.COM 	ac97_probe_controls(dev->ac97);
60410463SGarrett.Damore@Sun.COM 
60510463SGarrett.Damore@Sun.COM 	/* remove the AC'97 controls we don't want to expose */
60610463SGarrett.Damore@Sun.COM 	for (int i = 0; p16x_remove_ac97[i]; i++) {
60710463SGarrett.Damore@Sun.COM 		ac97_ctrl_t *ctrl;
60810463SGarrett.Damore@Sun.COM 		ctrl = ac97_control_find(dev->ac97, p16x_remove_ac97[i]);
60910463SGarrett.Damore@Sun.COM 		if (ctrl != NULL) {
61010463SGarrett.Damore@Sun.COM 			ac97_control_unregister(ctrl);
61110463SGarrett.Damore@Sun.COM 		}
61210463SGarrett.Damore@Sun.COM 	}
61310463SGarrett.Damore@Sun.COM 
61410463SGarrett.Damore@Sun.COM 	ac97_register_controls(dev->ac97);
61510463SGarrett.Damore@Sun.COM 
61610463SGarrett.Damore@Sun.COM 	if (audio_dev_register(dev->adev) != DDI_SUCCESS) {
61710463SGarrett.Damore@Sun.COM 		audio_dev_warn(dev->adev, "unable to register with framework");
61810463SGarrett.Damore@Sun.COM 		goto error;
61910463SGarrett.Damore@Sun.COM 	}
62010463SGarrett.Damore@Sun.COM 
62110463SGarrett.Damore@Sun.COM 	ddi_report_dev(dip);
62210463SGarrett.Damore@Sun.COM 
62310463SGarrett.Damore@Sun.COM 	return (DDI_SUCCESS);
62410463SGarrett.Damore@Sun.COM 
62510463SGarrett.Damore@Sun.COM error:
62610463SGarrett.Damore@Sun.COM 	p16x_destroy(dev);
62710463SGarrett.Damore@Sun.COM 	return (DDI_FAILURE);
62810463SGarrett.Damore@Sun.COM }
62910463SGarrett.Damore@Sun.COM 
63010463SGarrett.Damore@Sun.COM int
p16x_resume(dev_info_t * dip)63110463SGarrett.Damore@Sun.COM p16x_resume(dev_info_t *dip)
63210463SGarrett.Damore@Sun.COM {
63310463SGarrett.Damore@Sun.COM 	p16x_dev_t *dev;
63410463SGarrett.Damore@Sun.COM 
63510463SGarrett.Damore@Sun.COM 	dev = ddi_get_driver_private(dip);
63610463SGarrett.Damore@Sun.COM 
63710463SGarrett.Damore@Sun.COM 	p16x_hwinit(dev);
63810463SGarrett.Damore@Sun.COM 
639*11936Sgdamore@opensolaris.org 	ac97_reset(dev->ac97);
64010463SGarrett.Damore@Sun.COM 
641*11936Sgdamore@opensolaris.org 	audio_dev_resume(dev->adev);
64210463SGarrett.Damore@Sun.COM 
64310463SGarrett.Damore@Sun.COM 	return (DDI_SUCCESS);
64410463SGarrett.Damore@Sun.COM }
64510463SGarrett.Damore@Sun.COM 
64610463SGarrett.Damore@Sun.COM int
p16x_detach(p16x_dev_t * dev)64710463SGarrett.Damore@Sun.COM p16x_detach(p16x_dev_t *dev)
64810463SGarrett.Damore@Sun.COM {
64910463SGarrett.Damore@Sun.COM 	if (audio_dev_unregister(dev->adev) != DDI_SUCCESS)
65010463SGarrett.Damore@Sun.COM 		return (DDI_FAILURE);
65110463SGarrett.Damore@Sun.COM 
65210463SGarrett.Damore@Sun.COM 	p16x_destroy(dev);
65310463SGarrett.Damore@Sun.COM 	return (DDI_SUCCESS);
65410463SGarrett.Damore@Sun.COM }
65510463SGarrett.Damore@Sun.COM 
65610463SGarrett.Damore@Sun.COM int
p16x_suspend(p16x_dev_t * dev)65710463SGarrett.Damore@Sun.COM p16x_suspend(p16x_dev_t *dev)
65810463SGarrett.Damore@Sun.COM {
659*11936Sgdamore@opensolaris.org 	audio_dev_suspend(dev->adev);
66010463SGarrett.Damore@Sun.COM 
66110463SGarrett.Damore@Sun.COM 	return (DDI_SUCCESS);
66210463SGarrett.Damore@Sun.COM }
66310463SGarrett.Damore@Sun.COM 
66410463SGarrett.Damore@Sun.COM static int p16x_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
66510463SGarrett.Damore@Sun.COM static int p16x_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
66610463SGarrett.Damore@Sun.COM static int p16x_ddi_quiesce(dev_info_t *);
66710463SGarrett.Damore@Sun.COM 
66810463SGarrett.Damore@Sun.COM static struct dev_ops p16x_dev_ops = {
66910463SGarrett.Damore@Sun.COM 	DEVO_REV,		/* rev */
67010463SGarrett.Damore@Sun.COM 	0,			/* refcnt */
67110463SGarrett.Damore@Sun.COM 	NULL,			/* getinfo */
67210463SGarrett.Damore@Sun.COM 	nulldev,		/* identify */
67310463SGarrett.Damore@Sun.COM 	nulldev,		/* probe */
67410463SGarrett.Damore@Sun.COM 	p16x_ddi_attach,	/* attach */
67510463SGarrett.Damore@Sun.COM 	p16x_ddi_detach,	/* detach */
67610463SGarrett.Damore@Sun.COM 	nodev,			/* reset */
67710463SGarrett.Damore@Sun.COM 	NULL,			/* cb_ops */
67810463SGarrett.Damore@Sun.COM 	NULL,			/* bus_ops */
67910463SGarrett.Damore@Sun.COM 	NULL,			/* power */
68010463SGarrett.Damore@Sun.COM 	p16x_ddi_quiesce,	/* quiesce */
68110463SGarrett.Damore@Sun.COM };
68210463SGarrett.Damore@Sun.COM 
68310463SGarrett.Damore@Sun.COM static struct modldrv p16x_modldrv = {
68410463SGarrett.Damore@Sun.COM 	&mod_driverops,		/* drv_modops */
68510463SGarrett.Damore@Sun.COM 	"Creative P16X Audio",	/* linkinfo */
68610463SGarrett.Damore@Sun.COM 	&p16x_dev_ops,		/* dev_ops */
68710463SGarrett.Damore@Sun.COM };
68810463SGarrett.Damore@Sun.COM 
68910463SGarrett.Damore@Sun.COM static struct modlinkage modlinkage = {
69010463SGarrett.Damore@Sun.COM 	MODREV_1,
69110463SGarrett.Damore@Sun.COM 	{ &p16x_modldrv, NULL }
69210463SGarrett.Damore@Sun.COM };
69310463SGarrett.Damore@Sun.COM 
69410463SGarrett.Damore@Sun.COM int
_init(void)69510463SGarrett.Damore@Sun.COM _init(void)
69610463SGarrett.Damore@Sun.COM {
69710463SGarrett.Damore@Sun.COM 	int	rv;
69810463SGarrett.Damore@Sun.COM 
69910463SGarrett.Damore@Sun.COM 	audio_init_ops(&p16x_dev_ops, P16X_NAME);
70010463SGarrett.Damore@Sun.COM 	if ((rv = mod_install(&modlinkage)) != 0) {
70110463SGarrett.Damore@Sun.COM 		audio_fini_ops(&p16x_dev_ops);
70210463SGarrett.Damore@Sun.COM 	}
70310463SGarrett.Damore@Sun.COM 	return (rv);
70410463SGarrett.Damore@Sun.COM }
70510463SGarrett.Damore@Sun.COM 
70610463SGarrett.Damore@Sun.COM int
_fini(void)70710463SGarrett.Damore@Sun.COM _fini(void)
70810463SGarrett.Damore@Sun.COM {
70910463SGarrett.Damore@Sun.COM 	int	rv;
71010463SGarrett.Damore@Sun.COM 
71110463SGarrett.Damore@Sun.COM 	if ((rv = mod_remove(&modlinkage)) == 0) {
71210463SGarrett.Damore@Sun.COM 		audio_fini_ops(&p16x_dev_ops);
71310463SGarrett.Damore@Sun.COM 	}
71410463SGarrett.Damore@Sun.COM 	return (rv);
71510463SGarrett.Damore@Sun.COM }
71610463SGarrett.Damore@Sun.COM 
71710463SGarrett.Damore@Sun.COM int
_info(struct modinfo * modinfop)71810463SGarrett.Damore@Sun.COM _info(struct modinfo *modinfop)
71910463SGarrett.Damore@Sun.COM {
72010463SGarrett.Damore@Sun.COM 	return (mod_info(&modlinkage, modinfop));
72110463SGarrett.Damore@Sun.COM }
72210463SGarrett.Damore@Sun.COM 
72310463SGarrett.Damore@Sun.COM int
p16x_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)72410463SGarrett.Damore@Sun.COM p16x_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
72510463SGarrett.Damore@Sun.COM {
72610463SGarrett.Damore@Sun.COM 	switch (cmd) {
72710463SGarrett.Damore@Sun.COM 	case DDI_ATTACH:
72810463SGarrett.Damore@Sun.COM 		return (p16x_attach(dip));
72910463SGarrett.Damore@Sun.COM 
73010463SGarrett.Damore@Sun.COM 	case DDI_RESUME:
73110463SGarrett.Damore@Sun.COM 		return (p16x_resume(dip));
73210463SGarrett.Damore@Sun.COM 
73310463SGarrett.Damore@Sun.COM 	default:
73410463SGarrett.Damore@Sun.COM 		return (DDI_FAILURE);
73510463SGarrett.Damore@Sun.COM 	}
73610463SGarrett.Damore@Sun.COM }
73710463SGarrett.Damore@Sun.COM 
73810463SGarrett.Damore@Sun.COM int
p16x_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)73910463SGarrett.Damore@Sun.COM p16x_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
74010463SGarrett.Damore@Sun.COM {
74110463SGarrett.Damore@Sun.COM 	p16x_dev_t *dev;
74210463SGarrett.Damore@Sun.COM 
74310463SGarrett.Damore@Sun.COM 	dev = ddi_get_driver_private(dip);
74410463SGarrett.Damore@Sun.COM 
74510463SGarrett.Damore@Sun.COM 	switch (cmd) {
74610463SGarrett.Damore@Sun.COM 	case DDI_DETACH:
74710463SGarrett.Damore@Sun.COM 		return (p16x_detach(dev));
74810463SGarrett.Damore@Sun.COM 
74910463SGarrett.Damore@Sun.COM 	case DDI_SUSPEND:
75010463SGarrett.Damore@Sun.COM 		return (p16x_suspend(dev));
75110463SGarrett.Damore@Sun.COM 
75210463SGarrett.Damore@Sun.COM 	default:
75310463SGarrett.Damore@Sun.COM 		return (DDI_FAILURE);
75410463SGarrett.Damore@Sun.COM 	}
75510463SGarrett.Damore@Sun.COM }
75610463SGarrett.Damore@Sun.COM 
75710463SGarrett.Damore@Sun.COM int
p16x_ddi_quiesce(dev_info_t * dip)75810463SGarrett.Damore@Sun.COM p16x_ddi_quiesce(dev_info_t *dip)
75910463SGarrett.Damore@Sun.COM {
76010463SGarrett.Damore@Sun.COM 	p16x_dev_t	*dev;
76110463SGarrett.Damore@Sun.COM 
76210463SGarrett.Damore@Sun.COM 	dev = ddi_get_driver_private(dip);
76310463SGarrett.Damore@Sun.COM 
76410463SGarrett.Damore@Sun.COM 	write_reg(dev, SA, 0, 0);
76510463SGarrett.Damore@Sun.COM 	OUTL(dev, 0x01, HC);
76610463SGarrett.Damore@Sun.COM 
76710463SGarrett.Damore@Sun.COM 	return (DDI_SUCCESS);
76810463SGarrett.Damore@Sun.COM }
769