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