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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Purpose: Driver for the VIA VT82C686A AC97 audio controller
32 * Copyright (C) 4Front Technologies 1996-2009.
35 #include <sys/types.h>
36 #include <sys/modctl.h>
40 #include <sys/sunddi.h>
43 #include <sys/audio/audio_driver.h>
44 #include <sys/audio/ac97.h>
46 #include "audiovia97.h"
48 static struct ddi_device_acc_attr dev_attr
= {
54 static struct ddi_device_acc_attr buf_attr
= {
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 */
71 8, /* granularity of device */
72 0 /* Bus specific DMA flags */
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 */
86 4, /* granularity of device */
87 0 /* Bus specific DMA flags */
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
*);
95 static int via97_open(void *, int, unsigned *, 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);
105 static uint16_t via97_read_ac97(void *, uint8_t);
106 static void via97_write_ac97(void *, uint8_t, uint16_t);
107 static int via97_alloc_port(via97_devc_t
*, int);
108 static void via97_start_port(via97_portc_t
*);
109 static void via97_stop_port(via97_portc_t
*);
110 static void via97_update_port(via97_portc_t
*);
111 static void via97_reset_port(via97_portc_t
*);
112 static void via97_destroy(via97_devc_t
*);
113 static int via97_setup_intrs(via97_devc_t
*);
114 static void via97_hwinit(via97_devc_t
*);
115 static uint_t
via97_intr(caddr_t
, caddr_t
);
117 static audio_engine_ops_t via97_engine_ops
= {
118 AUDIO_ENGINE_VERSION
,
134 via97_read_ac97(void *arg
, uint8_t index
)
136 via97_devc_t
*devc
= arg
;
139 /* Index has only 7 bits */
143 mutex_enter(&devc
->low_mutex
);
144 addr
= (index
<< 16) + CODEC_RD
;
145 OUTL(devc
, devc
->base
+ AC97CODEC
, addr
);
148 /* Check AC CODEC access time out */
149 for (i
= 0; i
< CODEC_TIMEOUT_COUNT
; i
++) {
150 /* if send command over, break */
151 if (INL(devc
, devc
->base
+ AC97CODEC
) & STA_VALID
)
155 if (i
== CODEC_TIMEOUT_COUNT
) {
156 mutex_exit(&devc
->low_mutex
);
160 /* Check if Index still ours? If yes, return data, else return FAIL */
161 tmp
= INL(devc
, devc
->base
+ AC97CODEC
);
162 OUTB(devc
, devc
->base
+ AC97CODEC
+ 3, 0x02);
163 if (((tmp
& CODEC_INDEX
) >> 16) == index
) {
164 mutex_exit(&devc
->low_mutex
);
165 return ((int)tmp
& CODEC_DATA
);
167 mutex_exit(&devc
->low_mutex
);
172 via97_write_ac97(void *arg
, uint8_t index
, uint16_t data
)
174 via97_devc_t
*devc
= arg
;
178 mutex_enter(&devc
->low_mutex
);
179 value
= (index
<< 16) + data
;
180 OUTL(devc
, devc
->base
+ AC97CODEC
, value
);
183 /* Check AC CODEC access time out */
184 for (i
= 0; i
< CODEC_TIMEOUT_COUNT
; i
++) {
185 /* if send command over, break */
186 if (!(INL(devc
, devc
->base
+ AC97CODEC
) & IN_CMD
))
190 mutex_exit(&devc
->low_mutex
);
194 via97_recintr(via97_devc_t
*devc
)
198 status
= INB(devc
, devc
->base
+ 0x10);
200 if (!(status
& 0x01)) /* No interrupt */
203 audio_engine_produce(devc
->portc
[VIA97_REC_SGD_NUM
]->engine
);
205 OUTB(devc
, devc
->base
+ 0x10, status
| 0x01); /* Ack */
210 via97_playintr(via97_devc_t
*devc
)
214 status
= INB(devc
, devc
->base
+ 0x00);
216 if (!(status
& 0x01)) /* No interrupt */
219 audio_engine_consume(devc
->portc
[VIA97_PLAY_SGD_NUM
]->engine
);
221 OUTB(devc
, devc
->base
+ 0x00, status
| 0x01); /* Ack */
226 via97_intr(caddr_t argp
, caddr_t nocare
)
228 via97_devc_t
*devc
= (void *)argp
;
230 _NOTE(ARGUNUSED(nocare
));
232 if (devc
->suspended
) {
233 return (DDI_INTR_UNCLAIMED
);
236 if (!via97_recintr(devc
) && !via97_playintr(devc
)) {
237 return (DDI_INTR_UNCLAIMED
);
241 VIA97_KIOP(devc
)->intrs
[KSTAT_INTR_HARD
]++;
244 return (DDI_INTR_CLAIMED
);
252 via97_open(void *arg
, int flag
,
253 unsigned *fragfrp
, unsigned *nfragsp
, caddr_t
*bufp
)
255 via97_portc_t
*portc
= arg
;
256 via97_devc_t
*devc
= portc
->devc
;
258 _NOTE(ARGUNUSED(flag
));
260 portc
->started
= B_FALSE
;
262 *fragfrp
= portc
->fragfr
;
263 *nfragsp
= VIA97_NUM_SGD
;
264 *bufp
= portc
->buf_kaddr
;
266 mutex_enter(&devc
->mutex
);
267 via97_reset_port(portc
);
268 mutex_exit(&devc
->mutex
);
274 via97_close(void *arg
)
276 via97_portc_t
*portc
= arg
;
277 via97_devc_t
*devc
= portc
->devc
;
279 mutex_enter(&devc
->mutex
);
280 via97_stop_port(portc
);
281 portc
->started
= B_FALSE
;
282 mutex_exit(&devc
->mutex
);
286 via97_start(void *arg
)
288 via97_portc_t
*portc
= arg
;
289 via97_devc_t
*devc
= portc
->devc
;
291 mutex_enter(&devc
->mutex
);
292 if (!portc
->started
) {
293 via97_start_port(portc
);
294 portc
->started
= B_TRUE
;
296 mutex_exit(&devc
->mutex
);
301 via97_stop(void *arg
)
303 via97_portc_t
*portc
= arg
;
304 via97_devc_t
*devc
= portc
->devc
;
306 mutex_enter(&devc
->mutex
);
307 if (portc
->started
) {
308 via97_stop_port(portc
);
309 portc
->started
= B_FALSE
;
311 mutex_exit(&devc
->mutex
);
315 via97_format(void *arg
)
317 _NOTE(ARGUNUSED(arg
));
319 return (AUDIO_FORMAT_S16_LE
);
323 via97_channels(void *arg
)
325 _NOTE(ARGUNUSED(arg
));
331 via97_rate(void *arg
)
333 _NOTE(ARGUNUSED(arg
));
339 via97_sync(void *arg
, unsigned nframes
)
341 via97_portc_t
*portc
= arg
;
342 _NOTE(ARGUNUSED(nframes
));
344 (void) ddi_dma_sync(portc
->buf_dmah
, 0, 0, portc
->syncdir
);
348 via97_count(void *arg
)
350 via97_portc_t
*portc
= arg
;
351 via97_devc_t
*devc
= portc
->devc
;
354 mutex_enter(&devc
->mutex
);
355 via97_update_port(portc
);
357 * The residual is in bytes. We have to convert to frames,
358 * and then subtract it from the fragment size to get the
359 * number of frames processed. Note that we have 16 bit
363 (portc
->fragfr
- (portc
->resid
/ (2 * 2)));
364 mutex_exit(&devc
->mutex
);
370 /* private implementation bits */
373 via97_start_port(via97_portc_t
*portc
)
375 via97_devc_t
*devc
= portc
->devc
;
377 ASSERT(mutex_owned(&devc
->mutex
));
381 OUTB(devc
, portc
->base
+ 0x01, 0x80); /* Start */
385 via97_stop_port(via97_portc_t
*portc
)
387 via97_devc_t
*devc
= portc
->devc
;
392 OUTB(devc
, portc
->base
+ 0x01, 0x40); /* Stop */
396 via97_update_port(via97_portc_t
*portc
)
399 * Unfortunately the controller seems to raise interrupt about 32 bytes before
400 * the DMA pointer moves to a new fragment. This means that the bytes value
401 * returned will be bogus during few samples before
402 * the pointer wraps back to the beginning of buffer.
404 via97_devc_t
*devc
= portc
->devc
;
405 uint32_t frag
, resid
;
408 ASSERT(mutex_owned(&devc
->mutex
));
409 if (devc
->suspended
) {
411 portc
->resid
= portc
->fragsz
;
414 resid
= INL(devc
, portc
->base
+ 0x0c) & 0xffffff;
415 resid
= portc
->fragsz
- resid
;
418 ((INL(devc
, portc
->base
+ 0x04) - portc
->sgd_paddr
) / 8) -
421 portc
->resid
= resid
;
423 if (frag
>= portc
->cur_frag
) {
424 n
= frag
- portc
->cur_frag
;
426 n
= frag
+ VIA97_NUM_SGD
- portc
->cur_frag
;
428 portc
->count
+= (n
* portc
->fragfr
);
429 portc
->cur_frag
= frag
;
434 via97_reset_port(via97_portc_t
*portc
)
436 via97_devc_t
*devc
= portc
->devc
;
439 portc
->resid
= portc
->fragsz
;
444 OUTB(devc
, portc
->base
+ 0x01, 0x40); /* Stop */
445 OUTL(devc
, portc
->base
+ 4, portc
->sgd_paddr
);
446 /* Set autostart at EOL, interrupt on FLAG, stereo, 16 bits */
447 OUTB(devc
, portc
->base
+ 0x02,
448 0x81 | /* Set autostart at EOL, interrupt on FLAG */
454 via97_alloc_port(via97_devc_t
*devc
, int num
)
456 via97_portc_t
*portc
;
458 ddi_dma_cookie_t cookie
;
468 portc
= kmem_zalloc(sizeof (*portc
), KM_SLEEP
);
469 devc
->portc
[num
] = portc
;
471 portc
->started
= B_FALSE
;
472 portc
->base
= devc
->base
+ num
* 0x10;
475 case VIA97_REC_SGD_NUM
:
476 prop
= "record-interrupts";
477 portc
->syncdir
= DDI_DMA_SYNC_FORKERNEL
;
478 caps
= ENGINE_INPUT_CAP
;
481 case VIA97_PLAY_SGD_NUM
:
482 prop
= "play-interrupts";
483 portc
->syncdir
= DDI_DMA_SYNC_FORDEV
;
484 caps
= ENGINE_OUTPUT_CAP
;
488 return (DDI_FAILURE
);
491 /* figure out fragment configuration */
492 portc
->intrs
= ddi_prop_get_int(DDI_DEV_T_ANY
, devc
->dip
,
493 DDI_PROP_DONTPASS
, prop
, VIA97_INTRS
);
495 /* make sure the values are good */
496 if (portc
->intrs
< VIA97_MIN_INTRS
) {
497 audio_dev_warn(adev
, "%s too low, %d, reset to %d",
498 prop
, portc
->intrs
, VIA97_INTRS
);
499 portc
->intrs
= VIA97_INTRS
;
500 } else if (portc
->intrs
> VIA97_MAX_INTRS
) {
501 audio_dev_warn(adev
, "%s too high, %d, reset to %d",
502 prop
, portc
->intrs
, VIA97_INTRS
);
503 portc
->intrs
= VIA97_INTRS
;
506 portc
->fragfr
= 48000 / portc
->intrs
;
507 portc
->fragsz
= portc
->fragfr
* 2 * 2; /* 16 bit stereo frames */
508 portc
->buf_size
= portc
->fragsz
* VIA97_NUM_SGD
;
510 /* first allocate up space for SGD list */
511 if (ddi_dma_alloc_handle(devc
->dip
, &dma_attr_sgd
,
512 DDI_DMA_SLEEP
, NULL
, &portc
->sgd_dmah
) != DDI_SUCCESS
) {
513 audio_dev_warn(adev
, "failed to allocate SGD handle");
514 return (DDI_FAILURE
);
517 if (ddi_dma_mem_alloc(portc
->sgd_dmah
,
518 VIA97_NUM_SGD
* 2 *sizeof (uint32_t), &dev_attr
,
519 DDI_DMA_CONSISTENT
, DDI_DMA_SLEEP
, NULL
, &portc
->sgd_kaddr
,
520 &len
, &portc
->sgd_acch
) != DDI_SUCCESS
) {
521 audio_dev_warn(adev
, "failed to allocate SGD memory");
522 return (DDI_FAILURE
);
525 if (ddi_dma_addr_bind_handle(portc
->sgd_dmah
, NULL
,
526 portc
->sgd_kaddr
, len
, DDI_DMA_CONSISTENT
| DDI_DMA_WRITE
,
527 DDI_DMA_SLEEP
, NULL
, &cookie
, &count
) != DDI_SUCCESS
) {
528 audio_dev_warn(adev
, "failed binding SGD DMA handle");
529 return (DDI_FAILURE
);
531 portc
->sgd_paddr
= cookie
.dmac_address
;
534 if (ddi_dma_alloc_handle(devc
->dip
, &dma_attr_buf
, DDI_DMA_SLEEP
, NULL
,
535 &portc
->buf_dmah
) != DDI_SUCCESS
) {
536 audio_dev_warn(adev
, "failed to allocate BUF handle");
537 return (DDI_FAILURE
);
540 if (ddi_dma_mem_alloc(portc
->buf_dmah
, portc
->buf_size
,
541 &buf_attr
, DDI_DMA_CONSISTENT
, DDI_DMA_SLEEP
, NULL
,
542 &portc
->buf_kaddr
, &len
, &portc
->buf_acch
) != DDI_SUCCESS
) {
543 audio_dev_warn(adev
, "failed to allocate BUF memory");
544 return (DDI_FAILURE
);
547 if (ddi_dma_addr_bind_handle(portc
->buf_dmah
, NULL
, portc
->buf_kaddr
,
548 len
, DDI_DMA_CONSISTENT
| dir
, DDI_DMA_SLEEP
, NULL
, &cookie
,
549 &count
) != DDI_SUCCESS
) {
550 audio_dev_warn(adev
, "failed binding BUF DMA handle");
551 return (DDI_FAILURE
);
553 portc
->buf_paddr
= cookie
.dmac_address
;
555 /* now wire descriptors up */
556 desc
= (void *)portc
->sgd_kaddr
;
557 paddr
= portc
->buf_paddr
;
558 for (int i
= 0; i
< VIA97_NUM_SGD
; i
++) {
561 flags
= 0x40000000 | portc
->fragsz
;
563 if (i
== (VIA97_NUM_SGD
- 1)) {
564 flags
|= 0x80000000; /* EOL */
567 ddi_put32(portc
->sgd_acch
, desc
++, paddr
);
568 ddi_put32(portc
->sgd_acch
, desc
++, flags
);
569 paddr
+= portc
->fragsz
;
572 OUTL(devc
, portc
->base
+ 4, portc
->sgd_paddr
);
573 (void) ddi_dma_sync(portc
->sgd_dmah
, 0, 0, DDI_DMA_SYNC_FORDEV
);
575 portc
->engine
= audio_engine_alloc(&via97_engine_ops
, caps
);
576 if (portc
->engine
== NULL
) {
577 audio_dev_warn(adev
, "audio_engine_alloc failed");
578 return (DDI_FAILURE
);
581 audio_engine_set_private(portc
->engine
, portc
);
582 audio_dev_add_engine(adev
, portc
->engine
);
584 return (DDI_SUCCESS
);
588 via97_setup_intrs(via97_devc_t
*devc
)
593 ddi_intr_handle_t ih
[1];
595 rv
= ddi_intr_alloc(devc
->dip
, ih
, DDI_INTR_TYPE_FIXED
,
596 0, 1, &actual
, DDI_INTR_ALLOC_STRICT
);
597 if ((rv
!= DDI_SUCCESS
) || (actual
!= 1)) {
598 audio_dev_warn(devc
->adev
,
599 "Can't alloc interrupt handle (rv %d actual %d)",
601 return (DDI_FAILURE
);
604 if (ddi_intr_get_pri(ih
[0], &ipri
) != DDI_SUCCESS
) {
605 audio_dev_warn(devc
->adev
, "Can't get interrupt priority");
606 (void) ddi_intr_free(ih
[0]);
607 return (DDI_FAILURE
);
610 if (ddi_intr_add_handler(ih
[0], via97_intr
, devc
, NULL
) !=
612 audio_dev_warn(devc
->adev
, "Can't add interrupt handler");
613 (void) ddi_intr_free(ih
[0]);
614 return (DDI_FAILURE
);
618 mutex_init(&devc
->mutex
, NULL
, MUTEX_DRIVER
, DDI_INTR_PRI(ipri
));
619 mutex_init(&devc
->low_mutex
, NULL
, MUTEX_DRIVER
, DDI_INTR_PRI(ipri
));
620 return (DDI_SUCCESS
);
624 via97_destroy(via97_devc_t
*devc
)
626 if (devc
->ih
!= NULL
) {
627 (void) ddi_intr_disable(devc
->ih
);
628 (void) ddi_intr_remove_handler(devc
->ih
);
629 (void) ddi_intr_free(devc
->ih
);
630 mutex_destroy(&devc
->mutex
);
631 mutex_destroy(&devc
->low_mutex
);
635 kstat_delete(devc
->ksp
);
638 for (int i
= 0; i
< VIA97_NUM_PORTC
; i
++) {
639 via97_portc_t
*portc
= devc
->portc
[i
];
643 audio_dev_remove_engine(devc
->adev
, portc
->engine
);
644 audio_engine_free(portc
->engine
);
646 if (portc
->sgd_paddr
) {
647 (void) ddi_dma_unbind_handle(portc
->sgd_dmah
);
649 if (portc
->sgd_acch
) {
650 ddi_dma_mem_free(&portc
->sgd_acch
);
652 if (portc
->sgd_dmah
) {
653 ddi_dma_free_handle(&portc
->sgd_dmah
);
655 if (portc
->buf_paddr
) {
656 (void) ddi_dma_unbind_handle(portc
->buf_dmah
);
658 if (portc
->buf_acch
) {
659 ddi_dma_mem_free(&portc
->buf_acch
);
661 if (portc
->buf_dmah
) {
662 ddi_dma_free_handle(&portc
->buf_dmah
);
664 kmem_free(portc
, sizeof (*portc
));
667 if (devc
->ac97
!= NULL
) {
668 ac97_free(devc
->ac97
);
670 if (devc
->adev
!= NULL
) {
671 audio_dev_free(devc
->adev
);
673 if (devc
->regsh
!= NULL
) {
674 ddi_regs_map_free(&devc
->regsh
);
676 if (devc
->pcih
!= NULL
) {
677 pci_config_teardown(&devc
->pcih
);
679 kmem_free(devc
, sizeof (*devc
));
683 via97_hwinit(via97_devc_t
*devc
)
685 ddi_acc_handle_t pcih
= devc
->pcih
;
688 /* Enable codec, etc */
690 pci_config_put8(pcih
, 0x41, 0xc0);
692 tmp
= pci_config_get8(pcih
, 0x41);
693 pci_config_put8(pcih
, 0x41, tmp
| 0x0c);
696 /* disable game port/MIDI */
697 pci_config_put8(pcih
, 0x42, 0x00);
699 pci_config_put8(pcih
, 0x48, 0x00);
701 /* Enable interrupt on FLAG and on EOL */
702 tmp
= INB(devc
, devc
->base
+ 0x22);
703 OUTB(devc
, devc
->base
+ 0x22, tmp
| 0x83);
707 via97_attach(dev_info_t
*dip
)
709 uint16_t pci_command
, vendor
, device
;
711 ddi_acc_handle_t pcih
;
713 devc
= kmem_zalloc(sizeof (*devc
), KM_SLEEP
);
715 ddi_set_driver_private(dip
, devc
);
717 if ((devc
->adev
= audio_dev_alloc(dip
, 0)) == NULL
) {
718 cmn_err(CE_WARN
, "audio_dev_alloc failed");
722 if (pci_config_setup(dip
, &pcih
) != DDI_SUCCESS
) {
723 audio_dev_warn(devc
->adev
, "pci_config_setup failed");
728 vendor
= pci_config_get16(pcih
, PCI_CONF_VENID
);
729 device
= pci_config_get16(pcih
, PCI_CONF_DEVID
);
730 if (vendor
!= VIA_VENDOR_ID
||
731 device
!= VIA_82C686
) {
732 audio_dev_warn(devc
->adev
, "Hardware not recognized "
733 "(vendor=%x, dev=%x)", vendor
, device
);
737 pci_command
= pci_config_get16(pcih
, PCI_CONF_COMM
);
738 pci_command
|= PCI_COMM_ME
| PCI_COMM_IO
;
739 pci_config_put16(pcih
, PCI_CONF_COMM
, pci_command
);
741 if ((ddi_regs_map_setup(dip
, 1, &devc
->base
, 0, 0, &dev_attr
,
742 &devc
->regsh
)) != DDI_SUCCESS
) {
743 audio_dev_warn(devc
->adev
, "failed to map registers");
747 audio_dev_set_description(devc
->adev
, "VIA 82C686 Audio");
751 if ((via97_alloc_port(devc
, VIA97_PLAY_SGD_NUM
) != DDI_SUCCESS
) ||
752 (via97_alloc_port(devc
, VIA97_REC_SGD_NUM
) != DDI_SUCCESS
)) {
756 if (via97_setup_intrs(devc
) != DDI_SUCCESS
) {
760 devc
->ac97
= ac97_alloc(dip
, via97_read_ac97
, via97_write_ac97
, devc
);
761 if (devc
->ac97
== NULL
) {
762 audio_dev_warn(devc
->adev
, "failed to allocate ac97 handle");
766 if (ac97_init(devc
->ac97
, devc
->adev
) != DDI_SUCCESS
) {
767 audio_dev_warn(devc
->adev
, "failed to init ac97");
771 /* set up kernel statistics */
772 if ((devc
->ksp
= kstat_create(VIA97_NAME
, ddi_get_instance(dip
),
773 VIA97_NAME
, "controller", KSTAT_TYPE_INTR
, 1,
774 KSTAT_FLAG_PERSISTENT
)) != NULL
) {
775 kstat_install(devc
->ksp
);
778 if (audio_dev_register(devc
->adev
) != DDI_SUCCESS
) {
779 audio_dev_warn(devc
->adev
, "unable to register with framework");
783 (void) ddi_intr_enable(devc
->ih
);
786 return (DDI_SUCCESS
);
790 return (DDI_FAILURE
);
794 via97_resume(dev_info_t
*dip
)
798 devc
= ddi_get_driver_private(dip
);
802 /* allow ac97 operations again */
803 ac97_resume(devc
->ac97
);
805 mutex_enter(&devc
->mutex
);
806 devc
->suspended
= B_FALSE
;
807 for (int i
= 0; i
< VIA97_NUM_PORTC
; i
++) {
809 via97_portc_t
*portc
= devc
->portc
[i
];
811 if (portc
->engine
!= NULL
)
812 audio_engine_reset(portc
->engine
);
815 via97_reset_port(portc
);
817 if (portc
->started
) {
818 via97_start_port(portc
);
820 via97_stop_port(portc
);
823 mutex_exit(&devc
->mutex
);
824 return (DDI_SUCCESS
);
828 via97_detach(via97_devc_t
*devc
)
830 if (audio_dev_unregister(devc
->adev
) != DDI_SUCCESS
)
831 return (DDI_FAILURE
);
834 return (DDI_SUCCESS
);
838 via97_suspend(via97_devc_t
*devc
)
840 ac97_suspend(devc
->ac97
);
842 mutex_enter(&devc
->mutex
);
843 for (int i
= 0; i
< VIA97_NUM_PORTC
; i
++) {
845 via97_portc_t
*portc
= devc
->portc
[i
];
846 via97_stop_port(portc
);
848 devc
->suspended
= B_TRUE
;
849 mutex_exit(&devc
->mutex
);
850 return (DDI_SUCCESS
);
853 static int via97_ddi_attach(dev_info_t
*, ddi_attach_cmd_t
);
854 static int via97_ddi_detach(dev_info_t
*, ddi_detach_cmd_t
);
855 static int via97_ddi_quiesce(dev_info_t
*);
857 static struct dev_ops via97_dev_ops
= {
861 nulldev
, /* identify */
863 via97_ddi_attach
, /* attach */
864 via97_ddi_detach
, /* detach */
869 via97_ddi_quiesce
, /* quiesce */
872 static struct modldrv via97_modldrv
= {
873 &mod_driverops
, /* drv_modops */
874 "Via 82C686 Audio", /* linkinfo */
875 &via97_dev_ops
, /* dev_ops */
878 static struct modlinkage modlinkage
= {
880 { &via97_modldrv
, NULL
}
888 audio_init_ops(&via97_dev_ops
, VIA97_NAME
);
889 if ((rv
= mod_install(&modlinkage
)) != 0) {
890 audio_fini_ops(&via97_dev_ops
);
900 if ((rv
= mod_remove(&modlinkage
)) == 0) {
901 audio_fini_ops(&via97_dev_ops
);
907 _info(struct modinfo
*modinfop
)
909 return (mod_info(&modlinkage
, modinfop
));
913 via97_ddi_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
917 return (via97_attach(dip
));
920 return (via97_resume(dip
));
923 return (DDI_FAILURE
);
928 via97_ddi_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
932 devc
= ddi_get_driver_private(dip
);
936 return (via97_detach(devc
));
939 return (via97_suspend(devc
));
942 return (DDI_FAILURE
);
947 via97_ddi_quiesce(dev_info_t
*dip
)
951 devc
= ddi_get_driver_private(dip
);
953 for (int i
= 0; i
< VIA97_NUM_PORTC
; i
++) {
955 via97_portc_t
*portc
= devc
->portc
[i
];
956 via97_stop_port(portc
);
960 * Turn off the hardware
962 OUTB(devc
, devc
->base
+ 0x01, 0x40);
963 OUTB(devc
, devc
->base
+ 0x11, 0x40);
964 OUTB(devc
, devc
->base
+ 0x02, 0);
965 OUTB(devc
, devc
->base
+ 0x12, 0);
966 OUTL(devc
, devc
->base
+ 0x04, 0);
967 OUTL(devc
, devc
->base
+ 0x14, 0);
968 OUTL(devc
, devc
->base
+ 0x22, 0);
969 return (DDI_SUCCESS
);