xref: /illumos-gate/usr/src/uts/common/io/audio/drv/audiovia823x/audiovia823x.c (revision 8fd04b8338ed5093ec2d1e668fa620b7de44c177)
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 VIA8233/8235 AC97 audio controller
29  */
30 /*
31  * This file is part of Open Sound System
32  *
33  * Copyright (C) 4Front Technologies 1996-2008.
34  *
35  * This software is released under CDDL 1.0 source license.
36  * See the COPYING file included in the main directory of this source
37  * distribution for the license terms and conditions.
38  */
39 
40 #include <sys/types.h>
41 #include <sys/modctl.h>
42 #include <sys/kmem.h>
43 #include <sys/conf.h>
44 #include <sys/ddi.h>
45 #include <sys/sunddi.h>
46 #include <sys/pci.h>
47 #include <sys/note.h>
48 #include <sys/audio/audio_driver.h>
49 #include <sys/audio/ac97.h>
50 
51 #include "audiovia823x.h"
52 
53 static struct ddi_device_acc_attr dev_attr = {
54 	DDI_DEVICE_ATTR_V0,
55 	DDI_STRUCTURE_LE_ACC,
56 	DDI_STRICTORDER_ACC
57 };
58 
59 static struct ddi_device_acc_attr buf_attr = {
60 	DDI_DEVICE_ATTR_V0,
61 	DDI_NEVERSWAP_ACC,
62 	DDI_STRICTORDER_ACC
63 };
64 
65 static ddi_dma_attr_t dma_attr_sgd = {
66 	DMA_ATTR_V0,		/* version number */
67 	0x00000000,		/* low DMA address range */
68 	0xffffffff,		/* high DMA address range */
69 	0x0000ffff,		/* DMA counter register */
70 	8,			/* DMA address alignment */
71 	0x3c,			/* DMA burstsizes */
72 	8,			/* min effective DMA size */
73 	0xffffffff,		/* max DMA xfer size */
74 	0x00000fff,		/* segment boundary */
75 	1,			/* s/g length */
76 	8,			/* granularity of device */
77 	0			/* Bus specific DMA flags */
78 };
79 
80 static ddi_dma_attr_t dma_attr_buf = {
81 	DMA_ATTR_V0,		/* version number */
82 	0x00000000,		/* low DMA address range */
83 	0xffffffff,		/* high DMA address range */
84 	0x0001fffe,		/* DMA counter register */
85 	4,			/* DMA address alignment */
86 	0x3c,			/* DMA burstsizes */
87 	4,			/* min effective DMA size */
88 	0x0001ffff,		/* max DMA xfer size */
89 	0x0001ffff,		/* segment boundary */
90 	1,			/* s/g length */
91 	4,			/* granularity of device */
92 	0			/* Bus specific DMA flags */
93 };
94 
95 static int auvia_attach(dev_info_t *);
96 static int auvia_resume(dev_info_t *);
97 static int auvia_detach(auvia_devc_t *);
98 static int auvia_suspend(auvia_devc_t *);
99 
100 static int auvia_open(void *, int, unsigned *, unsigned *, caddr_t *);
101 static void auvia_close(void *);
102 static int auvia_start(void *);
103 static void auvia_stop(void *);
104 static int auvia_format(void *);
105 static int auvia_channels(void *);
106 static int auvia_rate(void *);
107 static uint64_t auvia_count(void *);
108 static void auvia_sync(void *, unsigned);
109 
110 static uint16_t auvia_read_ac97(void *, uint8_t);
111 static void auvia_write_ac97(void *, uint8_t, uint16_t);
112 static int auvia_alloc_port(auvia_devc_t *, int);
113 static void auvia_start_port(auvia_portc_t *);
114 static void auvia_stop_port(auvia_portc_t *);
115 static void auvia_update_port(auvia_portc_t *);
116 static void auvia_reset_input(auvia_portc_t *);
117 static void auvia_reset_output(auvia_portc_t *);
118 static void auvia_destroy(auvia_devc_t *);
119 static int auvia_setup_intrs(auvia_devc_t *);
120 static void auvia_hwinit(auvia_devc_t *);
121 static uint_t auvia_intr(caddr_t, caddr_t);
122 
123 static audio_engine_ops_t auvia_engine_ops = {
124 	AUDIO_ENGINE_VERSION,
125 	auvia_open,
126 	auvia_close,
127 	auvia_start,
128 	auvia_stop,
129 	auvia_count,
130 	auvia_format,
131 	auvia_channels,
132 	auvia_rate,
133 	auvia_sync,
134 	NULL,
135 	NULL,
136 	NULL,
137 };
138 
139 static uint16_t
140 auvia_read_ac97(void *arg, uint8_t index)
141 {
142 	auvia_devc_t *devc = arg;
143 	uint32_t val = 0;
144 	int i;
145 
146 	mutex_enter(&devc->low_mutex);
147 
148 	val = ((uint32_t)index << 16) | CODEC_RD;
149 	OUTL(devc, devc->base + REG_CODEC, val);
150 	drv_usecwait(100);
151 
152 	/* Check AC CODEC access time out */
153 	for (i = 0; i < CODEC_TIMEOUT_COUNT; i++) {
154 
155 		/* if send command over, break */
156 		if (INL(devc, devc->base + REG_CODEC) & CODEC_STA_VALID)
157 			break;
158 		drv_usecwait(50);
159 	}
160 
161 	if (i == CODEC_TIMEOUT_COUNT) {
162 		goto failed;
163 	}
164 
165 	/* Check if Index still ours? If yes, return data, else return FAIL */
166 	val = INL(devc, devc->base + REG_CODEC);
167 	OUTB(devc, devc->base + REG_CODEC + 3, 0x02);
168 	if (((val & CODEC_INDEX) >> 16) == index) {
169 		mutex_exit(&devc->low_mutex);
170 		return (val & CODEC_DATA);
171 	}
172 
173 failed:
174 	mutex_exit(&devc->low_mutex);
175 	return (0xffff);
176 }
177 
178 static void
179 auvia_write_ac97(void *arg, uint8_t index, uint16_t data)
180 {
181 	auvia_devc_t *devc = arg;
182 	uint32_t val = 0;
183 	int i = 0;
184 
185 	mutex_enter(&devc->low_mutex);
186 
187 	val = ((uint32_t)index << 16) | data | CODEC_WR;
188 	OUTL(devc, devc->base + REG_CODEC, val);
189 	drv_usecwait(100);
190 
191 	/* Check AC CODEC access time out */
192 	for (i = 0; i < CODEC_TIMEOUT_COUNT; i++) {
193 		/* if send command over, break */
194 		if (!(INL(devc, devc->base + REG_CODEC) & CODEC_IN_CMD))
195 			break;
196 		drv_usecwait(50);
197 	}
198 
199 	mutex_exit(&devc->low_mutex);
200 }
201 
202 static uint_t
203 auvia_intr(caddr_t argp, caddr_t nocare)
204 {
205 	auvia_devc_t	*devc = (void *)argp;
206 	auvia_portc_t	*portc;
207 	uint8_t		status;
208 	unsigned	intrs = 0;
209 	boolean_t	claimed = B_FALSE;
210 
211 	_NOTE(ARGUNUSED(nocare));
212 
213 	mutex_enter(&devc->mutex);
214 	if (devc->suspended) {
215 		mutex_exit(&devc->mutex);
216 		return (DDI_INTR_UNCLAIMED);
217 	}
218 
219 	for (int i = 0; i < AUVIA_NUM_PORTC; i++) {
220 
221 		portc = devc->portc[i];
222 
223 		status = INB(devc, portc->base + OFF_STATUS);
224 		if ((status & STATUS_INTR) == 0) {
225 			/* clear any other interrupts */
226 			continue;
227 		}
228 
229 		/*
230 		 * NB: The old code did some goofy things to update
231 		 * the last valid SGD.  However, since we don't ever
232 		 * reach the last valid SGD (because we loop first), I
233 		 * don't believe we need to do that.  It would appear
234 		 * that NetBSD does the same.
235 		 */
236 		/* port interrupt */
237 		if (portc->started) {
238 			intrs |= (1U << i);
239 		}
240 		/* let the chip know we are acking the interrupt */
241 		OUTB(devc, portc->base + OFF_STATUS, status);
242 
243 		claimed = B_TRUE;
244 	}
245 
246 	mutex_exit(&devc->mutex);
247 
248 	if (!claimed) {
249 		return (DDI_INTR_UNCLAIMED);
250 	}
251 
252 	if (intrs & (1U << AUVIA_PLAY_SGD_NUM)) {
253 		audio_engine_consume(devc->portc[AUVIA_PLAY_SGD_NUM]->engine);
254 	}
255 	if (intrs & (1U << AUVIA_REC_SGD_NUM)) {
256 		audio_engine_produce(devc->portc[AUVIA_REC_SGD_NUM]->engine);
257 	}
258 	if (devc->ksp) {
259 		AUVIA_KIOP(devc)->intrs[KSTAT_INTR_HARD]++;
260 	}
261 
262 	return (DDI_INTR_CLAIMED);
263 }
264 
265 /*
266  * Audio routines
267  */
268 
269 int
270 auvia_open(void *arg, int flag,
271     unsigned *fragfrp, unsigned *nfragsp, caddr_t *bufp)
272 {
273 	auvia_portc_t	 *portc = arg;
274 	auvia_devc_t	 *devc = portc->devc;
275 
276 	_NOTE(ARGUNUSED(flag));
277 
278 	portc->started = B_FALSE;
279 	portc->count = 0;
280 	*fragfrp = portc->fragfr;
281 	*nfragsp = AUVIA_NUM_SGD;
282 	*bufp = portc->buf_kaddr;
283 
284 	mutex_enter(&devc->mutex);
285 	portc->reset(portc);
286 	mutex_exit(&devc->mutex);
287 
288 	return (0);
289 }
290 
291 void
292 auvia_close(void *arg)
293 {
294 	auvia_portc_t	 *portc = arg;
295 	auvia_devc_t	 *devc = portc->devc;
296 
297 	mutex_enter(&devc->mutex);
298 	auvia_stop_port(portc);
299 	portc->started = B_FALSE;
300 	mutex_exit(&devc->mutex);
301 }
302 
303 int
304 auvia_start(void *arg)
305 {
306 	auvia_portc_t	*portc = arg;
307 	auvia_devc_t	*devc = portc->devc;
308 
309 	mutex_enter(&devc->mutex);
310 	if (!portc->started) {
311 		auvia_start_port(portc);
312 		portc->started = B_TRUE;
313 	}
314 	mutex_exit(&devc->mutex);
315 	return (0);
316 }
317 
318 void
319 auvia_stop(void *arg)
320 {
321 	auvia_portc_t	*portc = arg;
322 	auvia_devc_t	*devc = portc->devc;
323 
324 	mutex_enter(&devc->mutex);
325 	if (portc->started) {
326 		auvia_stop_port(portc);
327 		portc->started = B_FALSE;
328 	}
329 	mutex_exit(&devc->mutex);
330 }
331 
332 int
333 auvia_format(void *arg)
334 {
335 	_NOTE(ARGUNUSED(arg));
336 
337 	return (AUDIO_FORMAT_S16_LE);
338 }
339 
340 int
341 auvia_channels(void *arg)
342 {
343 	auvia_portc_t	*portc = arg;
344 
345 	return (portc->nchan);
346 }
347 
348 int
349 auvia_rate(void *arg)
350 {
351 	_NOTE(ARGUNUSED(arg));
352 
353 	return (48000);
354 }
355 
356 void
357 auvia_sync(void *arg, unsigned nframes)
358 {
359 	auvia_portc_t *portc = arg;
360 	_NOTE(ARGUNUSED(nframes));
361 
362 	(void) ddi_dma_sync(portc->buf_dmah, 0, 0, portc->syncdir);
363 }
364 
365 uint64_t
366 auvia_count(void *arg)
367 {
368 	auvia_portc_t	*portc = arg;
369 	auvia_devc_t	*devc = portc->devc;
370 	uint64_t	val;
371 
372 	mutex_enter(&devc->mutex);
373 	auvia_update_port(portc);
374 	/*
375 	 * The residual is in bytes.  We have to convert to frames,
376 	 * and then subtract it from the fragment size to get the
377 	 * number of frames processed.  It is somewhat unfortunate thta
378 	 * this (the division) has to happen under the lock.  If we
379 	 * restricted ourself to stereo out, this would be a simple
380 	 * shift.
381 	 */
382 	val = portc->count +
383 	    (portc->fragfr - (portc->resid / (portc->nchan * 2)));
384 	mutex_exit(&devc->mutex);
385 
386 	return (val);
387 }
388 
389 
390 /* private implementation bits */
391 
392 void
393 auvia_start_port(auvia_portc_t *portc)
394 {
395 	auvia_devc_t	*devc = portc->devc;
396 
397 	ASSERT(mutex_owned(&devc->mutex));
398 
399 	if (devc->suspended)
400 		return;
401 
402 	/*
403 	 * Start with autoinit and SGD flag
404 	 * interrupts enabled.
405 	 */
406 	OUTB(devc, portc->base + OFF_CTRL,
407 	    CTRL_START | CTRL_AUTOSTART | CTRL_FLAG);
408 }
409 
410 void
411 auvia_stop_port(auvia_portc_t *portc)
412 {
413 	auvia_devc_t	*devc = portc->devc;
414 
415 	if (devc->suspended)
416 		return;
417 
418 	OUTB(devc, portc->base + OFF_CTRL, CTRL_TERMINATE);
419 }
420 
421 void
422 auvia_update_port(auvia_portc_t *portc)
423 {
424 	auvia_devc_t	*devc = portc->devc;
425 	uint32_t	frag;
426 	uint32_t	n;
427 
428 	ASSERT(mutex_owned(&devc->mutex));
429 	if (devc->suspended) {
430 		portc->cur_frag = 0;
431 		portc->resid = portc->fragsz;
432 		n = 0;
433 	} else {
434 		frag = INL(devc, portc->base + OFF_COUNT);
435 		portc->resid = (frag & 0xffffff);
436 		frag >>= 24;
437 		frag &= 0xff;
438 
439 		if (frag >= portc->cur_frag) {
440 			n = frag - portc->cur_frag;
441 		} else {
442 			n = frag + AUVIA_NUM_SGD - portc->cur_frag;
443 		}
444 		portc->count += (n * portc->fragfr);
445 		portc->cur_frag = frag;
446 	}
447 }
448 
449 void
450 auvia_reset_output(auvia_portc_t *portc)
451 {
452 	auvia_devc_t	*devc = portc->devc;
453 	uint32_t	cmap;
454 
455 	portc->cur_frag = 0;
456 	portc->resid = portc->fragsz;
457 
458 	if (devc->suspended)
459 		return;
460 
461 	OUTB(devc, portc->base + OFF_CTRL, CTRL_TERMINATE);	/* Stop */
462 	OUTL(devc, portc->base + OFF_DMA, portc->sgd_paddr);
463 
464 	OUTB(devc, portc->base + OFF_PLAYFMT,
465 	    PLAYFMT_16BIT | (portc->nchan << 4));
466 
467 	/* Select channel assignment - not valid for 8233A */
468 	if (devc->chip_type != CHIP_8233A) {
469 		/*
470 		 * Undocumented slot mapping table:
471 		 *
472 		 * slot 3 = 1 (left)
473 		 * slot 4 = 2 (right)
474 		 * slot 6 = 5 (center)
475 		 * slot 9 = 6 (lfe)
476 		 * slot 7 = 3 (left rear)
477 		 * slot 8 = 4 (right rear)
478 		 */
479 		switch (portc->nchan) {
480 		case 1:
481 			cmap = (1 << 0) | (1 << 4);
482 			break;
483 		case 2:
484 			cmap = (1 << 0) | (2 << 4);
485 			break;
486 		case 4:
487 			cmap = (1 << 0) | (2 << 4) | (3 << 8) | (4 << 12);
488 			break;
489 		case 6:
490 			cmap = (1 << 0) | (2 << 4) |
491 			    (5 << 8) | (6 << 12) | (3 << 16) | (4 << 20);
492 			break;
493 		default:
494 			cmap = 0;
495 			break;
496 		}
497 		OUTL(devc, portc->base + OFF_CHANNELS, cmap | 0xFF000000U);
498 	}
499 }
500 
501 static void
502 auvia_reset_input(auvia_portc_t *portc)
503 {
504 	auvia_devc_t	*devc = portc->devc;
505 	uint32_t	fmt;
506 
507 	portc->cur_frag = 0;
508 	portc->resid = portc->fragsz;
509 
510 	if (devc->suspended)
511 		return;
512 
513 	OUTB(devc, portc->base + OFF_CTRL, CTRL_TERMINATE);	/* Stop */
514 	OUTL(devc, portc->base + OFF_DMA, portc->sgd_paddr);
515 
516 	fmt = RECFMT_STEREO | RECFMT_16BIT;
517 
518 	if (devc->chip_type != CHIP_8233A) {
519 		fmt |= RECFMT_48K;
520 	}
521 	fmt |= (0xffU << 24);
522 	OUTB(devc, portc->base + OFF_RECFIFO, RECFIFO_ENABLE);
523 	OUTL(devc, portc->base + OFF_RECFMT, fmt);
524 }
525 
526 int
527 auvia_alloc_port(auvia_devc_t *devc, int num)
528 {
529 	auvia_portc_t		*portc;
530 	size_t			len;
531 	ddi_dma_cookie_t	cookie;
532 	uint_t			count;
533 	int			dir;
534 	char			*prop;
535 	unsigned		caps;
536 	audio_dev_t		*adev;
537 	uint32_t		*desc;
538 	uint32_t		paddr;
539 
540 	adev = devc->adev;
541 	portc = kmem_zalloc(sizeof (*portc), KM_SLEEP);
542 	devc->portc[num] = portc;
543 	portc->devc = devc;
544 	portc->started = B_FALSE;
545 
546 	switch (num) {
547 	case AUVIA_REC_SGD_NUM:
548 		prop = "record-interrupts";
549 		portc->base = devc->base + REG_RECBASE;
550 		portc->syncdir = DDI_DMA_SYNC_FORKERNEL;
551 		portc->nchan = 2;
552 		portc->reset = auvia_reset_input;
553 		caps = ENGINE_INPUT_CAP;
554 		dir = DDI_DMA_READ;
555 		break;
556 	case AUVIA_PLAY_SGD_NUM:
557 		prop = "play-interrupts";
558 		portc->base = devc->base + REG_PLAYBASE;
559 		portc->syncdir = DDI_DMA_SYNC_FORDEV;
560 		portc->nchan = 6;
561 		portc->reset = auvia_reset_output;
562 		caps = ENGINE_OUTPUT_CAP;
563 		dir = DDI_DMA_WRITE;
564 		break;
565 	default:
566 		return (DDI_FAILURE);
567 	}
568 
569 	/* make sure port is shut down */
570 	OUTB(portc->devc, portc->base + OFF_CTRL, CTRL_TERMINATE);
571 
572 	/* figure out fragment configuration */
573 	portc->intrs = ddi_prop_get_int(DDI_DEV_T_ANY, devc->dip,
574 	    DDI_PROP_DONTPASS, prop, AUVIA_INTRS);
575 
576 	/* make sure the values are good */
577 	if (portc->intrs < AUVIA_MIN_INTRS) {
578 		audio_dev_warn(adev, "%s too low, %d, reset to %d",
579 		    prop, portc->intrs, AUVIA_INTRS);
580 		portc->intrs = AUVIA_INTRS;
581 	} else if (portc->intrs > AUVIA_MAX_INTRS) {
582 		audio_dev_warn(adev, "%s too high, %d, reset to %d",
583 		    prop, portc->intrs, AUVIA_INTRS);
584 		portc->intrs = AUVIA_INTRS;
585 	}
586 
587 	portc->fragfr = 48000 / portc->intrs;
588 	portc->fragsz = portc->fragfr * portc->nchan * 2;
589 	portc->buf_size = portc->fragsz * AUVIA_NUM_SGD;
590 
591 	/* first allocate up space for SGD list */
592 	if (ddi_dma_alloc_handle(devc->dip, &dma_attr_sgd,
593 	    DDI_DMA_SLEEP, NULL, &portc->sgd_dmah) != DDI_SUCCESS) {
594 		audio_dev_warn(adev, "failed to allocate SGD handle");
595 		return (DDI_FAILURE);
596 	}
597 
598 	if (ddi_dma_mem_alloc(portc->sgd_dmah,
599 	    AUVIA_NUM_SGD * 2 * sizeof (uint32_t), &dev_attr,
600 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &portc->sgd_kaddr,
601 	    &len, &portc->sgd_acch) != DDI_SUCCESS) {
602 		audio_dev_warn(adev, "failed to allocate SGD memory");
603 		return (DDI_FAILURE);
604 	}
605 
606 	if (ddi_dma_addr_bind_handle(portc->sgd_dmah, NULL,
607 	    portc->sgd_kaddr, len, DDI_DMA_CONSISTENT | DDI_DMA_WRITE,
608 	    DDI_DMA_SLEEP, NULL, &cookie, &count) != DDI_SUCCESS) {
609 		audio_dev_warn(adev, "failed binding SGD DMA handle");
610 		return (DDI_FAILURE);
611 	}
612 	portc->sgd_paddr = cookie.dmac_address;
613 
614 	/* now buffers */
615 	if (ddi_dma_alloc_handle(devc->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
616 	    &portc->buf_dmah) != DDI_SUCCESS) {
617 		audio_dev_warn(adev, "failed to allocate BUF handle");
618 		return (DDI_FAILURE);
619 	}
620 
621 	if (ddi_dma_mem_alloc(portc->buf_dmah, portc->buf_size,
622 	    &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
623 	    &portc->buf_kaddr, &len, &portc->buf_acch) != DDI_SUCCESS) {
624 		audio_dev_warn(adev, "failed to allocate BUF memory");
625 		return (DDI_FAILURE);
626 	}
627 
628 	if (ddi_dma_addr_bind_handle(portc->buf_dmah, NULL, portc->buf_kaddr,
629 	    len, DDI_DMA_CONSISTENT | dir, DDI_DMA_SLEEP, NULL, &cookie,
630 	    &count) != DDI_SUCCESS) {
631 		audio_dev_warn(adev, "failed binding BUF DMA handle");
632 		return (DDI_FAILURE);
633 	}
634 	portc->buf_paddr = cookie.dmac_address;
635 
636 	/* now wire descriptors up */
637 	desc = (void *)portc->sgd_kaddr;
638 	paddr = portc->buf_paddr;
639 	for (int i = 0; i < AUVIA_NUM_SGD; i++) {
640 		uint32_t	flags;
641 
642 		flags = AUVIA_SGD_FLAG | portc->fragsz;
643 
644 		if (i == (AUVIA_NUM_SGD - 1)) {
645 			flags |= AUVIA_SGD_EOL;
646 		}
647 		ddi_put32(portc->sgd_acch, desc++, paddr);
648 		ddi_put32(portc->sgd_acch, desc++, flags);
649 		paddr += portc->fragsz;
650 	}
651 
652 	(void) ddi_dma_sync(portc->sgd_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
653 
654 	portc->engine = audio_engine_alloc(&auvia_engine_ops, caps);
655 	if (portc->engine == NULL) {
656 		audio_dev_warn(adev, "audio_engine_alloc failed");
657 		return (DDI_FAILURE);
658 	}
659 
660 	audio_engine_set_private(portc->engine, portc);
661 	audio_dev_add_engine(adev, portc->engine);
662 
663 	return (DDI_SUCCESS);
664 }
665 
666 int
667 auvia_setup_intrs(auvia_devc_t *devc)
668 {
669 	uint_t			ipri;
670 	int			actual;
671 	int			rv;
672 	ddi_intr_handle_t	ih[1];
673 
674 	rv = ddi_intr_alloc(devc->dip, ih, DDI_INTR_TYPE_FIXED,
675 	    0, 1, &actual, DDI_INTR_ALLOC_STRICT);
676 	if ((rv != DDI_SUCCESS) || (actual != 1)) {
677 		audio_dev_warn(devc->adev,
678 		    "Can't alloc interrupt handle (rv %d actual %d)",
679 		    rv, actual);
680 		return (DDI_FAILURE);
681 	}
682 
683 	if (ddi_intr_get_pri(ih[0], &ipri) != DDI_SUCCESS) {
684 		audio_dev_warn(devc->adev, "Can't get interrupt priority");
685 		(void) ddi_intr_free(ih[0]);
686 		return (DDI_FAILURE);
687 	}
688 
689 	if (ddi_intr_add_handler(ih[0], auvia_intr, devc, NULL) !=
690 	    DDI_SUCCESS) {
691 		audio_dev_warn(devc->adev, "Can't add interrupt handler");
692 		(void) ddi_intr_free(ih[0]);
693 		return (DDI_FAILURE);
694 	}
695 
696 	devc->ih = ih[0];
697 	mutex_init(&devc->mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
698 	mutex_init(&devc->low_mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
699 	return (DDI_SUCCESS);
700 }
701 
702 void
703 auvia_destroy(auvia_devc_t *devc)
704 {
705 	if (devc->ih != NULL) {
706 		(void) ddi_intr_disable(devc->ih);
707 		(void) ddi_intr_remove_handler(devc->ih);
708 		(void) ddi_intr_free(devc->ih);
709 		mutex_destroy(&devc->mutex);
710 		mutex_destroy(&devc->low_mutex);
711 	}
712 
713 	if (devc->ksp) {
714 		kstat_delete(devc->ksp);
715 	}
716 
717 	for (int i = 0; i < AUVIA_NUM_PORTC; i++) {
718 		auvia_portc_t *portc = devc->portc[i];
719 		if (!portc)
720 			continue;
721 		if (portc->engine) {
722 			audio_dev_remove_engine(devc->adev, portc->engine);
723 			audio_engine_free(portc->engine);
724 		}
725 		if (portc->sgd_paddr) {
726 			(void) ddi_dma_unbind_handle(portc->sgd_dmah);
727 		}
728 		if (portc->sgd_acch) {
729 			ddi_dma_mem_free(&portc->sgd_acch);
730 		}
731 		if (portc->sgd_dmah) {
732 			ddi_dma_free_handle(&portc->sgd_dmah);
733 		}
734 		if (portc->buf_paddr) {
735 			(void) ddi_dma_unbind_handle(portc->buf_dmah);
736 		}
737 		if (portc->buf_acch) {
738 			ddi_dma_mem_free(&portc->buf_acch);
739 		}
740 		if (portc->buf_dmah) {
741 			ddi_dma_free_handle(&portc->buf_dmah);
742 		}
743 		kmem_free(portc, sizeof (*portc));
744 	}
745 
746 	if (devc->ac97 != NULL) {
747 		ac97_free(devc->ac97);
748 	}
749 	if (devc->adev != NULL) {
750 		audio_dev_free(devc->adev);
751 	}
752 	if (devc->regsh != NULL) {
753 		ddi_regs_map_free(&devc->regsh);
754 	}
755 	if (devc->pcih != NULL) {
756 		pci_config_teardown(&devc->pcih);
757 	}
758 	kmem_free(devc, sizeof (*devc));
759 }
760 
761 void
762 auvia_hwinit(auvia_devc_t *devc)
763 {
764 	ddi_acc_handle_t	pcih = devc->pcih;
765 	uint32_t		val;
766 
767 	val = pci_config_get32(pcih, AUVIA_PCICFG);
768 	/* we want to disable all legacy */
769 	val &= ~AUVIA_PCICFG_LEGACY;
770 	val &= ~(AUVIA_PCICFG_FMEN | AUVIA_PCICFG_SBEN);
771 
772 	/* enable AC'97 link and clear the reset bit */
773 	val |= (AUVIA_PCICFG_ACLINKEN | AUVIA_PCICFG_NRST);
774 	/* disable SRC (we won't use it) */
775 	val &= ~AUVIA_PCICFG_SRCEN;
776 	/* enable the SGD engines */
777 	val |= AUVIA_PCICFG_SGDEN;
778 
779 	pci_config_put32(pcih, AUVIA_PCICFG, val);
780 
781 	drv_usecwait(10);
782 }
783 
784 int
785 auvia_attach(dev_info_t *dip)
786 {
787 	uint8_t 	pci_revision;
788 	uint16_t	pci_command, vendor, device;
789 	auvia_devc_t	*devc;
790 	ddi_acc_handle_t pcih;
791 	const char	*version;
792 
793 	devc = kmem_zalloc(sizeof (*devc), KM_SLEEP);
794 	devc->dip = dip;
795 	ddi_set_driver_private(dip, devc);
796 
797 	if ((devc->adev = audio_dev_alloc(dip, 0)) == NULL) {
798 		cmn_err(CE_WARN, "audio_dev_alloc failed");
799 		goto error;
800 	}
801 
802 	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
803 		audio_dev_warn(devc->adev, "pci_config_setup failed");
804 		goto error;
805 	}
806 	devc->pcih = pcih;
807 
808 	vendor = pci_config_get16(pcih, PCI_CONF_VENID);
809 	device = pci_config_get16(pcih, PCI_CONF_DEVID);
810 	if ((vendor != VIA_VENDOR_ID) || (device != VIA_8233_ID &&
811 	    device != VIA_8233A_ID)) {
812 		audio_dev_warn(devc->adev, "Hardware not recognized "
813 		    "(vendor=%x, dev=%x)", vendor, device);
814 		goto error;
815 	}
816 
817 	devc->chip_type = CHIP_8233;
818 	devc->chip_name = "VIA VT8233";
819 	version = "8233";
820 
821 	pci_revision = pci_config_get8(pcih, PCI_CONF_REVID);
822 
823 	if (pci_revision == 0x50) {
824 		devc->chip_name = "VIA VT8235";
825 		version = "8235";
826 	}
827 
828 	if (pci_revision == 0x60) {
829 		devc->chip_name = "VIA VT8237";
830 		version = "8237";
831 	}
832 
833 	if ((device == VIA_8233A_ID) ||
834 	    (device == VIA_8233_ID && pci_revision == 0x40)) {
835 		devc->chip_type = CHIP_8233A;
836 		devc->chip_name = "VIA VT8233A";
837 		version = "8233A";
838 	}
839 	audio_dev_set_description(devc->adev, devc->chip_name);
840 	audio_dev_set_version(devc->adev, version);
841 
842 	pci_command = pci_config_get16(pcih, PCI_CONF_COMM);
843 	pci_command |= PCI_COMM_ME | PCI_COMM_IO | PCI_COMM_MAE;
844 	pci_config_put16(pcih, PCI_CONF_COMM, pci_command);
845 
846 	if ((ddi_regs_map_setup(dip, 1, &devc->base, 0, 0, &dev_attr,
847 	    &devc->regsh)) != DDI_SUCCESS) {
848 		audio_dev_warn(devc->adev, "failed to map registers");
849 		goto error;
850 	}
851 
852 	auvia_hwinit(devc);
853 
854 	if ((auvia_alloc_port(devc, AUVIA_PLAY_SGD_NUM) != DDI_SUCCESS) ||
855 	    (auvia_alloc_port(devc, AUVIA_REC_SGD_NUM) != DDI_SUCCESS)) {
856 		goto error;
857 	}
858 
859 	if (auvia_setup_intrs(devc) != DDI_SUCCESS) {
860 		goto error;
861 	}
862 
863 	devc->ac97 = ac97_alloc(dip, auvia_read_ac97, auvia_write_ac97, devc);
864 	if (devc->ac97 == NULL) {
865 		audio_dev_warn(devc->adev, "failed to allocate ac97 handle");
866 		goto error;
867 	}
868 
869 	if (ac97_init(devc->ac97, devc->adev) != DDI_SUCCESS) {
870 		audio_dev_warn(devc->adev, "failed to init ac97");
871 		goto error;
872 	}
873 
874 	/* set up kernel statistics */
875 	if ((devc->ksp = kstat_create(AUVIA_NAME, ddi_get_instance(dip),
876 	    AUVIA_NAME, "controller", KSTAT_TYPE_INTR, 1,
877 	    KSTAT_FLAG_PERSISTENT)) != NULL) {
878 		kstat_install(devc->ksp);
879 	}
880 
881 	if (audio_dev_register(devc->adev) != DDI_SUCCESS) {
882 		audio_dev_warn(devc->adev, "unable to register with framework");
883 		goto error;
884 	}
885 
886 	(void) ddi_intr_enable(devc->ih);
887 	ddi_report_dev(dip);
888 
889 	return (DDI_SUCCESS);
890 
891 error:
892 	auvia_destroy(devc);
893 	return (DDI_FAILURE);
894 }
895 
896 int
897 auvia_resume(dev_info_t *dip)
898 {
899 	auvia_devc_t *devc;
900 
901 	devc = ddi_get_driver_private(dip);
902 
903 	auvia_hwinit(devc);
904 
905 	/* allow ac97 operations again */
906 	ac97_resume(devc->ac97);
907 
908 	mutex_enter(&devc->mutex);
909 	devc->suspended = B_TRUE;
910 	for (int i = 0; i < AUVIA_NUM_PORTC; i++) {
911 
912 		auvia_portc_t *portc = devc->portc[i];
913 
914 		if (portc->engine != NULL)
915 			audio_engine_reset(portc->engine);
916 
917 		/* reset the port */
918 		portc->reset(portc);
919 
920 		if (portc->started) {
921 			auvia_start_port(portc);
922 		} else {
923 			auvia_stop_port(portc);
924 		}
925 	}
926 	mutex_exit(&devc->mutex);
927 	return (DDI_SUCCESS);
928 }
929 
930 
931 int
932 auvia_detach(auvia_devc_t *devc)
933 {
934 	if (audio_dev_unregister(devc->adev) != DDI_SUCCESS)
935 		return (DDI_FAILURE);
936 
937 	auvia_destroy(devc);
938 	return (DDI_SUCCESS);
939 }
940 
941 int
942 auvia_suspend(auvia_devc_t *devc)
943 {
944 	ac97_suspend(devc->ac97);
945 
946 	mutex_enter(&devc->mutex);
947 	for (int i = 0; i < AUVIA_NUM_PORTC; i++) {
948 
949 		auvia_portc_t *portc = devc->portc[i];
950 		auvia_stop_port(portc);
951 	}
952 	devc->suspended = B_TRUE;
953 	mutex_exit(&devc->mutex);
954 	return (DDI_SUCCESS);
955 }
956 
957 static int auvia_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
958 static int auvia_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
959 static int auvia_ddi_quiesce(dev_info_t *);
960 
961 static struct dev_ops auvia_dev_ops = {
962 	DEVO_REV,		/* rev */
963 	0,			/* refcnt */
964 	NULL,			/* getinfo */
965 	nulldev,		/* identify */
966 	nulldev,		/* probe */
967 	auvia_ddi_attach,	/* attach */
968 	auvia_ddi_detach,	/* detach */
969 	nodev,			/* reset */
970 	NULL,			/* cb_ops */
971 	NULL,			/* bus_ops */
972 	NULL,			/* power */
973 	auvia_ddi_quiesce,	/* quiesce */
974 };
975 
976 static struct modldrv auvia_modldrv = {
977 	&mod_driverops,			/* drv_modops */
978 	"Via 823x Audio",		/* linkinfo */
979 	&auvia_dev_ops,			/* dev_ops */
980 };
981 
982 static struct modlinkage modlinkage = {
983 	MODREV_1,
984 	{ &auvia_modldrv, NULL }
985 };
986 
987 int
988 _init(void)
989 {
990 	int	rv;
991 
992 	audio_init_ops(&auvia_dev_ops, AUVIA_NAME);
993 	if ((rv = mod_install(&modlinkage)) != 0) {
994 		audio_fini_ops(&auvia_dev_ops);
995 	}
996 	return (rv);
997 }
998 
999 int
1000 _fini(void)
1001 {
1002 	int	rv;
1003 
1004 	if ((rv = mod_remove(&modlinkage)) == 0) {
1005 		audio_fini_ops(&auvia_dev_ops);
1006 	}
1007 	return (rv);
1008 }
1009 
1010 int
1011 _info(struct modinfo *modinfop)
1012 {
1013 	return (mod_info(&modlinkage, modinfop));
1014 }
1015 
1016 int
1017 auvia_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1018 {
1019 	switch (cmd) {
1020 	case DDI_ATTACH:
1021 		return (auvia_attach(dip));
1022 
1023 	case DDI_RESUME:
1024 		return (auvia_resume(dip));
1025 
1026 	default:
1027 		return (DDI_FAILURE);
1028 	}
1029 }
1030 
1031 int
1032 auvia_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1033 {
1034 	auvia_devc_t *devc;
1035 
1036 	devc = ddi_get_driver_private(dip);
1037 
1038 	switch (cmd) {
1039 	case DDI_DETACH:
1040 		return (auvia_detach(devc));
1041 
1042 	case DDI_SUSPEND:
1043 		return (auvia_suspend(devc));
1044 
1045 	default:
1046 		return (DDI_FAILURE);
1047 	}
1048 }
1049 
1050 int
1051 auvia_ddi_quiesce(dev_info_t *dip)
1052 {
1053 	auvia_devc_t	*devc;
1054 
1055 	devc = ddi_get_driver_private(dip);
1056 
1057 	for (int i = 0; i < AUVIA_NUM_PORTC; i++) {
1058 
1059 		auvia_portc_t *portc = devc->portc[i];
1060 		auvia_stop_port(portc);
1061 	}
1062 	return (DDI_SUCCESS);
1063 }
1064