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