xref: /illumos-gate/usr/src/uts/common/io/audio/drv/audiovia823x/audiovia823x.c (revision e0731422366620894c16c1ee6515551c5f00733d)
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 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 *, 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_reset_input(auvia_portc_t *);
114 static void auvia_reset_output(auvia_portc_t *);
115 static void auvia_destroy(auvia_devc_t *);
116 static void auvia_hwinit(auvia_devc_t *);
117 
118 static audio_engine_ops_t auvia_engine_ops = {
119 	AUDIO_ENGINE_VERSION,
120 	auvia_open,
121 	auvia_close,
122 	auvia_start,
123 	auvia_stop,
124 	auvia_count,
125 	auvia_format,
126 	auvia_channels,
127 	auvia_rate,
128 	auvia_sync,
129 	NULL,
130 	NULL,
131 	NULL,
132 };
133 
134 static uint16_t
135 auvia_read_ac97(void *arg, uint8_t index)
136 {
137 	auvia_devc_t *devc = arg;
138 	uint32_t val = 0;
139 	int i;
140 
141 	val = ((uint32_t)index << 16) | CODEC_RD;
142 	OUTL(devc, devc->base + REG_CODEC, val);
143 	drv_usecwait(100);
144 
145 	/* Check AC CODEC access time out */
146 	for (i = 0; i < CODEC_TIMEOUT_COUNT; i++) {
147 
148 		/* if send command over, break */
149 		if (INL(devc, devc->base + REG_CODEC) & CODEC_STA_VALID)
150 			break;
151 		drv_usecwait(50);
152 	}
153 
154 	if (i == CODEC_TIMEOUT_COUNT) {
155 		goto failed;
156 	}
157 
158 	/* Check if Index still ours? If yes, return data, else return FAIL */
159 	val = INL(devc, devc->base + REG_CODEC);
160 	OUTB(devc, devc->base + REG_CODEC + 3, 0x02);
161 	if (((val & CODEC_INDEX) >> 16) == index) {
162 		return (val & CODEC_DATA);
163 	}
164 
165 failed:
166 	return (0xffff);
167 }
168 
169 static void
170 auvia_write_ac97(void *arg, uint8_t index, uint16_t data)
171 {
172 	auvia_devc_t *devc = arg;
173 	uint32_t val = 0;
174 	int i = 0;
175 
176 	val = ((uint32_t)index << 16) | data | CODEC_WR;
177 	OUTL(devc, devc->base + REG_CODEC, val);
178 	drv_usecwait(100);
179 
180 	/* Check AC CODEC access time out */
181 	for (i = 0; i < CODEC_TIMEOUT_COUNT; i++) {
182 		/* if send command over, break */
183 		if (!(INL(devc, devc->base + REG_CODEC) & CODEC_IN_CMD))
184 			break;
185 		drv_usecwait(50);
186 	}
187 
188 }
189 
190 /*
191  * Audio routines
192  */
193 
194 int
195 auvia_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp)
196 {
197 	auvia_portc_t	 *portc = arg;
198 
199 	_NOTE(ARGUNUSED(flag));
200 
201 	portc->count = 0;
202 	*nframesp = portc->nframes;
203 	*bufp = portc->buf_kaddr;
204 
205 	return (0);
206 }
207 
208 void
209 auvia_close(void *arg)
210 {
211 	_NOTE(ARGUNUSED(arg));
212 }
213 
214 int
215 auvia_start(void *arg)
216 {
217 	auvia_portc_t	*portc = arg;
218 	auvia_devc_t	*devc = portc->devc;
219 
220 	portc->reset(portc);
221 	OUTB(devc, portc->base + OFF_CTRL, CTRL_START | CTRL_AUTOSTART);
222 	return (0);
223 }
224 
225 void
226 auvia_stop(void *arg)
227 {
228 	auvia_portc_t	*portc = arg;
229 	auvia_devc_t	*devc = portc->devc;
230 
231 	OUTB(devc, portc->base + OFF_CTRL, CTRL_TERMINATE);
232 }
233 
234 int
235 auvia_format(void *arg)
236 {
237 	_NOTE(ARGUNUSED(arg));
238 
239 	return (AUDIO_FORMAT_S16_LE);
240 }
241 
242 int
243 auvia_channels(void *arg)
244 {
245 	auvia_portc_t	*portc = arg;
246 
247 	return (portc->nchan);
248 }
249 
250 int
251 auvia_rate(void *arg)
252 {
253 	_NOTE(ARGUNUSED(arg));
254 
255 	return (48000);
256 }
257 
258 void
259 auvia_sync(void *arg, unsigned nframes)
260 {
261 	auvia_portc_t *portc = arg;
262 	_NOTE(ARGUNUSED(nframes));
263 
264 	(void) ddi_dma_sync(portc->buf_dmah, 0, 0, portc->syncdir);
265 }
266 
267 uint64_t
268 auvia_count(void *arg)
269 {
270 	auvia_portc_t	*portc = arg;
271 	auvia_devc_t	*devc = portc->devc;
272 	uint32_t	pos;
273 	uint32_t	n;
274 
275 	pos = INL(devc, portc->base + OFF_COUNT);
276 	pos &= 0xffffff;
277 	pos /= (sizeof (int16_t) * portc->nchan);
278 
279 	if (pos >= portc->pos) {
280 		n = portc->nframes - (pos - portc->pos);
281 	} else {
282 		n = portc->pos - pos;
283 	}
284 	portc->pos = pos;
285 	portc->count += n;
286 
287 	return (portc->count);
288 }
289 
290 
291 /* private implementation bits */
292 
293 void
294 auvia_reset_output(auvia_portc_t *portc)
295 {
296 	auvia_devc_t	*devc = portc->devc;
297 	uint32_t	cmap;
298 
299 	portc->pos = 0;
300 
301 	OUTB(devc, portc->base + OFF_CTRL, CTRL_TERMINATE);	/* Stop */
302 	OUTL(devc, portc->base + OFF_DMA, portc->sgd_paddr);
303 
304 	OUTB(devc, portc->base + OFF_PLAYFMT,
305 	    PLAYFMT_16BIT | (portc->nchan << 4));
306 
307 	/* Select channel assignment - not valid for 8233A */
308 	if (devc->chip_type != CHIP_8233A) {
309 		/*
310 		 * Undocumented slot mapping table:
311 		 *
312 		 * slot 3 = 1 (left)
313 		 * slot 4 = 2 (right)
314 		 * slot 6 = 5 (center)
315 		 * slot 9 = 6 (lfe)
316 		 * slot 7 = 3 (left rear)
317 		 * slot 8 = 4 (right rear)
318 		 */
319 		switch (portc->nchan) {
320 		case 1:
321 			cmap = (1 << 0) | (1 << 4);
322 			break;
323 		case 2:
324 			cmap = (1 << 0) | (2 << 4);
325 			break;
326 		case 4:
327 			cmap = (1 << 0) | (2 << 4) | (3 << 8) | (4 << 12);
328 			break;
329 		case 6:
330 			cmap = (1 << 0) | (2 << 4) |
331 			    (5 << 8) | (6 << 12) | (3 << 16) | (4 << 20);
332 			break;
333 		default:
334 			cmap = 0;
335 			break;
336 		}
337 		OUTL(devc, portc->base + OFF_CHANNELS, cmap | 0xFF000000U);
338 	}
339 }
340 
341 static void
342 auvia_reset_input(auvia_portc_t *portc)
343 {
344 	auvia_devc_t	*devc = portc->devc;
345 	uint32_t	fmt;
346 
347 	portc->pos = 0;
348 
349 	OUTB(devc, portc->base + OFF_CTRL, CTRL_TERMINATE);	/* Stop */
350 	OUTL(devc, portc->base + OFF_DMA, portc->sgd_paddr);
351 
352 	fmt = RECFMT_STEREO | RECFMT_16BIT;
353 
354 	if (devc->chip_type != CHIP_8233A) {
355 		fmt |= RECFMT_48K;
356 	}
357 	fmt |= (0xffU << 24);
358 	OUTB(devc, portc->base + OFF_RECFIFO, RECFIFO_ENABLE);
359 	OUTL(devc, portc->base + OFF_RECFMT, fmt);
360 }
361 
362 int
363 auvia_alloc_port(auvia_devc_t *devc, int num)
364 {
365 	auvia_portc_t		*portc;
366 	size_t			len;
367 	ddi_dma_cookie_t	cookie;
368 	uint_t			count;
369 	int			dir;
370 	unsigned		caps;
371 	audio_dev_t		*adev;
372 	uint32_t		*desc;
373 
374 	adev = devc->adev;
375 	portc = kmem_zalloc(sizeof (*portc), KM_SLEEP);
376 	devc->portc[num] = portc;
377 	portc->devc = devc;
378 
379 	switch (num) {
380 	case AUVIA_REC_SGD_NUM:
381 		portc->base = devc->base + REG_RECBASE;
382 		portc->syncdir = DDI_DMA_SYNC_FORKERNEL;
383 		portc->nchan = 2;
384 		portc->reset = auvia_reset_input;
385 		caps = ENGINE_INPUT_CAP;
386 		dir = DDI_DMA_READ;
387 		break;
388 	case AUVIA_PLAY_SGD_NUM:
389 		portc->base = devc->base + REG_PLAYBASE;
390 		portc->syncdir = DDI_DMA_SYNC_FORDEV;
391 		portc->nchan = 6;
392 		portc->reset = auvia_reset_output;
393 		caps = ENGINE_OUTPUT_CAP;
394 		dir = DDI_DMA_WRITE;
395 		break;
396 	default:
397 		return (DDI_FAILURE);
398 	}
399 
400 	/* make sure port is shut down */
401 	OUTB(portc->devc, portc->base + OFF_CTRL, CTRL_TERMINATE);
402 
403 	portc->nframes = 4096;
404 	portc->buf_size = portc->nframes * portc->nchan * sizeof (int16_t);
405 
406 	/* first allocate up space for SGD list */
407 	if (ddi_dma_alloc_handle(devc->dip, &dma_attr_sgd,
408 	    DDI_DMA_SLEEP, NULL, &portc->sgd_dmah) != DDI_SUCCESS) {
409 		audio_dev_warn(adev, "failed to allocate SGD handle");
410 		return (DDI_FAILURE);
411 	}
412 
413 	if (ddi_dma_mem_alloc(portc->sgd_dmah, 2 * sizeof (uint32_t), &dev_attr,
414 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &portc->sgd_kaddr,
415 	    &len, &portc->sgd_acch) != DDI_SUCCESS) {
416 		audio_dev_warn(adev, "failed to allocate SGD memory");
417 		return (DDI_FAILURE);
418 	}
419 
420 	if (ddi_dma_addr_bind_handle(portc->sgd_dmah, NULL,
421 	    portc->sgd_kaddr, len, DDI_DMA_CONSISTENT | DDI_DMA_WRITE,
422 	    DDI_DMA_SLEEP, NULL, &cookie, &count) != DDI_SUCCESS) {
423 		audio_dev_warn(adev, "failed binding SGD DMA handle");
424 		return (DDI_FAILURE);
425 	}
426 	portc->sgd_paddr = cookie.dmac_address;
427 
428 	/* now buffers */
429 	if (ddi_dma_alloc_handle(devc->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
430 	    &portc->buf_dmah) != DDI_SUCCESS) {
431 		audio_dev_warn(adev, "failed to allocate BUF handle");
432 		return (DDI_FAILURE);
433 	}
434 
435 	if (ddi_dma_mem_alloc(portc->buf_dmah, portc->buf_size,
436 	    &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
437 	    &portc->buf_kaddr, &len, &portc->buf_acch) != DDI_SUCCESS) {
438 		audio_dev_warn(adev, "failed to allocate BUF memory");
439 		return (DDI_FAILURE);
440 	}
441 
442 	if (ddi_dma_addr_bind_handle(portc->buf_dmah, NULL, portc->buf_kaddr,
443 	    len, DDI_DMA_CONSISTENT | dir, DDI_DMA_SLEEP, NULL, &cookie,
444 	    &count) != DDI_SUCCESS) {
445 		audio_dev_warn(adev, "failed binding BUF DMA handle");
446 		return (DDI_FAILURE);
447 	}
448 	portc->buf_paddr = cookie.dmac_address;
449 
450 	/* now wire up descriptor -- just one */
451 	desc = (void *)portc->sgd_kaddr;
452 
453 	ddi_put32(portc->sgd_acch, desc++, portc->buf_paddr);
454 	ddi_put32(portc->sgd_acch, desc++, AUVIA_SGD_EOL | portc->buf_size);
455 
456 	(void) ddi_dma_sync(portc->sgd_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
457 
458 	portc->engine = audio_engine_alloc(&auvia_engine_ops, caps);
459 	if (portc->engine == NULL) {
460 		audio_dev_warn(adev, "audio_engine_alloc failed");
461 		return (DDI_FAILURE);
462 	}
463 
464 	audio_engine_set_private(portc->engine, portc);
465 	audio_dev_add_engine(adev, portc->engine);
466 
467 	return (DDI_SUCCESS);
468 }
469 
470 void
471 auvia_destroy(auvia_devc_t *devc)
472 {
473 	for (int i = 0; i < AUVIA_NUM_PORTC; i++) {
474 		auvia_portc_t *portc = devc->portc[i];
475 		if (!portc)
476 			continue;
477 		if (portc->engine) {
478 			audio_dev_remove_engine(devc->adev, portc->engine);
479 			audio_engine_free(portc->engine);
480 		}
481 		if (portc->sgd_paddr) {
482 			(void) ddi_dma_unbind_handle(portc->sgd_dmah);
483 		}
484 		if (portc->sgd_acch) {
485 			ddi_dma_mem_free(&portc->sgd_acch);
486 		}
487 		if (portc->sgd_dmah) {
488 			ddi_dma_free_handle(&portc->sgd_dmah);
489 		}
490 		if (portc->buf_paddr) {
491 			(void) ddi_dma_unbind_handle(portc->buf_dmah);
492 		}
493 		if (portc->buf_acch) {
494 			ddi_dma_mem_free(&portc->buf_acch);
495 		}
496 		if (portc->buf_dmah) {
497 			ddi_dma_free_handle(&portc->buf_dmah);
498 		}
499 		kmem_free(portc, sizeof (*portc));
500 	}
501 
502 	if (devc->ac97 != NULL) {
503 		ac97_free(devc->ac97);
504 	}
505 	if (devc->adev != NULL) {
506 		audio_dev_free(devc->adev);
507 	}
508 	if (devc->regsh != NULL) {
509 		ddi_regs_map_free(&devc->regsh);
510 	}
511 	if (devc->pcih != NULL) {
512 		pci_config_teardown(&devc->pcih);
513 	}
514 	kmem_free(devc, sizeof (*devc));
515 }
516 
517 void
518 auvia_hwinit(auvia_devc_t *devc)
519 {
520 	ddi_acc_handle_t	pcih = devc->pcih;
521 	uint32_t		val;
522 
523 	val = pci_config_get32(pcih, AUVIA_PCICFG);
524 	/* we want to disable all legacy */
525 	val &= ~AUVIA_PCICFG_LEGACY;
526 	val &= ~(AUVIA_PCICFG_FMEN | AUVIA_PCICFG_SBEN);
527 
528 	/* enable AC'97 link and clear the reset bit */
529 	val |= (AUVIA_PCICFG_ACLINKEN | AUVIA_PCICFG_NRST);
530 	/* disable SRC (we won't use it) */
531 	val &= ~AUVIA_PCICFG_SRCEN;
532 	/* enable the SGD engines */
533 	val |= AUVIA_PCICFG_SGDEN;
534 
535 	pci_config_put32(pcih, AUVIA_PCICFG, val);
536 
537 	drv_usecwait(10);
538 }
539 
540 int
541 auvia_attach(dev_info_t *dip)
542 {
543 	uint8_t 	pci_revision;
544 	uint16_t	pci_command, vendor, device;
545 	auvia_devc_t	*devc;
546 	ddi_acc_handle_t pcih;
547 	const char	*version;
548 
549 	devc = kmem_zalloc(sizeof (*devc), KM_SLEEP);
550 	devc->dip = dip;
551 	ddi_set_driver_private(dip, devc);
552 
553 	if ((devc->adev = audio_dev_alloc(dip, 0)) == NULL) {
554 		cmn_err(CE_WARN, "audio_dev_alloc failed");
555 		goto error;
556 	}
557 
558 	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
559 		audio_dev_warn(devc->adev, "pci_config_setup failed");
560 		goto error;
561 	}
562 	devc->pcih = pcih;
563 
564 	vendor = pci_config_get16(pcih, PCI_CONF_VENID);
565 	device = pci_config_get16(pcih, PCI_CONF_DEVID);
566 	if ((vendor != VIA_VENDOR_ID) || (device != VIA_8233_ID &&
567 	    device != VIA_8233A_ID)) {
568 		audio_dev_warn(devc->adev, "Hardware not recognized "
569 		    "(vendor=%x, dev=%x)", vendor, device);
570 		goto error;
571 	}
572 
573 	devc->chip_type = CHIP_8233;
574 	devc->chip_name = "VIA VT8233";
575 	version = "8233";
576 
577 	pci_revision = pci_config_get8(pcih, PCI_CONF_REVID);
578 
579 	if (pci_revision == 0x50) {
580 		devc->chip_name = "VIA VT8235";
581 		version = "8235";
582 	}
583 
584 	if (pci_revision == 0x60) {
585 		devc->chip_name = "VIA VT8237";
586 		version = "8237";
587 	}
588 
589 	if ((device == VIA_8233A_ID) ||
590 	    (device == VIA_8233_ID && pci_revision == 0x40)) {
591 		devc->chip_type = CHIP_8233A;
592 		devc->chip_name = "VIA VT8233A";
593 		version = "8233A";
594 	}
595 	audio_dev_set_description(devc->adev, devc->chip_name);
596 	audio_dev_set_version(devc->adev, version);
597 
598 	pci_command = pci_config_get16(pcih, PCI_CONF_COMM);
599 	pci_command |= PCI_COMM_ME | PCI_COMM_IO | PCI_COMM_MAE;
600 	pci_config_put16(pcih, PCI_CONF_COMM, pci_command);
601 
602 	if ((ddi_regs_map_setup(dip, 1, &devc->base, 0, 0, &dev_attr,
603 	    &devc->regsh)) != DDI_SUCCESS) {
604 		audio_dev_warn(devc->adev, "failed to map registers");
605 		goto error;
606 	}
607 
608 	auvia_hwinit(devc);
609 
610 	if ((auvia_alloc_port(devc, AUVIA_PLAY_SGD_NUM) != DDI_SUCCESS) ||
611 	    (auvia_alloc_port(devc, AUVIA_REC_SGD_NUM) != DDI_SUCCESS)) {
612 		goto error;
613 	}
614 
615 	devc->ac97 = ac97_alloc(dip, auvia_read_ac97, auvia_write_ac97, devc);
616 	if (devc->ac97 == NULL) {
617 		audio_dev_warn(devc->adev, "failed to allocate ac97 handle");
618 		goto error;
619 	}
620 
621 	if (ac97_init(devc->ac97, devc->adev) != DDI_SUCCESS) {
622 		audio_dev_warn(devc->adev, "failed to init ac97");
623 		goto error;
624 	}
625 
626 	if (audio_dev_register(devc->adev) != DDI_SUCCESS) {
627 		audio_dev_warn(devc->adev, "unable to register with framework");
628 		goto error;
629 	}
630 
631 	ddi_report_dev(dip);
632 
633 	return (DDI_SUCCESS);
634 
635 error:
636 	auvia_destroy(devc);
637 	return (DDI_FAILURE);
638 }
639 
640 int
641 auvia_resume(dev_info_t *dip)
642 {
643 	auvia_devc_t *devc;
644 
645 	devc = ddi_get_driver_private(dip);
646 
647 	auvia_hwinit(devc);
648 
649 	ac97_reset(devc->ac97);
650 
651 	audio_dev_resume(devc->adev);
652 
653 	return (DDI_SUCCESS);
654 }
655 
656 
657 int
658 auvia_detach(auvia_devc_t *devc)
659 {
660 	if (audio_dev_unregister(devc->adev) != DDI_SUCCESS)
661 		return (DDI_FAILURE);
662 
663 	auvia_destroy(devc);
664 	return (DDI_SUCCESS);
665 }
666 
667 int
668 auvia_suspend(auvia_devc_t *devc)
669 {
670 	audio_dev_suspend(devc->adev);
671 
672 	return (DDI_SUCCESS);
673 }
674 
675 static int auvia_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
676 static int auvia_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
677 static int auvia_ddi_quiesce(dev_info_t *);
678 
679 static struct dev_ops auvia_dev_ops = {
680 	DEVO_REV,		/* rev */
681 	0,			/* refcnt */
682 	NULL,			/* getinfo */
683 	nulldev,		/* identify */
684 	nulldev,		/* probe */
685 	auvia_ddi_attach,	/* attach */
686 	auvia_ddi_detach,	/* detach */
687 	nodev,			/* reset */
688 	NULL,			/* cb_ops */
689 	NULL,			/* bus_ops */
690 	NULL,			/* power */
691 	auvia_ddi_quiesce,	/* quiesce */
692 };
693 
694 static struct modldrv auvia_modldrv = {
695 	&mod_driverops,			/* drv_modops */
696 	"Via 823x Audio",		/* linkinfo */
697 	&auvia_dev_ops,			/* dev_ops */
698 };
699 
700 static struct modlinkage modlinkage = {
701 	MODREV_1,
702 	{ &auvia_modldrv, NULL }
703 };
704 
705 int
706 _init(void)
707 {
708 	int	rv;
709 
710 	audio_init_ops(&auvia_dev_ops, AUVIA_NAME);
711 	if ((rv = mod_install(&modlinkage)) != 0) {
712 		audio_fini_ops(&auvia_dev_ops);
713 	}
714 	return (rv);
715 }
716 
717 int
718 _fini(void)
719 {
720 	int	rv;
721 
722 	if ((rv = mod_remove(&modlinkage)) == 0) {
723 		audio_fini_ops(&auvia_dev_ops);
724 	}
725 	return (rv);
726 }
727 
728 int
729 _info(struct modinfo *modinfop)
730 {
731 	return (mod_info(&modlinkage, modinfop));
732 }
733 
734 int
735 auvia_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
736 {
737 	switch (cmd) {
738 	case DDI_ATTACH:
739 		return (auvia_attach(dip));
740 
741 	case DDI_RESUME:
742 		return (auvia_resume(dip));
743 
744 	default:
745 		return (DDI_FAILURE);
746 	}
747 }
748 
749 int
750 auvia_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
751 {
752 	auvia_devc_t *devc;
753 
754 	devc = ddi_get_driver_private(dip);
755 
756 	switch (cmd) {
757 	case DDI_DETACH:
758 		return (auvia_detach(devc));
759 
760 	case DDI_SUSPEND:
761 		return (auvia_suspend(devc));
762 
763 	default:
764 		return (DDI_FAILURE);
765 	}
766 }
767 
768 int
769 auvia_ddi_quiesce(dev_info_t *dip)
770 {
771 	auvia_devc_t	*devc;
772 
773 	devc = ddi_get_driver_private(dip);
774 
775 	for (int i = 0; i < AUVIA_NUM_PORTC; i++) {
776 
777 		auvia_portc_t *portc = devc->portc[i];
778 		OUTB(devc, portc->base + OFF_CTRL, CTRL_TERMINATE);
779 	}
780 	return (DDI_SUCCESS);
781 }
782