xref: /illumos-gate/usr/src/uts/common/io/audio/drv/audiosolo/audiosolo.c (revision 0d166b18feda26f6f45f5be1c0c8c5e539b90e6c)
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 void
938 solo_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
939 {
940 	solo_engine_t *e = arg;
941 
942 	if (e->swapped) {
943 		*offset = !chan;
944 	} else {
945 		*offset = chan;
946 	}
947 	*incr = 2;
948 }
949 
950 static void
951 solo_sync(void *arg, unsigned nframes)
952 {
953 	solo_engine_t *e = arg;
954 
955 	_NOTE(ARGUNUSED(nframes));
956 
957 	(void) ddi_dma_sync(e->dmah, 0, 0, e->syncdir);
958 }
959 
960 
961 static uint64_t
962 solo_count(void *arg)
963 {
964 	solo_engine_t	*e = arg;
965 	solo_dev_t	*dev = e->dev;
966 	uint64_t	count;
967 
968 	mutex_enter(&dev->mutex);
969 	if (!dev->suspended)
970 		e->update(e);
971 	count = e->count;
972 	mutex_exit(&dev->mutex);
973 
974 	return (count);
975 }
976 
977 static int
978 solo_open(void *arg, int f, unsigned *ffr, unsigned *nfr, caddr_t *buf)
979 {
980 	solo_engine_t	*e = arg;
981 	solo_dev_t	*dev = e->dev;
982 
983 	_NOTE(ARGUNUSED(f));
984 
985 	/* NB: For simplicity, we just fix the interrupt rate at 100 Hz */
986 	*ffr = SOLO_FRAGFR;
987 	*nfr = SOLO_NFRAGS;
988 	*buf = e->kaddr;
989 
990 	mutex_enter(&dev->mutex);
991 	e->started = false;
992 	e->count = 0;
993 	mutex_exit(&dev->mutex);
994 
995 	return (0);
996 }
997 
998 void
999 solo_close(void *arg)
1000 {
1001 	solo_engine_t	*e = arg;
1002 	solo_dev_t	*dev = e->dev;
1003 
1004 	mutex_enter(&dev->mutex);
1005 	if (!dev->suspended)
1006 		e->stop(e);
1007 	e->started = false;
1008 	mutex_exit(&dev->mutex);
1009 }
1010 
1011 
1012 static int
1013 solo_start(void *arg)
1014 {
1015 	solo_engine_t	*e = arg;
1016 	solo_dev_t	*dev = e->dev;
1017 
1018 	mutex_enter(&dev->mutex);
1019 	if (!e->started) {
1020 		if (!dev->suspended)
1021 			e->start(e);
1022 		e->started = true;
1023 	}
1024 	mutex_exit(&dev->mutex);
1025 
1026 	return (0);
1027 }
1028 
1029 static void
1030 solo_stop(void *arg)
1031 {
1032 	solo_engine_t	*e = arg;
1033 	solo_dev_t	*dev = e->dev;
1034 
1035 	mutex_enter(&dev->mutex);
1036 	if (e->started) {
1037 		if (!dev->suspended)
1038 			e->stop(e);
1039 		e->started = false;
1040 	}
1041 	mutex_exit(&dev->mutex);
1042 
1043 }
1044 
1045 static audio_engine_ops_t solo_engine_ops = {
1046 	AUDIO_ENGINE_VERSION,
1047 	solo_open,
1048 	solo_close,
1049 	solo_start,
1050 	solo_stop,
1051 	solo_count,
1052 	solo_format,
1053 	solo_channels,
1054 	solo_rate,
1055 	solo_sync,
1056 	NULL,
1057 	solo_chinfo,
1058 	NULL,
1059 };
1060 
1061 static void
1062 solo_release_resources(solo_dev_t *dev)
1063 {
1064 	if (dev->ihandle != NULL) {
1065 		(void) ddi_intr_disable(dev->ihandle);
1066 		(void) ddi_intr_remove_handler(dev->ihandle);
1067 		(void) ddi_intr_free(dev->ihandle);
1068 		mutex_destroy(&dev->mutex);
1069 	}
1070 
1071 	if (dev->io.acch != NULL) {
1072 		ddi_regs_map_free(&dev->io.acch);
1073 	}
1074 
1075 	if (dev->sb.acch != NULL) {
1076 		ddi_regs_map_free(&dev->sb.acch);
1077 	}
1078 
1079 	if (dev->vc.acch != NULL) {
1080 		ddi_regs_map_free(&dev->vc.acch);
1081 	}
1082 
1083 	if (dev->pcih != NULL) {
1084 		pci_config_teardown(&dev->pcih);
1085 	}
1086 
1087 	/* release play resources */
1088 	if (dev->play.paddr != 0)
1089 		(void) ddi_dma_unbind_handle(dev->play.dmah);
1090 	if (dev->play.acch != NULL)
1091 		ddi_dma_mem_free(&dev->play.acch);
1092 	if (dev->play.dmah != NULL)
1093 		ddi_dma_free_handle(&dev->play.dmah);
1094 
1095 	if (dev->play.engine != NULL) {
1096 		audio_dev_remove_engine(dev->adev, dev->play.engine);
1097 		audio_engine_free(dev->play.engine);
1098 	}
1099 
1100 	/* release record resources */
1101 	if (dev->rec.paddr != 0)
1102 		(void) ddi_dma_unbind_handle(dev->rec.dmah);
1103 	if (dev->rec.acch != NULL)
1104 		ddi_dma_mem_free(&dev->rec.acch);
1105 	if (dev->rec.dmah != NULL)
1106 		ddi_dma_free_handle(&dev->rec.dmah);
1107 
1108 	if (dev->rec.engine != NULL) {
1109 		audio_dev_remove_engine(dev->adev, dev->rec.engine);
1110 		audio_engine_free(dev->rec.engine);
1111 	}
1112 
1113 	if (dev->adev != NULL) {
1114 		audio_dev_free(dev->adev);
1115 	}
1116 
1117 	kmem_free(dev, sizeof (*dev));
1118 }
1119 
1120 static bool
1121 solo_setup_interrupts(solo_dev_t *dev)
1122 {
1123 	int actual;
1124 	uint_t ipri;
1125 
1126 	if ((ddi_intr_alloc(dev->dip, &dev->ihandle, DDI_INTR_TYPE_FIXED,
1127 	    0, 1, &actual, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) ||
1128 	    (actual != 1)) {
1129 		audio_dev_warn(dev->adev, "can't alloc intr handle");
1130 		return (false);
1131 	}
1132 
1133 	if (ddi_intr_get_pri(dev->ihandle, &ipri) != DDI_SUCCESS) {
1134 		audio_dev_warn(dev->adev,  "can't determine intr priority");
1135 		(void) ddi_intr_free(dev->ihandle);
1136 		dev->ihandle = NULL;
1137 		return (false);
1138 	}
1139 
1140 	if (ddi_intr_add_handler(dev->ihandle, solo_intr, dev,
1141 	    NULL) != DDI_SUCCESS) {
1142 		audio_dev_warn(dev->adev, "can't add intr handler");
1143 		(void) ddi_intr_free(dev->ihandle);
1144 		dev->ihandle = NULL;
1145 		return (false);
1146 	}
1147 
1148 	mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
1149 
1150 	return (true);
1151 }
1152 
1153 static bool
1154 solo_map_registers(solo_dev_t *dev)
1155 {
1156 	dev_info_t	*dip = dev->dip;
1157 
1158 	/* map registers */
1159 	if (ddi_regs_map_setup(dip, 1, &dev->io.base, 0, 0, &acc_attr,
1160 	    &dev->io.acch) != DDI_SUCCESS) {
1161 		audio_dev_warn(dev->adev, "can't map IO registers");
1162 		return (false);
1163 	}
1164 	if (ddi_regs_map_setup(dip, 2, &dev->sb.base, 0, 0, &acc_attr,
1165 	    &dev->sb.acch) != DDI_SUCCESS) {
1166 		audio_dev_warn(dev->adev, "can't map SB registers");
1167 		return (false);
1168 	}
1169 	if (ddi_regs_map_setup(dip, 3, &dev->vc.base, 0, 0, &acc_attr,
1170 	    &dev->vc.acch) != DDI_SUCCESS) {
1171 		audio_dev_warn(dev->adev, "can't map VC registers");
1172 		return (false);
1173 	}
1174 
1175 	return (true);
1176 }
1177 
1178 #define	ESS_PCI_LEGACYCONTROL		0x40
1179 #define	ESS_PCI_CONFIG			0x50
1180 #define	ESS_PCI_DDMACONTROL		0x60
1181 
1182 static bool
1183 solo_init_hw(solo_dev_t *dev)
1184 {
1185 	uint32_t	data;
1186 
1187 	/*
1188 	 * Legacy audio register -- disable legacy audio.  We also
1189 	 * arrange for 16-bit I/O address decoding.
1190 	 */
1191 	/* this version disables the MPU, FM synthesis (Adlib), and Game Port */
1192 	pci_config_put16(dev->pcih, ESS_PCI_LEGACYCONTROL, 0x8041);
1193 
1194 	/*
1195 	 * Note that Solo-1 uses I/O space for all BARs, and hardwires
1196 	 * the upper 32-bits to zero.
1197 	 */
1198 	data = pci_config_get32(dev->pcih, PCI_CONF_BASE2);
1199 	data |= 1;
1200 	pci_config_put16(dev->pcih, ESS_PCI_DDMACONTROL, data & 0xffff);
1201 
1202 	/*
1203 	 * Make sure that legacy IRQ and DRQ are disbled.  We disable most
1204 	 * other legacy features too.
1205 	 */
1206 	pci_config_put16(dev->pcih, ESS_PCI_CONFIG, 0);
1207 
1208 	if (!solo_reset_dsp(dev))
1209 		return (false);
1210 
1211 	/* enable extended mode */
1212 	(void) solo_cmd(dev, 0xc6);
1213 
1214 
1215 	PORT_WR8(dev->io, 0x7, 0x30); /* enable audio irqs */
1216 
1217 	/* demand mode, 4 bytes/xfer */
1218 	solo_write(dev, 0xb9, 0x01);
1219 
1220 	/*
1221 	 * This sets Audio 2 (playback) to use its own independent
1222 	 * rate control, and gives us 48 kHz compatible divisors.  It
1223 	 * also bypasses the switched capacitor filter.
1224 	 */
1225 	solo_setmixer(dev, 0x71, 0x2a);
1226 
1227 	/* irq control */
1228 	solo_write(dev, 0xb1, (solo_read(dev, 0xb1) & 0x0f) | 0x50);
1229 	/* drq control */
1230 	solo_write(dev, 0xb2, (solo_read(dev, 0xb2) & 0x0f) | 0x50);
1231 
1232 	solo_setmixer(dev, 0, 0); /* reset mixer settings */
1233 
1234 	solo_configure_mixer(dev);
1235 	return (true);
1236 }
1237 
1238 static bool
1239 solo_alloc_engine(solo_dev_t *dev, int engno)
1240 {
1241 	size_t			rlen;
1242 	ddi_dma_attr_t		*dattr;
1243 	ddi_dma_cookie_t	c;
1244 	unsigned		ccnt;
1245 	unsigned		caps;
1246 	unsigned		dflags;
1247 	const char		*desc;
1248 	solo_engine_t		*e;
1249 
1250 	ASSERT((engno == 1) || (engno = 2));
1251 
1252 	switch (engno) {
1253 	case 1:	/* record */
1254 		e = &dev->rec;
1255 		desc = "record";
1256 		dattr = &dma_attr_audio1;
1257 		caps = ENGINE_INPUT_CAP;
1258 		dflags = DDI_DMA_READ | DDI_DMA_CONSISTENT;
1259 		e->syncdir = DDI_DMA_SYNC_FORKERNEL;
1260 		e->update = solo_aud1_update;
1261 		e->start = solo_aud1_start;
1262 		e->stop = solo_aud1_stop;
1263 		e->format = AUDIO_FORMAT_S16_BE;
1264 		e->swapped = true;
1265 		break;
1266 
1267 	case 2:	/* playback */
1268 		e = &dev->play;
1269 		desc = "playback";
1270 		dattr = &dma_attr_audio2;
1271 		caps = ENGINE_OUTPUT_CAP;
1272 		dflags = DDI_DMA_WRITE | DDI_DMA_CONSISTENT;
1273 		e->syncdir = DDI_DMA_SYNC_FORDEV;
1274 		e->update = solo_aud2_update;
1275 		e->start = solo_aud2_start;
1276 		e->stop = solo_aud2_stop;
1277 		e->format = AUDIO_FORMAT_S16_LE;
1278 		e->swapped = false;
1279 		break;
1280 
1281 	default:
1282 		audio_dev_warn(dev->adev, "bad engine number!");
1283 		return (false);
1284 	}
1285 
1286 	printf("%s %sswapped!", desc, e->swapped ? "" : "not ");
1287 	e->dev = dev;
1288 
1289 	if (ddi_dma_alloc_handle(dev->dip, dattr, DDI_DMA_SLEEP, NULL,
1290 	    &e->dmah) != DDI_SUCCESS) {
1291 		audio_dev_warn(dev->adev, "%s dma handle alloc failed", desc);
1292 		return (false);
1293 	}
1294 	if (ddi_dma_mem_alloc(e->dmah, SOLO_BUFSZ, &buf_attr,
1295 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &e->kaddr,
1296 	    &rlen, &e->acch) != DDI_SUCCESS) {
1297 		audio_dev_warn(dev->adev, "%s dma memory alloc failed", desc);
1298 		return (false);
1299 	}
1300 	/* ensure that the buffer is zeroed out properly */
1301 	bzero(e->kaddr, rlen);
1302 	if (ddi_dma_addr_bind_handle(e->dmah, NULL, e->kaddr, SOLO_BUFSZ,
1303 	    dflags, DDI_DMA_SLEEP, NULL, &c, &ccnt) != DDI_DMA_MAPPED) {
1304 		audio_dev_warn(dev->adev, "%s dma binding failed", desc);
1305 		return (false);
1306 	}
1307 	e->paddr = c.dmac_address;
1308 
1309 	/*
1310 	 * Allocate and configure audio engine.
1311 	 */
1312 	e->engine = audio_engine_alloc(&solo_engine_ops, caps);
1313 	if (e->engine == NULL) {
1314 		audio_dev_warn(dev->adev, "record audio_engine_alloc failed");
1315 		return (false);
1316 	}
1317 
1318 	audio_engine_set_private(e->engine, e);
1319 	audio_dev_add_engine(dev->adev, e->engine);
1320 
1321 	return (true);
1322 }
1323 
1324 
1325 static int
1326 solo_suspend(solo_dev_t *dev)
1327 {
1328 	mutex_enter(&dev->mutex);
1329 	/* play */
1330 	solo_aud2_stop(&dev->play);
1331 	solo_aud2_update(&dev->play);
1332 	/* record */
1333 	solo_aud1_stop(&dev->rec);
1334 	solo_aud1_update(&dev->rec);
1335 
1336 	dev->suspended = true;
1337 	mutex_exit(&dev->mutex);
1338 
1339 	return (DDI_SUCCESS);
1340 }
1341 
1342 static int
1343 solo_resume(solo_dev_t *dev)
1344 {
1345 	solo_engine_t	*e;
1346 	audio_engine_t	*prod = NULL;
1347 	audio_engine_t	*cons = NULL;
1348 
1349 	audio_engine_reset(dev->rec.engine);
1350 	audio_engine_reset(dev->play.engine);
1351 
1352 	mutex_enter(&dev->mutex);
1353 	if (!solo_init_hw(dev)) {
1354 		/* yikes! */
1355 		audio_dev_warn(dev->adev, "unable to resume audio!");
1356 		audio_dev_warn(dev->adev, "reboot or reload driver to reset");
1357 		mutex_exit(&dev->mutex);
1358 		return (DDI_SUCCESS);
1359 	}
1360 	dev->suspended = false;
1361 
1362 	/* record - audio 1 */
1363 	e = &dev->rec;
1364 	if (e->started) {
1365 		e->start(e);
1366 		prod = e->engine;
1367 	}
1368 
1369 	/* play - audio 2 */
1370 	e = &dev->play;
1371 	if (e->started) {
1372 		e->start(e);
1373 		cons = e->engine;
1374 	}
1375 
1376 	mutex_exit(&dev->mutex);
1377 
1378 	if (cons)
1379 		audio_engine_consume(cons);
1380 	if (prod)
1381 		audio_engine_produce(prod);
1382 
1383 	return (DDI_SUCCESS);
1384 }
1385 
1386 static int
1387 solo_attach(dev_info_t *dip)
1388 {
1389 	solo_dev_t	*dev;
1390 	uint32_t	data;
1391 
1392 	dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
1393 	dev->dip = dip;
1394 	ddi_set_driver_private(dip, dev);
1395 
1396 	dev->adev = audio_dev_alloc(dip, 0);
1397 	if (dev->adev == NULL)
1398 		goto no;
1399 
1400 	audio_dev_set_description(dev->adev, "ESS Solo-1 PCI AudioDrive");
1401 	audio_dev_set_version(dev->adev, "ES1938");
1402 
1403 	if (pci_config_setup(dip, &dev->pcih) != DDI_SUCCESS) {
1404 		audio_dev_warn(NULL, "pci_config_setup failed");
1405 		goto no;
1406 	}
1407 
1408 	data = pci_config_get16(dev->pcih, PCI_CONF_COMM);
1409 	data |= PCI_COMM_ME | PCI_COMM_IO;
1410 	pci_config_put16(dev->pcih, PCI_CONF_COMM, data);
1411 
1412 	if ((!solo_map_registers(dev)) ||
1413 	    (!solo_setup_interrupts(dev)) ||
1414 	    (!solo_alloc_engine(dev, 1)) ||
1415 	    (!solo_alloc_engine(dev, 2)) ||
1416 	    (!solo_add_controls(dev)) ||
1417 	    (!solo_init_hw(dev))) {
1418 		goto no;
1419 	}
1420 
1421 	if (audio_dev_register(dev->adev) != DDI_SUCCESS) {
1422 		audio_dev_warn(dev->adev,
1423 		    "unable to register with audio framework");
1424 		goto no;
1425 	}
1426 
1427 	(void) ddi_intr_enable(dev->ihandle);
1428 	ddi_report_dev(dip);
1429 
1430 	return (DDI_SUCCESS);
1431 
1432 no:
1433 	solo_release_resources(dev);
1434 	return (DDI_FAILURE);
1435 }
1436 
1437 static int
1438 solo_detach(solo_dev_t *dev)
1439 {
1440 	if (audio_dev_unregister(dev->adev) != DDI_SUCCESS) {
1441 		return (DDI_FAILURE);
1442 	}
1443 
1444 	solo_release_resources(dev);
1445 	return (DDI_SUCCESS);
1446 }
1447 
1448 static int
1449 solo_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1450 {
1451 	solo_dev_t *dev;
1452 
1453 	switch (cmd) {
1454 	case DDI_ATTACH:
1455 		return (solo_attach(dip));
1456 
1457 	case DDI_RESUME:
1458 		if ((dev = ddi_get_driver_private(dip)) == NULL) {
1459 			return (DDI_FAILURE);
1460 		}
1461 		return (solo_resume(dev));
1462 
1463 	default:
1464 		return (DDI_FAILURE);
1465 	}
1466 }
1467 
1468 static int
1469 solo_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1470 {
1471 	solo_dev_t *dev;
1472 
1473 	if ((dev = ddi_get_driver_private(dip)) == NULL) {
1474 		return (DDI_FAILURE);
1475 	}
1476 
1477 	switch (cmd) {
1478 	case DDI_DETACH:
1479 		return (solo_detach(dev));
1480 
1481 	case DDI_SUSPEND:
1482 		return (solo_suspend(dev));
1483 	default:
1484 		return (DDI_FAILURE);
1485 	}
1486 }
1487 
1488 static int
1489 solo_quiesce(dev_info_t *dip)
1490 {
1491 	solo_dev_t *dev;
1492 
1493 	dev = ddi_get_driver_private(dip);
1494 
1495 	solo_aud1_stop(&dev->rec);
1496 	solo_aud2_stop(&dev->play);
1497 
1498 	solo_setmixer(dev, 0, 0);
1499 	PORT_WR8(dev->io, 0x7, 0); /* disable all irqs */
1500 	return (0);
1501 }
1502 
1503 struct dev_ops solo_dev_ops = {
1504 	DEVO_REV,		/* rev */
1505 	0,			/* refcnt */
1506 	NULL,			/* getinfo */
1507 	nulldev,		/* identify */
1508 	nulldev,		/* probe */
1509 	solo_ddi_attach,	/* attach */
1510 	solo_ddi_detach,	/* detach */
1511 	nodev,			/* reset */
1512 	NULL,			/* cb_ops */
1513 	NULL,			/* bus_ops */
1514 	NULL,			/* power */
1515 	solo_quiesce,		/* quiesce */
1516 };
1517 
1518 static struct modldrv solo_modldrv = {
1519 	&mod_driverops,			/* drv_modops */
1520 	"ESS Solo-1 Audio",		/* linkinfo */
1521 	&solo_dev_ops,			/* dev_ops */
1522 };
1523 
1524 static struct modlinkage modlinkage = {
1525 	MODREV_1,
1526 	{ &solo_modldrv, NULL }
1527 };
1528 
1529 int
1530 _init(void)
1531 {
1532 	int	rv;
1533 
1534 	audio_init_ops(&solo_dev_ops, DRVNAME);
1535 	if ((rv = mod_install(&modlinkage)) != 0) {
1536 		audio_fini_ops(&solo_dev_ops);
1537 	}
1538 	return (rv);
1539 }
1540 
1541 int
1542 _fini(void)
1543 {
1544 	int	rv;
1545 
1546 	if ((rv = mod_remove(&modlinkage)) == 0) {
1547 		audio_fini_ops(&solo_dev_ops);
1548 	}
1549 	return (rv);
1550 }
1551 
1552 int
1553 _info(struct modinfo *modinfop)
1554 {
1555 	return (mod_info(&modlinkage, modinfop));
1556 }
1557