xref: /titanic_41/usr/src/uts/common/io/audio/drv/audiovia97/audiovia97.c (revision 68c47f65208790c466e5e484f2293d3baed71c6a)
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 VIA VT82C686A AC97 audio controller
29  */
30 /*
31  *
32  * Copyright (C) 4Front Technologies 1996-2009.
33  */
34 
35 #include <sys/types.h>
36 #include <sys/modctl.h>
37 #include <sys/kmem.h>
38 #include <sys/conf.h>
39 #include <sys/ddi.h>
40 #include <sys/sunddi.h>
41 #include <sys/pci.h>
42 #include <sys/note.h>
43 #include <sys/audio/audio_driver.h>
44 #include <sys/audio/ac97.h>
45 
46 #include "audiovia97.h"
47 
48 static struct ddi_device_acc_attr dev_attr = {
49 	DDI_DEVICE_ATTR_V0,
50 	DDI_STRUCTURE_LE_ACC,
51 	DDI_STRICTORDER_ACC
52 };
53 
54 static struct ddi_device_acc_attr buf_attr = {
55 	DDI_DEVICE_ATTR_V0,
56 	DDI_NEVERSWAP_ACC,
57 	DDI_STRICTORDER_ACC
58 };
59 
60 static ddi_dma_attr_t dma_attr_sgd = {
61 	DMA_ATTR_V0,		/* version number */
62 	0x00000000,		/* low DMA address range */
63 	0xffffffff,		/* high DMA address range */
64 	0x0000ffff,		/* DMA counter register */
65 	8,			/* DMA address alignment */
66 	0x3c,			/* DMA burstsizes */
67 	8,			/* min effective DMA size */
68 	0xffffffff,		/* max DMA xfer size */
69 	0x00000fff,		/* segment boundary */
70 	1,			/* s/g length */
71 	8,			/* granularity of device */
72 	0			/* Bus specific DMA flags */
73 };
74 
75 static ddi_dma_attr_t dma_attr_buf = {
76 	DMA_ATTR_V0,		/* version number */
77 	0x00000000,		/* low DMA address range */
78 	0xffffffff,		/* high DMA address range */
79 	0xfffffffe,		/* DMA counter register */
80 	4,			/* DMA address alignment */
81 	0x3c,			/* DMA burstsizes */
82 	4,			/* min effective DMA size */
83 	0xffffffff,		/* max DMA xfer size */
84 	0xffffffff,		/* segment boundary */
85 	1,			/* s/g length */
86 	4,			/* granularity of device */
87 	0			/* Bus specific DMA flags */
88 };
89 
90 static int via97_attach(dev_info_t *);
91 static int via97_resume(dev_info_t *);
92 static int via97_detach(via97_devc_t *);
93 static int via97_suspend(via97_devc_t *);
94 
95 static int via97_open(void *, int, unsigned *, caddr_t *);
96 static void via97_close(void *);
97 static int via97_start(void *);
98 static void via97_stop(void *);
99 static int via97_format(void *);
100 static int via97_channels(void *);
101 static int via97_rate(void *);
102 static uint64_t via97_count(void *);
103 static void via97_sync(void *, unsigned);
104 static uint_t via97_playahead(void *);
105 
106 static uint16_t via97_read_ac97(void *, uint8_t);
107 static void via97_write_ac97(void *, uint8_t, uint16_t);
108 static int via97_alloc_port(via97_devc_t *, int);
109 static void via97_destroy(via97_devc_t *);
110 static void via97_hwinit(via97_devc_t *);
111 
112 static audio_engine_ops_t via97_engine_ops = {
113 	AUDIO_ENGINE_VERSION,
114 	via97_open,
115 	via97_close,
116 	via97_start,
117 	via97_stop,
118 	via97_count,
119 	via97_format,
120 	via97_channels,
121 	via97_rate,
122 	via97_sync,
123 	NULL,
124 	NULL,
125 	via97_playahead
126 };
127 
128 static uint16_t
via97_read_ac97(void * arg,uint8_t index)129 via97_read_ac97(void *arg, uint8_t index)
130 {
131 	via97_devc_t *devc = arg;
132 	int tmp, addr, i;
133 
134 	/* Index has only 7 bits */
135 	if (index > 0x7F)
136 		return (0xffff);
137 
138 	addr = (index << 16) + CODEC_RD;
139 	OUTL(devc, devc->base + AC97CODEC, addr);
140 	drv_usecwait(100);
141 
142 	/* Check AC CODEC access time out */
143 	for (i = 0; i < CODEC_TIMEOUT_COUNT; i++) {
144 		/* if send command over, break */
145 		if (INL(devc, devc->base + AC97CODEC) & STA_VALID)
146 			break;
147 		drv_usecwait(50);
148 	}
149 	if (i == CODEC_TIMEOUT_COUNT) {
150 		return (0xffff);
151 	}
152 
153 	/* Check if Index still ours? If yes, return data, else return FAIL */
154 	tmp = INL(devc, devc->base + AC97CODEC);
155 	OUTB(devc, devc->base + AC97CODEC + 3, 0x02);
156 	if (((tmp & CODEC_INDEX) >> 16) == index) {
157 		return ((int)tmp & CODEC_DATA);
158 	}
159 	return (0xffff);
160 }
161 
162 static void
via97_write_ac97(void * arg,uint8_t index,uint16_t data)163 via97_write_ac97(void *arg, uint8_t index, uint16_t data)
164 {
165 	via97_devc_t *devc = arg;
166 	int value = 0;
167 	unsigned int i = 0;
168 
169 	value = (index << 16) + data;
170 	OUTL(devc, devc->base + AC97CODEC, value);
171 	drv_usecwait(100);
172 
173 	/* Check AC CODEC access time out */
174 	for (i = 0; i < CODEC_TIMEOUT_COUNT; i++) {
175 		/* if send command over, break */
176 		if (!(INL(devc, devc->base + AC97CODEC) & IN_CMD))
177 			break;
178 		drv_usecwait(50);
179 	}
180 }
181 
182 /*
183  * Audio routines
184  */
185 
186 int
via97_open(void * arg,int flag,unsigned * nframesp,caddr_t * bufp)187 via97_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp)
188 {
189 	via97_portc_t	 *portc = arg;
190 
191 	_NOTE(ARGUNUSED(flag));
192 
193 	portc->count = 0;
194 	*nframesp = portc->nframes;
195 	*bufp = portc->buf_kaddr;
196 
197 	return (0);
198 }
199 
200 void
via97_close(void * arg)201 via97_close(void *arg)
202 {
203 	_NOTE(ARGUNUSED(arg));
204 }
205 
206 int
via97_start(void * arg)207 via97_start(void *arg)
208 {
209 	via97_portc_t	*portc = arg;
210 	via97_devc_t	*devc = portc->devc;
211 
212 	portc->pos = 0;
213 
214 	OUTB(devc, portc->base + 0x01, 0x40); /* Stop */
215 	OUTL(devc, portc->base + 4, portc->sgd_paddr);
216 	/* Set autostart at EOL, stereo, 16 bits */
217 	OUTB(devc, portc->base + 0x02,
218 	    0x80 |	/* Set autostart at EOL */
219 	    0x20 |	/* 16 bits */
220 	    0x10);	/* Stereo */
221 
222 	OUTB(devc, portc->base + 0x01, 0x80); /* Start */
223 
224 	return (0);
225 }
226 
227 void
via97_stop(void * arg)228 via97_stop(void *arg)
229 {
230 	via97_portc_t	*portc = arg;
231 	via97_devc_t	*devc = portc->devc;
232 
233 	OUTB(devc, portc->base + 0x01, 0x40); /* Stop */
234 }
235 
236 int
via97_format(void * arg)237 via97_format(void *arg)
238 {
239 	_NOTE(ARGUNUSED(arg));
240 
241 	return (AUDIO_FORMAT_S16_LE);
242 }
243 
244 int
via97_channels(void * arg)245 via97_channels(void *arg)
246 {
247 	_NOTE(ARGUNUSED(arg));
248 
249 	return (2);
250 }
251 
252 int
via97_rate(void * arg)253 via97_rate(void *arg)
254 {
255 	_NOTE(ARGUNUSED(arg));
256 
257 	return (48000);
258 }
259 
260 void
via97_sync(void * arg,unsigned nframes)261 via97_sync(void *arg, unsigned nframes)
262 {
263 	via97_portc_t *portc = arg;
264 	_NOTE(ARGUNUSED(nframes));
265 
266 	(void) ddi_dma_sync(portc->buf_dmah, 0, 0, portc->syncdir);
267 }
268 
269 uint_t
via97_playahead(void * arg)270 via97_playahead(void *arg)
271 {
272 	_NOTE(ARGUNUSED(arg));
273 
274 	/*
275 	 * We see some situations where the default 1.5 fragments from
276 	 * the framework is not enough.  800-900 frame jitter is not
277 	 * uncommon.  Especially at startup.
278 	 */
279 	return (1024);
280 }
281 
282 uint64_t
via97_count(void * arg)283 via97_count(void *arg)
284 {
285 	via97_portc_t	*portc = arg;
286 	via97_devc_t	*devc = portc->devc;
287 	uint32_t	pos;
288 	uint32_t	n;
289 
290 	pos = INL(devc, portc->base + 0x0c) & 0xffffff;
291 	/* convert from bytes to 16-bit stereo frames */
292 	pos /= (sizeof (int16_t) * 2);
293 
294 	if (pos >= portc->pos) {
295 		n = portc->nframes - (pos - portc->pos);
296 	} else {
297 		n = portc->pos - pos;
298 	}
299 	portc->pos = pos;
300 	portc->count += n;
301 
302 	return (portc->count);
303 }
304 
305 
306 /* private implementation bits */
307 
308 int
via97_alloc_port(via97_devc_t * devc,int num)309 via97_alloc_port(via97_devc_t *devc, int num)
310 {
311 	via97_portc_t		*portc;
312 	size_t			len;
313 	ddi_dma_cookie_t	cookie;
314 	uint_t			count;
315 	int			dir;
316 	unsigned		caps;
317 	audio_dev_t		*adev;
318 	uint32_t		*desc;
319 
320 	adev = devc->adev;
321 	portc = kmem_zalloc(sizeof (*portc), KM_SLEEP);
322 	devc->portc[num] = portc;
323 	portc->devc = devc;
324 	portc->base = devc->base + num * 0x10;
325 
326 	switch (num) {
327 	case VIA97_REC_SGD_NUM:
328 		portc->syncdir = DDI_DMA_SYNC_FORKERNEL;
329 		caps = ENGINE_INPUT_CAP;
330 		dir = DDI_DMA_READ;
331 		break;
332 	case VIA97_PLAY_SGD_NUM:
333 		portc->syncdir = DDI_DMA_SYNC_FORDEV;
334 		caps = ENGINE_OUTPUT_CAP;
335 		dir = DDI_DMA_WRITE;
336 		break;
337 	default:
338 		return (DDI_FAILURE);
339 	}
340 
341 	/* Simplicity -- a single contiguous looping buffer */
342 	portc->nframes = 2048;
343 	portc->buf_size = portc->nframes * sizeof (int16_t) * 2;
344 
345 	/* first allocate up space for SGD list */
346 	if (ddi_dma_alloc_handle(devc->dip, &dma_attr_sgd,
347 	    DDI_DMA_SLEEP, NULL, &portc->sgd_dmah) != DDI_SUCCESS) {
348 		audio_dev_warn(adev, "failed to allocate SGD handle");
349 		return (DDI_FAILURE);
350 	}
351 
352 	/* a single SGD entry is only 8 bytes long */
353 	if (ddi_dma_mem_alloc(portc->sgd_dmah, 8, &dev_attr,
354 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &portc->sgd_kaddr,
355 	    &len, &portc->sgd_acch) != DDI_SUCCESS) {
356 		audio_dev_warn(adev, "failed to allocate SGD memory");
357 		return (DDI_FAILURE);
358 	}
359 
360 	if (ddi_dma_addr_bind_handle(portc->sgd_dmah, NULL,
361 	    portc->sgd_kaddr, len, DDI_DMA_CONSISTENT | DDI_DMA_WRITE,
362 	    DDI_DMA_SLEEP, NULL, &cookie, &count) != DDI_SUCCESS) {
363 		audio_dev_warn(adev, "failed binding SGD DMA handle");
364 		return (DDI_FAILURE);
365 	}
366 	portc->sgd_paddr = cookie.dmac_address;
367 
368 	/* now buffers */
369 	if (ddi_dma_alloc_handle(devc->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
370 	    &portc->buf_dmah) != DDI_SUCCESS) {
371 		audio_dev_warn(adev, "failed to allocate BUF handle");
372 		return (DDI_FAILURE);
373 	}
374 
375 	if (ddi_dma_mem_alloc(portc->buf_dmah, portc->buf_size,
376 	    &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
377 	    &portc->buf_kaddr, &len, &portc->buf_acch) != DDI_SUCCESS) {
378 		audio_dev_warn(adev, "failed to allocate BUF memory");
379 		return (DDI_FAILURE);
380 	}
381 
382 	if (ddi_dma_addr_bind_handle(portc->buf_dmah, NULL, portc->buf_kaddr,
383 	    len, DDI_DMA_CONSISTENT | dir, DDI_DMA_SLEEP, NULL, &cookie,
384 	    &count) != DDI_SUCCESS) {
385 		audio_dev_warn(adev, "failed binding BUF DMA handle");
386 		return (DDI_FAILURE);
387 	}
388 	portc->buf_paddr = cookie.dmac_address;
389 
390 	/* now wire descriptor up -- we only use one (which has EOL set)! */
391 	desc = (void *)portc->sgd_kaddr;
392 	ddi_put32(portc->sgd_acch, desc++, portc->buf_paddr);
393 	ddi_put32(portc->sgd_acch, desc++, 0x80000000U | portc->buf_size);
394 
395 	OUTL(devc, portc->base + 4, portc->sgd_paddr);
396 	(void) ddi_dma_sync(portc->sgd_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
397 
398 	portc->engine = audio_engine_alloc(&via97_engine_ops, caps);
399 	if (portc->engine == NULL) {
400 		audio_dev_warn(adev, "audio_engine_alloc failed");
401 		return (DDI_FAILURE);
402 	}
403 
404 	audio_engine_set_private(portc->engine, portc);
405 	audio_dev_add_engine(adev, portc->engine);
406 
407 	return (DDI_SUCCESS);
408 }
409 
410 void
via97_destroy(via97_devc_t * devc)411 via97_destroy(via97_devc_t *devc)
412 {
413 	for (int i = 0; i < VIA97_NUM_PORTC; i++) {
414 		via97_portc_t *portc = devc->portc[i];
415 		if (!portc)
416 			continue;
417 		if (portc->engine) {
418 			audio_dev_remove_engine(devc->adev, portc->engine);
419 			audio_engine_free(portc->engine);
420 		}
421 		if (portc->sgd_paddr) {
422 			(void) ddi_dma_unbind_handle(portc->sgd_dmah);
423 		}
424 		if (portc->sgd_acch) {
425 			ddi_dma_mem_free(&portc->sgd_acch);
426 		}
427 		if (portc->sgd_dmah) {
428 			ddi_dma_free_handle(&portc->sgd_dmah);
429 		}
430 		if (portc->buf_paddr) {
431 			(void) ddi_dma_unbind_handle(portc->buf_dmah);
432 		}
433 		if (portc->buf_acch) {
434 			ddi_dma_mem_free(&portc->buf_acch);
435 		}
436 		if (portc->buf_dmah) {
437 			ddi_dma_free_handle(&portc->buf_dmah);
438 		}
439 		kmem_free(portc, sizeof (*portc));
440 	}
441 
442 	if (devc->ac97 != NULL) {
443 		ac97_free(devc->ac97);
444 	}
445 	if (devc->adev != NULL) {
446 		audio_dev_free(devc->adev);
447 	}
448 	if (devc->regsh != NULL) {
449 		ddi_regs_map_free(&devc->regsh);
450 	}
451 	if (devc->pcih != NULL) {
452 		pci_config_teardown(&devc->pcih);
453 	}
454 	kmem_free(devc, sizeof (*devc));
455 }
456 
457 void
via97_hwinit(via97_devc_t * devc)458 via97_hwinit(via97_devc_t *devc)
459 {
460 	ddi_acc_handle_t	pcih = devc->pcih;
461 	uint32_t		tmp;
462 
463 	/* Enable codec, etc */
464 
465 	pci_config_put8(pcih, 0x41, 0xc0);
466 	drv_usecwait(10);
467 	tmp = pci_config_get8(pcih, 0x41);
468 	pci_config_put8(pcih, 0x41, tmp | 0x0c);
469 	drv_usecwait(10);
470 
471 	/* disable game port/MIDI */
472 	pci_config_put8(pcih, 0x42, 0x00);
473 	/* disable FM io */
474 	pci_config_put8(pcih, 0x48, 0x00);
475 
476 	/* Enable interrupt on FLAG and on EOL */
477 	tmp = INB(devc, devc->base + 0x22);
478 	OUTB(devc, devc->base + 0x22, tmp | 0x83);
479 }
480 
481 int
via97_attach(dev_info_t * dip)482 via97_attach(dev_info_t *dip)
483 {
484 	uint16_t	pci_command, vendor, device;
485 	via97_devc_t	*devc;
486 	ddi_acc_handle_t pcih;
487 
488 	devc = kmem_zalloc(sizeof (*devc), KM_SLEEP);
489 	devc->dip = dip;
490 	ddi_set_driver_private(dip, devc);
491 
492 	if ((devc->adev = audio_dev_alloc(dip, 0)) == NULL) {
493 		cmn_err(CE_WARN, "audio_dev_alloc failed");
494 		goto error;
495 	}
496 
497 	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
498 		audio_dev_warn(devc->adev, "pci_config_setup failed");
499 		goto error;
500 	}
501 	devc->pcih = pcih;
502 
503 	vendor = pci_config_get16(pcih, PCI_CONF_VENID);
504 	device = pci_config_get16(pcih, PCI_CONF_DEVID);
505 	if (vendor != VIA_VENDOR_ID ||
506 	    device != VIA_82C686) {
507 		audio_dev_warn(devc->adev, "Hardware not recognized "
508 		    "(vendor=%x, dev=%x)", vendor, device);
509 		goto error;
510 	}
511 
512 	pci_command = pci_config_get16(pcih, PCI_CONF_COMM);
513 	pci_command |= PCI_COMM_ME | PCI_COMM_IO;
514 	pci_config_put16(pcih, PCI_CONF_COMM, pci_command);
515 
516 	if ((ddi_regs_map_setup(dip, 1, &devc->base, 0, 0, &dev_attr,
517 	    &devc->regsh)) != DDI_SUCCESS) {
518 		audio_dev_warn(devc->adev, "failed to map registers");
519 		goto error;
520 	}
521 
522 	audio_dev_set_description(devc->adev, "VIA 82C686 Audio");
523 
524 	via97_hwinit(devc);
525 
526 	if ((via97_alloc_port(devc, VIA97_PLAY_SGD_NUM) != DDI_SUCCESS) ||
527 	    (via97_alloc_port(devc, VIA97_REC_SGD_NUM) != DDI_SUCCESS)) {
528 		goto error;
529 	}
530 
531 	devc->ac97 = ac97_alloc(dip, via97_read_ac97, via97_write_ac97, devc);
532 	if (devc->ac97 == NULL) {
533 		audio_dev_warn(devc->adev, "failed to allocate ac97 handle");
534 		goto error;
535 	}
536 
537 	if (ac97_init(devc->ac97, devc->adev) != DDI_SUCCESS) {
538 		audio_dev_warn(devc->adev, "failed to init ac97");
539 		goto error;
540 	}
541 
542 	if (audio_dev_register(devc->adev) != DDI_SUCCESS) {
543 		audio_dev_warn(devc->adev, "unable to register with framework");
544 		goto error;
545 	}
546 
547 	ddi_report_dev(dip);
548 
549 	return (DDI_SUCCESS);
550 
551 error:
552 	via97_destroy(devc);
553 	return (DDI_FAILURE);
554 }
555 
556 int
via97_resume(dev_info_t * dip)557 via97_resume(dev_info_t *dip)
558 {
559 	via97_devc_t *devc;
560 
561 	devc = ddi_get_driver_private(dip);
562 
563 	via97_hwinit(devc);
564 
565 	ac97_reset(devc->ac97);
566 
567 	audio_dev_resume(devc->adev);
568 	return (DDI_SUCCESS);
569 }
570 
571 int
via97_detach(via97_devc_t * devc)572 via97_detach(via97_devc_t *devc)
573 {
574 	if (audio_dev_unregister(devc->adev) != DDI_SUCCESS)
575 		return (DDI_FAILURE);
576 
577 	via97_destroy(devc);
578 	return (DDI_SUCCESS);
579 }
580 
581 int
via97_suspend(via97_devc_t * devc)582 via97_suspend(via97_devc_t *devc)
583 {
584 	audio_dev_suspend(devc->adev);
585 	return (DDI_SUCCESS);
586 }
587 
588 static int via97_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
589 static int via97_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
590 static int via97_ddi_quiesce(dev_info_t *);
591 
592 static struct dev_ops via97_dev_ops = {
593 	DEVO_REV,		/* rev */
594 	0,			/* refcnt */
595 	NULL,			/* getinfo */
596 	nulldev,		/* identify */
597 	nulldev,		/* probe */
598 	via97_ddi_attach,	/* attach */
599 	via97_ddi_detach,	/* detach */
600 	nodev,			/* reset */
601 	NULL,			/* cb_ops */
602 	NULL,			/* bus_ops */
603 	NULL,			/* power */
604 	via97_ddi_quiesce,	/* quiesce */
605 };
606 
607 static struct modldrv via97_modldrv = {
608 	&mod_driverops,			/* drv_modops */
609 	"Via 82C686 Audio",		/* linkinfo */
610 	&via97_dev_ops,			/* dev_ops */
611 };
612 
613 static struct modlinkage modlinkage = {
614 	MODREV_1,
615 	{ &via97_modldrv, NULL }
616 };
617 
618 int
_init(void)619 _init(void)
620 {
621 	int	rv;
622 
623 	audio_init_ops(&via97_dev_ops, VIA97_NAME);
624 	if ((rv = mod_install(&modlinkage)) != 0) {
625 		audio_fini_ops(&via97_dev_ops);
626 	}
627 	return (rv);
628 }
629 
630 int
_fini(void)631 _fini(void)
632 {
633 	int	rv;
634 
635 	if ((rv = mod_remove(&modlinkage)) == 0) {
636 		audio_fini_ops(&via97_dev_ops);
637 	}
638 	return (rv);
639 }
640 
641 int
_info(struct modinfo * modinfop)642 _info(struct modinfo *modinfop)
643 {
644 	return (mod_info(&modlinkage, modinfop));
645 }
646 
647 int
via97_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)648 via97_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
649 {
650 	switch (cmd) {
651 	case DDI_ATTACH:
652 		return (via97_attach(dip));
653 
654 	case DDI_RESUME:
655 		return (via97_resume(dip));
656 
657 	default:
658 		return (DDI_FAILURE);
659 	}
660 }
661 
662 int
via97_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)663 via97_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
664 {
665 	via97_devc_t *devc;
666 
667 	devc = ddi_get_driver_private(dip);
668 
669 	switch (cmd) {
670 	case DDI_DETACH:
671 		return (via97_detach(devc));
672 
673 	case DDI_SUSPEND:
674 		return (via97_suspend(devc));
675 
676 	default:
677 		return (DDI_FAILURE);
678 	}
679 }
680 
681 int
via97_ddi_quiesce(dev_info_t * dip)682 via97_ddi_quiesce(dev_info_t *dip)
683 {
684 	via97_devc_t	*devc;
685 
686 	devc = ddi_get_driver_private(dip);
687 
688 	/*
689 	 * Turn off the hardware
690 	 */
691 	OUTB(devc, devc->base + 0x01, 0x40);
692 	OUTB(devc, devc->base + 0x11, 0x40);
693 	OUTB(devc, devc->base + 0x02, 0);
694 	OUTB(devc, devc->base + 0x12, 0);
695 	OUTL(devc, devc->base + 0x04, 0);
696 	OUTL(devc, devc->base + 0x14, 0);
697 	OUTL(devc, devc->base + 0x22, 0);
698 	return (DDI_SUCCESS);
699 }
700