audiovia823x.c revision 9484:fbd5ddc28e96
1233294Sstas/*
272445Sassar * CDDL HEADER START
372445Sassar *
472445Sassar * The contents of this file are subject to the terms of the
572445Sassar * Common Development and Distribution License (the "License").
672445Sassar * You may not use this file except in compliance with the License.
772445Sassar *
872445Sassar * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
972445Sassar * or http://www.opensolaris.org/os/licensing.
1072445Sassar * See the License for the specific language governing permissions
1172445Sassar * and limitations under the License.
1272445Sassar *
1372445Sassar * When distributing Covered Code, include this CDDL HEADER in each
1472445Sassar * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15178825Sdfr * If applicable, add the following below this CDDL HEADER, with the
16178825Sdfr * fields enclosed by brackets "[]" replaced with your own identifying
17178825Sdfr * information: Portions Copyright [yyyy] [name of copyright owner]
18178825Sdfr *
19178825Sdfr * CDDL HEADER END
20178825Sdfr */
21178825Sdfr
22178825Sdfr/*
23178825Sdfr * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24178825Sdfr * Use is subject to license terms.
25178825Sdfr */
26178825Sdfr
27178825Sdfr/*
28178825Sdfr * Purpose: Driver for the VIA8233/8235 AC97 audio controller
29178825Sdfr */
30178825Sdfr/*
31178825Sdfr * This file is part of Open Sound System
32178825Sdfr *
33233294Sstas * Copyright (C) 4Front Technologies 1996-2008.
34233294Sstas *
35178825Sdfr * This software is released under CDDL 1.0 source license.
36178825Sdfr * See the COPYING file included in the main directory of this source
37178825Sdfr * distribution for the license terms and conditions.
38178825Sdfr */
39178825Sdfr
40178825Sdfr#include <sys/types.h>
41178825Sdfr#include <sys/modctl.h>
42178825Sdfr#include <sys/kmem.h>
43178825Sdfr#include <sys/conf.h>
4472445Sassar#include <sys/ddi.h>
45233294Sstas#include <sys/sunddi.h>
46233294Sstas#include <sys/pci.h>
47233294Sstas#include <sys/note.h>
48233294Sstas#include <sys/audio/audio_driver.h>
49233294Sstas#include <sys/audio/ac97.h>
50233294Sstas
51233294Sstas#include "audiovia823x.h"
52233294Sstas
53233294Sstasstatic struct ddi_device_acc_attr dev_attr = {
54233294Sstas	DDI_DEVICE_ATTR_V0,
55233294Sstas	DDI_STRUCTURE_LE_ACC,
5672445Sassar	DDI_STRICTORDER_ACC
5772445Sassar};
5872445Sassar
5972445Sassarstatic struct ddi_device_acc_attr buf_attr = {
60178825Sdfr	DDI_DEVICE_ATTR_V0,
6172445Sassar	DDI_NEVERSWAP_ACC,
6272445Sassar	DDI_STRICTORDER_ACC
6372445Sassar};
64233294Sstas
6572445Sassarstatic ddi_dma_attr_t dma_attr_sgd = {
66233294Sstas	DMA_ATTR_V0,		/* version number */
67233294Sstas	0x00000000,		/* low DMA address range */
68233294Sstas	0xffffffff,		/* high DMA address range */
6972445Sassar	0x0000ffff,		/* DMA counter register */
7072445Sassar	8,			/* DMA address alignment */
71233294Sstas	0x3c,			/* DMA burstsizes */
72233294Sstas	8,			/* min effective DMA size */
7372445Sassar	0xffffffff,		/* max DMA xfer size */
7472445Sassar	0x00000fff,		/* segment boundary */
7572445Sassar	1,			/* s/g length */
7672445Sassar	8,			/* granularity of device */
7772445Sassar	0			/* Bus specific DMA flags */
7872445Sassar};
7972445Sassar
8072445Sassarstatic ddi_dma_attr_t dma_attr_buf = {
81233294Sstas	DMA_ATTR_V0,		/* version number */
8272445Sassar	0x00000000,		/* low DMA address range */
8372445Sassar	0xffffffff,		/* high DMA address range */
8472445Sassar	0x0001fffe,		/* DMA counter register */
8572445Sassar	4,			/* DMA address alignment */
8672445Sassar	0x3c,			/* DMA burstsizes */
8772445Sassar	4,			/* min effective DMA size */
8872445Sassar	0x0001ffff,		/* max DMA xfer size */
8972445Sassar	0x0001ffff,		/* segment boundary */
9072445Sassar	1,			/* s/g length */
9172445Sassar	4,			/* granularity of device */
9272445Sassar	0			/* Bus specific DMA flags */
9372445Sassar};
94233294Sstas
95233294Sstasstatic int auvia_attach(dev_info_t *);
96233294Sstasstatic int auvia_resume(dev_info_t *);
97233294Sstasstatic int auvia_detach(auvia_devc_t *);
98233294Sstasstatic int auvia_suspend(auvia_devc_t *);
99233294Sstas
100233294Sstasstatic int auvia_open(void *, int, unsigned *, unsigned *, caddr_t *);
101233294Sstasstatic void auvia_close(void *);
10272445Sassarstatic int auvia_start(void *);
10372445Sassarstatic void auvia_stop(void *);
10472445Sassarstatic int auvia_format(void *);
10572445Sassarstatic int auvia_channels(void *);
10672445Sassarstatic int auvia_rate(void *);
10772445Sassarstatic uint64_t auvia_count(void *);
10872445Sassarstatic void auvia_sync(void *, unsigned);
109233294Sstasstatic size_t auvia_qlen(void *);
110233294Sstas
111233294Sstasstatic uint16_t auvia_read_ac97(void *, uint8_t);
112233294Sstasstatic void auvia_write_ac97(void *, uint8_t, uint16_t);
113233294Sstasstatic int auvia_alloc_port(auvia_devc_t *, int);
114178825Sdfrstatic void auvia_start_port(auvia_portc_t *);
115233294Sstasstatic void auvia_stop_port(auvia_portc_t *);
116178825Sdfrstatic void auvia_update_port(auvia_portc_t *);
117233294Sstasstatic void auvia_reset_input(auvia_portc_t *);
118178825Sdfrstatic void auvia_reset_output(auvia_portc_t *);
119233294Sstasstatic void auvia_destroy(auvia_devc_t *);
120178825Sdfrstatic int auvia_setup_intrs(auvia_devc_t *);
121178825Sdfrstatic void auvia_hwinit(auvia_devc_t *);
122178825Sdfrstatic uint_t auvia_intr(caddr_t, caddr_t);
12372445Sassar
12472445Sassarstatic audio_engine_ops_t auvia_engine_ops = {
12572445Sassar	AUDIO_ENGINE_VERSION,
126233294Sstas	auvia_open,
12772445Sassar	auvia_close,
128233294Sstas	auvia_start,
12972445Sassar	auvia_stop,
130233294Sstas	auvia_count,
13172445Sassar	auvia_format,
132233294Sstas	auvia_channels,
133233294Sstas	auvia_rate,
134233294Sstas	auvia_sync,
135233294Sstas	auvia_qlen
136233294Sstas};
137233294Sstas
138233294Sstasstatic uint16_t
139233294Sstasauvia_read_ac97(void *arg, uint8_t index)
140233294Sstas{
141233294Sstas	auvia_devc_t *devc = arg;
142233294Sstas	uint32_t val = 0;
143233294Sstas	int i;
144233294Sstas
145233294Sstas	mutex_enter(&devc->low_mutex);
146233294Sstas
14772445Sassar	val = ((uint32_t)index << 16) | CODEC_RD;
14872445Sassar	OUTL(devc, devc->base + REG_CODEC, val);
14972445Sassar	drv_usecwait(100);
150233294Sstas
151233294Sstas	/* Check AC CODEC access time out */
152233294Sstas	for (i = 0; i < CODEC_TIMEOUT_COUNT; i++) {
153233294Sstas
154233294Sstas		/* if send command over, break */
155233294Sstas		if (INL(devc, devc->base + REG_CODEC) & CODEC_STA_VALID)
156233294Sstas			break;
157233294Sstas		drv_usecwait(50);
158233294Sstas	}
159233294Sstas
160233294Sstas	if (i == CODEC_TIMEOUT_COUNT) {
161233294Sstas		goto failed;
162233294Sstas	}
163233294Sstas
164233294Sstas	/* Check if Index still ours? If yes, return data, else return FAIL */
165233294Sstas	val = INL(devc, devc->base + REG_CODEC);
166233294Sstas	OUTB(devc, devc->base + REG_CODEC + 3, 0x02);
16772445Sassar	if (((val & CODEC_INDEX) >> 16) == index) {
16872445Sassar		mutex_exit(&devc->low_mutex);
16972445Sassar		return (val & CODEC_DATA);
17072445Sassar	}
17172445Sassar
17272445Sassarfailed:
17372445Sassar	mutex_exit(&devc->low_mutex);
174233294Sstas	return (0xffff);
175233294Sstas}
176233294Sstas
177233294Sstasstatic void
178233294Sstasauvia_write_ac97(void *arg, uint8_t index, uint16_t data)
179233294Sstas{
180233294Sstas	auvia_devc_t *devc = arg;
181233294Sstas	uint32_t val = 0;
182233294Sstas	int i = 0;
183233294Sstas
184233294Sstas	mutex_enter(&devc->low_mutex);
185233294Sstas
186233294Sstas	val = ((uint32_t)index << 16) | data | CODEC_WR;
187233294Sstas	OUTL(devc, devc->base + REG_CODEC, val);
188233294Sstas	drv_usecwait(100);
189233294Sstas
190233294Sstas	/* Check AC CODEC access time out */
191233294Sstas	for (i = 0; i < CODEC_TIMEOUT_COUNT; i++) {
192233294Sstas		/* if send command over, break */
193233294Sstas		if (!(INL(devc, devc->base + REG_CODEC) & CODEC_IN_CMD))
194233294Sstas			break;
195233294Sstas		drv_usecwait(50);
196233294Sstas	}
197233294Sstas
198233294Sstas	mutex_exit(&devc->low_mutex);
199233294Sstas}
200233294Sstas
201233294Sstasstatic uint_t
202233294Sstasauvia_intr(caddr_t argp, caddr_t nocare)
203233294Sstas{
204233294Sstas	auvia_devc_t	*devc = (void *)argp;
205233294Sstas	auvia_portc_t	*portc;
206233294Sstas	uint32_t	gstat;
207233294Sstas	uint8_t		status;
208233294Sstas	unsigned	intrs = 0;
209233294Sstas
210233294Sstas	_NOTE(ARGUNUSED(nocare));
211233294Sstas
212233294Sstas	mutex_enter(&devc->mutex);
213233294Sstas	if (devc->suspended) {
214233294Sstas		mutex_exit(&devc->mutex);
215233294Sstas		return (DDI_INTR_UNCLAIMED);
216233294Sstas	}
217233294Sstas
218233294Sstas	gstat = INL(devc, devc->base + REG_GSTAT);
219233294Sstas	if (gstat == 0) {
220233294Sstas		mutex_exit(&devc->mutex);
221233294Sstas		return (DDI_INTR_UNCLAIMED);
222233294Sstas	}
223233294Sstas
224233294Sstas	for (int i = 0; i < AUVIA_NUM_PORTC; i++) {
225233294Sstas
226233294Sstas		portc = devc->portc[i];
227233294Sstas
228233294Sstas		status = INB(devc, portc->base + OFF_STATUS);
229233294Sstas		if ((status & STATUS_INTR) == 0) {
230233294Sstas			/* clear any other interrupts */
231233294Sstas			continue;
232233294Sstas		}
233233294Sstas
234233294Sstas		/*
235233294Sstas		 * NB: The old code did some goofy things to update
236233294Sstas		 * the last valid SGD.  However, since we don't ever
237233294Sstas		 * reach the last valid SGD (because we loop first), I
238233294Sstas		 * don't believe we need to do that.  It would appear
239233294Sstas		 * that NetBSD does the same.
240233294Sstas		 */
241233294Sstas		/* port interrupt */
242233294Sstas		if (portc->started) {
243233294Sstas			intrs |= (1U << i);
244233294Sstas		}
245233294Sstas		/* XXX: do we really need to do this? */
24672445Sassar		OUTB(devc, portc->base + OFF_STATUS, status);
24772445Sassar	}
24872445Sassar
24972445Sassar	OUTL(devc, devc->base + REG_GSTAT, gstat);
250233294Sstas
25172445Sassar	mutex_exit(&devc->mutex);
252233294Sstas	if (intrs & (1U << AUVIA_PLAY_SGD_NUM)) {
253178825Sdfr		audio_engine_consume(devc->portc[AUVIA_PLAY_SGD_NUM]->engine);
25472445Sassar	}
255233294Sstas	if (intrs & (1U << AUVIA_REC_SGD_NUM)) {
256233294Sstas		audio_engine_produce(devc->portc[AUVIA_REC_SGD_NUM]->engine);
25772445Sassar	}
25872445Sassar	if (devc->ksp) {
259233294Sstas		AUVIA_KIOP(devc)->intrs[KSTAT_INTR_HARD]++;
260233294Sstas	}
261233294Sstas
262233294Sstas	return (DDI_INTR_CLAIMED);
263233294Sstas}
264233294Sstas
265233294Sstas/*
266233294Sstas * Audio routines
267233294Sstas */
268233294Sstas
26972445Sassarint
270233294Sstasauvia_open(void *arg, int flag,
27172445Sassar    unsigned *fragfrp, unsigned *nfragsp, caddr_t *bufp)
27272445Sassar{
27372445Sassar	auvia_portc_t	 *portc = arg;
27472445Sassar	auvia_devc_t	 *devc = portc->devc;
27572445Sassar
27672445Sassar	_NOTE(ARGUNUSED(flag));
27772445Sassar
27872445Sassar	portc->started = B_FALSE;
27972445Sassar	portc->count = 0;
28072445Sassar	*fragfrp = portc->fragfr;
281233294Sstas	*nfragsp = AUVIA_NUM_SGD;
28272445Sassar	*bufp = portc->buf_kaddr;
28372445Sassar
284233294Sstas	mutex_enter(&devc->mutex);
28572445Sassar	portc->reset(portc);
28672445Sassar	mutex_exit(&devc->mutex);
28772445Sassar
28872445Sassar	return (0);
289178825Sdfr}
290233294Sstas
291233294Sstasvoid
29272445Sassarauvia_close(void *arg)
293233294Sstas{
29472445Sassar	auvia_portc_t	 *portc = arg;
295233294Sstas	auvia_devc_t	 *devc = portc->devc;
296233294Sstas
29772445Sassar	mutex_enter(&devc->mutex);
29872445Sassar	auvia_stop_port(portc);
29972445Sassar	portc->started = B_FALSE;
30072445Sassar	mutex_exit(&devc->mutex);
301233294Sstas}
302233294Sstas
303233294Sstasint
304233294Sstasauvia_start(void *arg)
305233294Sstas{
306233294Sstas	auvia_portc_t	*portc = arg;
307233294Sstas	auvia_devc_t	*devc = portc->devc;
308233294Sstas
309233294Sstas	mutex_enter(&devc->mutex);
310233294Sstas	if (!portc->started) {
311233294Sstas		auvia_start_port(portc);
312233294Sstas		portc->started = B_TRUE;
313233294Sstas	}
31472445Sassar	mutex_exit(&devc->mutex);
31572445Sassar	return (0);
31672445Sassar}
31772445Sassar
31872445Sassarvoid
319233294Sstasauvia_stop(void *arg)
320233294Sstas{
32172445Sassar	auvia_portc_t	*portc = arg;
322233294Sstas	auvia_devc_t	*devc = portc->devc;
32372445Sassar
324233294Sstas	mutex_enter(&devc->mutex);
325233294Sstas	if (portc->started) {
32672445Sassar		auvia_stop_port(portc);
32772445Sassar		portc->started = B_FALSE;
32872445Sassar	}
32972445Sassar	mutex_exit(&devc->mutex);
330233294Sstas}
331233294Sstas
332233294Sstasint
333233294Sstasauvia_format(void *arg)
334233294Sstas{
33572445Sassar	_NOTE(ARGUNUSED(arg));
336233294Sstas
337233294Sstas	return (AUDIO_FORMAT_S16_LE);
338233294Sstas}
339233294Sstas
340233294Sstasint
341233294Sstasauvia_channels(void *arg)
342233294Sstas{
343233294Sstas	auvia_portc_t	*portc = arg;
344233294Sstas
345233294Sstas	return (portc->nchan);
34672445Sassar}
347233294Sstas
34872445Sassarint
34972445Sassarauvia_rate(void *arg)
35072445Sassar{
351233294Sstas	_NOTE(ARGUNUSED(arg));
35272445Sassar
353233294Sstas	return (48000);
35472445Sassar}
35572445Sassar
35672445Sassarvoid
357233294Sstasauvia_sync(void *arg, unsigned nframes)
358178825Sdfr{
35972445Sassar	auvia_portc_t *portc = arg;
360233294Sstas	_NOTE(ARGUNUSED(nframes));
361233294Sstas
36272445Sassar	(void) ddi_dma_sync(portc->buf_dmah, 0, 0, portc->syncdir);
36372445Sassar}
36472445Sassar
365233294Sstassize_t
366233294Sstasauvia_qlen(void *arg)
367233294Sstas{
368233294Sstas	_NOTE(ARGUNUSED(arg));
369233294Sstas	return (0);
370233294Sstas}
371233294Sstas
372233294Sstasuint64_t
373233294Sstasauvia_count(void *arg)
374233294Sstas{
37572445Sassar	auvia_portc_t	*portc = arg;
376233294Sstas	auvia_devc_t	*devc = portc->devc;
37772445Sassar	uint64_t	val;
37872445Sassar
37972445Sassar	mutex_enter(&devc->mutex);
38072445Sassar	auvia_update_port(portc);
38172445Sassar	/*
382233294Sstas	 * The residual is in bytes.  We have to convert to frames,
38372445Sassar	 * and then subtract it from the fragment size to get the
38472445Sassar	 * number of frames processed.  It is somewhat unfortunate thta
385233294Sstas	 * this (the division) has to happen under the lock.  If we
38672445Sassar	 * restricted ourself to stereo out, this would be a simple
38772445Sassar	 * shift.
38872445Sassar	 */
38972445Sassar	val = portc->count +
390233294Sstas	    (portc->fragfr - (portc->resid / (portc->nchan * 2)));
391178825Sdfr	mutex_exit(&devc->mutex);
39272445Sassar
393233294Sstas	return (val);
394233294Sstas}
39572445Sassar
396233294Sstas
397233294Sstas/* private implementation bits */
398233294Sstas
399233294Sstasvoid
400233294Sstasauvia_start_port(auvia_portc_t *portc)
401233294Sstas{
402233294Sstas	auvia_devc_t	*devc = portc->devc;
403233294Sstas
40472445Sassar	ASSERT(mutex_owned(&devc->mutex));
40572445Sassar
40672445Sassar	if (devc->suspended)
407233294Sstas		return;
408178825Sdfr
40972445Sassar	/*
410233294Sstas	 * Start with autoinit and SGD flag
411233294Sstas	 * interrupts enabled.
41272445Sassar	 */
413233294Sstas	OUTB(devc, portc->base + OFF_CTRL,
414233294Sstas	    CTRL_START | CTRL_AUTOSTART | CTRL_FLAG);
415233294Sstas}
416233294Sstas
417233294Sstasvoid
418233294Sstasauvia_stop_port(auvia_portc_t *portc)
419233294Sstas{
420233294Sstas	auvia_devc_t	*devc = portc->devc;
42172445Sassar
422233294Sstas	if (devc->suspended)
423		return;
424
425	OUTB(devc, portc->base + OFF_CTRL, CTRL_TERMINATE);
426}
427
428void
429auvia_update_port(auvia_portc_t *portc)
430{
431	auvia_devc_t	*devc = portc->devc;
432	uint32_t	frag;
433	uint32_t	n;
434
435	ASSERT(mutex_owned(&devc->mutex));
436	if (devc->suspended) {
437		portc->cur_frag = 0;
438		portc->resid = portc->fragsz;
439		n = 0;
440	} else {
441		frag = INL(devc, portc->base + OFF_COUNT);
442		portc->resid = (frag & 0xffffff);
443		frag >>= 24;
444		frag &= 0xff;
445
446		if (frag >= portc->cur_frag) {
447			n = frag - portc->cur_frag;
448		} else {
449			n = frag + AUVIA_NUM_SGD - portc->cur_frag;
450		}
451		portc->count += (n * portc->fragfr);
452		portc->cur_frag = frag;
453	}
454}
455
456void
457auvia_reset_output(auvia_portc_t *portc)
458{
459	auvia_devc_t	*devc = portc->devc;
460	uint32_t	cmap;
461
462	portc->cur_frag = 0;
463	portc->resid = portc->fragsz;
464
465	if (devc->suspended)
466		return;
467
468	OUTB(devc, portc->base + OFF_CTRL, CTRL_TERMINATE);	/* Stop */
469	OUTL(devc, portc->base + OFF_DMA, portc->sgd_paddr);
470
471	OUTB(devc, portc->base + OFF_PLAYFMT,
472	    PLAYFMT_16BIT | (portc->nchan << 4));
473
474	/* Select channel assignment - not valid for 8233A */
475	if (devc->chip_type != CHIP_8233A) {
476		/*
477		 * Undocumented slot mapping table:
478		 *
479		 * slot 3 = 1 (left)
480		 * slot 4 = 2 (right)
481		 * slot 6 = 5 (center)
482		 * slot 9 = 6 (lfe)
483		 * slot 7 = 3 (left rear)
484		 * slot 8 = 4 (right rear)
485		 */
486		switch (portc->nchan) {
487		case 1:
488			cmap = (1 << 0) | (1 << 4);
489			break;
490		case 2:
491			cmap = (1 << 0) | (2 << 4);
492			break;
493		case 4:
494			cmap = (1 << 0) | (2 << 4) | (3 << 8) | (4 << 12);
495			break;
496		case 6:
497			cmap = (1 << 0) | (2 << 4) |
498			    (5 << 8) | (6 << 12) | (3 << 16) | (4 << 20);
499			break;
500		default:
501			cmap = 0;
502			break;
503		}
504		OUTL(devc, portc->base + OFF_CHANNELS, cmap | 0xFF000000U);
505	}
506}
507
508static void
509auvia_reset_input(auvia_portc_t *portc)
510{
511	auvia_devc_t	*devc = portc->devc;
512	uint32_t	fmt;
513
514	portc->cur_frag = 0;
515	portc->resid = portc->fragsz;
516
517	if (devc->suspended)
518		return;
519
520	OUTB(devc, portc->base + OFF_CTRL, CTRL_TERMINATE);	/* Stop */
521	OUTL(devc, portc->base + OFF_DMA, portc->sgd_paddr);
522
523	fmt = RECFMT_STEREO | RECFMT_16BIT;
524
525	if (devc->chip_type != CHIP_8233A) {
526		fmt |= RECFMT_48K;
527	}
528	fmt |= (0xffU << 24);
529	OUTB(devc, portc->base + OFF_RECFIFO, RECFIFO_ENABLE);
530	OUTL(devc, portc->base + OFF_RECFMT, fmt);
531}
532
533int
534auvia_alloc_port(auvia_devc_t *devc, int num)
535{
536	auvia_portc_t		*portc;
537	size_t			len;
538	ddi_dma_cookie_t	cookie;
539	uint_t			count;
540	int			dir;
541	char			*prop;
542	unsigned		caps;
543	audio_dev_t		*adev;
544	uint32_t		*desc;
545	uint32_t		paddr;
546
547	adev = devc->adev;
548	portc = kmem_zalloc(sizeof (*portc), KM_SLEEP);
549	devc->portc[num] = portc;
550	portc->devc = devc;
551	portc->started = B_FALSE;
552
553	switch (num) {
554	case AUVIA_REC_SGD_NUM:
555		prop = "record-interrupts";
556		portc->base = devc->base + REG_RECBASE;
557		portc->syncdir = DDI_DMA_SYNC_FORKERNEL;
558		portc->nchan = 2;
559		portc->reset = auvia_reset_input;
560		caps = ENGINE_INPUT_CAP;
561		dir = DDI_DMA_READ;
562		break;
563	case AUVIA_PLAY_SGD_NUM:
564		prop = "play-interrupts";
565		portc->base = devc->base + REG_PLAYBASE;
566		portc->syncdir = DDI_DMA_SYNC_FORDEV;
567		portc->nchan = 6;
568		portc->reset = auvia_reset_output;
569		caps = ENGINE_OUTPUT_CAP;
570		dir = DDI_DMA_WRITE;
571		break;
572	default:
573		return (DDI_FAILURE);
574	}
575
576	/* make sure port is shut down */
577	OUTB(portc->devc, portc->base + OFF_CTRL, CTRL_TERMINATE);
578
579	/* figure out fragment configuration */
580	portc->intrs = ddi_prop_get_int(DDI_DEV_T_ANY, devc->dip,
581	    DDI_PROP_DONTPASS, prop, AUVIA_INTRS);
582
583	/* make sure the values are good */
584	if (portc->intrs < AUVIA_MIN_INTRS) {
585		audio_dev_warn(adev, "%s too low, %d, reset to %d",
586		    prop, portc->intrs, AUVIA_INTRS);
587		portc->intrs = AUVIA_INTRS;
588	} else if (portc->intrs > AUVIA_MAX_INTRS) {
589		audio_dev_warn(adev, "%s too high, %d, reset to %d",
590		    prop, portc->intrs, AUVIA_INTRS);
591		portc->intrs = AUVIA_INTRS;
592	}
593
594	portc->fragfr = 48000 / portc->intrs;
595	portc->fragsz = portc->fragfr * portc->nchan * 2;
596	portc->buf_size = portc->fragsz * AUVIA_NUM_SGD;
597
598	/* first allocate up space for SGD list */
599	if (ddi_dma_alloc_handle(devc->dip, &dma_attr_sgd,
600	    DDI_DMA_SLEEP, NULL, &portc->sgd_dmah) != DDI_SUCCESS) {
601		audio_dev_warn(adev, "failed to allocate SGD handle");
602		return (DDI_FAILURE);
603	}
604
605	if (ddi_dma_mem_alloc(portc->sgd_dmah,
606	    AUVIA_NUM_SGD * 2 * sizeof (uint32_t), &dev_attr,
607	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &portc->sgd_kaddr,
608	    &len, &portc->sgd_acch) != DDI_SUCCESS) {
609		audio_dev_warn(adev, "failed to allocate SGD memory");
610		return (DDI_FAILURE);
611	}
612
613	if (ddi_dma_addr_bind_handle(portc->sgd_dmah, NULL,
614	    portc->sgd_kaddr, len, DDI_DMA_CONSISTENT | DDI_DMA_WRITE,
615	    DDI_DMA_SLEEP, NULL, &cookie, &count) != DDI_SUCCESS) {
616		audio_dev_warn(adev, "failed binding SGD DMA handle");
617		return (DDI_FAILURE);
618	}
619	portc->sgd_paddr = cookie.dmac_address;
620
621	/* now buffers */
622	if (ddi_dma_alloc_handle(devc->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
623	    &portc->buf_dmah) != DDI_SUCCESS) {
624		audio_dev_warn(adev, "failed to allocate BUF handle");
625		return (DDI_FAILURE);
626	}
627
628	if (ddi_dma_mem_alloc(portc->buf_dmah, portc->buf_size,
629	    &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
630	    &portc->buf_kaddr, &len, &portc->buf_acch) != DDI_SUCCESS) {
631		audio_dev_warn(adev, "failed to allocate BUF memory");
632		return (DDI_FAILURE);
633	}
634
635	if (ddi_dma_addr_bind_handle(portc->buf_dmah, NULL, portc->buf_kaddr,
636	    len, DDI_DMA_CONSISTENT | dir, DDI_DMA_SLEEP, NULL, &cookie,
637	    &count) != DDI_SUCCESS) {
638		audio_dev_warn(adev, "failed binding BUF DMA handle");
639		return (DDI_FAILURE);
640	}
641	portc->buf_paddr = cookie.dmac_address;
642
643	/* now wire descriptors up */
644	desc = (void *)portc->sgd_kaddr;
645	paddr = portc->buf_paddr;
646	for (int i = 0; i < AUVIA_NUM_SGD; i++) {
647		uint32_t	flags;
648
649		flags = AUVIA_SGD_FLAG | portc->fragsz;
650
651		if (i == (AUVIA_NUM_SGD - 1)) {
652			flags |= AUVIA_SGD_EOL;
653		}
654		ddi_put32(portc->sgd_acch, desc++, paddr);
655		ddi_put32(portc->sgd_acch, desc++, flags);
656		paddr += portc->fragsz;
657	}
658
659	ddi_dma_sync(portc->sgd_dmah, 0, 0, DDI_DMA_SYNC_FORDEV);
660
661	portc->engine = audio_engine_alloc(&auvia_engine_ops, caps);
662	if (portc->engine == NULL) {
663		audio_dev_warn(adev, "audio_engine_alloc failed");
664		return (DDI_FAILURE);
665	}
666
667	audio_engine_set_private(portc->engine, portc);
668	audio_dev_add_engine(adev, portc->engine);
669
670	return (DDI_SUCCESS);
671}
672
673int
674auvia_setup_intrs(auvia_devc_t *devc)
675{
676	uint_t			ipri;
677	int			actual;
678	int			rv;
679	ddi_intr_handle_t	ih[1];
680
681	rv = ddi_intr_alloc(devc->dip, ih, DDI_INTR_TYPE_FIXED,
682	    0, 1, &actual, DDI_INTR_ALLOC_STRICT);
683	if ((rv != DDI_SUCCESS) || (actual != 1)) {
684		audio_dev_warn(devc->adev,
685		    "Can't alloc interrupt handle (rv %d actual %d)",
686		    rv, actual);
687		return (DDI_FAILURE);
688	}
689
690	if (ddi_intr_get_pri(ih[0], &ipri) != DDI_SUCCESS) {
691		audio_dev_warn(devc->adev, "Can't get interrupt priority");
692		(void) ddi_intr_free(ih[0]);
693		return (DDI_FAILURE);
694	}
695
696	if (ddi_intr_add_handler(ih[0], auvia_intr, devc, NULL) !=
697	    DDI_SUCCESS) {
698		audio_dev_warn(devc->adev, "Can't add interrupt handler");
699		(void) ddi_intr_free(ih[0]);
700		return (DDI_FAILURE);
701	}
702
703	devc->ih = ih[0];
704	mutex_init(&devc->mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
705	mutex_init(&devc->low_mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
706	return (DDI_SUCCESS);
707}
708
709void
710auvia_destroy(auvia_devc_t *devc)
711{
712	if (devc->ih != NULL) {
713		(void) ddi_intr_disable(devc->ih);
714		(void) ddi_intr_remove_handler(devc->ih);
715		(void) ddi_intr_free(devc->ih);
716		mutex_destroy(&devc->mutex);
717		mutex_destroy(&devc->low_mutex);
718	}
719
720	if (devc->ksp) {
721		kstat_delete(devc->ksp);
722	}
723
724	for (int i = 0; i < AUVIA_NUM_PORTC; i++) {
725		auvia_portc_t *portc = devc->portc[i];
726		if (!portc)
727			continue;
728		if (portc->engine) {
729			audio_dev_remove_engine(devc->adev, portc->engine);
730			audio_engine_free(portc->engine);
731		}
732		if (portc->sgd_paddr) {
733			(void) ddi_dma_unbind_handle(portc->sgd_dmah);
734		}
735		if (portc->sgd_acch) {
736			ddi_dma_mem_free(&portc->sgd_acch);
737		}
738		if (portc->sgd_dmah) {
739			ddi_dma_free_handle(&portc->sgd_dmah);
740		}
741		if (portc->buf_paddr) {
742			(void) ddi_dma_unbind_handle(portc->buf_dmah);
743		}
744		if (portc->buf_acch) {
745			ddi_dma_mem_free(&portc->buf_acch);
746		}
747		if (portc->buf_dmah) {
748			ddi_dma_free_handle(&portc->buf_dmah);
749		}
750		kmem_free(portc, sizeof (*portc));
751	}
752
753	if (devc->ac97 != NULL) {
754		ac97_free(devc->ac97);
755	}
756	if (devc->adev != NULL) {
757		audio_dev_free(devc->adev);
758	}
759	if (devc->regsh != NULL) {
760		ddi_regs_map_free(&devc->regsh);
761	}
762	if (devc->pcih != NULL) {
763		pci_config_teardown(&devc->pcih);
764	}
765	kmem_free(devc, sizeof (*devc));
766}
767
768void
769auvia_hwinit(auvia_devc_t *devc)
770{
771	ddi_acc_handle_t	pcih = devc->pcih;
772	uint32_t		val;
773
774	val = pci_config_get32(pcih, AUVIA_PCICFG);
775	/* we want to disable all legacy */
776	val &= ~AUVIA_PCICFG_LEGACY;
777	val &= ~(AUVIA_PCICFG_FMEN | AUVIA_PCICFG_SBEN);
778
779	/* enable AC'97 link and clear the reset bit */
780	val |= (AUVIA_PCICFG_ACLINKEN | AUVIA_PCICFG_NRST);
781	/* disable SRC (we won't use it) */
782	val &= ~AUVIA_PCICFG_SRCEN;
783	/* enable the SGD engines */
784	val |= AUVIA_PCICFG_SGDEN;
785
786	pci_config_put32(pcih, AUVIA_PCICFG, val);
787
788	drv_usecwait(10);
789}
790
791int
792auvia_attach(dev_info_t *dip)
793{
794	uint8_t 	pci_revision;
795	uint16_t	pci_command, vendor, device;
796	auvia_devc_t	*devc;
797	ddi_acc_handle_t pcih;
798	const char	*version;
799
800	devc = kmem_zalloc(sizeof (*devc), KM_SLEEP);
801	devc->dip = dip;
802	ddi_set_driver_private(dip, devc);
803
804	if ((devc->adev = audio_dev_alloc(dip, 0)) == NULL) {
805		cmn_err(CE_WARN, "audio_dev_alloc failed");
806		goto error;
807	}
808
809	if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
810		audio_dev_warn(devc->adev, "pci_config_setup failed");
811		goto error;
812	}
813	devc->pcih = pcih;
814
815	vendor = pci_config_get16(pcih, PCI_CONF_VENID);
816	device = pci_config_get16(pcih, PCI_CONF_DEVID);
817	if ((vendor != VIA_VENDOR_ID) || (device != VIA_8233_ID &&
818	    device != VIA_8233A_ID)) {
819		audio_dev_warn(devc->adev, "Hardware not recognized "
820		    "(vendor=%x, dev=%x)", vendor, device);
821		goto error;
822	}
823
824	devc->chip_type = CHIP_8233;
825	devc->chip_name = "VIA VT8233";
826	version = "8233";
827
828	pci_revision = pci_config_get8(pcih, PCI_CONF_REVID);
829
830	if (pci_revision == 0x50) {
831		devc->chip_name = "VIA VT8235";
832		version = "8235";
833	}
834
835	if (pci_revision == 0x60) {
836		devc->chip_name = "VIA VT8237";
837		version = "8237";
838	}
839
840	if ((device == VIA_8233A_ID) ||
841	    (device == VIA_8233_ID && pci_revision == 0x40)) {
842		devc->chip_type = CHIP_8233A;
843		devc->chip_name = "VIA VT8233A";
844		version = "8233A";
845	}
846	audio_dev_set_description(devc->adev, devc->chip_name);
847	audio_dev_set_version(devc->adev, version);
848
849	pci_command = pci_config_get16(pcih, PCI_CONF_COMM);
850	pci_command |= PCI_COMM_ME | PCI_COMM_IO | PCI_COMM_MAE;
851	pci_config_put16(pcih, PCI_CONF_COMM, pci_command);
852
853	if ((ddi_regs_map_setup(dip, 1, &devc->base, 0, 0, &dev_attr,
854	    &devc->regsh)) != DDI_SUCCESS) {
855		audio_dev_warn(devc->adev, "failed to map registers");
856		goto error;
857	}
858
859	auvia_hwinit(devc);
860
861	if ((auvia_alloc_port(devc, AUVIA_PLAY_SGD_NUM) != DDI_SUCCESS) ||
862	    (auvia_alloc_port(devc, AUVIA_REC_SGD_NUM) != DDI_SUCCESS)) {
863		goto error;
864	}
865
866	if (auvia_setup_intrs(devc) != DDI_SUCCESS) {
867		goto error;
868	}
869
870	devc->ac97 = ac97_alloc(dip, auvia_read_ac97, auvia_write_ac97, devc);
871	if (devc->ac97 == NULL) {
872		audio_dev_warn(devc->adev, "failed to allocate ac97 handle");
873		goto error;
874	}
875
876	if (ac97_init(devc->ac97, devc->adev) != DDI_SUCCESS) {
877		audio_dev_warn(devc->adev, "failed to init ac97");
878		goto error;
879	}
880
881	/* set up kernel statistics */
882	if ((devc->ksp = kstat_create(AUVIA_NAME, ddi_get_instance(dip),
883	    AUVIA_NAME, "controller", KSTAT_TYPE_INTR, 1,
884	    KSTAT_FLAG_PERSISTENT)) != NULL) {
885		kstat_install(devc->ksp);
886	}
887
888	if (audio_dev_register(devc->adev) != DDI_SUCCESS) {
889		audio_dev_warn(devc->adev, "unable to register with framework");
890		goto error;
891	}
892
893	(void) ddi_intr_enable(devc->ih);
894	ddi_report_dev(dip);
895
896	return (DDI_SUCCESS);
897
898error:
899	auvia_destroy(devc);
900	return (DDI_FAILURE);
901}
902
903int
904auvia_resume(dev_info_t *dip)
905{
906	auvia_devc_t *devc;
907
908	devc = ddi_get_driver_private(dip);
909
910	auvia_hwinit(devc);
911
912	/* allow ac97 operations again */
913	ac97_resume(devc->ac97);
914
915	mutex_enter(&devc->mutex);
916	devc->suspended = B_TRUE;
917	for (int i = 0; i < AUVIA_NUM_PORTC; i++) {
918
919		auvia_portc_t *portc = devc->portc[i];
920
921		if (portc->engine != NULL)
922			audio_engine_reset(portc->engine);
923
924		/* reset the port */
925		portc->reset(portc);
926
927		if (portc->started) {
928			auvia_start_port(portc);
929		} else {
930			auvia_stop_port(portc);
931		}
932	}
933	mutex_exit(&devc->mutex);
934	return (DDI_SUCCESS);
935}
936
937
938int
939auvia_detach(auvia_devc_t *devc)
940{
941	if (audio_dev_unregister(devc->adev) != DDI_SUCCESS)
942		return (DDI_FAILURE);
943
944	auvia_destroy(devc);
945	return (DDI_SUCCESS);
946}
947
948int
949auvia_suspend(auvia_devc_t *devc)
950{
951	ac97_suspend(devc->ac97);
952
953	mutex_enter(&devc->mutex);
954	for (int i = 0; i < AUVIA_NUM_PORTC; i++) {
955
956		auvia_portc_t *portc = devc->portc[i];
957		auvia_stop_port(portc);
958	}
959	devc->suspended = B_TRUE;
960	mutex_exit(&devc->mutex);
961	return (DDI_SUCCESS);
962}
963
964static int auvia_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
965static int auvia_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
966static int auvia_ddi_quiesce(dev_info_t *);
967
968static struct dev_ops auvia_dev_ops = {
969	DEVO_REV,		/* rev */
970	0,			/* refcnt */
971	NULL,			/* getinfo */
972	nulldev,		/* identify */
973	nulldev,		/* probe */
974	auvia_ddi_attach,	/* attach */
975	auvia_ddi_detach,	/* detach */
976	nodev,			/* reset */
977	NULL,			/* cb_ops */
978	NULL,			/* bus_ops */
979	NULL,			/* power */
980	auvia_ddi_quiesce,	/* quiesce */
981};
982
983static struct modldrv auvia_modldrv = {
984	&mod_driverops,			/* drv_modops */
985	"Via 823x Audio",		/* linkinfo */
986	&auvia_dev_ops,			/* dev_ops */
987};
988
989static struct modlinkage modlinkage = {
990	MODREV_1,
991	{ &auvia_modldrv, NULL }
992};
993
994int
995_init(void)
996{
997	int	rv;
998
999	audio_init_ops(&auvia_dev_ops, AUVIA_NAME);
1000	if ((rv = mod_install(&modlinkage)) != 0) {
1001		audio_fini_ops(&auvia_dev_ops);
1002	}
1003	return (rv);
1004}
1005
1006int
1007_fini(void)
1008{
1009	int	rv;
1010
1011	if ((rv = mod_remove(&modlinkage)) == 0) {
1012		audio_fini_ops(&auvia_dev_ops);
1013	}
1014	return (rv);
1015}
1016
1017int
1018_info(struct modinfo *modinfop)
1019{
1020	return (mod_info(&modlinkage, modinfop));
1021}
1022
1023int
1024auvia_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1025{
1026	switch (cmd) {
1027	case DDI_ATTACH:
1028		return (auvia_attach(dip));
1029
1030	case DDI_RESUME:
1031		return (auvia_resume(dip));
1032
1033	default:
1034		return (DDI_FAILURE);
1035	}
1036}
1037
1038int
1039auvia_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1040{
1041	auvia_devc_t *devc;
1042
1043	devc = ddi_get_driver_private(dip);
1044
1045	switch (cmd) {
1046	case DDI_DETACH:
1047		return (auvia_detach(devc));
1048
1049	case DDI_SUSPEND:
1050		return (auvia_suspend(devc));
1051
1052	default:
1053		return (DDI_FAILURE);
1054	}
1055}
1056
1057int
1058auvia_ddi_quiesce(dev_info_t *dip)
1059{
1060	auvia_devc_t	*devc;
1061
1062	devc = ddi_get_driver_private(dip);
1063
1064	for (int i = 0; i < AUVIA_NUM_PORTC; i++) {
1065
1066		auvia_portc_t *portc = devc->portc[i];
1067		auvia_stop_port(portc);
1068	}
1069	return (DDI_SUCCESS);
1070}
1071