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