xref: /illumos-gate/usr/src/uts/common/io/audio/drv/audiop16x/audiop16x.c (revision 68c47f65208790c466e5e484f2293d3baed71c6a)
1d9cbf529SGarrett D'Amore /*
2d9cbf529SGarrett D'Amore  * CDDL HEADER START
3d9cbf529SGarrett D'Amore  *
4d9cbf529SGarrett D'Amore  * The contents of this file are subject to the terms of the
5d9cbf529SGarrett D'Amore  * Common Development and Distribution License (the "License").
6d9cbf529SGarrett D'Amore  * You may not use this file except in compliance with the License.
7d9cbf529SGarrett D'Amore  *
8d9cbf529SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d9cbf529SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
10d9cbf529SGarrett D'Amore  * See the License for the specific language governing permissions
11d9cbf529SGarrett D'Amore  * and limitations under the License.
12d9cbf529SGarrett D'Amore  *
13d9cbf529SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
14d9cbf529SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d9cbf529SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
16d9cbf529SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
17d9cbf529SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
18d9cbf529SGarrett D'Amore  *
19d9cbf529SGarrett D'Amore  * CDDL HEADER END
20d9cbf529SGarrett D'Amore  */
21d9cbf529SGarrett D'Amore 
22d9cbf529SGarrett D'Amore /*
23*68c47f65SGarrett D'Amore  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24d9cbf529SGarrett D'Amore  * Use is subject to license terms.
25d9cbf529SGarrett D'Amore  */
26d9cbf529SGarrett D'Amore 
27d9cbf529SGarrett D'Amore /*
28d9cbf529SGarrett D'Amore  * Purpose: Driver for the Creative P16X AC97 audio controller
29d9cbf529SGarrett D'Amore  */
30d9cbf529SGarrett D'Amore /*
31d9cbf529SGarrett D'Amore  *
32d9cbf529SGarrett D'Amore  * Copyright (C) 4Front Technologies 1996-2009.
33d9cbf529SGarrett D'Amore  *
34d9cbf529SGarrett D'Amore  * This software is released under CDDL 1.0 source license.
35d9cbf529SGarrett D'Amore  * See the COPYING file included in the main directory of this source
36d9cbf529SGarrett D'Amore  * distribution for the license terms and conditions.
37d9cbf529SGarrett D'Amore  */
38d9cbf529SGarrett D'Amore 
39d9cbf529SGarrett D'Amore #include <sys/types.h>
40d9cbf529SGarrett D'Amore #include <sys/modctl.h>
41d9cbf529SGarrett D'Amore #include <sys/kmem.h>
42d9cbf529SGarrett D'Amore #include <sys/conf.h>
43d9cbf529SGarrett D'Amore #include <sys/ddi.h>
44d9cbf529SGarrett D'Amore #include <sys/sunddi.h>
45d9cbf529SGarrett D'Amore #include <sys/pci.h>
46d9cbf529SGarrett D'Amore #include <sys/note.h>
47d9cbf529SGarrett D'Amore #include <sys/audio/audio_driver.h>
48d9cbf529SGarrett D'Amore #include <sys/audio/ac97.h>
49d9cbf529SGarrett D'Amore 
50d9cbf529SGarrett D'Amore #include "audiop16x.h"
51d9cbf529SGarrett D'Amore 
52d9cbf529SGarrett D'Amore /*
53d9cbf529SGarrett D'Amore  * These boards use an AC'97 codec, but don't have all of the
54d9cbf529SGarrett D'Amore  * various outputs that the AC'97 codec can offer.  We just
55d9cbf529SGarrett D'Amore  * suppress them for now.
56d9cbf529SGarrett D'Amore  */
57d9cbf529SGarrett D'Amore static char *p16x_remove_ac97[] = {
58d9cbf529SGarrett D'Amore 	AUDIO_CTRL_ID_BEEP,
59d9cbf529SGarrett D'Amore 	AUDIO_CTRL_ID_VIDEO,
60d9cbf529SGarrett D'Amore 	AUDIO_CTRL_ID_MICSRC,
61d9cbf529SGarrett D'Amore 	AUDIO_CTRL_ID_SPEAKER,
62d9cbf529SGarrett D'Amore 	AUDIO_CTRL_ID_SPKSRC,
63d9cbf529SGarrett D'Amore 	NULL
64d9cbf529SGarrett D'Amore };
65d9cbf529SGarrett D'Amore 
66d9cbf529SGarrett D'Amore static struct ddi_device_acc_attr dev_attr = {
67d9cbf529SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
68d9cbf529SGarrett D'Amore 	DDI_STRUCTURE_LE_ACC,
69d9cbf529SGarrett D'Amore 	DDI_STRICTORDER_ACC
70d9cbf529SGarrett D'Amore };
71d9cbf529SGarrett D'Amore 
72d9cbf529SGarrett D'Amore static struct ddi_device_acc_attr buf_attr = {
73d9cbf529SGarrett D'Amore 	DDI_DEVICE_ATTR_V0,
74d9cbf529SGarrett D'Amore 	DDI_NEVERSWAP_ACC,
75d9cbf529SGarrett D'Amore 	DDI_STRICTORDER_ACC
76d9cbf529SGarrett D'Amore };
77d9cbf529SGarrett D'Amore 
78d9cbf529SGarrett D'Amore static ddi_dma_attr_t dma_attr_buf = {
79d9cbf529SGarrett D'Amore 	DMA_ATTR_V0,		/* version number */
80d9cbf529SGarrett D'Amore 	0x00000000,		/* low DMA address range */
81d9cbf529SGarrett D'Amore 	0xffffffff,		/* high DMA address range */
82d9cbf529SGarrett D'Amore 	0xfffffffe,		/* DMA counter register */
83d9cbf529SGarrett D'Amore 	4,			/* DMA address alignment */
84d9cbf529SGarrett D'Amore 	0x3c,			/* DMA burstsizes */
85d9cbf529SGarrett D'Amore 	4,			/* min effective DMA size */
86d9cbf529SGarrett D'Amore 	0xffffffff,		/* max DMA xfer size */
87d9cbf529SGarrett D'Amore 	0xffffffff,		/* segment boundary */
88d9cbf529SGarrett D'Amore 	1,			/* s/g length */
89d9cbf529SGarrett D'Amore 	4,			/* granularity of device */
90d9cbf529SGarrett D'Amore 	0			/* Bus specific DMA flags */
91d9cbf529SGarrett D'Amore };
92d9cbf529SGarrett D'Amore 
93d9cbf529SGarrett D'Amore static int p16x_attach(dev_info_t *);
94d9cbf529SGarrett D'Amore static int p16x_resume(dev_info_t *);
95d9cbf529SGarrett D'Amore static int p16x_detach(p16x_dev_t *);
96d9cbf529SGarrett D'Amore static int p16x_suspend(p16x_dev_t *);
97d9cbf529SGarrett D'Amore 
98*68c47f65SGarrett D'Amore static int p16x_open(void *, int, unsigned *, caddr_t *);
99d9cbf529SGarrett D'Amore static void p16x_close(void *);
100d9cbf529SGarrett D'Amore static int p16x_start(void *);
101d9cbf529SGarrett D'Amore static void p16x_stop(void *);
102d9cbf529SGarrett D'Amore static int p16x_format(void *);
103d9cbf529SGarrett D'Amore static int p16x_channels(void *);
104d9cbf529SGarrett D'Amore static int p16x_rate(void *);
105d9cbf529SGarrett D'Amore static uint64_t p16x_count(void *);
106d9cbf529SGarrett D'Amore static void p16x_sync(void *, unsigned);
107d9cbf529SGarrett D'Amore static void p16x_chinfo(void *, int, unsigned *, unsigned *);
108d9cbf529SGarrett D'Amore 
109d9cbf529SGarrett D'Amore static uint16_t p16x_read_ac97(void *, uint8_t);
110d9cbf529SGarrett D'Amore static void p16x_write_ac97(void *, uint8_t, uint16_t);
111d9cbf529SGarrett D'Amore static int p16x_alloc_port(p16x_dev_t *, int);
112d9cbf529SGarrett D'Amore static void p16x_destroy(p16x_dev_t *);
113d9cbf529SGarrett D'Amore static void p16x_hwinit(p16x_dev_t *);
114d9cbf529SGarrett D'Amore 
115d9cbf529SGarrett D'Amore static audio_engine_ops_t p16x_engine_ops = {
116d9cbf529SGarrett D'Amore 	AUDIO_ENGINE_VERSION,
117d9cbf529SGarrett D'Amore 	p16x_open,
118d9cbf529SGarrett D'Amore 	p16x_close,
119d9cbf529SGarrett D'Amore 	p16x_start,
120d9cbf529SGarrett D'Amore 	p16x_stop,
121d9cbf529SGarrett D'Amore 	p16x_count,
122d9cbf529SGarrett D'Amore 	p16x_format,
123d9cbf529SGarrett D'Amore 	p16x_channels,
124d9cbf529SGarrett D'Amore 	p16x_rate,
125d9cbf529SGarrett D'Amore 	p16x_sync,
126f9ead4a5SGarrett D'Amore 	NULL,
127d9cbf529SGarrett D'Amore 	p16x_chinfo,
128f9ead4a5SGarrett D'Amore 	NULL
129d9cbf529SGarrett D'Amore };
130d9cbf529SGarrett D'Amore 
131d9cbf529SGarrett D'Amore static unsigned int
read_reg(p16x_dev_t * dev,int reg,int chn)132d9cbf529SGarrett D'Amore read_reg(p16x_dev_t *dev, int reg, int chn)
133d9cbf529SGarrett D'Amore {
134d9cbf529SGarrett D'Amore 	unsigned int val;
135d9cbf529SGarrett D'Amore 
136*68c47f65SGarrett D'Amore 	mutex_enter(&dev->mutex);
137d9cbf529SGarrett D'Amore 	OUTL(dev, (reg << 16) | (chn & 0xffff), PTR);	/* Pointer */
138d9cbf529SGarrett D'Amore 	val = INL(dev, DR);	/* Data */
139*68c47f65SGarrett D'Amore 	mutex_exit(&dev->mutex);
140d9cbf529SGarrett D'Amore 
141d9cbf529SGarrett D'Amore 	return (val);
142d9cbf529SGarrett D'Amore }
143d9cbf529SGarrett D'Amore 
144d9cbf529SGarrett D'Amore static void
write_reg(p16x_dev_t * dev,int reg,int chn,unsigned int value)145d9cbf529SGarrett D'Amore write_reg(p16x_dev_t *dev, int reg, int chn, unsigned int value)
146d9cbf529SGarrett D'Amore {
147d9cbf529SGarrett D'Amore 
148*68c47f65SGarrett D'Amore 	mutex_enter(&dev->mutex);
149d9cbf529SGarrett D'Amore 	OUTL(dev, (reg << 16) | (chn & 0xffff), PTR);	/* Pointer */
150d9cbf529SGarrett D'Amore 	OUTL(dev, value, DR);	/* Data */
151*68c47f65SGarrett D'Amore 	mutex_exit(&dev->mutex);
152*68c47f65SGarrett D'Amore }
153*68c47f65SGarrett D'Amore 
154*68c47f65SGarrett D'Amore static void
set_reg_bits(p16x_dev_t * dev,int reg,int chn,unsigned int mask)155*68c47f65SGarrett D'Amore set_reg_bits(p16x_dev_t *dev, int reg, int chn, unsigned int mask)
156*68c47f65SGarrett D'Amore {
157*68c47f65SGarrett D'Amore 	unsigned int	val;
158*68c47f65SGarrett D'Amore 	mutex_enter(&dev->mutex);
159*68c47f65SGarrett D'Amore 	OUTL(dev, (reg << 16) | (chn & 0xffff), PTR);	/* Pointer */
160*68c47f65SGarrett D'Amore 	val = INL(dev, DR);	/* Data */
161*68c47f65SGarrett D'Amore 	val |= mask;
162*68c47f65SGarrett D'Amore 	OUTL(dev, val, DR);	/* Data */
163*68c47f65SGarrett D'Amore 	mutex_exit(&dev->mutex);
164*68c47f65SGarrett D'Amore }
165*68c47f65SGarrett D'Amore 
166*68c47f65SGarrett D'Amore static void
clear_reg_bits(p16x_dev_t * dev,int reg,int chn,unsigned int mask)167*68c47f65SGarrett D'Amore clear_reg_bits(p16x_dev_t *dev, int reg, int chn, unsigned int mask)
168*68c47f65SGarrett D'Amore {
169*68c47f65SGarrett D'Amore 	unsigned int	val;
170*68c47f65SGarrett D'Amore 	mutex_enter(&dev->mutex);
171*68c47f65SGarrett D'Amore 	OUTL(dev, (reg << 16) | (chn & 0xffff), PTR);	/* Pointer */
172*68c47f65SGarrett D'Amore 	val = INL(dev, DR);	/* Data */
173*68c47f65SGarrett D'Amore 	val &= ~(mask);
174*68c47f65SGarrett D'Amore 	OUTL(dev, val, DR);	/* Data */
175*68c47f65SGarrett D'Amore 	mutex_exit(&dev->mutex);
176d9cbf529SGarrett D'Amore }
177d9cbf529SGarrett D'Amore 
178d9cbf529SGarrett D'Amore static uint16_t
p16x_read_ac97(void * arg,uint8_t index)179d9cbf529SGarrett D'Amore p16x_read_ac97(void *arg, uint8_t index)
180d9cbf529SGarrett D'Amore {
181d9cbf529SGarrett D'Amore 	p16x_dev_t *dev = arg;
182d9cbf529SGarrett D'Amore 	uint16_t value;
183d9cbf529SGarrett D'Amore 	int i;
184d9cbf529SGarrett D'Amore 
185d9cbf529SGarrett D'Amore 	OUTB(dev, index, AC97A);
186d9cbf529SGarrett D'Amore 	for (i = 0; i < 10000; i++)
187d9cbf529SGarrett D'Amore 		if (INB(dev, AC97A) & 0x80)
188d9cbf529SGarrett D'Amore 			break;
189d9cbf529SGarrett D'Amore 	value = INW(dev, AC97D);
190d9cbf529SGarrett D'Amore 	return (value);
191d9cbf529SGarrett D'Amore }
192d9cbf529SGarrett D'Amore 
193d9cbf529SGarrett D'Amore static void
p16x_write_ac97(void * arg,uint8_t index,uint16_t data)194d9cbf529SGarrett D'Amore p16x_write_ac97(void *arg, uint8_t index, uint16_t data)
195d9cbf529SGarrett D'Amore {
196d9cbf529SGarrett D'Amore 	p16x_dev_t *dev = arg;
197d9cbf529SGarrett D'Amore 	unsigned int i;
198d9cbf529SGarrett D'Amore 
199d9cbf529SGarrett D'Amore 	OUTB(dev, index, AC97A);
200d9cbf529SGarrett D'Amore 	for (i = 0; i < 10000; i++)
201d9cbf529SGarrett D'Amore 		if (INB(dev, AC97A) & 0x80)
202d9cbf529SGarrett D'Amore 			break;
203d9cbf529SGarrett D'Amore 	OUTW(dev, data, AC97D);
204d9cbf529SGarrett D'Amore }
205d9cbf529SGarrett D'Amore 
206d9cbf529SGarrett D'Amore /*
207d9cbf529SGarrett D'Amore  * Audio routines
208d9cbf529SGarrett D'Amore  */
209d9cbf529SGarrett D'Amore 
210*68c47f65SGarrett D'Amore int
p16x_open(void * arg,int flag,uint_t * nframes,caddr_t * bufp)211*68c47f65SGarrett D'Amore p16x_open(void *arg, int flag, uint_t *nframes, caddr_t *bufp)
212d9cbf529SGarrett D'Amore {
213*68c47f65SGarrett D'Amore 	p16x_port_t	*port = arg;
214*68c47f65SGarrett D'Amore 
215*68c47f65SGarrett D'Amore 	_NOTE(ARGUNUSED(flag));
216*68c47f65SGarrett D'Amore 
217*68c47f65SGarrett D'Amore 	port->count = 0;
218*68c47f65SGarrett D'Amore 	*nframes = port->buf_frames;
219*68c47f65SGarrett D'Amore 	*bufp = port->buf_kaddr;
220*68c47f65SGarrett D'Amore 
221*68c47f65SGarrett D'Amore 	return (0);
222*68c47f65SGarrett D'Amore }
223*68c47f65SGarrett D'Amore 
224*68c47f65SGarrett D'Amore void
p16x_close(void * arg)225*68c47f65SGarrett D'Amore p16x_close(void *arg)
226*68c47f65SGarrett D'Amore {
227*68c47f65SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
228*68c47f65SGarrett D'Amore }
229*68c47f65SGarrett D'Amore 
230*68c47f65SGarrett D'Amore int
p16x_start(void * arg)231*68c47f65SGarrett D'Amore p16x_start(void *arg)
232*68c47f65SGarrett D'Amore {
233*68c47f65SGarrett D'Amore 	p16x_port_t	*port = arg;
234d9cbf529SGarrett D'Amore 	p16x_dev_t	*dev = port->dev;
235d9cbf529SGarrett D'Amore 
236*68c47f65SGarrett D'Amore 	port->offset = 0;
237d9cbf529SGarrett D'Amore 
238d9cbf529SGarrett D'Amore 	if (port->port_num == P16X_REC) {
239d9cbf529SGarrett D'Amore 		write_reg(dev, CRFA, 0, 0);
240d9cbf529SGarrett D'Amore 		write_reg(dev, CRCAV, 0, 0);
241d9cbf529SGarrett D'Amore 
242*68c47f65SGarrett D'Amore 		/* Enable rec channel */
243*68c47f65SGarrett D'Amore 		set_reg_bits(dev, SA, 0, 0x100);
244d9cbf529SGarrett D'Amore 	} else {
245d9cbf529SGarrett D'Amore 		for (int i = 0; i < 3; i++) {
246d9cbf529SGarrett D'Amore 			write_reg(dev, PTBA, i, 0);
247d9cbf529SGarrett D'Amore 			write_reg(dev, PTBS, i, 0);
248d9cbf529SGarrett D'Amore 			write_reg(dev, PTCA, i, 0);
249d9cbf529SGarrett D'Amore 			write_reg(dev, PFEA, i, 0);
250d9cbf529SGarrett D'Amore 			write_reg(dev, CPFA, i, 0);
251d9cbf529SGarrett D'Amore 			write_reg(dev, CPCAV, i, 0);
252d9cbf529SGarrett D'Amore 		}
253d9cbf529SGarrett D'Amore 
254*68c47f65SGarrett D'Amore 		/* Enable play channel */
255*68c47f65SGarrett D'Amore 		set_reg_bits(dev, SA, 0, 0x7);
256d9cbf529SGarrett D'Amore 	}
257d9cbf529SGarrett D'Amore 
258d9cbf529SGarrett D'Amore 	return (0);
259d9cbf529SGarrett D'Amore }
260d9cbf529SGarrett D'Amore 
261d9cbf529SGarrett D'Amore void
p16x_stop(void * arg)262d9cbf529SGarrett D'Amore p16x_stop(void *arg)
263d9cbf529SGarrett D'Amore {
264d9cbf529SGarrett D'Amore 	p16x_port_t	*port = arg;
265d9cbf529SGarrett D'Amore 	p16x_dev_t	*dev = port->dev;
266d9cbf529SGarrett D'Amore 
267*68c47f65SGarrett D'Amore 	if (port->port_num == P16X_REC) {
268*68c47f65SGarrett D'Amore 		/* Disable rec channel */
269*68c47f65SGarrett D'Amore 		clear_reg_bits(dev, SA, 0, 0x100);
270*68c47f65SGarrett D'Amore 
271*68c47f65SGarrett D'Amore 	} else {
272*68c47f65SGarrett D'Amore 		/* Disable Play channel */
273*68c47f65SGarrett D'Amore 		clear_reg_bits(dev, SA, 0, 0x7);
274d9cbf529SGarrett D'Amore 	}
275d9cbf529SGarrett D'Amore }
276d9cbf529SGarrett D'Amore 
277d9cbf529SGarrett D'Amore int
p16x_format(void * arg)278d9cbf529SGarrett D'Amore p16x_format(void *arg)
279d9cbf529SGarrett D'Amore {
280d9cbf529SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
281d9cbf529SGarrett D'Amore 
282d9cbf529SGarrett D'Amore 	return (AUDIO_FORMAT_S16_LE);
283d9cbf529SGarrett D'Amore }
284d9cbf529SGarrett D'Amore 
285d9cbf529SGarrett D'Amore int
p16x_channels(void * arg)286d9cbf529SGarrett D'Amore p16x_channels(void *arg)
287d9cbf529SGarrett D'Amore {
288d9cbf529SGarrett D'Amore 	p16x_port_t *port = arg;
289d9cbf529SGarrett D'Amore 
290d9cbf529SGarrett D'Amore 	return (port->nchan);
291d9cbf529SGarrett D'Amore }
292d9cbf529SGarrett D'Amore 
293d9cbf529SGarrett D'Amore int
p16x_rate(void * arg)294d9cbf529SGarrett D'Amore p16x_rate(void *arg)
295d9cbf529SGarrett D'Amore {
296d9cbf529SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
297d9cbf529SGarrett D'Amore 
298d9cbf529SGarrett D'Amore 	return (48000);
299d9cbf529SGarrett D'Amore }
300d9cbf529SGarrett D'Amore 
301d9cbf529SGarrett D'Amore void
p16x_sync(void * arg,unsigned nframes)302d9cbf529SGarrett D'Amore p16x_sync(void *arg, unsigned nframes)
303d9cbf529SGarrett D'Amore {
304d9cbf529SGarrett D'Amore 	p16x_port_t *port = arg;
305d9cbf529SGarrett D'Amore 	_NOTE(ARGUNUSED(nframes));
306d9cbf529SGarrett D'Amore 
307d9cbf529SGarrett D'Amore 	(void) ddi_dma_sync(port->buf_dmah, 0, 0, port->syncdir);
308d9cbf529SGarrett D'Amore }
309d9cbf529SGarrett D'Amore 
310d9cbf529SGarrett D'Amore uint64_t
p16x_count(void * arg)311d9cbf529SGarrett D'Amore p16x_count(void *arg)
312d9cbf529SGarrett D'Amore {
313d9cbf529SGarrett D'Amore 	p16x_port_t	*port = arg;
314d9cbf529SGarrett D'Amore 	p16x_dev_t	*dev = port->dev;
315d9cbf529SGarrett D'Amore 	uint64_t	val;
316*68c47f65SGarrett D'Amore 	uint32_t	offset, n;
317d9cbf529SGarrett D'Amore 
318*68c47f65SGarrett D'Amore 	if (port->port_num == P16X_PLAY) {
319*68c47f65SGarrett D'Amore 		offset = read_reg(dev, CPFA, 0);
320*68c47f65SGarrett D'Amore 	} else {
321*68c47f65SGarrett D'Amore 		offset = read_reg(dev, CRFA, 0);
322*68c47f65SGarrett D'Amore 	}
323*68c47f65SGarrett D'Amore 
324*68c47f65SGarrett D'Amore 	/* get the offset, and switch to frames */
325*68c47f65SGarrett D'Amore 	offset /= (2 * sizeof (uint16_t));
326*68c47f65SGarrett D'Amore 
327*68c47f65SGarrett D'Amore 	if (offset >= port->offset) {
328*68c47f65SGarrett D'Amore 		n = offset - port->offset;
329*68c47f65SGarrett D'Amore 	} else {
330*68c47f65SGarrett D'Amore 		n = offset + (port->buf_frames - port->offset);
331*68c47f65SGarrett D'Amore 	}
332*68c47f65SGarrett D'Amore 	port->offset = offset;
333*68c47f65SGarrett D'Amore 	port->count += n;
334d9cbf529SGarrett D'Amore 	val = port->count;
335d9cbf529SGarrett D'Amore 
336d9cbf529SGarrett D'Amore 	return (val);
337d9cbf529SGarrett D'Amore }
338d9cbf529SGarrett D'Amore 
339d9cbf529SGarrett D'Amore static void
p16x_chinfo(void * arg,int chan,unsigned * offset,unsigned * incr)340d9cbf529SGarrett D'Amore p16x_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
341d9cbf529SGarrett D'Amore {
342d9cbf529SGarrett D'Amore 	p16x_port_t *port = arg;
343d9cbf529SGarrett D'Amore 	unsigned mult;
344d9cbf529SGarrett D'Amore 
345d9cbf529SGarrett D'Amore 	if (port->port_num == P16X_PLAY) {
346d9cbf529SGarrett D'Amore 		switch (chan) {
347d9cbf529SGarrett D'Amore 		case 0:	/* left front */
348d9cbf529SGarrett D'Amore 		case 1:	/* right front */
349d9cbf529SGarrett D'Amore 			mult = 0;
350d9cbf529SGarrett D'Amore 			break;
351d9cbf529SGarrett D'Amore 		case 2:	/* center */
352d9cbf529SGarrett D'Amore 		case 3:	/* lfe */
353d9cbf529SGarrett D'Amore 			mult = 2;
354d9cbf529SGarrett D'Amore 			break;
355d9cbf529SGarrett D'Amore 		case 4:	/* left surround */
356d9cbf529SGarrett D'Amore 		case 5:	/* right surround */
357d9cbf529SGarrett D'Amore 			mult = 1;
358d9cbf529SGarrett D'Amore 			break;
359d9cbf529SGarrett D'Amore 		}
360d9cbf529SGarrett D'Amore 		*offset = (port->buf_frames * 2 * mult) + (chan % 2);
361d9cbf529SGarrett D'Amore 		*incr = 2;
362d9cbf529SGarrett D'Amore 	} else {
363d9cbf529SGarrett D'Amore 		*offset = chan;
364d9cbf529SGarrett D'Amore 		*incr = 2;
365d9cbf529SGarrett D'Amore 	}
366d9cbf529SGarrett D'Amore }
367d9cbf529SGarrett D'Amore 
368d9cbf529SGarrett D'Amore /* private implementation bits */
369d9cbf529SGarrett D'Amore 
370d9cbf529SGarrett D'Amore int
p16x_alloc_port(p16x_dev_t * dev,int num)371d9cbf529SGarrett D'Amore p16x_alloc_port(p16x_dev_t *dev, int num)
372d9cbf529SGarrett D'Amore {
373d9cbf529SGarrett D'Amore 	p16x_port_t		*port;
374d9cbf529SGarrett D'Amore 	size_t			len;
375d9cbf529SGarrett D'Amore 	ddi_dma_cookie_t	cookie;
376d9cbf529SGarrett D'Amore 	uint_t			count;
377d9cbf529SGarrett D'Amore 	int			dir;
378d9cbf529SGarrett D'Amore 	unsigned		caps;
379d9cbf529SGarrett D'Amore 	audio_dev_t		*adev;
380d9cbf529SGarrett D'Amore 
381d9cbf529SGarrett D'Amore 	adev = dev->adev;
382d9cbf529SGarrett D'Amore 	port = kmem_zalloc(sizeof (*port), KM_SLEEP);
383d9cbf529SGarrett D'Amore 	dev->port[num] = port;
384d9cbf529SGarrett D'Amore 	port->dev = dev;
385d9cbf529SGarrett D'Amore 
386d9cbf529SGarrett D'Amore 	switch (num) {
387d9cbf529SGarrett D'Amore 	case P16X_REC:
388d9cbf529SGarrett D'Amore 		port->syncdir = DDI_DMA_SYNC_FORKERNEL;
389d9cbf529SGarrett D'Amore 		caps = ENGINE_INPUT_CAP;
390d9cbf529SGarrett D'Amore 		dir = DDI_DMA_READ;
391d9cbf529SGarrett D'Amore 		port->port_num = P16X_REC;
392d9cbf529SGarrett D'Amore 		port->nchan = 2;
393d9cbf529SGarrett D'Amore 		break;
394d9cbf529SGarrett D'Amore 	case P16X_PLAY:
395d9cbf529SGarrett D'Amore 		port->syncdir = DDI_DMA_SYNC_FORDEV;
396d9cbf529SGarrett D'Amore 		caps = ENGINE_OUTPUT_CAP;
397d9cbf529SGarrett D'Amore 		dir = DDI_DMA_WRITE;
398d9cbf529SGarrett D'Amore 		port->port_num = P16X_PLAY;
399d9cbf529SGarrett D'Amore 		port->nchan = 6;
400d9cbf529SGarrett D'Amore 		break;
401d9cbf529SGarrett D'Amore 	default:
402d9cbf529SGarrett D'Amore 		return (DDI_FAILURE);
403d9cbf529SGarrett D'Amore 	}
404d9cbf529SGarrett D'Amore 
405d9cbf529SGarrett D'Amore 	/*
406*68c47f65SGarrett D'Amore 	 * NB: The device operates in pairs of dwords at a time, for
407d9cbf529SGarrett D'Amore 	 * performance reasons.  So make sure that our buffer is
408*68c47f65SGarrett D'Amore 	 * arranged as a whole number of these.  The value below gives
409*68c47f65SGarrett D'Amore 	 * a reasonably large buffer so we can support a deep
410*68c47f65SGarrett D'Amore 	 * playahead if we need to (and we should avoid input
411*68c47f65SGarrett D'Amore 	 * overruns.)
412d9cbf529SGarrett D'Amore 	 */
413*68c47f65SGarrett D'Amore 	port->buf_frames = 4096;
414*68c47f65SGarrett D'Amore 	port->buf_size = port->buf_frames * port->nchan * sizeof (uint16_t);
415d9cbf529SGarrett D'Amore 
416d9cbf529SGarrett D'Amore 	/* now allocate buffers */
417d9cbf529SGarrett D'Amore 	if (ddi_dma_alloc_handle(dev->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
418d9cbf529SGarrett D'Amore 	    &port->buf_dmah) != DDI_SUCCESS) {
419d9cbf529SGarrett D'Amore 		audio_dev_warn(adev, "failed to allocate BUF handle");
420d9cbf529SGarrett D'Amore 		return (DDI_FAILURE);
421d9cbf529SGarrett D'Amore 	}
422d9cbf529SGarrett D'Amore 
423d9cbf529SGarrett D'Amore 	if (ddi_dma_mem_alloc(port->buf_dmah, port->buf_size,
424d9cbf529SGarrett D'Amore 	    &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
425d9cbf529SGarrett D'Amore 	    &port->buf_kaddr, &len, &port->buf_acch) != DDI_SUCCESS) {
426d9cbf529SGarrett D'Amore 		audio_dev_warn(adev, "failed to allocate BUF memory");
427d9cbf529SGarrett D'Amore 		return (DDI_FAILURE);
428d9cbf529SGarrett D'Amore 	}
429d9cbf529SGarrett D'Amore 
430d9cbf529SGarrett D'Amore 	if (ddi_dma_addr_bind_handle(port->buf_dmah, NULL, port->buf_kaddr,
431d9cbf529SGarrett D'Amore 	    len, DDI_DMA_CONSISTENT | dir, DDI_DMA_SLEEP, NULL, &cookie,
432d9cbf529SGarrett D'Amore 	    &count) != DDI_SUCCESS) {
433d9cbf529SGarrett D'Amore 		audio_dev_warn(adev, "failed binding BUF DMA handle");
434d9cbf529SGarrett D'Amore 		return (DDI_FAILURE);
435d9cbf529SGarrett D'Amore 	}
436d9cbf529SGarrett D'Amore 	port->buf_paddr = cookie.dmac_address;
437d9cbf529SGarrett D'Amore 
438d9cbf529SGarrett D'Amore 	port->engine = audio_engine_alloc(&p16x_engine_ops, caps);
439d9cbf529SGarrett D'Amore 	if (port->engine == NULL) {
440d9cbf529SGarrett D'Amore 		audio_dev_warn(adev, "audio_engine_alloc failed");
441d9cbf529SGarrett D'Amore 		return (DDI_FAILURE);
442d9cbf529SGarrett D'Amore 	}
443d9cbf529SGarrett D'Amore 
444d9cbf529SGarrett D'Amore 	audio_engine_set_private(port->engine, port);
445d9cbf529SGarrett D'Amore 	audio_dev_add_engine(adev, port->engine);
446d9cbf529SGarrett D'Amore 
447d9cbf529SGarrett D'Amore 	return (DDI_SUCCESS);
448d9cbf529SGarrett D'Amore }
449d9cbf529SGarrett D'Amore 
450d9cbf529SGarrett D'Amore void
p16x_destroy(p16x_dev_t * dev)451d9cbf529SGarrett D'Amore p16x_destroy(p16x_dev_t *dev)
452d9cbf529SGarrett D'Amore {
453d9cbf529SGarrett D'Amore 	mutex_destroy(&dev->mutex);
454d9cbf529SGarrett D'Amore 
455d9cbf529SGarrett D'Amore 	for (int i = 0; i < P16X_NUM_PORT; i++) {
456d9cbf529SGarrett D'Amore 		p16x_port_t *port = dev->port[i];
457d9cbf529SGarrett D'Amore 		if (!port)
458d9cbf529SGarrett D'Amore 			continue;
459d9cbf529SGarrett D'Amore 		if (port->engine) {
460d9cbf529SGarrett D'Amore 			audio_dev_remove_engine(dev->adev, port->engine);
461d9cbf529SGarrett D'Amore 			audio_engine_free(port->engine);
462d9cbf529SGarrett D'Amore 		}
463d9cbf529SGarrett D'Amore 		if (port->buf_paddr) {
464d9cbf529SGarrett D'Amore 			(void) ddi_dma_unbind_handle(port->buf_dmah);
465d9cbf529SGarrett D'Amore 		}
466d9cbf529SGarrett D'Amore 		if (port->buf_acch) {
467d9cbf529SGarrett D'Amore 			ddi_dma_mem_free(&port->buf_acch);
468d9cbf529SGarrett D'Amore 		}
469d9cbf529SGarrett D'Amore 		if (port->buf_dmah) {
470d9cbf529SGarrett D'Amore 			ddi_dma_free_handle(&port->buf_dmah);
471d9cbf529SGarrett D'Amore 		}
472d9cbf529SGarrett D'Amore 		kmem_free(port, sizeof (*port));
473d9cbf529SGarrett D'Amore 	}
474d9cbf529SGarrett D'Amore 
475d9cbf529SGarrett D'Amore 	if (dev->ac97 != NULL) {
476d9cbf529SGarrett D'Amore 		ac97_free(dev->ac97);
477d9cbf529SGarrett D'Amore 	}
478d9cbf529SGarrett D'Amore 	if (dev->adev != NULL) {
479d9cbf529SGarrett D'Amore 		audio_dev_free(dev->adev);
480d9cbf529SGarrett D'Amore 	}
481d9cbf529SGarrett D'Amore 	if (dev->regsh != NULL) {
482d9cbf529SGarrett D'Amore 		ddi_regs_map_free(&dev->regsh);
483d9cbf529SGarrett D'Amore 	}
484d9cbf529SGarrett D'Amore 	if (dev->pcih != NULL) {
485d9cbf529SGarrett D'Amore 		pci_config_teardown(&dev->pcih);
486d9cbf529SGarrett D'Amore 	}
487d9cbf529SGarrett D'Amore 	kmem_free(dev, sizeof (*dev));
488d9cbf529SGarrett D'Amore }
489d9cbf529SGarrett D'Amore 
490d9cbf529SGarrett D'Amore void
p16x_hwinit(p16x_dev_t * dev)491d9cbf529SGarrett D'Amore p16x_hwinit(p16x_dev_t *dev)
492d9cbf529SGarrett D'Amore {
493d9cbf529SGarrett D'Amore 	p16x_port_t		*port;
494d9cbf529SGarrett D'Amore 	uint32_t		paddr;
495d9cbf529SGarrett D'Amore 	uint32_t		chunksz;
496d9cbf529SGarrett D'Amore 	int i;
497d9cbf529SGarrett D'Amore 
498d9cbf529SGarrett D'Amore 	for (i = 0; i < 3; i++) {
499d9cbf529SGarrett D'Amore 		write_reg(dev, PTBA, i, 0);
500d9cbf529SGarrett D'Amore 		write_reg(dev, PTBS, i, 0);
501d9cbf529SGarrett D'Amore 		write_reg(dev, PTCA, i, 0);
502d9cbf529SGarrett D'Amore 		write_reg(dev, PFEA, i, 0);
503d9cbf529SGarrett D'Amore 		write_reg(dev, CPFA, i, 0);
504d9cbf529SGarrett D'Amore 		write_reg(dev, CPCAV, i, 0);
505d9cbf529SGarrett D'Amore 		write_reg(dev, CRFA, i, 0);
506d9cbf529SGarrett D'Amore 		write_reg(dev, CRCAV, i, 0);
507d9cbf529SGarrett D'Amore 	}
508d9cbf529SGarrett D'Amore 	write_reg(dev, SCS0, 0, 0x02108504);
509d9cbf529SGarrett D'Amore 	write_reg(dev, SCS1, 0, 0x02108504);
510d9cbf529SGarrett D'Amore 	write_reg(dev, SCS2, 0, 0x02108504);
511d9cbf529SGarrett D'Amore 
512d9cbf529SGarrett D'Amore 	/* set the spdif/analog combo jack to analog out */
513d9cbf529SGarrett D'Amore 	write_reg(dev, SPC, 0, 0x00000700);
514d9cbf529SGarrett D'Amore 	write_reg(dev, EA_aux, 0, 0x0001003f);
515d9cbf529SGarrett D'Amore 
516d9cbf529SGarrett D'Amore 	port = dev->port[P16X_REC];
517d9cbf529SGarrett D'Amore 	/* Set physical address of the DMA buffer */
518d9cbf529SGarrett D'Amore 	write_reg(dev, RFBA, 0, port->buf_paddr);
519d9cbf529SGarrett D'Amore 	write_reg(dev, RFBS, 0, (port->buf_size) << 16);
520d9cbf529SGarrett D'Amore 
521d9cbf529SGarrett D'Amore 	/* Set physical address of the DMA buffer */
522d9cbf529SGarrett D'Amore 	port = dev->port[P16X_PLAY];
523d9cbf529SGarrett D'Amore 	paddr = port->buf_paddr;
524d9cbf529SGarrett D'Amore 	chunksz = port->buf_frames * 4;
525d9cbf529SGarrett D'Amore 	write_reg(dev, PFBA, 0, paddr);
526d9cbf529SGarrett D'Amore 	write_reg(dev, PFBS, 0, chunksz << 16);
527d9cbf529SGarrett D'Amore 	paddr += chunksz;
528d9cbf529SGarrett D'Amore 	write_reg(dev, PFBA, 1, paddr);
529d9cbf529SGarrett D'Amore 	write_reg(dev, PFBS, 1, chunksz << 16);
530d9cbf529SGarrett D'Amore 	paddr += chunksz;
531d9cbf529SGarrett D'Amore 	write_reg(dev, PFBA, 2, paddr);
532d9cbf529SGarrett D'Amore 	write_reg(dev, PFBS, 2, chunksz << 16);
533d9cbf529SGarrett D'Amore 
534d9cbf529SGarrett D'Amore 	OUTL(dev, 0x1080, GPIO);	/* GPIO */
535d9cbf529SGarrett D'Amore 	/* Clear any pending interrupts */
536d9cbf529SGarrett D'Amore 	OUTL(dev, INTR_ALL, IP);
537d9cbf529SGarrett D'Amore 	OUTL(dev, 0, IE);
538d9cbf529SGarrett D'Amore 	OUTL(dev, 0x9, HC);	/* Enable audio */
539d9cbf529SGarrett D'Amore }
540d9cbf529SGarrett D'Amore 
541d9cbf529SGarrett D'Amore int
p16x_attach(dev_info_t * dip)542d9cbf529SGarrett D'Amore p16x_attach(dev_info_t *dip)
543d9cbf529SGarrett D'Amore {
544d9cbf529SGarrett D'Amore 	uint16_t	vendor, device;
545d9cbf529SGarrett D'Amore 	p16x_dev_t	*dev;
546d9cbf529SGarrett D'Amore 	ddi_acc_handle_t pcih;
547d9cbf529SGarrett D'Amore 
548d9cbf529SGarrett D'Amore 	dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
549d9cbf529SGarrett D'Amore 	dev->dip = dip;
550d9cbf529SGarrett D'Amore 	ddi_set_driver_private(dip, dev);
551d9cbf529SGarrett D'Amore 
552*68c47f65SGarrett D'Amore 	mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL);
553d9cbf529SGarrett D'Amore 
554d9cbf529SGarrett D'Amore 	if ((dev->adev = audio_dev_alloc(dip, 0)) == NULL) {
555d9cbf529SGarrett D'Amore 		cmn_err(CE_WARN, "audio_dev_alloc failed");
556d9cbf529SGarrett D'Amore 		goto error;
557d9cbf529SGarrett D'Amore 	}
558d9cbf529SGarrett D'Amore 
559d9cbf529SGarrett D'Amore 	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
560d9cbf529SGarrett D'Amore 		audio_dev_warn(dev->adev, "pci_config_setup failed");
561d9cbf529SGarrett D'Amore 		goto error;
562d9cbf529SGarrett D'Amore 	}
563d9cbf529SGarrett D'Amore 	dev->pcih = pcih;
564d9cbf529SGarrett D'Amore 
565d9cbf529SGarrett D'Amore 	vendor = pci_config_get16(pcih, PCI_CONF_VENID);
566d9cbf529SGarrett D'Amore 	device = pci_config_get16(pcih, PCI_CONF_DEVID);
567d9cbf529SGarrett D'Amore 	if (vendor != CREATIVE_VENDOR_ID ||
568d9cbf529SGarrett D'Amore 	    device != SB_P16X_ID) {
569d9cbf529SGarrett D'Amore 		audio_dev_warn(dev->adev, "Hardware not recognized "
570d9cbf529SGarrett D'Amore 		    "(vendor=%x, dev=%x)", vendor, device);
571d9cbf529SGarrett D'Amore 		goto error;
572d9cbf529SGarrett D'Amore 	}
573d9cbf529SGarrett D'Amore 
574d9cbf529SGarrett D'Amore 	/* set PCI command register */
575d9cbf529SGarrett D'Amore 	pci_config_put16(pcih, PCI_CONF_COMM,
576d9cbf529SGarrett D'Amore 	    pci_config_get16(pcih, PCI_CONF_COMM) |
577d9cbf529SGarrett D'Amore 	    PCI_COMM_MAE | PCI_COMM_IO);
578d9cbf529SGarrett D'Amore 
579d9cbf529SGarrett D'Amore 
580d9cbf529SGarrett D'Amore 	if ((ddi_regs_map_setup(dip, 1, &dev->base, 0, 0, &dev_attr,
581d9cbf529SGarrett D'Amore 	    &dev->regsh)) != DDI_SUCCESS) {
582d9cbf529SGarrett D'Amore 		audio_dev_warn(dev->adev, "failed to map registers");
583d9cbf529SGarrett D'Amore 		goto error;
584d9cbf529SGarrett D'Amore 	}
585d9cbf529SGarrett D'Amore 
586d9cbf529SGarrett D'Amore 	audio_dev_set_description(dev->adev, "Creative Sound Blaster Live!");
587d9cbf529SGarrett D'Amore 	audio_dev_set_version(dev->adev, "SBO200");
588d9cbf529SGarrett D'Amore 
589d9cbf529SGarrett D'Amore 	if ((p16x_alloc_port(dev, P16X_PLAY) != DDI_SUCCESS) ||
590d9cbf529SGarrett D'Amore 	    (p16x_alloc_port(dev, P16X_REC) != DDI_SUCCESS)) {
591d9cbf529SGarrett D'Amore 		goto error;
592d9cbf529SGarrett D'Amore 	}
593d9cbf529SGarrett D'Amore 
594d9cbf529SGarrett D'Amore 	p16x_hwinit(dev);
595d9cbf529SGarrett D'Amore 
596d9cbf529SGarrett D'Amore 	dev->ac97 = ac97_allocate(dev->adev, dip,
597d9cbf529SGarrett D'Amore 	    p16x_read_ac97, p16x_write_ac97, dev);
598d9cbf529SGarrett D'Amore 	if (dev->ac97 == NULL) {
599d9cbf529SGarrett D'Amore 		audio_dev_warn(dev->adev, "failed to allocate ac97 handle");
600d9cbf529SGarrett D'Amore 		goto error;
601d9cbf529SGarrett D'Amore 	}
602d9cbf529SGarrett D'Amore 
603d9cbf529SGarrett D'Amore 	ac97_probe_controls(dev->ac97);
604d9cbf529SGarrett D'Amore 
605d9cbf529SGarrett D'Amore 	/* remove the AC'97 controls we don't want to expose */
606d9cbf529SGarrett D'Amore 	for (int i = 0; p16x_remove_ac97[i]; i++) {
607d9cbf529SGarrett D'Amore 		ac97_ctrl_t *ctrl;
608d9cbf529SGarrett D'Amore 		ctrl = ac97_control_find(dev->ac97, p16x_remove_ac97[i]);
609d9cbf529SGarrett D'Amore 		if (ctrl != NULL) {
610d9cbf529SGarrett D'Amore 			ac97_control_unregister(ctrl);
611d9cbf529SGarrett D'Amore 		}
612d9cbf529SGarrett D'Amore 	}
613d9cbf529SGarrett D'Amore 
614d9cbf529SGarrett D'Amore 	ac97_register_controls(dev->ac97);
615d9cbf529SGarrett D'Amore 
616d9cbf529SGarrett D'Amore 	if (audio_dev_register(dev->adev) != DDI_SUCCESS) {
617d9cbf529SGarrett D'Amore 		audio_dev_warn(dev->adev, "unable to register with framework");
618d9cbf529SGarrett D'Amore 		goto error;
619d9cbf529SGarrett D'Amore 	}
620d9cbf529SGarrett D'Amore 
621d9cbf529SGarrett D'Amore 	ddi_report_dev(dip);
622d9cbf529SGarrett D'Amore 
623d9cbf529SGarrett D'Amore 	return (DDI_SUCCESS);
624d9cbf529SGarrett D'Amore 
625d9cbf529SGarrett D'Amore error:
626d9cbf529SGarrett D'Amore 	p16x_destroy(dev);
627d9cbf529SGarrett D'Amore 	return (DDI_FAILURE);
628d9cbf529SGarrett D'Amore }
629d9cbf529SGarrett D'Amore 
630d9cbf529SGarrett D'Amore int
p16x_resume(dev_info_t * dip)631d9cbf529SGarrett D'Amore p16x_resume(dev_info_t *dip)
632d9cbf529SGarrett D'Amore {
633d9cbf529SGarrett D'Amore 	p16x_dev_t *dev;
634d9cbf529SGarrett D'Amore 
635d9cbf529SGarrett D'Amore 	dev = ddi_get_driver_private(dip);
636d9cbf529SGarrett D'Amore 
637d9cbf529SGarrett D'Amore 	p16x_hwinit(dev);
638d9cbf529SGarrett D'Amore 
639*68c47f65SGarrett D'Amore 	ac97_reset(dev->ac97);
640d9cbf529SGarrett D'Amore 
641*68c47f65SGarrett D'Amore 	audio_dev_resume(dev->adev);
642d9cbf529SGarrett D'Amore 
643d9cbf529SGarrett D'Amore 	return (DDI_SUCCESS);
644d9cbf529SGarrett D'Amore }
645d9cbf529SGarrett D'Amore 
646d9cbf529SGarrett D'Amore int
p16x_detach(p16x_dev_t * dev)647d9cbf529SGarrett D'Amore p16x_detach(p16x_dev_t *dev)
648d9cbf529SGarrett D'Amore {
649d9cbf529SGarrett D'Amore 	if (audio_dev_unregister(dev->adev) != DDI_SUCCESS)
650d9cbf529SGarrett D'Amore 		return (DDI_FAILURE);
651d9cbf529SGarrett D'Amore 
652d9cbf529SGarrett D'Amore 	p16x_destroy(dev);
653d9cbf529SGarrett D'Amore 	return (DDI_SUCCESS);
654d9cbf529SGarrett D'Amore }
655d9cbf529SGarrett D'Amore 
656d9cbf529SGarrett D'Amore int
p16x_suspend(p16x_dev_t * dev)657d9cbf529SGarrett D'Amore p16x_suspend(p16x_dev_t *dev)
658d9cbf529SGarrett D'Amore {
659*68c47f65SGarrett D'Amore 	audio_dev_suspend(dev->adev);
660d9cbf529SGarrett D'Amore 
661d9cbf529SGarrett D'Amore 	return (DDI_SUCCESS);
662d9cbf529SGarrett D'Amore }
663d9cbf529SGarrett D'Amore 
664d9cbf529SGarrett D'Amore static int p16x_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
665d9cbf529SGarrett D'Amore static int p16x_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
666d9cbf529SGarrett D'Amore static int p16x_ddi_quiesce(dev_info_t *);
667d9cbf529SGarrett D'Amore 
668d9cbf529SGarrett D'Amore static struct dev_ops p16x_dev_ops = {
669d9cbf529SGarrett D'Amore 	DEVO_REV,		/* rev */
670d9cbf529SGarrett D'Amore 	0,			/* refcnt */
671d9cbf529SGarrett D'Amore 	NULL,			/* getinfo */
672d9cbf529SGarrett D'Amore 	nulldev,		/* identify */
673d9cbf529SGarrett D'Amore 	nulldev,		/* probe */
674d9cbf529SGarrett D'Amore 	p16x_ddi_attach,	/* attach */
675d9cbf529SGarrett D'Amore 	p16x_ddi_detach,	/* detach */
676d9cbf529SGarrett D'Amore 	nodev,			/* reset */
677d9cbf529SGarrett D'Amore 	NULL,			/* cb_ops */
678d9cbf529SGarrett D'Amore 	NULL,			/* bus_ops */
679d9cbf529SGarrett D'Amore 	NULL,			/* power */
680d9cbf529SGarrett D'Amore 	p16x_ddi_quiesce,	/* quiesce */
681d9cbf529SGarrett D'Amore };
682d9cbf529SGarrett D'Amore 
683d9cbf529SGarrett D'Amore static struct modldrv p16x_modldrv = {
684d9cbf529SGarrett D'Amore 	&mod_driverops,		/* drv_modops */
685d9cbf529SGarrett D'Amore 	"Creative P16X Audio",	/* linkinfo */
686d9cbf529SGarrett D'Amore 	&p16x_dev_ops,		/* dev_ops */
687d9cbf529SGarrett D'Amore };
688d9cbf529SGarrett D'Amore 
689d9cbf529SGarrett D'Amore static struct modlinkage modlinkage = {
690d9cbf529SGarrett D'Amore 	MODREV_1,
691d9cbf529SGarrett D'Amore 	{ &p16x_modldrv, NULL }
692d9cbf529SGarrett D'Amore };
693d9cbf529SGarrett D'Amore 
694d9cbf529SGarrett D'Amore int
_init(void)695d9cbf529SGarrett D'Amore _init(void)
696d9cbf529SGarrett D'Amore {
697d9cbf529SGarrett D'Amore 	int	rv;
698d9cbf529SGarrett D'Amore 
699d9cbf529SGarrett D'Amore 	audio_init_ops(&p16x_dev_ops, P16X_NAME);
700d9cbf529SGarrett D'Amore 	if ((rv = mod_install(&modlinkage)) != 0) {
701d9cbf529SGarrett D'Amore 		audio_fini_ops(&p16x_dev_ops);
702d9cbf529SGarrett D'Amore 	}
703d9cbf529SGarrett D'Amore 	return (rv);
704d9cbf529SGarrett D'Amore }
705d9cbf529SGarrett D'Amore 
706d9cbf529SGarrett D'Amore int
_fini(void)707d9cbf529SGarrett D'Amore _fini(void)
708d9cbf529SGarrett D'Amore {
709d9cbf529SGarrett D'Amore 	int	rv;
710d9cbf529SGarrett D'Amore 
711d9cbf529SGarrett D'Amore 	if ((rv = mod_remove(&modlinkage)) == 0) {
712d9cbf529SGarrett D'Amore 		audio_fini_ops(&p16x_dev_ops);
713d9cbf529SGarrett D'Amore 	}
714d9cbf529SGarrett D'Amore 	return (rv);
715d9cbf529SGarrett D'Amore }
716d9cbf529SGarrett D'Amore 
717d9cbf529SGarrett D'Amore int
_info(struct modinfo * modinfop)718d9cbf529SGarrett D'Amore _info(struct modinfo *modinfop)
719d9cbf529SGarrett D'Amore {
720d9cbf529SGarrett D'Amore 	return (mod_info(&modlinkage, modinfop));
721d9cbf529SGarrett D'Amore }
722d9cbf529SGarrett D'Amore 
723d9cbf529SGarrett D'Amore int
p16x_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)724d9cbf529SGarrett D'Amore p16x_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
725d9cbf529SGarrett D'Amore {
726d9cbf529SGarrett D'Amore 	switch (cmd) {
727d9cbf529SGarrett D'Amore 	case DDI_ATTACH:
728d9cbf529SGarrett D'Amore 		return (p16x_attach(dip));
729d9cbf529SGarrett D'Amore 
730d9cbf529SGarrett D'Amore 	case DDI_RESUME:
731d9cbf529SGarrett D'Amore 		return (p16x_resume(dip));
732d9cbf529SGarrett D'Amore 
733d9cbf529SGarrett D'Amore 	default:
734d9cbf529SGarrett D'Amore 		return (DDI_FAILURE);
735d9cbf529SGarrett D'Amore 	}
736d9cbf529SGarrett D'Amore }
737d9cbf529SGarrett D'Amore 
738d9cbf529SGarrett D'Amore int
p16x_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)739d9cbf529SGarrett D'Amore p16x_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
740d9cbf529SGarrett D'Amore {
741d9cbf529SGarrett D'Amore 	p16x_dev_t *dev;
742d9cbf529SGarrett D'Amore 
743d9cbf529SGarrett D'Amore 	dev = ddi_get_driver_private(dip);
744d9cbf529SGarrett D'Amore 
745d9cbf529SGarrett D'Amore 	switch (cmd) {
746d9cbf529SGarrett D'Amore 	case DDI_DETACH:
747d9cbf529SGarrett D'Amore 		return (p16x_detach(dev));
748d9cbf529SGarrett D'Amore 
749d9cbf529SGarrett D'Amore 	case DDI_SUSPEND:
750d9cbf529SGarrett D'Amore 		return (p16x_suspend(dev));
751d9cbf529SGarrett D'Amore 
752d9cbf529SGarrett D'Amore 	default:
753d9cbf529SGarrett D'Amore 		return (DDI_FAILURE);
754d9cbf529SGarrett D'Amore 	}
755d9cbf529SGarrett D'Amore }
756d9cbf529SGarrett D'Amore 
757d9cbf529SGarrett D'Amore int
p16x_ddi_quiesce(dev_info_t * dip)758d9cbf529SGarrett D'Amore p16x_ddi_quiesce(dev_info_t *dip)
759d9cbf529SGarrett D'Amore {
760d9cbf529SGarrett D'Amore 	p16x_dev_t	*dev;
761d9cbf529SGarrett D'Amore 
762d9cbf529SGarrett D'Amore 	dev = ddi_get_driver_private(dip);
763d9cbf529SGarrett D'Amore 
764d9cbf529SGarrett D'Amore 	write_reg(dev, SA, 0, 0);
765d9cbf529SGarrett D'Amore 	OUTL(dev, 0x01, HC);
766d9cbf529SGarrett D'Amore 
767d9cbf529SGarrett D'Amore 	return (DDI_SUCCESS);
768d9cbf529SGarrett D'Amore }
769