xref: /illumos-gate/usr/src/uts/common/io/audio/drv/audiosolo/audiosolo.c (revision aedf2b3bb56b025fcaf87b49ec6c8aeea07f16d7)
1 /*
2  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 /*
27  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 /*
32  * Copyright (C) 4Front Technologies 1996-2008.
33  */
34 
35 #include <sys/audio/audio_driver.h>
36 #include <sys/note.h>
37 #include <sys/pci.h>
38 #include <sys/stdbool.h>
39 
40 
41 /*
42  * NB: The Solo-1 is a bit schizophrenic compared to most devices.
43  * It has two separate DMA engines for PCM data.  The first can do
44  * either capture or playback, and supports various Sound Blaster
45  * compatibility features.  The second is dedicated to playback.  The
46  * two engines have very little in common when it comes to programming
47  * them.
48  *
49  * We configure engine 1 for record, and engine 2 for playback.  Both
50  * are configured for 48 kHz stereo 16-bit signed PCM.
51  */
52 
53 /*
54  * ESS Solo-1 only implements the low 24-bits on Audio1, and requires
55  * 64KB alignment.  For Audio2, it implements the full 32-bit address
56  * space, but requires a 1MB address boundary.  Audio1 is used for
57  * recording, and Audio2 is used for playback.
58  */
59 static struct ddi_dma_attr dma_attr_audio1 = {
60 	DMA_ATTR_VERSION,	/* dma_attr_version */
61 	0x0,			/* dma_attr_addr_lo */
62 	0x00ffffffU,		/* dma_attr_addr_hi */
63 	0xffff,			/* dma_attr_count_max */
64 	0x10000,		/* dma_attr_align */
65 	0x7f,			/* dma_attr_burstsizes */
66 	0x4,			/* dma_attr_minxfer */
67 	0xffff,			/* dma_attr_maxxfer */
68 	0xffff,			/* dma_attr_seg */
69 	0x1,			/* dma_attr_sgllen */
70 	0x1,			/* dma_attr_granular */
71 	0			/* dma_attr_flags */
72 };
73 
74 static struct ddi_dma_attr dma_attr_audio2 = {
75 	DMA_ATTR_VERSION,	/* dma_attr_version */
76 	0x0,			/* dma_attr_addr_lo */
77 	0xffffffffU,		/* dma_attr_addr_hi */
78 	0xfff0,			/* dma_attr_count_max */
79 	0x100000,		/* dma_attr_align */
80 	0x7f,			/* dma_attr_burstsizes */
81 	0x4,			/* dma_attr_minxfer */
82 	0xfff0,			/* dma_attr_maxxfer */
83 	0xffff,			/* dma_attr_seg */
84 	0x1,			/* dma_attr_sgllen */
85 	0x1,			/* dma_attr_granular */
86 	0			/* dma_attr_flags */
87 };
88 
89 static ddi_device_acc_attr_t acc_attr = {
90 	DDI_DEVICE_ATTR_V0,
91 	DDI_STRUCTURE_LE_ACC,
92 	DDI_STRICTORDER_ACC
93 };
94 
95 static ddi_device_acc_attr_t buf_attr = {
96 	DDI_DEVICE_ATTR_V0,
97 	DDI_NEVERSWAP_ACC,
98 	DDI_STRICTORDER_ACC
99 };
100 
101 
102 /*
103  * For the sake of simplicity, this driver fixes a few parameters with
104  * constants.  If you want these values to be tunable, upgrade to a
105  * nicer and newer device.  This is all tuned for 100 Hz (10
106  * millisecs) latency.
107  */
108 #define	SOLO_RATE	48000
109 #define	SOLO_INTRS	100
110 #define	SOLO_FRAGFR	(SOLO_RATE / SOLO_INTRS)
111 #define	SOLO_NFRAGS	8
112 #define	SOLO_NCHAN	2
113 #define	SOLO_SAMPSZ	2
114 #define	SOLO_FRAGSZ	(SOLO_FRAGFR * (SOLO_NCHAN * SOLO_SAMPSZ))
115 #define	SOLO_BUFFR	(SOLO_NFRAGS * SOLO_FRAGFR)
116 #define	SOLO_BUFSZ	(SOLO_NFRAGS * SOLO_FRAGSZ)
117 
118 #define	INPUT_MIC	0
119 #define	INPUT_LINE	1
120 #define	INPUT_CD	2
121 #define	INPUT_AUX	3
122 #define	INPUT_MONO	4
123 #define	INSRCS		0x1f		/* bits 0-4 */
124 
125 #define	DRVNAME		"audiosolo"
126 
127 static const char *solo_insrcs[] = {
128 	AUDIO_PORT_MIC,
129 	AUDIO_PORT_LINEIN,
130 	AUDIO_PORT_CD,
131 	AUDIO_PORT_AUX1IN,
132 	AUDIO_PORT_AUX2IN,	/* this is really mono-in */
133 	NULL
134 };
135 
136 typedef struct solo_regs {
137 	ddi_acc_handle_t	acch;
138 	caddr_t			base;
139 } solo_regs_t;
140 
141 typedef struct solo_engine {
142 	struct solo_dev		*dev;
143 	audio_engine_t		*engine;
144 	ddi_dma_handle_t	dmah;
145 	ddi_acc_handle_t	acch;
146 	caddr_t			kaddr;
147 	uint32_t		paddr;
148 
149 	bool			started;
150 	uint64_t		count;
151 	uint16_t		offset;
152 	int			syncdir;
153 	int			format;
154 	bool			swapped;
155 
156 	void			(*start)(struct solo_engine *);
157 	void			(*stop)(struct solo_engine *);
158 	void			(*update)(struct solo_engine *);
159 } solo_engine_t;
160 
161 typedef enum {
162 	CTL_FRONT = 0,
163 	CTL_VOLUME,
164 	CTL_MIC,
165 	CTL_LINE,
166 	CTL_CD,
167 	CTL_AUX,
168 	CTL_MONO,
169 	CTL_MICBOOST,
170 	CTL_RECGAIN,
171 	CTL_RECSRC,
172 	CTL_MONSRC,
173 	CTL_SPEAKER,
174 	CTL_LOOPBACK,
175 	CTL_NUM,			/* must be last */
176 } solo_ctrl_num_t;
177 
178 typedef struct solo_ctrl {
179 	struct solo_dev		*dev;
180 	audio_ctrl_t		*ctrl;
181 	solo_ctrl_num_t		num;
182 	uint64_t		val;
183 } solo_ctrl_t;
184 
185 typedef struct solo_dev {
186 	dev_info_t		*dip;
187 	audio_dev_t		*adev;
188 	kmutex_t		mutex;
189 	ddi_intr_handle_t	ihandle;
190 
191 	bool			suspended;
192 
193 	/*
194 	 * Audio engines
195 	 */
196 	solo_engine_t		rec;
197 	solo_engine_t		play;
198 	uint32_t		last_capture;
199 
200 	/*
201 	 * Controls.
202 	 */
203 	solo_ctrl_t		ctrls[CTL_NUM];
204 
205 	/*
206 	 * Mapped registers
207 	 */
208 	ddi_acc_handle_t	pcih;
209 	solo_regs_t		io;
210 	solo_regs_t		sb;
211 	solo_regs_t		vc;
212 
213 } solo_dev_t;
214 
215 /*
216  * Common code for the pcm function
217  *
218  * solo_cmd write a single byte to the CMD port.
219  * solo_cmd1 write a CMD + 1 byte arg
220  * ess_get_byte returns a single byte from the DSP data port
221  *
222  * solo_write is actually solo_cmd1
223  * solo_read access ext. regs via solo_cmd(0xc0, reg) followed by solo_get_byte
224  */
225 
226 #define	PORT_RD8(port, regno)		\
227 	ddi_get8(port.acch, (void *)(port.base + (regno)))
228 #define	PORT_RD16(port, regno)		\
229 	ddi_get16(port.acch, (void *)(port.base + (regno)))
230 #define	PORT_RD32(port, regno)		\
231 	ddi_get32(port.acch, (void *)(port.base + (regno)))
232 #define	PORT_WR8(port, regno, data)	\
233 	ddi_put8(port.acch, (void *)(port.base + (regno)), data)
234 #define	PORT_WR16(port, regno, data)	\
235 	ddi_put16(port.acch, (void *)(port.base + (regno)), data)
236 #define	PORT_WR32(port, regno, data)	\
237 	ddi_put32(port.acch, (void *)(port.base + (regno)), data)
238 
239 static bool
240 solo_dspready(solo_dev_t *dev)
241 {
242 	return ((PORT_RD8(dev->sb, 0xc) & 0x80) == 0 ? true : false);
243 }
244 
245 static bool
246 solo_dspwr(solo_dev_t *dev, uint8_t val)
247 {
248 	int  i;
249 
250 	for (i = 0; i < 1000; i++) {
251 		if (solo_dspready(dev)) {
252 			PORT_WR8(dev->sb, 0xc, val);
253 			return (true);
254 		}
255 		if (i > 10)
256 			drv_usecwait((i > 100)? 1000 : 10);
257 	}
258 	audio_dev_warn(dev->adev, "solo_dspwr(0x%02x) timed out", val);
259 	return (false);
260 }
261 
262 static bool
263 solo_cmd(solo_dev_t *dev, uint8_t val)
264 {
265 	return (solo_dspwr(dev, val));
266 }
267 
268 static void
269 solo_cmd1(solo_dev_t *dev, uint8_t cmd, uint8_t val)
270 {
271 	if (solo_dspwr(dev, cmd)) {
272 		(void) solo_dspwr(dev, val);
273 	}
274 }
275 
276 static void
277 solo_setmixer(solo_dev_t *dev, uint8_t port, uint8_t value)
278 {
279 	PORT_WR8(dev->sb, 0x4, port); /* Select register */
280 	drv_usecwait(10);
281 	PORT_WR8(dev->sb, 0x5, value);
282 	drv_usecwait(10);
283 }
284 
285 static uint8_t
286 solo_getmixer(solo_dev_t *dev, uint8_t port)
287 {
288 	uint8_t val;
289 
290 	PORT_WR8(dev->sb, 0x4, port); /* Select register */
291 	drv_usecwait(10);
292 	val = PORT_RD8(dev->sb, 0x5);
293 	drv_usecwait(10);
294 
295 	return (val);
296 }
297 
298 static uint8_t
299 solo_get_byte(solo_dev_t *dev)
300 {
301 	for (int i = 1000; i > 0; i--) {
302 		if (PORT_RD8(dev->sb, 0xc) & 0x40)
303 			return (PORT_RD8(dev->sb, 0xa));
304 		else
305 			drv_usecwait(20);
306 	}
307 	audio_dev_warn(dev->adev, "timeout waiting to read DSP port");
308 	return (0xff);
309 }
310 
311 static void
312 solo_write(solo_dev_t *dev, uint8_t reg, uint8_t val)
313 {
314 	solo_cmd1(dev, reg, val);
315 }
316 
317 static uint8_t
318 solo_read(solo_dev_t *dev, uint8_t reg)
319 {
320 	if (solo_cmd(dev, 0xc0) && solo_cmd(dev, reg)) {
321 		return (solo_get_byte(dev));
322 	}
323 	return (0xff);
324 }
325 
326 static bool
327 solo_reset_dsp(solo_dev_t *dev)
328 {
329 	PORT_WR8(dev->sb, 0x6, 3);
330 	drv_usecwait(100);
331 	PORT_WR8(dev->sb, 0x6, 0);
332 	if (solo_get_byte(dev) != 0xAA) {
333 		audio_dev_warn(dev->adev, "solo_reset_dsp failed");
334 		return (false);	/* Sorry */
335 	}
336 	return (true);
337 }
338 
339 static uint_t
340 solo_intr(caddr_t arg1, caddr_t arg2)
341 {
342 	solo_dev_t	*dev = (void *)arg1;
343 	audio_engine_t	*prod = NULL;
344 	audio_engine_t	*cons = NULL;
345 	uint8_t		status;
346 	uint_t		rv = DDI_INTR_UNCLAIMED;
347 
348 	_NOTE(ARGUNUSED(arg2));
349 
350 	mutex_enter(&dev->mutex);
351 
352 	if (dev->suspended) {
353 		mutex_exit(&dev->mutex);
354 		return (rv);
355 	}
356 
357 	status = PORT_RD8(dev->io, 0x7);
358 	if (status & 0x20) {
359 		rv = DDI_INTR_CLAIMED;
360 		cons = dev->play.engine;
361 		/* ack the interrupt */
362 		solo_setmixer(dev, 0x7a, solo_getmixer(dev, 0x7a) & ~0x80);
363 	}
364 
365 	if (status & 0x10) {
366 		rv = DDI_INTR_CLAIMED;
367 		prod = dev->rec.engine;
368 		/* ack the interrupt */
369 		(void) PORT_RD8(dev->sb, 0xe);
370 	}
371 	mutex_exit(&dev->mutex);
372 
373 	if (cons) {
374 		audio_engine_consume(cons);
375 	}
376 
377 	if (prod) {
378 		audio_engine_produce(prod);
379 	}
380 
381 	return (rv);
382 }
383 
384 static uint8_t
385 solo_mixer_scale(solo_dev_t *dev, solo_ctrl_num_t num)
386 {
387 	uint32_t	l, r;
388 	uint64_t	value = dev->ctrls[num].val;
389 
390 	l = (value >> 8) & 0xff;
391 	r = value & 0xff;
392 
393 	l = (l * 15) / 100;
394 	r = (r * 15) / 100;
395 	return ((uint8_t)((l << 4) | (r)));
396 }
397 
398 static void
399 solo_configure_mixer(solo_dev_t *dev)
400 {
401 	uint32_t v;
402 	uint32_t mon, rec;
403 
404 	/*
405 	 * We disable hardware volume control (i.e. async updates to volume).
406 	 * We could in theory support this, but making it work right can be
407 	 * tricky, and we doubt it is widely used.
408 	 */
409 	solo_setmixer(dev, 0x64, solo_getmixer(dev, 0x64) | 0xc);
410 	solo_setmixer(dev, 0x66, 0);
411 
412 	/* master volume has 6 bits per channel, bit 6 indicates mute  */
413 	/* left */
414 	v = (dev->ctrls[CTL_FRONT].val >> 8) & 0xff;
415 	v = v ? (v * 63) / 100 : 64;
416 	solo_setmixer(dev, 0x60, v & 0xff);
417 
418 	/* right */
419 	v = dev->ctrls[CTL_FRONT].val & 0xff;
420 	v = v ? (v * 63) / 100 : 64;
421 	solo_setmixer(dev, 0x62, v & 0xff);
422 
423 	v = solo_mixer_scale(dev, CTL_VOLUME);
424 	v = v | (v << 4);
425 	solo_setmixer(dev, 0x7c, v & 0xff);
426 	solo_setmixer(dev, 0x14, v & 0xff);
427 
428 	mon = dev->ctrls[CTL_MONSRC].val;
429 	rec = dev->ctrls[CTL_RECSRC].val;
430 
431 	/*
432 	 * The Solo-1 has dual stereo mixers (one for input and one for output),
433 	 * with separate volume controls for each.
434 	 */
435 	v = solo_mixer_scale(dev, CTL_MIC);
436 	solo_setmixer(dev, 0x68, rec & (1 << INPUT_MIC) ? v : 0);
437 	solo_setmixer(dev, 0x1a, mon & (1 << INPUT_MIC) ? v : 0);
438 
439 	v = solo_mixer_scale(dev, CTL_LINE);
440 	solo_setmixer(dev, 0x6e, rec & (1 << INPUT_LINE) ? v : 0);
441 	solo_setmixer(dev, 0x3e, mon & (1 << INPUT_LINE) ? v : 0);
442 
443 	v = solo_mixer_scale(dev, CTL_CD);
444 	solo_setmixer(dev, 0x6a, rec & (1 << INPUT_CD) ? v : 0);
445 	solo_setmixer(dev, 0x38, mon & (1 << INPUT_CD) ? v : 0);
446 
447 	v = solo_mixer_scale(dev, CTL_AUX);
448 	solo_setmixer(dev, 0x6c, rec & (1 << INPUT_AUX) ? v : 0);
449 	solo_setmixer(dev, 0x3a, mon & (1 << INPUT_AUX) ? v : 0);
450 
451 	v = solo_mixer_scale(dev, CTL_MONO);
452 	v = v | (v << 4);
453 	solo_setmixer(dev, 0x6f, rec & (1 << INPUT_MONO) ? v : 0);
454 	solo_setmixer(dev, 0x6d, mon & (1 << INPUT_MONO) ? v : 0);
455 
456 	if (dev->ctrls[CTL_MICBOOST].val) {
457 		solo_setmixer(dev, 0x7d, solo_getmixer(dev, 0x7d) | 0x8);
458 	} else {
459 		solo_setmixer(dev, 0x7d, solo_getmixer(dev, 0x7d) & ~(0x8));
460 	}
461 
462 	v = solo_mixer_scale(dev, CTL_RECGAIN);
463 	v = v | (v << 4);
464 	solo_write(dev, 0xb4, v & 0xff);
465 
466 	v = dev->ctrls[CTL_SPEAKER].val & 0xff;
467 	v = (v * 7) / 100;
468 	solo_setmixer(dev, 0x3c, v & 0xff);
469 
470 	if (dev->ctrls[CTL_LOOPBACK].val) {
471 		/* record-what-you-hear mode */
472 		solo_setmixer(dev, 0x1c, 0x3);
473 	} else {
474 		/* use record mixer */
475 		solo_setmixer(dev, 0x1c, 0x5);
476 	}
477 
478 }
479 
480 static int
481 solo_set_mixsrc(void *arg, uint64_t val)
482 {
483 	solo_ctrl_t	*pc = arg;
484 	solo_dev_t	*dev = pc->dev;
485 
486 	if ((val & ~INSRCS) != 0)
487 		return (EINVAL);
488 
489 	mutex_enter(&dev->mutex);
490 	pc->val = val;
491 	if (!dev->suspended)
492 		solo_configure_mixer(dev);
493 	mutex_exit(&dev->mutex);
494 	return (0);
495 }
496 
497 static int
498 solo_set_mono(void *arg, uint64_t val)
499 {
500 	solo_ctrl_t	*pc = arg;
501 	solo_dev_t	*dev = pc->dev;
502 
503 	val &= 0xff;
504 	if (val > 100)
505 		return (EINVAL);
506 
507 	val = (val & 0xff) | ((val & 0xff) << 8);
508 
509 	mutex_enter(&dev->mutex);
510 	pc->val = val;
511 	if (!dev->suspended)
512 		solo_configure_mixer(dev);
513 	mutex_exit(&dev->mutex);
514 	return (0);
515 }
516 
517 static int
518 solo_set_stereo(void *arg, uint64_t val)
519 {
520 	solo_ctrl_t	*pc = arg;
521 	solo_dev_t	*dev = pc->dev;
522 	uint8_t		l;
523 	uint8_t		r;
524 
525 	l = (val & 0xff00) >> 8;
526 	r = val & 0xff;
527 
528 	if ((l > 100) || (r > 100))
529 		return (EINVAL);
530 
531 	mutex_enter(&dev->mutex);
532 	pc->val = val;
533 	if (!dev->suspended)
534 		solo_configure_mixer(dev);
535 	mutex_exit(&dev->mutex);
536 	return (0);
537 }
538 
539 static int
540 solo_set_bool(void *arg, uint64_t val)
541 {
542 	solo_ctrl_t	*pc = arg;
543 	solo_dev_t	*dev = pc->dev;
544 
545 	mutex_enter(&dev->mutex);
546 	pc->val = val;
547 	if (!dev->suspended)
548 		solo_configure_mixer(dev);
549 	mutex_exit(&dev->mutex);
550 	return (0);
551 }
552 
553 static int
554 solo_get_value(void *arg, uint64_t *val)
555 {
556 	solo_ctrl_t	*pc = arg;
557 	solo_dev_t	*dev = pc->dev;
558 
559 	mutex_enter(&dev->mutex);
560 	*val = pc->val;
561 	mutex_exit(&dev->mutex);
562 	return (0);
563 }
564 
565 #define	PLAYCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY)
566 #define	RECCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_REC)
567 #define	MONCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_MONITOR)
568 #define	PCMVOL	(PLAYCTL | AUDIO_CTRL_FLAG_PCMVOL)
569 #define	MAINVOL	(PLAYCTL | AUDIO_CTRL_FLAG_MAINVOL)
570 #define	RECVOL	(RECCTL | AUDIO_CTRL_FLAG_RECVOL)
571 
572 static void
573 solo_alloc_ctrl(solo_dev_t *dev, uint32_t num, uint64_t val)
574 {
575 	audio_ctrl_desc_t	desc;
576 	audio_ctrl_wr_t		fn;
577 	solo_ctrl_t		*pc;
578 
579 	bzero(&desc, sizeof (desc));
580 
581 	pc = &dev->ctrls[num];
582 	pc->num = num;
583 	pc->dev = dev;
584 
585 	switch (num) {
586 	case CTL_VOLUME:
587 		desc.acd_name = AUDIO_CTRL_ID_VOLUME;
588 		desc.acd_type = AUDIO_CTRL_TYPE_MONO;
589 		desc.acd_minvalue = 0;
590 		desc.acd_maxvalue = 100;
591 		desc.acd_flags = PCMVOL;
592 		fn = solo_set_mono;
593 		break;
594 
595 	case CTL_FRONT:
596 		desc.acd_name = AUDIO_CTRL_ID_LINEOUT;
597 		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
598 		desc.acd_minvalue = 0;
599 		desc.acd_maxvalue = 100;
600 		desc.acd_flags = MAINVOL;
601 		fn = solo_set_stereo;
602 		break;
603 
604 	case CTL_SPEAKER:
605 		desc.acd_name = AUDIO_CTRL_ID_SPEAKER;
606 		desc.acd_type = AUDIO_CTRL_TYPE_MONO;
607 		desc.acd_minvalue = 0;
608 		desc.acd_maxvalue = 100;
609 		desc.acd_flags = MAINVOL;
610 		fn = solo_set_mono;
611 		break;
612 
613 	case CTL_MIC:
614 		desc.acd_name = AUDIO_CTRL_ID_MIC;
615 		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
616 		desc.acd_minvalue = 0;
617 		desc.acd_maxvalue = 100;
618 		desc.acd_flags = RECVOL;
619 		fn = solo_set_stereo;
620 		break;
621 
622 	case CTL_LINE:
623 		desc.acd_name = AUDIO_CTRL_ID_LINEIN;
624 		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
625 		desc.acd_minvalue = 0;
626 		desc.acd_maxvalue = 100;
627 		desc.acd_flags = RECVOL;
628 		fn = solo_set_stereo;
629 		break;
630 
631 	case CTL_CD:
632 		desc.acd_name = AUDIO_CTRL_ID_CD;
633 		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
634 		desc.acd_minvalue = 0;
635 		desc.acd_maxvalue = 100;
636 		desc.acd_flags = RECVOL;
637 		fn = solo_set_stereo;
638 		break;
639 
640 	case CTL_AUX:
641 		desc.acd_name = AUDIO_CTRL_ID_AUX1IN;
642 		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
643 		desc.acd_minvalue = 0;
644 		desc.acd_maxvalue = 100;
645 		desc.acd_flags = RECVOL;
646 		fn = solo_set_stereo;
647 		break;
648 
649 	case CTL_MONO:
650 		desc.acd_name = AUDIO_CTRL_ID_AUX2IN;
651 		desc.acd_type = AUDIO_CTRL_TYPE_MONO;
652 		desc.acd_minvalue = 0;
653 		desc.acd_maxvalue = 100;
654 		desc.acd_flags = RECVOL;
655 		fn = solo_set_mono;
656 		break;
657 
658 	case CTL_RECSRC:
659 		desc.acd_name = AUDIO_CTRL_ID_RECSRC;
660 		desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
661 		desc.acd_minvalue = INSRCS;
662 		desc.acd_maxvalue = INSRCS;
663 		desc.acd_flags = RECCTL | AUDIO_CTRL_FLAG_MULTI;
664 		for (int i = 0; solo_insrcs[i]; i++) {
665 			desc.acd_enum[i] = solo_insrcs[i];
666 		}
667 		fn = solo_set_mixsrc;
668 		break;
669 
670 	case CTL_MONSRC:
671 		desc.acd_name = AUDIO_CTRL_ID_MONSRC;
672 		desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
673 		desc.acd_minvalue = INSRCS;
674 		desc.acd_maxvalue = INSRCS;
675 		desc.acd_flags = MONCTL | AUDIO_CTRL_FLAG_MULTI;
676 		for (int i = 0; solo_insrcs[i]; i++) {
677 			desc.acd_enum[i] = solo_insrcs[i];
678 		}
679 		fn = solo_set_mixsrc;
680 		break;
681 
682 	case CTL_MICBOOST:
683 		desc.acd_name = AUDIO_CTRL_ID_MICBOOST;
684 		desc.acd_type = AUDIO_CTRL_TYPE_BOOLEAN;
685 		desc.acd_minvalue = 0;
686 		desc.acd_maxvalue = 1;
687 		desc.acd_flags = RECCTL;
688 		fn = solo_set_bool;
689 		break;
690 
691 	case CTL_LOOPBACK:
692 		desc.acd_name = AUDIO_CTRL_ID_LOOPBACK;
693 		desc.acd_type = AUDIO_CTRL_TYPE_BOOLEAN;
694 		desc.acd_minvalue = 0;
695 		desc.acd_maxvalue = 1;
696 		desc.acd_flags = RECCTL;
697 		fn = solo_set_bool;
698 		break;
699 
700 	case CTL_RECGAIN:
701 		desc.acd_name = AUDIO_CTRL_ID_RECGAIN;
702 		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
703 		desc.acd_minvalue = 0;
704 		desc.acd_maxvalue = 100;
705 		desc.acd_flags = RECCTL;
706 		fn = solo_set_stereo;
707 		break;
708 	}
709 
710 	pc->val = val;
711 	pc->ctrl = audio_dev_add_control(dev->adev, &desc,
712 	    solo_get_value, fn, pc);
713 }
714 
715 static bool
716 solo_add_controls(solo_dev_t *dev)
717 {
718 	solo_alloc_ctrl(dev, CTL_VOLUME, 0x4b);
719 	solo_alloc_ctrl(dev, CTL_FRONT, 0x5a5a);
720 	solo_alloc_ctrl(dev, CTL_SPEAKER, 0x4b);
721 	solo_alloc_ctrl(dev, CTL_MIC, 0x3232);
722 	solo_alloc_ctrl(dev, CTL_LINE, 0x4b4b);
723 	solo_alloc_ctrl(dev, CTL_CD, 0x4b4b);
724 	solo_alloc_ctrl(dev, CTL_AUX, 0);
725 	solo_alloc_ctrl(dev, CTL_MONO, 0);
726 	solo_alloc_ctrl(dev, CTL_RECSRC, (1U << INPUT_MIC));
727 	solo_alloc_ctrl(dev, CTL_MONSRC, 0);
728 	solo_alloc_ctrl(dev, CTL_RECGAIN, 0x4b4b);
729 	solo_alloc_ctrl(dev, CTL_MICBOOST, 1);
730 	solo_alloc_ctrl(dev, CTL_LOOPBACK, 0);
731 
732 	return (true);
733 }
734 
735 
736 /* utility functions for ESS */
737 static uint8_t
738 solo_calcfilter(int spd)
739 {
740 	int cutoff;
741 
742 	cutoff = (spd * 9 * 82) / 20;
743 	return (256 - (7160000 / cutoff));
744 }
745 
746 static void
747 solo_aud1_update(solo_engine_t *e)
748 {
749 	solo_dev_t	*dev = e->dev;
750 	uint16_t	offset, n;
751 	uint32_t	ptr;
752 	uint32_t	count;
753 	uint32_t	diff;
754 
755 	ASSERT(mutex_owned(&dev->mutex));
756 
757 	/*
758 	 * During recording, this register is known to give back
759 	 * garbage if it's not quiescent while being read.  This hack
760 	 * attempts to work around it.
761 	 */
762 	ptr = PORT_RD32(dev->vc, 0);
763 	count = PORT_RD16(dev->vc, 4);
764 	diff = e->paddr + SOLO_BUFSZ - ptr - count;
765 	if ((diff > 3) || (ptr < e->paddr) ||
766 	    (ptr >= (e->paddr + SOLO_BUFSZ))) {
767 		ptr = dev->last_capture;
768 	} else {
769 		dev->last_capture = ptr;
770 	}
771 	offset = ptr - e->paddr;
772 	offset /= (SOLO_NCHAN * SOLO_SAMPSZ);
773 
774 	n = offset >= e->offset ?
775 	    offset - e->offset :
776 	    offset + SOLO_BUFSZ - e->offset;
777 
778 	e->offset = offset;
779 	e->count += n / (SOLO_NCHAN * SOLO_SAMPSZ);
780 }
781 
782 static void
783 solo_aud1_start(solo_engine_t *e)
784 {
785 	solo_dev_t	*dev = e->dev;
786 	int		len;
787 	uint32_t	v;
788 
789 	ASSERT(mutex_owned(&dev->mutex));
790 
791 	len = SOLO_FRAGSZ / 2;
792 	len = -len;
793 
794 	/* sample rate - 48 kHz */
795 	solo_write(dev, 0xa1, 0xf0);
796 	/* filter cutoff */
797 	solo_write(dev, 0xa2, solo_calcfilter(SOLO_RATE));
798 
799 
800 	/* mono/stereo - bit 0 set, bit 1 clear */
801 	solo_write(dev, 0xa8, (solo_read(dev, 0xa8) & ~0x03) | 1);
802 
803 	(void) solo_cmd(dev, 0xd3);	/* turn off DAC1 output */
804 
805 	/* setup fifo for signed 16-bit stereo */
806 	solo_write(dev, 0xb7, 0x71);
807 	solo_write(dev, 0xb7, 0xbc);
808 
809 	v = solo_mixer_scale(dev, CTL_RECGAIN);
810 	v = v | (v << 4);
811 	solo_write(dev, 0xb4, v & 0xff);
812 
813 	PORT_WR8(dev->vc, 0x8, 0xc4); /* command */
814 	PORT_WR8(dev->vc, 0xd, 0xff); /* clear DMA */
815 	PORT_WR8(dev->vc, 0xf, 0x01); /* stop DMA  */
816 
817 	PORT_WR8(dev->vc, 0xd, 0xff); /* reset */
818 	PORT_WR8(dev->vc, 0xf, 0x01); /* mask */
819 	PORT_WR8(dev->vc, 0xb, 0x14); /* mode */
820 
821 	PORT_WR32(dev->vc, 0x0, e->paddr);
822 	PORT_WR16(dev->vc, 0x4, SOLO_BUFSZ - 1);
823 
824 	/* transfer length low, high */
825 	solo_write(dev, 0xa4, len & 0x00ff);
826 	solo_write(dev, 0xa5, (len & 0xff00) >> 8);
827 
828 	/* autoinit, dma dir, go for it */
829 	solo_write(dev, 0xb8, 0x0f);
830 	PORT_WR8(dev->vc, 0xf, 0);	/* start DMA */
831 
832 	dev->last_capture = e->paddr;
833 }
834 
835 static void
836 solo_aud1_stop(solo_engine_t *e)
837 {
838 	solo_dev_t	*dev = e->dev;
839 
840 	/* NB: We might be in quiesce, without a lock held */
841 	solo_write(dev, 0xb8, solo_read(dev, 0xb8) & ~0x01);
842 }
843 
844 static void
845 solo_aud2_update(solo_engine_t *e)
846 {
847 	solo_dev_t	*dev = e->dev;
848 	uint16_t	offset = 0, n;
849 
850 	ASSERT(mutex_owned(&dev->mutex));
851 
852 	offset = SOLO_BUFSZ - PORT_RD16(dev->io, 0x4);
853 	offset /= (SOLO_NCHAN * SOLO_SAMPSZ);
854 
855 	n = offset >= e->offset ?
856 	    offset - e->offset :
857 	    offset + SOLO_BUFFR - e->offset;
858 
859 	e->offset = offset;
860 	e->count += n;
861 }
862 
863 static void
864 solo_aud2_start(solo_engine_t *e)
865 {
866 	solo_dev_t	*dev = e->dev;
867 	int		len;
868 	uint32_t	v;
869 
870 	ASSERT(mutex_owned(&dev->mutex));
871 
872 	len = SOLO_FRAGSZ / 2;
873 	len = -len;
874 
875 	/* program transfer type */
876 	solo_setmixer(dev, 0x78, 0x10);
877 	/* sample rate - 48 kHz */
878 	solo_setmixer(dev, 0x70, 0xf0);
879 	solo_setmixer(dev, 0x72, solo_calcfilter(SOLO_RATE));
880 	/* transfer length low & high */
881 	solo_setmixer(dev, 0x74, len & 0x00ff);
882 	solo_setmixer(dev, 0x76, (len & 0xff00) >> 8);
883 	/* enable irq, set signed 16-bit stereo format */
884 	solo_setmixer(dev, 0x7a, 0x47);
885 
886 	PORT_WR8(dev->io, 0x6, 0);
887 	PORT_WR32(dev->io, 0x0, e->paddr);
888 	PORT_WR16(dev->io, 0x4, SOLO_BUFSZ);
889 
890 	/* this crazy initialization appears to help with fifo weirdness */
891 	/* start the engine running */
892 	solo_setmixer(dev, 0x78, 0x92);
893 	drv_usecwait(10);
894 	solo_setmixer(dev, 0x78, 0x93);
895 
896 	PORT_WR8(dev->io, 0x6, 0x0a); /* autoinit, enable */
897 
898 	v = solo_mixer_scale(dev, CTL_VOLUME);
899 	v = v | (v << 4);
900 	solo_setmixer(dev, 0x7c, v & 0xff);
901 }
902 
903 static void
904 solo_aud2_stop(solo_engine_t *e)
905 {
906 	solo_dev_t	*dev = e->dev;
907 
908 	/* NB: We might be in quiesce, without a lock held */
909 	PORT_WR8(dev->io, 0x6, 0);
910 	solo_setmixer(dev, 0x78, solo_getmixer(dev, 0x78) & ~0x03);
911 }
912 
913 /*
914  * Audio entry points.
915  */
916 static int
917 solo_format(void *arg)
918 {
919 	solo_engine_t	*e = arg;
920 	return (e->format);
921 }
922 
923 static int
924 solo_channels(void *arg)
925 {
926 	_NOTE(ARGUNUSED(arg));
927 	return (SOLO_NCHAN);
928 }
929 
930 static int
931 solo_rate(void *arg)
932 {
933 	_NOTE(ARGUNUSED(arg));
934 	return (SOLO_RATE);
935 }
936 
937 static size_t
938 solo_qlen(void *arg)
939 {
940 	_NOTE(ARGUNUSED(arg));
941 	return (0);
942 }
943 
944 static void
945 solo_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
946 {
947 	solo_engine_t *e = arg;
948 
949 	if (e->swapped) {
950 		*offset = !chan;
951 	} else {
952 		*offset = chan;
953 	}
954 	*incr = 2;
955 }
956 
957 static void
958 solo_sync(void *arg, unsigned nframes)
959 {
960 	solo_engine_t *e = arg;
961 
962 	_NOTE(ARGUNUSED(nframes));
963 
964 	(void) ddi_dma_sync(e->dmah, 0, 0, e->syncdir);
965 }
966 
967 
968 static uint64_t
969 solo_count(void *arg)
970 {
971 	solo_engine_t	*e = arg;
972 	solo_dev_t	*dev = e->dev;
973 	uint64_t	count;
974 
975 	mutex_enter(&dev->mutex);
976 	if (!dev->suspended)
977 		e->update(e);
978 	count = e->count;
979 	mutex_exit(&dev->mutex);
980 
981 	return (count);
982 }
983 
984 static int
985 solo_open(void *arg, int f, unsigned *ffr, unsigned *nfr, caddr_t *buf)
986 {
987 	solo_engine_t	*e = arg;
988 	solo_dev_t	*dev = e->dev;
989 
990 	_NOTE(ARGUNUSED(f));
991 
992 	/* NB: For simplicity, we just fix the interrupt rate at 100 Hz */
993 	*ffr = SOLO_FRAGFR;
994 	*nfr = SOLO_NFRAGS;
995 	*buf = e->kaddr;
996 
997 	mutex_enter(&dev->mutex);
998 	e->started = false;
999 	e->count = 0;
1000 	mutex_exit(&dev->mutex);
1001 
1002 	return (0);
1003 }
1004 
1005 void
1006 solo_close(void *arg)
1007 {
1008 	solo_engine_t	*e = arg;
1009 	solo_dev_t	*dev = e->dev;
1010 
1011 	mutex_enter(&dev->mutex);
1012 	if (!dev->suspended)
1013 		e->stop(e);
1014 	e->started = false;
1015 	mutex_exit(&dev->mutex);
1016 }
1017 
1018 
1019 static int
1020 solo_start(void *arg)
1021 {
1022 	solo_engine_t	*e = arg;
1023 	solo_dev_t	*dev = e->dev;
1024 
1025 	mutex_enter(&dev->mutex);
1026 	if (!e->started) {
1027 		if (!dev->suspended)
1028 			e->start(e);
1029 		e->started = true;
1030 	}
1031 	mutex_exit(&dev->mutex);
1032 
1033 	return (0);
1034 }
1035 
1036 static void
1037 solo_stop(void *arg)
1038 {
1039 	solo_engine_t	*e = arg;
1040 	solo_dev_t	*dev = e->dev;
1041 
1042 	mutex_enter(&dev->mutex);
1043 	if (e->started) {
1044 		if (!dev->suspended)
1045 			e->stop(e);
1046 		e->started = false;
1047 	}
1048 	mutex_exit(&dev->mutex);
1049 
1050 }
1051 
1052 static audio_engine_ops_t solo_engine_ops = {
1053 	AUDIO_ENGINE_VERSION,
1054 	solo_open,
1055 	solo_close,
1056 	solo_start,
1057 	solo_stop,
1058 	solo_count,
1059 	solo_format,
1060 	solo_channels,
1061 	solo_rate,
1062 	solo_sync,
1063 	solo_qlen,
1064 	solo_chinfo,
1065 };
1066 
1067 static void
1068 solo_release_resources(solo_dev_t *dev)
1069 {
1070 	if (dev->ihandle != NULL) {
1071 		(void) ddi_intr_disable(dev->ihandle);
1072 		(void) ddi_intr_remove_handler(dev->ihandle);
1073 		(void) ddi_intr_free(dev->ihandle);
1074 		mutex_destroy(&dev->mutex);
1075 	}
1076 
1077 	if (dev->io.acch != NULL) {
1078 		ddi_regs_map_free(&dev->io.acch);
1079 	}
1080 
1081 	if (dev->sb.acch != NULL) {
1082 		ddi_regs_map_free(&dev->sb.acch);
1083 	}
1084 
1085 	if (dev->vc.acch != NULL) {
1086 		ddi_regs_map_free(&dev->vc.acch);
1087 	}
1088 
1089 	if (dev->pcih != NULL) {
1090 		pci_config_teardown(&dev->pcih);
1091 	}
1092 
1093 	/* release play resources */
1094 	if (dev->play.paddr != 0)
1095 		(void) ddi_dma_unbind_handle(dev->play.dmah);
1096 	if (dev->play.acch != NULL)
1097 		ddi_dma_mem_free(&dev->play.acch);
1098 	if (dev->play.dmah != NULL)
1099 		ddi_dma_free_handle(&dev->play.dmah);
1100 
1101 	if (dev->play.engine != NULL) {
1102 		audio_dev_remove_engine(dev->adev, dev->play.engine);
1103 		audio_engine_free(dev->play.engine);
1104 	}
1105 
1106 	/* release record resources */
1107 	if (dev->rec.paddr != 0)
1108 		(void) ddi_dma_unbind_handle(dev->rec.dmah);
1109 	if (dev->rec.acch != NULL)
1110 		ddi_dma_mem_free(&dev->rec.acch);
1111 	if (dev->rec.dmah != NULL)
1112 		ddi_dma_free_handle(&dev->rec.dmah);
1113 
1114 	if (dev->rec.engine != NULL) {
1115 		audio_dev_remove_engine(dev->adev, dev->rec.engine);
1116 		audio_engine_free(dev->rec.engine);
1117 	}
1118 
1119 	if (dev->adev != NULL) {
1120 		audio_dev_free(dev->adev);
1121 	}
1122 
1123 	kmem_free(dev, sizeof (*dev));
1124 }
1125 
1126 static bool
1127 solo_setup_interrupts(solo_dev_t *dev)
1128 {
1129 	int actual;
1130 	uint_t ipri;
1131 
1132 	if ((ddi_intr_alloc(dev->dip, &dev->ihandle, DDI_INTR_TYPE_FIXED,
1133 	    0, 1, &actual, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) ||
1134 	    (actual != 1)) {
1135 		audio_dev_warn(dev->adev, "can't alloc intr handle");
1136 		return (false);
1137 	}
1138 
1139 	if (ddi_intr_get_pri(dev->ihandle, &ipri) != DDI_SUCCESS) {
1140 		audio_dev_warn(dev->adev,  "can't determine intr priority");
1141 		(void) ddi_intr_free(dev->ihandle);
1142 		dev->ihandle = NULL;
1143 		return (false);
1144 	}
1145 
1146 	if (ddi_intr_add_handler(dev->ihandle, solo_intr, dev,
1147 	    NULL) != DDI_SUCCESS) {
1148 		audio_dev_warn(dev->adev, "can't add intr handler");
1149 		(void) ddi_intr_free(dev->ihandle);
1150 		dev->ihandle = NULL;
1151 		return (false);
1152 	}
1153 
1154 	mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
1155 
1156 	return (true);
1157 }
1158 
1159 static bool
1160 solo_map_registers(solo_dev_t *dev)
1161 {
1162 	dev_info_t	*dip = dev->dip;
1163 
1164 	/* map registers */
1165 	if (ddi_regs_map_setup(dip, 1, &dev->io.base, 0, 0, &acc_attr,
1166 	    &dev->io.acch) != DDI_SUCCESS) {
1167 		audio_dev_warn(dev->adev, "can't map IO registers");
1168 		return (false);
1169 	}
1170 	if (ddi_regs_map_setup(dip, 2, &dev->sb.base, 0, 0, &acc_attr,
1171 	    &dev->sb.acch) != DDI_SUCCESS) {
1172 		audio_dev_warn(dev->adev, "can't map SB registers");
1173 		return (false);
1174 	}
1175 	if (ddi_regs_map_setup(dip, 3, &dev->vc.base, 0, 0, &acc_attr,
1176 	    &dev->vc.acch) != DDI_SUCCESS) {
1177 		audio_dev_warn(dev->adev, "can't map VC registers");
1178 		return (false);
1179 	}
1180 
1181 	return (true);
1182 }
1183 
1184 #define	ESS_PCI_LEGACYCONTROL		0x40
1185 #define	ESS_PCI_CONFIG			0x50
1186 #define	ESS_PCI_DDMACONTROL		0x60
1187 
1188 static bool
1189 solo_init_hw(solo_dev_t *dev)
1190 {
1191 	uint32_t	data;
1192 
1193 	/*
1194 	 * Legacy audio register -- disable legacy audio.  We also
1195 	 * arrange for 16-bit I/O address decoding.
1196 	 */
1197 	/* this version disables the MPU, FM synthesis (Adlib), and Game Port */
1198 	pci_config_put16(dev->pcih, ESS_PCI_LEGACYCONTROL, 0x8041);
1199 
1200 	/*
1201 	 * Note that Solo-1 uses I/O space for all BARs, and hardwires
1202 	 * the upper 32-bits to zero.
1203 	 */
1204 	data = pci_config_get32(dev->pcih, PCI_CONF_BASE2);
1205 	data |= 1;
1206 	pci_config_put16(dev->pcih, ESS_PCI_DDMACONTROL, data & 0xffff);
1207 
1208 	/*
1209 	 * Make sure that legacy IRQ and DRQ are disbled.  We disable most
1210 	 * other legacy features too.
1211 	 */
1212 	pci_config_put16(dev->pcih, ESS_PCI_CONFIG, 0);
1213 
1214 	if (!solo_reset_dsp(dev))
1215 		return (false);
1216 
1217 	/* enable extended mode */
1218 	(void) solo_cmd(dev, 0xc6);
1219 
1220 
1221 	PORT_WR8(dev->io, 0x7, 0x30); /* enable audio irqs */
1222 
1223 	/* demand mode, 4 bytes/xfer */
1224 	solo_write(dev, 0xb9, 0x01);
1225 
1226 	/*
1227 	 * This sets Audio 2 (playback) to use its own independent
1228 	 * rate control, and gives us 48 kHz compatible divisors.  It
1229 	 * also bypasses the switched capacitor filter.
1230 	 */
1231 	solo_setmixer(dev, 0x71, 0x2a);
1232 
1233 	/* irq control */
1234 	solo_write(dev, 0xb1, (solo_read(dev, 0xb1) & 0x0f) | 0x50);
1235 	/* drq control */
1236 	solo_write(dev, 0xb2, (solo_read(dev, 0xb2) & 0x0f) | 0x50);
1237 
1238 	solo_setmixer(dev, 0, 0); /* reset mixer settings */
1239 
1240 	solo_configure_mixer(dev);
1241 	return (true);
1242 }
1243 
1244 static bool
1245 solo_alloc_engine(solo_dev_t *dev, int engno)
1246 {
1247 	size_t			rlen;
1248 	ddi_dma_attr_t		*dattr;
1249 	ddi_dma_cookie_t	c;
1250 	unsigned		ccnt;
1251 	unsigned		caps;
1252 	unsigned		dflags;
1253 	const char		*desc;
1254 	solo_engine_t		*e;
1255 
1256 	ASSERT((engno == 1) || (engno = 2));
1257 
1258 	switch (engno) {
1259 	case 1:	/* record */
1260 		e = &dev->rec;
1261 		desc = "record";
1262 		dattr = &dma_attr_audio1;
1263 		caps = ENGINE_INPUT_CAP;
1264 		dflags = DDI_DMA_READ | DDI_DMA_CONSISTENT;
1265 		e->syncdir = DDI_DMA_SYNC_FORKERNEL;
1266 		e->update = solo_aud1_update;
1267 		e->start = solo_aud1_start;
1268 		e->stop = solo_aud1_stop;
1269 		e->format = AUDIO_FORMAT_S16_BE;
1270 		e->swapped = true;
1271 		break;
1272 
1273 	case 2:	/* playback */
1274 		e = &dev->play;
1275 		desc = "playback";
1276 		dattr = &dma_attr_audio2;
1277 		caps = ENGINE_OUTPUT_CAP;
1278 		dflags = DDI_DMA_WRITE | DDI_DMA_CONSISTENT;
1279 		e->syncdir = DDI_DMA_SYNC_FORDEV;
1280 		e->update = solo_aud2_update;
1281 		e->start = solo_aud2_start;
1282 		e->stop = solo_aud2_stop;
1283 		e->format = AUDIO_FORMAT_S16_LE;
1284 		e->swapped = false;
1285 		break;
1286 
1287 	default:
1288 		audio_dev_warn(dev->adev, "bad engine number!");
1289 		return (false);
1290 	}
1291 
1292 	printf("%s %sswapped!", desc, e->swapped ? "" : "not ");
1293 	e->dev = dev;
1294 
1295 	if (ddi_dma_alloc_handle(dev->dip, dattr, DDI_DMA_SLEEP, NULL,
1296 	    &e->dmah) != DDI_SUCCESS) {
1297 		audio_dev_warn(dev->adev, "%s dma handle alloc failed", desc);
1298 		return (false);
1299 	}
1300 	if (ddi_dma_mem_alloc(e->dmah, SOLO_BUFSZ, &buf_attr,
1301 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &e->kaddr,
1302 	    &rlen, &e->acch) != DDI_SUCCESS) {
1303 		audio_dev_warn(dev->adev, "%s dma memory alloc failed", desc);
1304 		return (false);
1305 	}
1306 	/* ensure that the buffer is zeroed out properly */
1307 	bzero(e->kaddr, rlen);
1308 	if (ddi_dma_addr_bind_handle(e->dmah, NULL, e->kaddr, SOLO_BUFSZ,
1309 	    dflags, DDI_DMA_SLEEP, NULL, &c, &ccnt) != DDI_DMA_MAPPED) {
1310 		audio_dev_warn(dev->adev, "%s dma binding failed", desc);
1311 		return (false);
1312 	}
1313 	e->paddr = c.dmac_address;
1314 
1315 	/*
1316 	 * Allocate and configure audio engine.
1317 	 */
1318 	e->engine = audio_engine_alloc(&solo_engine_ops, caps);
1319 	if (e->engine == NULL) {
1320 		audio_dev_warn(dev->adev, "record audio_engine_alloc failed");
1321 		return (false);
1322 	}
1323 
1324 	audio_engine_set_private(e->engine, e);
1325 	audio_dev_add_engine(dev->adev, e->engine);
1326 
1327 	return (true);
1328 }
1329 
1330 
1331 static int
1332 solo_suspend(solo_dev_t *dev)
1333 {
1334 	mutex_enter(&dev->mutex);
1335 	/* play */
1336 	solo_aud2_stop(&dev->play);
1337 	solo_aud2_update(&dev->play);
1338 	/* record */
1339 	solo_aud1_stop(&dev->rec);
1340 	solo_aud1_update(&dev->rec);
1341 
1342 	dev->suspended = true;
1343 	mutex_exit(&dev->mutex);
1344 
1345 	return (DDI_SUCCESS);
1346 }
1347 
1348 static int
1349 solo_resume(solo_dev_t *dev)
1350 {
1351 	solo_engine_t	*e;
1352 	audio_engine_t	*prod = NULL;
1353 	audio_engine_t	*cons = NULL;
1354 
1355 	audio_engine_reset(dev->rec.engine);
1356 	audio_engine_reset(dev->play.engine);
1357 
1358 	mutex_enter(&dev->mutex);
1359 	if (!solo_init_hw(dev)) {
1360 		/* yikes! */
1361 		audio_dev_warn(dev->adev, "unable to resume audio!");
1362 		audio_dev_warn(dev->adev, "reboot or reload driver to reset");
1363 		mutex_exit(&dev->mutex);
1364 		return (DDI_SUCCESS);
1365 	}
1366 	dev->suspended = false;
1367 
1368 	/* record - audio 1 */
1369 	e = &dev->rec;
1370 	if (e->started) {
1371 		e->start(e);
1372 		prod = e->engine;
1373 	}
1374 
1375 	/* play - audio 2 */
1376 	e = &dev->play;
1377 	if (e->started) {
1378 		e->start(e);
1379 		cons = e->engine;
1380 	}
1381 
1382 	mutex_exit(&dev->mutex);
1383 
1384 	if (cons)
1385 		audio_engine_consume(cons);
1386 	if (prod)
1387 		audio_engine_produce(prod);
1388 
1389 	return (DDI_SUCCESS);
1390 }
1391 
1392 static int
1393 solo_attach(dev_info_t *dip)
1394 {
1395 	solo_dev_t	*dev;
1396 	uint32_t	data;
1397 
1398 	dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
1399 	dev->dip = dip;
1400 	ddi_set_driver_private(dip, dev);
1401 
1402 	dev->adev = audio_dev_alloc(dip, 0);
1403 	if (dev->adev == NULL)
1404 		goto no;
1405 
1406 	audio_dev_set_description(dev->adev, "ESS Solo-1 PCI AudioDrive");
1407 	audio_dev_set_version(dev->adev, "ES1938");
1408 
1409 	if (pci_config_setup(dip, &dev->pcih) != DDI_SUCCESS) {
1410 		audio_dev_warn(NULL, "pci_config_setup failed");
1411 		goto no;
1412 	}
1413 
1414 	data = pci_config_get16(dev->pcih, PCI_CONF_COMM);
1415 	data |= PCI_COMM_ME | PCI_COMM_IO;
1416 	pci_config_put16(dev->pcih, PCI_CONF_COMM, data);
1417 
1418 	if ((!solo_map_registers(dev)) ||
1419 	    (!solo_setup_interrupts(dev)) ||
1420 	    (!solo_alloc_engine(dev, 1)) ||
1421 	    (!solo_alloc_engine(dev, 2)) ||
1422 	    (!solo_add_controls(dev)) ||
1423 	    (!solo_init_hw(dev))) {
1424 		goto no;
1425 	}
1426 
1427 	if (audio_dev_register(dev->adev) != DDI_SUCCESS) {
1428 		audio_dev_warn(dev->adev,
1429 		    "unable to register with audio framework");
1430 		goto no;
1431 	}
1432 
1433 	(void) ddi_intr_enable(dev->ihandle);
1434 	ddi_report_dev(dip);
1435 
1436 	return (DDI_SUCCESS);
1437 
1438 no:
1439 	solo_release_resources(dev);
1440 	return (DDI_FAILURE);
1441 }
1442 
1443 static int
1444 solo_detach(solo_dev_t *dev)
1445 {
1446 	if (audio_dev_unregister(dev->adev) != DDI_SUCCESS) {
1447 		return (DDI_FAILURE);
1448 	}
1449 
1450 	solo_release_resources(dev);
1451 	return (DDI_SUCCESS);
1452 }
1453 
1454 static int
1455 solo_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1456 {
1457 	solo_dev_t *dev;
1458 
1459 	switch (cmd) {
1460 	case DDI_ATTACH:
1461 		return (solo_attach(dip));
1462 
1463 	case DDI_RESUME:
1464 		if ((dev = ddi_get_driver_private(dip)) == NULL) {
1465 			return (DDI_FAILURE);
1466 		}
1467 		return (solo_resume(dev));
1468 
1469 	default:
1470 		return (DDI_FAILURE);
1471 	}
1472 }
1473 
1474 static int
1475 solo_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1476 {
1477 	solo_dev_t *dev;
1478 
1479 	if ((dev = ddi_get_driver_private(dip)) == NULL) {
1480 		return (DDI_FAILURE);
1481 	}
1482 
1483 	switch (cmd) {
1484 	case DDI_DETACH:
1485 		return (solo_detach(dev));
1486 
1487 	case DDI_SUSPEND:
1488 		return (solo_suspend(dev));
1489 	default:
1490 		return (DDI_FAILURE);
1491 	}
1492 }
1493 
1494 static int
1495 solo_quiesce(dev_info_t *dip)
1496 {
1497 	solo_dev_t *dev;
1498 
1499 	dev = ddi_get_driver_private(dip);
1500 
1501 	solo_aud1_stop(&dev->rec);
1502 	solo_aud2_stop(&dev->play);
1503 
1504 	solo_setmixer(dev, 0, 0);
1505 	PORT_WR8(dev->io, 0x7, 0); /* disable all irqs */
1506 	return (0);
1507 }
1508 
1509 struct dev_ops solo_dev_ops = {
1510 	DEVO_REV,		/* rev */
1511 	0,			/* refcnt */
1512 	NULL,			/* getinfo */
1513 	nulldev,		/* identify */
1514 	nulldev,		/* probe */
1515 	solo_ddi_attach,	/* attach */
1516 	solo_ddi_detach,	/* detach */
1517 	nodev,			/* reset */
1518 	NULL,			/* cb_ops */
1519 	NULL,			/* bus_ops */
1520 	NULL,			/* power */
1521 	solo_quiesce,		/* quiesce */
1522 };
1523 
1524 static struct modldrv solo_modldrv = {
1525 	&mod_driverops,			/* drv_modops */
1526 	"ESS Solo-1 Audio",		/* linkinfo */
1527 	&solo_dev_ops,			/* dev_ops */
1528 };
1529 
1530 static struct modlinkage modlinkage = {
1531 	MODREV_1,
1532 	{ &solo_modldrv, NULL }
1533 };
1534 
1535 int
1536 _init(void)
1537 {
1538 	int	rv;
1539 
1540 	audio_init_ops(&solo_dev_ops, DRVNAME);
1541 	if ((rv = mod_install(&modlinkage)) != 0) {
1542 		audio_fini_ops(&solo_dev_ops);
1543 	}
1544 	return (rv);
1545 }
1546 
1547 int
1548 _fini(void)
1549 {
1550 	int	rv;
1551 
1552 	if ((rv = mod_remove(&modlinkage)) == 0) {
1553 		audio_fini_ops(&solo_dev_ops);
1554 	}
1555 	return (rv);
1556 }
1557 
1558 int
1559 _info(struct modinfo *modinfop)
1560 {
1561 	return (mod_info(&modlinkage, modinfop));
1562 }
1563