xref: /illumos-gate/usr/src/uts/common/io/audio/drv/audiop16x/audiop16x.c (revision e753f464d28e02e23aa93bd7d51d39fc56f79897)
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 2009 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 *, 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_update_port(p16x_port_t *);
113 static void p16x_start_port(p16x_port_t *);
114 static void p16x_stop_port(p16x_port_t *);
115 static void p16x_destroy(p16x_dev_t *);
116 static int p16x_setup_intrs(p16x_dev_t *);
117 static void p16x_hwinit(p16x_dev_t *);
118 static uint_t p16x_intr(caddr_t, caddr_t);
119 
120 static audio_engine_ops_t p16x_engine_ops = {
121 	AUDIO_ENGINE_VERSION,
122 	p16x_open,
123 	p16x_close,
124 	p16x_start,
125 	p16x_stop,
126 	p16x_count,
127 	p16x_format,
128 	p16x_channels,
129 	p16x_rate,
130 	p16x_sync,
131 	NULL,
132 	p16x_chinfo,
133 	NULL
134 };
135 
136 static unsigned int
137 read_reg(p16x_dev_t *dev, int reg, int chn)
138 {
139 	unsigned int val;
140 
141 	mutex_enter(&dev->low_mutex);
142 	OUTL(dev, (reg << 16) | (chn & 0xffff), PTR);	/* Pointer */
143 	val = INL(dev, DR);	/* Data */
144 	mutex_exit(&dev->low_mutex);
145 
146 	return (val);
147 }
148 
149 static void
150 write_reg(p16x_dev_t *dev, int reg, int chn, unsigned int value)
151 {
152 
153 	mutex_enter(&dev->low_mutex);
154 	OUTL(dev, (reg << 16) | (chn & 0xffff), PTR);	/* Pointer */
155 	OUTL(dev, value, DR);	/* Data */
156 	mutex_exit(&dev->low_mutex);
157 }
158 
159 static uint16_t
160 p16x_read_ac97(void *arg, uint8_t index)
161 {
162 	p16x_dev_t *dev = arg;
163 	uint16_t value;
164 	int i;
165 
166 	mutex_enter(&dev->low_mutex);
167 	OUTB(dev, index, AC97A);
168 	for (i = 0; i < 10000; i++)
169 		if (INB(dev, AC97A) & 0x80)
170 			break;
171 	value = INW(dev, AC97D);
172 	mutex_exit(&dev->low_mutex);
173 	return (value);
174 }
175 
176 static void
177 p16x_write_ac97(void *arg, uint8_t index, uint16_t data)
178 {
179 	p16x_dev_t *dev = arg;
180 	unsigned int i;
181 
182 	mutex_enter(&dev->low_mutex);
183 	OUTB(dev, index, AC97A);
184 	for (i = 0; i < 10000; i++)
185 		if (INB(dev, AC97A) & 0x80)
186 			break;
187 	OUTW(dev, data, AC97D);
188 	mutex_exit(&dev->low_mutex);
189 }
190 
191 static uint_t
192 p16x_intr(caddr_t argp, caddr_t nocare)
193 {
194 	p16x_dev_t	*dev = (void *)argp;
195 	unsigned int	status;
196 	audio_engine_t	*consume = NULL;
197 	audio_engine_t	*produce = NULL;
198 
199 	_NOTE(ARGUNUSED(nocare));
200 
201 	mutex_enter(&dev->mutex);
202 	if (dev->suspended) {
203 		mutex_exit(&dev->mutex);
204 		return (DDI_INTR_UNCLAIMED);
205 	}
206 	/* Read the interrupt status */
207 	status = INL(dev, IP);
208 	OUTL(dev, status, IP);	/* Acknowledge */
209 
210 	if (!(status & INTR_ALL)) {
211 		mutex_exit(&dev->mutex);
212 		return (DDI_INTR_UNCLAIMED);
213 	}
214 
215 	if (status & INTR_PCI) {
216 		audio_dev_warn(dev->adev, "PCI error triggered, PCI status %x",
217 		    pci_config_get16(dev->pcih, PCI_CONF_STAT));
218 	}
219 
220 	if ((status & (INTR_PFF | INTR_PFH)) &&
221 	    (dev->port[P16X_PLAY]->started)) {
222 		consume = dev->port[P16X_PLAY]->engine;
223 	}
224 
225 	if ((status & (INTR_RFF | INTR_RFH)) &&
226 	    (dev->port[P16X_REC]->started)) {
227 		produce = dev->port[P16X_REC]->engine;
228 	}
229 
230 	mutex_exit(&dev->mutex);
231 
232 	if (consume) {
233 		audio_engine_consume(consume);
234 	}
235 
236 	if (produce) {
237 		audio_engine_produce(produce);
238 	}
239 
240 	return (DDI_INTR_CLAIMED);
241 }
242 
243 /*
244  * Audio routines
245  */
246 
247 static void
248 p16x_init_port(p16x_port_t *port)
249 {
250 	p16x_dev_t	*dev = port->dev;
251 
252 	if (port->suspended)
253 		return;
254 
255 	if (port->port_num == P16X_REC) {
256 		write_reg(dev, CRFA, 0, 0);
257 		write_reg(dev, CRCAV, 0, 0);
258 
259 	} else {
260 		for (int i = 0; i < 3; i++) {
261 			write_reg(dev, PTBA, i, 0);
262 			write_reg(dev, PTBS, i, 0);
263 			write_reg(dev, PTCA, i, 0);
264 			write_reg(dev, PFEA, i, 0);
265 			write_reg(dev, CPFA, i, 0);
266 			write_reg(dev, CPCAV, i, 0);
267 		}
268 
269 	}
270 }
271 
272 
273 static int
274 p16x_open(void *arg, int flag, uint_t *fragfrp, uint_t *nfp, caddr_t *bufp)
275 {
276 	p16x_port_t	*port = arg;
277 	p16x_dev_t	*dev = port->dev;
278 
279 	_NOTE(ARGUNUSED(flag));
280 
281 	mutex_enter(&dev->mutex);
282 
283 	port->started = B_FALSE;
284 	port->count = 0;
285 	port->offset = 0;
286 
287 	p16x_init_port(port);
288 
289 	*fragfrp = port->fragfr;
290 	*nfp = port->nfrags;
291 	*bufp = port->buf_kaddr;
292 	mutex_exit(&dev->mutex);
293 
294 	return (0);
295 }
296 
297 void
298 p16x_close(void *arg)
299 {
300 	p16x_port_t	 *port = arg;
301 	p16x_dev_t	 *dev = port->dev;
302 
303 	mutex_enter(&dev->mutex);
304 	p16x_stop_port(port);
305 	port->started = B_FALSE;
306 	mutex_exit(&dev->mutex);
307 }
308 
309 int
310 p16x_start(void *arg)
311 {
312 	p16x_port_t	*port = arg;
313 	p16x_dev_t	*dev = port->dev;
314 
315 	mutex_enter(&dev->mutex);
316 	if (!port->started) {
317 		p16x_start_port(port);
318 		port->started = B_TRUE;
319 	}
320 	mutex_exit(&dev->mutex);
321 	return (0);
322 }
323 
324 void
325 p16x_stop(void *arg)
326 {
327 	p16x_port_t	*port = arg;
328 	p16x_dev_t	*dev = port->dev;
329 
330 	mutex_enter(&dev->mutex);
331 	if (port->started) {
332 		p16x_stop_port(port);
333 		port->started = B_FALSE;
334 	}
335 	mutex_exit(&dev->mutex);
336 }
337 
338 int
339 p16x_format(void *arg)
340 {
341 	_NOTE(ARGUNUSED(arg));
342 
343 	return (AUDIO_FORMAT_S16_LE);
344 }
345 
346 int
347 p16x_channels(void *arg)
348 {
349 	p16x_port_t *port = arg;
350 
351 	return (port->nchan);
352 }
353 
354 int
355 p16x_rate(void *arg)
356 {
357 	_NOTE(ARGUNUSED(arg));
358 
359 	return (48000);
360 }
361 
362 void
363 p16x_sync(void *arg, unsigned nframes)
364 {
365 	p16x_port_t *port = arg;
366 	_NOTE(ARGUNUSED(nframes));
367 
368 	(void) ddi_dma_sync(port->buf_dmah, 0, 0, port->syncdir);
369 }
370 
371 uint64_t
372 p16x_count(void *arg)
373 {
374 	p16x_port_t	*port = arg;
375 	p16x_dev_t	*dev = port->dev;
376 	uint64_t	val;
377 
378 	mutex_enter(&dev->mutex);
379 	if (port->started && !dev->suspended)
380 		p16x_update_port(port);
381 	val = port->count;
382 	mutex_exit(&dev->mutex);
383 
384 	return (val);
385 }
386 
387 static void
388 p16x_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
389 {
390 	p16x_port_t *port = arg;
391 	unsigned mult;
392 
393 	if (port->port_num == P16X_PLAY) {
394 		switch (chan) {
395 		case 0:	/* left front */
396 		case 1:	/* right front */
397 			mult = 0;
398 			break;
399 		case 2:	/* center */
400 		case 3:	/* lfe */
401 			mult = 2;
402 			break;
403 		case 4:	/* left surround */
404 		case 5:	/* right surround */
405 			mult = 1;
406 			break;
407 		}
408 		*offset = (port->buf_frames * 2 * mult) + (chan % 2);
409 		*incr = 2;
410 	} else {
411 		*offset = chan;
412 		*incr = 2;
413 	}
414 }
415 
416 /* private implementation bits */
417 
418 void
419 p16x_update_port(p16x_port_t *port)
420 {
421 	p16x_dev_t	*dev = port->dev;
422 	uint32_t	offset, n;
423 
424 	if (dev->suspended)
425 		return;
426 
427 	if (port->port_num == P16X_PLAY) {
428 		offset = read_reg(dev, CPFA, 0);
429 	} else {
430 		offset = read_reg(dev, CRFA, 0);
431 	}
432 
433 	/* get the offset, and switch to frames */
434 	offset /= (2 * sizeof (uint16_t));
435 
436 	if (offset >= port->offset) {
437 		n = offset - port->offset;
438 	} else {
439 		n = offset + (port->buf_frames - port->offset);
440 	}
441 	port->offset = offset;
442 	port->count += n;
443 }
444 
445 void
446 p16x_start_port(p16x_port_t *port)
447 {
448 	p16x_dev_t	*dev = port->dev;
449 	unsigned int	tmp;
450 
451 	ASSERT(mutex_owned(&dev->mutex));
452 
453 	if (dev->suspended)
454 		return;
455 
456 	if (port->port_num == P16X_REC) {
457 		/* Enable Rec Channel */
458 		tmp = read_reg(dev, SA, 0);
459 		tmp |= 0x100;
460 		write_reg(dev, SA, 0, tmp);
461 		tmp = INL(dev, IE);
462 		tmp |= INTR_REC;
463 		OUTL(dev, tmp, IE);
464 	} else {
465 		/* Enable play channel and go */
466 		tmp = read_reg(dev, SA, 0);
467 		tmp |= 7;
468 		write_reg(dev, SA, 0, tmp);
469 		tmp = INL(dev, IE);
470 		tmp |= INTR_PLAY;
471 		OUTL(dev, tmp, IE);
472 	}
473 }
474 
475 void
476 p16x_stop_port(p16x_port_t *port)
477 {
478 	p16x_dev_t	*dev = port->dev;
479 	unsigned int tmp;
480 
481 
482 	if (dev->suspended)
483 		return;
484 
485 	if (port->port_num == P16X_REC) {
486 		/* Disable rec channel */
487 		tmp = read_reg(dev, SA, 0);
488 		tmp &= ~0x100;
489 		write_reg(dev, SA, 0, tmp);
490 		tmp = INL(dev, IE);
491 		tmp &= ~INTR_REC;
492 		OUTL(dev, tmp, IE);
493 
494 	} else {
495 		/* Disable Play channel */
496 		tmp = read_reg(dev, SA, 0);
497 		tmp &= ~7;
498 		write_reg(dev, SA, 0, tmp);
499 		tmp = INL(dev, IE);
500 		tmp &= ~INTR_PLAY;
501 		OUTL(dev, tmp, IE);
502 	}
503 }
504 
505 int
506 p16x_alloc_port(p16x_dev_t *dev, int num)
507 {
508 	p16x_port_t		*port;
509 	size_t			len;
510 	ddi_dma_cookie_t	cookie;
511 	uint_t			count;
512 	int			dir;
513 	char			*prop;
514 	unsigned		caps;
515 	audio_dev_t		*adev;
516 
517 	adev = dev->adev;
518 	port = kmem_zalloc(sizeof (*port), KM_SLEEP);
519 	dev->port[num] = port;
520 	port->dev = dev;
521 	port->started = B_FALSE;
522 
523 	switch (num) {
524 	case P16X_REC:
525 		prop = "record-interrupts";
526 		port->syncdir = DDI_DMA_SYNC_FORKERNEL;
527 		caps = ENGINE_INPUT_CAP;
528 		dir = DDI_DMA_READ;
529 		port->port_num = P16X_REC;
530 		port->nchan = 2;
531 		break;
532 	case P16X_PLAY:
533 		prop = "play-interrupts";
534 		port->syncdir = DDI_DMA_SYNC_FORDEV;
535 		caps = ENGINE_OUTPUT_CAP;
536 		dir = DDI_DMA_WRITE;
537 		port->port_num = P16X_PLAY;
538 		port->nchan = 6;
539 		break;
540 	default:
541 		return (DDI_FAILURE);
542 	}
543 
544 	/* figure out fragment configuration */
545 	port->intrs = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip,
546 	    DDI_PROP_DONTPASS, prop, P16X_DEF_INTRS);
547 
548 	/* make sure the values are good */
549 	if (port->intrs < P16X_MIN_INTRS) {
550 		audio_dev_warn(adev, "%s too low, %d, reset to %d",
551 		    prop, port->intrs, P16X_MIN_INTRS);
552 		port->intrs = P16X_MIN_INTRS;
553 	} else if (port->intrs > P16X_MAX_INTRS) {
554 		audio_dev_warn(adev, "%s too high, %d, reset to %d",
555 		    prop, port->intrs, P16X_DEF_INTRS);
556 		port->intrs = P16X_DEF_INTRS;
557 	}
558 
559 	/*
560 	 * This needs the very latest Boomer changes.
561 	 */
562 	port->nfrags = 2;
563 	port->fragfr = 48000 / port->intrs;
564 	/*
565 	 * The device operates in pairs of dwords at a time, for
566 	 * performance reasons.	 So make sure that our buffer is
567 	 * arranged as a whole number of these.	 We could probably
568 	 * fine tune by just ensuring that the overall buffer was 128
569 	 * (64 for half and 64 for full), but this is simpler.
570 	 */
571 	port->fragfr = (port->fragfr + 63) & ~(63);
572 	port->fragsz = port->fragfr * port->nchan * 2; /* 16 bit frames */
573 	port->buf_size = port->nfrags * port->fragsz;
574 	port->buf_frames = port->fragfr * port->nfrags;
575 
576 	/* now allocate buffers */
577 	if (ddi_dma_alloc_handle(dev->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
578 	    &port->buf_dmah) != DDI_SUCCESS) {
579 		audio_dev_warn(adev, "failed to allocate BUF handle");
580 		return (DDI_FAILURE);
581 	}
582 
583 	if (ddi_dma_mem_alloc(port->buf_dmah, port->buf_size,
584 	    &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
585 	    &port->buf_kaddr, &len, &port->buf_acch) != DDI_SUCCESS) {
586 		audio_dev_warn(adev, "failed to allocate BUF memory");
587 		return (DDI_FAILURE);
588 	}
589 
590 	if (ddi_dma_addr_bind_handle(port->buf_dmah, NULL, port->buf_kaddr,
591 	    len, DDI_DMA_CONSISTENT | dir, DDI_DMA_SLEEP, NULL, &cookie,
592 	    &count) != DDI_SUCCESS) {
593 		audio_dev_warn(adev, "failed binding BUF DMA handle");
594 		return (DDI_FAILURE);
595 	}
596 	port->buf_paddr = cookie.dmac_address;
597 
598 	port->engine = audio_engine_alloc(&p16x_engine_ops, caps);
599 	if (port->engine == NULL) {
600 		audio_dev_warn(adev, "audio_engine_alloc failed");
601 		return (DDI_FAILURE);
602 	}
603 
604 	audio_engine_set_private(port->engine, port);
605 	audio_dev_add_engine(adev, port->engine);
606 
607 	return (DDI_SUCCESS);
608 }
609 
610 void
611 p16x_destroy(p16x_dev_t *dev)
612 {
613 	if (dev->ih != NULL) {
614 		(void) ddi_intr_disable(dev->ih);
615 		(void) ddi_intr_remove_handler(dev->ih);
616 		(void) ddi_intr_free(dev->ih);
617 		mutex_destroy(&dev->mutex);
618 		mutex_destroy(&dev->low_mutex);
619 	}
620 
621 	if (dev->ksp) {
622 		kstat_delete(dev->ksp);
623 	}
624 
625 	for (int i = 0; i < P16X_NUM_PORT; i++) {
626 		p16x_port_t *port = dev->port[i];
627 		if (!port)
628 			continue;
629 		if (port->engine) {
630 			audio_dev_remove_engine(dev->adev, port->engine);
631 			audio_engine_free(port->engine);
632 		}
633 		if (port->buf_paddr) {
634 			(void) ddi_dma_unbind_handle(port->buf_dmah);
635 		}
636 		if (port->buf_acch) {
637 			ddi_dma_mem_free(&port->buf_acch);
638 		}
639 		if (port->buf_dmah) {
640 			ddi_dma_free_handle(&port->buf_dmah);
641 		}
642 		kmem_free(port, sizeof (*port));
643 	}
644 
645 	if (dev->ac97 != NULL) {
646 		ac97_free(dev->ac97);
647 	}
648 	if (dev->adev != NULL) {
649 		audio_dev_free(dev->adev);
650 	}
651 	if (dev->regsh != NULL) {
652 		ddi_regs_map_free(&dev->regsh);
653 	}
654 	if (dev->pcih != NULL) {
655 		pci_config_teardown(&dev->pcih);
656 	}
657 	kmem_free(dev, sizeof (*dev));
658 }
659 
660 void
661 p16x_hwinit(p16x_dev_t *dev)
662 {
663 	p16x_port_t		*port;
664 	uint32_t		paddr;
665 	uint32_t		chunksz;
666 	int i;
667 
668 	for (i = 0; i < 3; i++) {
669 		write_reg(dev, PTBA, i, 0);
670 		write_reg(dev, PTBS, i, 0);
671 		write_reg(dev, PTCA, i, 0);
672 		write_reg(dev, PFEA, i, 0);
673 		write_reg(dev, CPFA, i, 0);
674 		write_reg(dev, CPCAV, i, 0);
675 		write_reg(dev, CRFA, i, 0);
676 		write_reg(dev, CRCAV, i, 0);
677 	}
678 	write_reg(dev, SCS0, 0, 0x02108504);
679 	write_reg(dev, SCS1, 0, 0x02108504);
680 	write_reg(dev, SCS2, 0, 0x02108504);
681 
682 	/* set the spdif/analog combo jack to analog out */
683 	write_reg(dev, SPC, 0, 0x00000700);
684 	write_reg(dev, EA_aux, 0, 0x0001003f);
685 
686 	port = dev->port[P16X_REC];
687 	/* Set physical address of the DMA buffer */
688 	write_reg(dev, RFBA, 0, port->buf_paddr);
689 	write_reg(dev, RFBS, 0, (port->buf_size) << 16);
690 
691 	/* Set physical address of the DMA buffer */
692 	port = dev->port[P16X_PLAY];
693 	paddr = port->buf_paddr;
694 	chunksz = port->buf_frames * 4;
695 	write_reg(dev, PFBA, 0, paddr);
696 	write_reg(dev, PFBS, 0, chunksz << 16);
697 	paddr += chunksz;
698 	write_reg(dev, PFBA, 1, paddr);
699 	write_reg(dev, PFBS, 1, chunksz << 16);
700 	paddr += chunksz;
701 	write_reg(dev, PFBA, 2, paddr);
702 	write_reg(dev, PFBS, 2, chunksz << 16);
703 
704 	OUTL(dev, 0x1080, GPIO);	/* GPIO */
705 	/* Clear any pending interrupts */
706 	OUTL(dev, INTR_ALL, IP);
707 	OUTL(dev, 0, IE);
708 	OUTL(dev, 0x9, HC);	/* Enable audio */
709 }
710 
711 int
712 p16x_setup_intrs(p16x_dev_t *dev)
713 {
714 	uint_t			ipri;
715 	int			actual;
716 	int			rv;
717 	ddi_intr_handle_t	ih[1];
718 
719 	rv = ddi_intr_alloc(dev->dip, ih, DDI_INTR_TYPE_FIXED,
720 	    0, 1, &actual, DDI_INTR_ALLOC_STRICT);
721 	if ((rv != DDI_SUCCESS) || (actual != 1)) {
722 		audio_dev_warn(dev->adev,
723 		    "Can't alloc interrupt handle (rv %d actual %d)",
724 		    rv, actual);
725 		return (DDI_FAILURE);
726 	}
727 
728 	if (ddi_intr_get_pri(ih[0], &ipri) != DDI_SUCCESS) {
729 		audio_dev_warn(dev->adev, "Can't get interrupt priority");
730 		(void) ddi_intr_free(ih[0]);
731 		return (DDI_FAILURE);
732 	}
733 
734 	if (ddi_intr_add_handler(ih[0], p16x_intr, dev, NULL) !=
735 	    DDI_SUCCESS) {
736 		audio_dev_warn(dev->adev, "Can't add interrupt handler");
737 		(void) ddi_intr_free(ih[0]);
738 		return (DDI_FAILURE);
739 	}
740 
741 	dev->ih = ih[0];
742 	mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
743 	mutex_init(&dev->low_mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
744 	return (DDI_SUCCESS);
745 }
746 
747 int
748 p16x_attach(dev_info_t *dip)
749 {
750 	uint16_t	vendor, device;
751 	p16x_dev_t	*dev;
752 	ddi_acc_handle_t pcih;
753 
754 	dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
755 	dev->dip = dip;
756 	ddi_set_driver_private(dip, dev);
757 
758 	/* we don't support high level interrupts in the driver */
759 	if (ddi_intr_hilevel(dip, 0) != 0) {
760 		cmn_err(CE_WARN,
761 		    "!%s%d: unsupported high level interrupt",
762 		    ddi_driver_name(dip), ddi_get_instance(dip));
763 		return (DDI_FAILURE);
764 	}
765 
766 	if (ddi_get_iblock_cookie(dip, 0, &dev->iblock) != DDI_SUCCESS) {
767 		cmn_err(CE_WARN,
768 		    "!%s%d: cannot get iblock cookie",
769 		    ddi_driver_name(dip), ddi_get_instance(dip));
770 		kmem_free(dev, sizeof (*dev));
771 		return (DDI_FAILURE);
772 	}
773 	mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, dev->iblock);
774 	mutex_init(&dev->low_mutex, NULL, MUTEX_DRIVER, dev->iblock);
775 
776 	if ((dev->adev = audio_dev_alloc(dip, 0)) == NULL) {
777 		cmn_err(CE_WARN, "audio_dev_alloc failed");
778 		goto error;
779 	}
780 
781 	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
782 		audio_dev_warn(dev->adev, "pci_config_setup failed");
783 		goto error;
784 	}
785 	dev->pcih = pcih;
786 
787 	vendor = pci_config_get16(pcih, PCI_CONF_VENID);
788 	device = pci_config_get16(pcih, PCI_CONF_DEVID);
789 	if (vendor != CREATIVE_VENDOR_ID ||
790 	    device != SB_P16X_ID) {
791 		audio_dev_warn(dev->adev, "Hardware not recognized "
792 		    "(vendor=%x, dev=%x)", vendor, device);
793 		goto error;
794 	}
795 
796 	/* set PCI command register */
797 	pci_config_put16(pcih, PCI_CONF_COMM,
798 	    pci_config_get16(pcih, PCI_CONF_COMM) |
799 	    PCI_COMM_MAE | PCI_COMM_IO);
800 
801 
802 	if ((ddi_regs_map_setup(dip, 1, &dev->base, 0, 0, &dev_attr,
803 	    &dev->regsh)) != DDI_SUCCESS) {
804 		audio_dev_warn(dev->adev, "failed to map registers");
805 		goto error;
806 	}
807 
808 	audio_dev_set_description(dev->adev, "Creative Sound Blaster Live!");
809 	audio_dev_set_version(dev->adev, "SBO200");
810 
811 	if ((p16x_alloc_port(dev, P16X_PLAY) != DDI_SUCCESS) ||
812 	    (p16x_alloc_port(dev, P16X_REC) != DDI_SUCCESS)) {
813 		goto error;
814 	}
815 
816 	p16x_hwinit(dev);
817 
818 	/* set up the interrupt handler */
819 	if (p16x_setup_intrs(dev) != DDI_SUCCESS) {
820 		goto error;
821 	}
822 
823 	/* Enable PCI interrupts */
824 	OUTL(dev, INTR_PCI, IE);
825 
826 	dev->ac97 = ac97_allocate(dev->adev, dip,
827 	    p16x_read_ac97, p16x_write_ac97, dev);
828 	if (dev->ac97 == NULL) {
829 		audio_dev_warn(dev->adev, "failed to allocate ac97 handle");
830 		goto error;
831 	}
832 
833 	ac97_probe_controls(dev->ac97);
834 
835 	/* remove the AC'97 controls we don't want to expose */
836 	for (int i = 0; p16x_remove_ac97[i]; i++) {
837 		ac97_ctrl_t *ctrl;
838 		ctrl = ac97_control_find(dev->ac97, p16x_remove_ac97[i]);
839 		if (ctrl != NULL) {
840 			ac97_control_unregister(ctrl);
841 		}
842 	}
843 
844 	ac97_register_controls(dev->ac97);
845 
846 	/* set up kernel statistics */
847 	if ((dev->ksp = kstat_create(P16X_NAME, ddi_get_instance(dip),
848 	    P16X_NAME, "controller", KSTAT_TYPE_INTR, 1,
849 	    KSTAT_FLAG_PERSISTENT)) != NULL) {
850 		kstat_install(dev->ksp);
851 	}
852 
853 	if (audio_dev_register(dev->adev) != DDI_SUCCESS) {
854 		audio_dev_warn(dev->adev, "unable to register with framework");
855 		goto error;
856 	}
857 
858 	(void) ddi_intr_enable(dev->ih);
859 	ddi_report_dev(dip);
860 
861 	return (DDI_SUCCESS);
862 
863 error:
864 	p16x_destroy(dev);
865 	return (DDI_FAILURE);
866 }
867 
868 int
869 p16x_resume(dev_info_t *dip)
870 {
871 	p16x_dev_t *dev;
872 
873 	dev = ddi_get_driver_private(dip);
874 
875 	p16x_hwinit(dev);
876 
877 	/* allow ac97 operations again */
878 	ac97_resume(dev->ac97);
879 
880 	mutex_enter(&dev->mutex);
881 	dev->suspended = B_FALSE;
882 
883 	for (int i = 0; i < P16X_NUM_PORT; i++) {
884 
885 		p16x_port_t *port = dev->port[i];
886 
887 		if (port->engine != NULL)
888 			audio_engine_reset(port->engine);
889 
890 		/* reset the port */
891 		p16x_init_port(port);
892 
893 		if (port->started) {
894 			p16x_start_port(port);
895 		} else {
896 			p16x_stop_port(port);
897 		}
898 	}
899 	mutex_exit(&dev->mutex);
900 	return (DDI_SUCCESS);
901 }
902 
903 int
904 p16x_detach(p16x_dev_t *dev)
905 {
906 	if (audio_dev_unregister(dev->adev) != DDI_SUCCESS)
907 		return (DDI_FAILURE);
908 
909 	p16x_destroy(dev);
910 	return (DDI_SUCCESS);
911 }
912 
913 int
914 p16x_suspend(p16x_dev_t *dev)
915 {
916 	ac97_suspend(dev->ac97);
917 
918 	mutex_enter(&dev->mutex);
919 	for (int i = 0; i < P16X_NUM_PORT; i++) {
920 
921 		p16x_port_t *port = dev->port[i];
922 		p16x_stop_port(port);
923 	}
924 
925 	write_reg(dev, SA, 0, 0);
926 	OUTL(dev, 0x00, IE);	/* Interrupt disable */
927 	OUTL(dev, 0x01, HC);
928 
929 	dev->suspended = B_TRUE;
930 	mutex_exit(&dev->mutex);
931 	return (DDI_SUCCESS);
932 }
933 
934 static int p16x_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
935 static int p16x_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
936 static int p16x_ddi_quiesce(dev_info_t *);
937 
938 static struct dev_ops p16x_dev_ops = {
939 	DEVO_REV,		/* rev */
940 	0,			/* refcnt */
941 	NULL,			/* getinfo */
942 	nulldev,		/* identify */
943 	nulldev,		/* probe */
944 	p16x_ddi_attach,	/* attach */
945 	p16x_ddi_detach,	/* detach */
946 	nodev,			/* reset */
947 	NULL,			/* cb_ops */
948 	NULL,			/* bus_ops */
949 	NULL,			/* power */
950 	p16x_ddi_quiesce,	/* quiesce */
951 };
952 
953 static struct modldrv p16x_modldrv = {
954 	&mod_driverops,		/* drv_modops */
955 	"Creative P16X Audio",	/* linkinfo */
956 	&p16x_dev_ops,		/* dev_ops */
957 };
958 
959 static struct modlinkage modlinkage = {
960 	MODREV_1,
961 	{ &p16x_modldrv, NULL }
962 };
963 
964 int
965 _init(void)
966 {
967 	int	rv;
968 
969 	audio_init_ops(&p16x_dev_ops, P16X_NAME);
970 	if ((rv = mod_install(&modlinkage)) != 0) {
971 		audio_fini_ops(&p16x_dev_ops);
972 	}
973 	return (rv);
974 }
975 
976 int
977 _fini(void)
978 {
979 	int	rv;
980 
981 	if ((rv = mod_remove(&modlinkage)) == 0) {
982 		audio_fini_ops(&p16x_dev_ops);
983 	}
984 	return (rv);
985 }
986 
987 int
988 _info(struct modinfo *modinfop)
989 {
990 	return (mod_info(&modlinkage, modinfop));
991 }
992 
993 int
994 p16x_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
995 {
996 	switch (cmd) {
997 	case DDI_ATTACH:
998 		return (p16x_attach(dip));
999 
1000 	case DDI_RESUME:
1001 		return (p16x_resume(dip));
1002 
1003 	default:
1004 		return (DDI_FAILURE);
1005 	}
1006 }
1007 
1008 int
1009 p16x_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1010 {
1011 	p16x_dev_t *dev;
1012 
1013 	dev = ddi_get_driver_private(dip);
1014 
1015 	switch (cmd) {
1016 	case DDI_DETACH:
1017 		return (p16x_detach(dev));
1018 
1019 	case DDI_SUSPEND:
1020 		return (p16x_suspend(dev));
1021 
1022 	default:
1023 		return (DDI_FAILURE);
1024 	}
1025 }
1026 
1027 int
1028 p16x_ddi_quiesce(dev_info_t *dip)
1029 {
1030 	p16x_dev_t	*dev;
1031 
1032 	dev = ddi_get_driver_private(dip);
1033 
1034 	for (int i = 0; i < P16X_NUM_PORT; i++) {
1035 
1036 		p16x_port_t *port = dev->port[i];
1037 		p16x_stop_port(port);
1038 	}
1039 
1040 	write_reg(dev, SA, 0, 0);
1041 	OUTL(dev, 0x00, IE);	    /* Interrupt disable */
1042 	OUTL(dev, 0x01, HC);
1043 
1044 	return (DDI_SUCCESS);
1045 }
1046