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
auvia_read_ac97(void * arg,uint8_t index)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
auvia_write_ac97(void * arg,uint8_t index,uint16_t data)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
auvia_open(void * arg,int flag,unsigned * nframesp,caddr_t * bufp)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
auvia_close(void * arg)209 auvia_close(void *arg)
210 {
211 _NOTE(ARGUNUSED(arg));
212 }
213
214 int
auvia_start(void * arg)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
auvia_stop(void * arg)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
auvia_format(void * arg)235 auvia_format(void *arg)
236 {
237 _NOTE(ARGUNUSED(arg));
238
239 return (AUDIO_FORMAT_S16_LE);
240 }
241
242 int
auvia_channels(void * arg)243 auvia_channels(void *arg)
244 {
245 auvia_portc_t *portc = arg;
246
247 return (portc->nchan);
248 }
249
250 int
auvia_rate(void * arg)251 auvia_rate(void *arg)
252 {
253 _NOTE(ARGUNUSED(arg));
254
255 return (48000);
256 }
257
258 void
auvia_sync(void * arg,unsigned nframes)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
auvia_count(void * arg)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
auvia_reset_output(auvia_portc_t * portc)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
auvia_reset_input(auvia_portc_t * portc)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
auvia_alloc_port(auvia_devc_t * devc,int num)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
auvia_destroy(auvia_devc_t * devc)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
auvia_hwinit(auvia_devc_t * devc)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
auvia_attach(dev_info_t * dip)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
auvia_resume(dev_info_t * dip)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
auvia_detach(auvia_devc_t * devc)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
auvia_suspend(auvia_devc_t * devc)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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)729 _info(struct modinfo *modinfop)
730 {
731 return (mod_info(&modlinkage, modinfop));
732 }
733
734 int
auvia_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
auvia_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
auvia_ddi_quiesce(dev_info_t * dip)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