PSARC 2009/639 audio_engine_playahead/qlen
[unleashed.git] / usr / src / uts / common / io / audio / drv / audiovia97 / audiovia97.c
blob814d4bbd18d934c53faef48bb5ee6227a4f50530
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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>
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>
46 #include "audiovia97.h"
48 static struct ddi_device_acc_attr dev_attr = {
49 DDI_DEVICE_ATTR_V0,
50 DDI_STRUCTURE_LE_ACC,
51 DDI_STRICTORDER_ACC
54 static struct ddi_device_acc_attr buf_attr = {
55 DDI_DEVICE_ATTR_V0,
56 DDI_NEVERSWAP_ACC,
57 DDI_STRICTORDER_ACC
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 */
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 */
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,
119 via97_open,
120 via97_close,
121 via97_start,
122 via97_stop,
123 via97_count,
124 via97_format,
125 via97_channels,
126 via97_rate,
127 via97_sync,
128 NULL,
129 NULL,
130 NULL
133 static uint16_t
134 via97_read_ac97(void *arg, uint8_t index)
136 via97_devc_t *devc = arg;
137 int tmp, addr, i;
139 /* Index has only 7 bits */
140 if (index > 0x7F)
141 return (0xffff);
143 mutex_enter(&devc->low_mutex);
144 addr = (index << 16) + CODEC_RD;
145 OUTL(devc, devc->base + AC97CODEC, addr);
146 drv_usecwait(100);
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)
152 break;
153 drv_usecwait(50);
155 if (i == CODEC_TIMEOUT_COUNT) {
156 mutex_exit(&devc->low_mutex);
157 return (0xffff);
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);
168 return (0xffff);
171 static void
172 via97_write_ac97(void *arg, uint8_t index, uint16_t data)
174 via97_devc_t *devc = arg;
175 int value = 0;
176 unsigned int i = 0;
178 mutex_enter(&devc->low_mutex);
179 value = (index << 16) + data;
180 OUTL(devc, devc->base + AC97CODEC, value);
181 drv_usecwait(100);
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))
187 break;
188 drv_usecwait(50);
190 mutex_exit(&devc->low_mutex);
193 static uint_t
194 via97_recintr(via97_devc_t *devc)
196 int status;
198 status = INB(devc, devc->base + 0x10);
200 if (!(status & 0x01)) /* No interrupt */
201 return (B_FALSE);
203 audio_engine_produce(devc->portc[VIA97_REC_SGD_NUM]->engine);
205 OUTB(devc, devc->base + 0x10, status | 0x01); /* Ack */
206 return (B_TRUE);
209 static uint_t
210 via97_playintr(via97_devc_t *devc)
212 int status;
214 status = INB(devc, devc->base + 0x00);
216 if (!(status & 0x01)) /* No interrupt */
217 return (B_FALSE);
219 audio_engine_consume(devc->portc[VIA97_PLAY_SGD_NUM]->engine);
221 OUTB(devc, devc->base + 0x00, status | 0x01); /* Ack */
222 return (B_TRUE);
225 static uint_t
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);
240 if (devc->ksp) {
241 VIA97_KIOP(devc)->intrs[KSTAT_INTR_HARD]++;
244 return (DDI_INTR_CLAIMED);
248 * Audio routines
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;
261 portc->count = 0;
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);
270 return (0);
273 void
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);
297 return (0);
300 void
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));
327 return (2);
331 via97_rate(void *arg)
333 _NOTE(ARGUNUSED(arg));
335 return (48000);
338 void
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);
347 uint64_t
348 via97_count(void *arg)
350 via97_portc_t *portc = arg;
351 via97_devc_t *devc = portc->devc;
352 uint64_t val;
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
360 * stereo frames.
362 val = portc->count +
363 (portc->fragfr - (portc->resid / (2 * 2)));
364 mutex_exit(&devc->mutex);
366 return (val);
370 /* private implementation bits */
372 void
373 via97_start_port(via97_portc_t *portc)
375 via97_devc_t *devc = portc->devc;
377 ASSERT(mutex_owned(&devc->mutex));
379 if (devc->suspended)
380 return;
381 OUTB(devc, portc->base + 0x01, 0x80); /* Start */
384 void
385 via97_stop_port(via97_portc_t *portc)
387 via97_devc_t *devc = portc->devc;
389 if (devc->suspended)
390 return;
392 OUTB(devc, portc->base + 0x01, 0x40); /* Stop */
395 void
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;
406 uint32_t n;
408 ASSERT(mutex_owned(&devc->mutex));
409 if (devc->suspended) {
410 portc->cur_frag = 0;
411 portc->resid = portc->fragsz;
412 n = 0;
413 } else {
414 resid = INL(devc, portc->base + 0x0c) & 0xffffff;
415 resid = portc->fragsz - resid;
417 frag =
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;
425 } else {
426 n = frag + VIA97_NUM_SGD - portc->cur_frag;
428 portc->count += (n * portc->fragfr);
429 portc->cur_frag = frag;
433 void
434 via97_reset_port(via97_portc_t *portc)
436 via97_devc_t *devc = portc->devc;
438 portc->cur_frag = 0;
439 portc->resid = portc->fragsz;
441 if (devc->suspended)
442 return;
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 */
449 0x20 | /* 16 bits */
450 0x10); /* Stereo */
454 via97_alloc_port(via97_devc_t *devc, int num)
456 via97_portc_t *portc;
457 size_t len;
458 ddi_dma_cookie_t cookie;
459 uint_t count;
460 int dir;
461 char *prop;
462 unsigned caps;
463 audio_dev_t *adev;
464 uint32_t *desc;
465 uint32_t paddr;
467 adev = devc->adev;
468 portc = kmem_zalloc(sizeof (*portc), KM_SLEEP);
469 devc->portc[num] = portc;
470 portc->devc = devc;
471 portc->started = B_FALSE;
472 portc->base = devc->base + num * 0x10;
474 switch (num) {
475 case VIA97_REC_SGD_NUM:
476 prop = "record-interrupts";
477 portc->syncdir = DDI_DMA_SYNC_FORKERNEL;
478 caps = ENGINE_INPUT_CAP;
479 dir = DDI_DMA_READ;
480 break;
481 case VIA97_PLAY_SGD_NUM:
482 prop = "play-interrupts";
483 portc->syncdir = DDI_DMA_SYNC_FORDEV;
484 caps = ENGINE_OUTPUT_CAP;
485 dir = DDI_DMA_WRITE;
486 break;
487 default:
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;
533 /* now buffers */
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++) {
559 uint32_t flags;
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)
590 uint_t ipri;
591 int actual;
592 int rv;
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)",
600 rv, actual);
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) !=
611 DDI_SUCCESS) {
612 audio_dev_warn(devc->adev, "Can't add interrupt handler");
613 (void) ddi_intr_free(ih[0]);
614 return (DDI_FAILURE);
617 devc->ih = ih[0];
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);
623 void
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);
634 if (devc->ksp) {
635 kstat_delete(devc->ksp);
638 for (int i = 0; i < VIA97_NUM_PORTC; i++) {
639 via97_portc_t *portc = devc->portc[i];
640 if (!portc)
641 continue;
642 if (portc->engine) {
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));
682 void
683 via97_hwinit(via97_devc_t *devc)
685 ddi_acc_handle_t pcih = devc->pcih;
686 uint32_t tmp;
688 /* Enable codec, etc */
690 pci_config_put8(pcih, 0x41, 0xc0);
691 drv_usecwait(10);
692 tmp = pci_config_get8(pcih, 0x41);
693 pci_config_put8(pcih, 0x41, tmp | 0x0c);
694 drv_usecwait(10);
696 /* disable game port/MIDI */
697 pci_config_put8(pcih, 0x42, 0x00);
698 /* disable FM io */
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;
710 via97_devc_t *devc;
711 ddi_acc_handle_t pcih;
713 devc = kmem_zalloc(sizeof (*devc), KM_SLEEP);
714 devc->dip = dip;
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");
719 goto error;
722 if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
723 audio_dev_warn(devc->adev, "pci_config_setup failed");
724 goto error;
726 devc->pcih = pcih;
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);
734 goto error;
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");
744 goto error;
747 audio_dev_set_description(devc->adev, "VIA 82C686 Audio");
749 via97_hwinit(devc);
751 if ((via97_alloc_port(devc, VIA97_PLAY_SGD_NUM) != DDI_SUCCESS) ||
752 (via97_alloc_port(devc, VIA97_REC_SGD_NUM) != DDI_SUCCESS)) {
753 goto error;
756 if (via97_setup_intrs(devc) != DDI_SUCCESS) {
757 goto error;
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");
763 goto error;
766 if (ac97_init(devc->ac97, devc->adev) != DDI_SUCCESS) {
767 audio_dev_warn(devc->adev, "failed to init ac97");
768 goto error;
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");
780 goto error;
783 (void) ddi_intr_enable(devc->ih);
784 ddi_report_dev(dip);
786 return (DDI_SUCCESS);
788 error:
789 via97_destroy(devc);
790 return (DDI_FAILURE);
794 via97_resume(dev_info_t *dip)
796 via97_devc_t *devc;
798 devc = ddi_get_driver_private(dip);
800 via97_hwinit(devc);
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);
814 /* reset the port */
815 via97_reset_port(portc);
817 if (portc->started) {
818 via97_start_port(portc);
819 } else {
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);
833 via97_destroy(devc);
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 = {
858 DEVO_REV, /* rev */
859 0, /* refcnt */
860 NULL, /* getinfo */
861 nulldev, /* identify */
862 nulldev, /* probe */
863 via97_ddi_attach, /* attach */
864 via97_ddi_detach, /* detach */
865 nodev, /* reset */
866 NULL, /* cb_ops */
867 NULL, /* bus_ops */
868 NULL, /* power */
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 = {
879 MODREV_1,
880 { &via97_modldrv, NULL }
884 _init(void)
886 int rv;
888 audio_init_ops(&via97_dev_ops, VIA97_NAME);
889 if ((rv = mod_install(&modlinkage)) != 0) {
890 audio_fini_ops(&via97_dev_ops);
892 return (rv);
896 _fini(void)
898 int rv;
900 if ((rv = mod_remove(&modlinkage)) == 0) {
901 audio_fini_ops(&via97_dev_ops);
903 return (rv);
907 _info(struct modinfo *modinfop)
909 return (mod_info(&modlinkage, modinfop));
913 via97_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
915 switch (cmd) {
916 case DDI_ATTACH:
917 return (via97_attach(dip));
919 case DDI_RESUME:
920 return (via97_resume(dip));
922 default:
923 return (DDI_FAILURE);
928 via97_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
930 via97_devc_t *devc;
932 devc = ddi_get_driver_private(dip);
934 switch (cmd) {
935 case DDI_DETACH:
936 return (via97_detach(devc));
938 case DDI_SUSPEND:
939 return (via97_suspend(devc));
941 default:
942 return (DDI_FAILURE);
947 via97_ddi_quiesce(dev_info_t *dip)
949 via97_devc_t *devc;
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);