xref: /illumos-gate/usr/src/uts/common/io/audio/drv/audiosolo/audiosolo.c (revision d8a7fe16f62711cdc5c4267da8b34ff24a6b668c)
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 	e->offset = 0;
792 	len = SOLO_FRAGSZ / 2;
793 	len = -len;
794 
795 	/* sample rate - 48 kHz */
796 	solo_write(dev, 0xa1, 0xf0);
797 	/* filter cutoff */
798 	solo_write(dev, 0xa2, solo_calcfilter(SOLO_RATE));
799 
800 
801 	/* mono/stereo - bit 0 set, bit 1 clear */
802 	solo_write(dev, 0xa8, (solo_read(dev, 0xa8) & ~0x03) | 1);
803 
804 	(void) solo_cmd(dev, 0xd3);	/* turn off DAC1 output */
805 
806 	/* setup fifo for signed 16-bit stereo */
807 	solo_write(dev, 0xb7, 0x71);
808 	solo_write(dev, 0xb7, 0xbc);
809 
810 	v = solo_mixer_scale(dev, CTL_RECGAIN);
811 	v = v | (v << 4);
812 	solo_write(dev, 0xb4, v & 0xff);
813 
814 	PORT_WR8(dev->vc, 0x8, 0xc4); /* command */
815 	PORT_WR8(dev->vc, 0xd, 0xff); /* clear DMA */
816 	PORT_WR8(dev->vc, 0xf, 0x01); /* stop DMA  */
817 
818 	PORT_WR8(dev->vc, 0xd, 0xff); /* reset */
819 	PORT_WR8(dev->vc, 0xf, 0x01); /* mask */
820 	PORT_WR8(dev->vc, 0xb, 0x14); /* mode */
821 
822 	PORT_WR32(dev->vc, 0x0, e->paddr);
823 	PORT_WR16(dev->vc, 0x4, SOLO_BUFSZ - 1);
824 
825 	/* transfer length low, high */
826 	solo_write(dev, 0xa4, len & 0x00ff);
827 	solo_write(dev, 0xa5, (len & 0xff00) >> 8);
828 
829 	/* autoinit, dma dir, go for it */
830 	solo_write(dev, 0xb8, 0x0f);
831 	PORT_WR8(dev->vc, 0xf, 0);	/* start DMA */
832 
833 	dev->last_capture = e->paddr;
834 }
835 
836 static void
837 solo_aud1_stop(solo_engine_t *e)
838 {
839 	solo_dev_t	*dev = e->dev;
840 
841 	/* NB: We might be in quiesce, without a lock held */
842 	solo_write(dev, 0xb8, solo_read(dev, 0xb8) & ~0x01);
843 }
844 
845 static void
846 solo_aud2_update(solo_engine_t *e)
847 {
848 	solo_dev_t	*dev = e->dev;
849 	uint16_t	offset = 0, n;
850 
851 	ASSERT(mutex_owned(&dev->mutex));
852 
853 	offset = SOLO_BUFSZ - PORT_RD16(dev->io, 0x4);
854 	offset /= (SOLO_NCHAN * SOLO_SAMPSZ);
855 
856 	n = offset >= e->offset ?
857 	    offset - e->offset :
858 	    offset + SOLO_BUFFR - e->offset;
859 
860 	e->offset = offset;
861 	e->count += n;
862 }
863 
864 static void
865 solo_aud2_start(solo_engine_t *e)
866 {
867 	solo_dev_t	*dev = e->dev;
868 	int		len;
869 	uint32_t	v;
870 
871 	ASSERT(mutex_owned(&dev->mutex));
872 
873 	e->offset = 0;
874 	len = SOLO_FRAGSZ / 2;
875 	len = -len;
876 
877 	/* program transfer type */
878 	solo_setmixer(dev, 0x78, 0x10);
879 	/* sample rate - 48 kHz */
880 	solo_setmixer(dev, 0x70, 0xf0);
881 	solo_setmixer(dev, 0x72, solo_calcfilter(SOLO_RATE));
882 	/* transfer length low & high */
883 	solo_setmixer(dev, 0x74, len & 0x00ff);
884 	solo_setmixer(dev, 0x76, (len & 0xff00) >> 8);
885 	/* enable irq, set signed 16-bit stereo format */
886 	solo_setmixer(dev, 0x7a, 0x47);
887 
888 	PORT_WR8(dev->io, 0x6, 0);
889 	PORT_WR32(dev->io, 0x0, e->paddr);
890 	PORT_WR16(dev->io, 0x4, SOLO_BUFSZ);
891 
892 	/* this crazy initialization appears to help with fifo weirdness */
893 	/* start the engine running */
894 	solo_setmixer(dev, 0x78, 0x92);
895 	drv_usecwait(10);
896 	solo_setmixer(dev, 0x78, 0x93);
897 
898 	PORT_WR8(dev->io, 0x6, 0x0a); /* autoinit, enable */
899 
900 	v = solo_mixer_scale(dev, CTL_VOLUME);
901 	v = v | (v << 4);
902 	solo_setmixer(dev, 0x7c, v & 0xff);
903 }
904 
905 static void
906 solo_aud2_stop(solo_engine_t *e)
907 {
908 	solo_dev_t	*dev = e->dev;
909 
910 	/* NB: We might be in quiesce, without a lock held */
911 	PORT_WR8(dev->io, 0x6, 0);
912 	solo_setmixer(dev, 0x78, solo_getmixer(dev, 0x78) & ~0x03);
913 }
914 
915 /*
916  * Audio entry points.
917  */
918 static int
919 solo_format(void *arg)
920 {
921 	solo_engine_t	*e = arg;
922 	return (e->format);
923 }
924 
925 static int
926 solo_channels(void *arg)
927 {
928 	_NOTE(ARGUNUSED(arg));
929 	return (SOLO_NCHAN);
930 }
931 
932 static int
933 solo_rate(void *arg)
934 {
935 	_NOTE(ARGUNUSED(arg));
936 	return (SOLO_RATE);
937 }
938 
939 static void
940 solo_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
941 {
942 	solo_engine_t *e = arg;
943 
944 	if (e->swapped) {
945 		*offset = !chan;
946 	} else {
947 		*offset = chan;
948 	}
949 	*incr = 2;
950 }
951 
952 static void
953 solo_sync(void *arg, unsigned nframes)
954 {
955 	solo_engine_t *e = arg;
956 
957 	_NOTE(ARGUNUSED(nframes));
958 
959 	(void) ddi_dma_sync(e->dmah, 0, 0, e->syncdir);
960 }
961 
962 
963 static uint64_t
964 solo_count(void *arg)
965 {
966 	solo_engine_t	*e = arg;
967 	solo_dev_t	*dev = e->dev;
968 	uint64_t	count;
969 
970 	mutex_enter(&dev->mutex);
971 	if (!dev->suspended)
972 		e->update(e);
973 	count = e->count;
974 	mutex_exit(&dev->mutex);
975 
976 	return (count);
977 }
978 
979 static int
980 solo_open(void *arg, int f, unsigned *ffr, unsigned *nfr, caddr_t *buf)
981 {
982 	solo_engine_t	*e = arg;
983 	solo_dev_t	*dev = e->dev;
984 
985 	_NOTE(ARGUNUSED(f));
986 
987 	/* NB: For simplicity, we just fix the interrupt rate at 100 Hz */
988 	*ffr = SOLO_FRAGFR;
989 	*nfr = SOLO_NFRAGS;
990 	*buf = e->kaddr;
991 
992 	mutex_enter(&dev->mutex);
993 	e->started = false;
994 	e->count = 0;
995 	mutex_exit(&dev->mutex);
996 
997 	return (0);
998 }
999 
1000 void
1001 solo_close(void *arg)
1002 {
1003 	solo_engine_t	*e = arg;
1004 	solo_dev_t	*dev = e->dev;
1005 
1006 	mutex_enter(&dev->mutex);
1007 	if (!dev->suspended)
1008 		e->stop(e);
1009 	e->started = false;
1010 	mutex_exit(&dev->mutex);
1011 }
1012 
1013 
1014 static int
1015 solo_start(void *arg)
1016 {
1017 	solo_engine_t	*e = arg;
1018 	solo_dev_t	*dev = e->dev;
1019 
1020 	mutex_enter(&dev->mutex);
1021 	if (!e->started) {
1022 		if (!dev->suspended)
1023 			e->start(e);
1024 		e->started = true;
1025 	}
1026 	mutex_exit(&dev->mutex);
1027 
1028 	return (0);
1029 }
1030 
1031 static void
1032 solo_stop(void *arg)
1033 {
1034 	solo_engine_t	*e = arg;
1035 	solo_dev_t	*dev = e->dev;
1036 
1037 	mutex_enter(&dev->mutex);
1038 	if (e->started) {
1039 		if (!dev->suspended)
1040 			e->stop(e);
1041 		e->started = false;
1042 	}
1043 	mutex_exit(&dev->mutex);
1044 
1045 }
1046 
1047 static audio_engine_ops_t solo_engine_ops = {
1048 	AUDIO_ENGINE_VERSION,
1049 	solo_open,
1050 	solo_close,
1051 	solo_start,
1052 	solo_stop,
1053 	solo_count,
1054 	solo_format,
1055 	solo_channels,
1056 	solo_rate,
1057 	solo_sync,
1058 	NULL,
1059 	solo_chinfo,
1060 	NULL,
1061 };
1062 
1063 static void
1064 solo_release_resources(solo_dev_t *dev)
1065 {
1066 	if (dev->ihandle != NULL) {
1067 		(void) ddi_intr_disable(dev->ihandle);
1068 		(void) ddi_intr_remove_handler(dev->ihandle);
1069 		(void) ddi_intr_free(dev->ihandle);
1070 		mutex_destroy(&dev->mutex);
1071 	}
1072 
1073 	if (dev->io.acch != NULL) {
1074 		ddi_regs_map_free(&dev->io.acch);
1075 	}
1076 
1077 	if (dev->sb.acch != NULL) {
1078 		ddi_regs_map_free(&dev->sb.acch);
1079 	}
1080 
1081 	if (dev->vc.acch != NULL) {
1082 		ddi_regs_map_free(&dev->vc.acch);
1083 	}
1084 
1085 	if (dev->pcih != NULL) {
1086 		pci_config_teardown(&dev->pcih);
1087 	}
1088 
1089 	/* release play resources */
1090 	if (dev->play.paddr != 0)
1091 		(void) ddi_dma_unbind_handle(dev->play.dmah);
1092 	if (dev->play.acch != NULL)
1093 		ddi_dma_mem_free(&dev->play.acch);
1094 	if (dev->play.dmah != NULL)
1095 		ddi_dma_free_handle(&dev->play.dmah);
1096 
1097 	if (dev->play.engine != NULL) {
1098 		audio_dev_remove_engine(dev->adev, dev->play.engine);
1099 		audio_engine_free(dev->play.engine);
1100 	}
1101 
1102 	/* release record resources */
1103 	if (dev->rec.paddr != 0)
1104 		(void) ddi_dma_unbind_handle(dev->rec.dmah);
1105 	if (dev->rec.acch != NULL)
1106 		ddi_dma_mem_free(&dev->rec.acch);
1107 	if (dev->rec.dmah != NULL)
1108 		ddi_dma_free_handle(&dev->rec.dmah);
1109 
1110 	if (dev->rec.engine != NULL) {
1111 		audio_dev_remove_engine(dev->adev, dev->rec.engine);
1112 		audio_engine_free(dev->rec.engine);
1113 	}
1114 
1115 	for (int i = 0; i < CTL_NUM; i++) {
1116 		if (dev->ctrls[i].ctrl != NULL) {
1117 			audio_dev_del_control(dev->ctrls[i].ctrl);
1118 		}
1119 	}
1120 
1121 	if (dev->adev != NULL) {
1122 		audio_dev_free(dev->adev);
1123 	}
1124 
1125 	kmem_free(dev, sizeof (*dev));
1126 }
1127 
1128 static bool
1129 solo_setup_interrupts(solo_dev_t *dev)
1130 {
1131 	int actual;
1132 	uint_t ipri;
1133 
1134 	if ((ddi_intr_alloc(dev->dip, &dev->ihandle, DDI_INTR_TYPE_FIXED,
1135 	    0, 1, &actual, DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) ||
1136 	    (actual != 1)) {
1137 		audio_dev_warn(dev->adev, "can't alloc intr handle");
1138 		return (false);
1139 	}
1140 
1141 	if (ddi_intr_get_pri(dev->ihandle, &ipri) != DDI_SUCCESS) {
1142 		audio_dev_warn(dev->adev,  "can't determine intr priority");
1143 		(void) ddi_intr_free(dev->ihandle);
1144 		dev->ihandle = NULL;
1145 		return (false);
1146 	}
1147 
1148 	if (ddi_intr_add_handler(dev->ihandle, solo_intr, dev,
1149 	    NULL) != DDI_SUCCESS) {
1150 		audio_dev_warn(dev->adev, "can't add intr handler");
1151 		(void) ddi_intr_free(dev->ihandle);
1152 		dev->ihandle = NULL;
1153 		return (false);
1154 	}
1155 
1156 	mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(ipri));
1157 
1158 	return (true);
1159 }
1160 
1161 static bool
1162 solo_map_registers(solo_dev_t *dev)
1163 {
1164 	dev_info_t	*dip = dev->dip;
1165 
1166 	/* map registers */
1167 	if (ddi_regs_map_setup(dip, 1, &dev->io.base, 0, 0, &acc_attr,
1168 	    &dev->io.acch) != DDI_SUCCESS) {
1169 		audio_dev_warn(dev->adev, "can't map IO registers");
1170 		return (false);
1171 	}
1172 	if (ddi_regs_map_setup(dip, 2, &dev->sb.base, 0, 0, &acc_attr,
1173 	    &dev->sb.acch) != DDI_SUCCESS) {
1174 		audio_dev_warn(dev->adev, "can't map SB registers");
1175 		return (false);
1176 	}
1177 	if (ddi_regs_map_setup(dip, 3, &dev->vc.base, 0, 0, &acc_attr,
1178 	    &dev->vc.acch) != DDI_SUCCESS) {
1179 		audio_dev_warn(dev->adev, "can't map VC registers");
1180 		return (false);
1181 	}
1182 
1183 	return (true);
1184 }
1185 
1186 #define	ESS_PCI_LEGACYCONTROL		0x40
1187 #define	ESS_PCI_CONFIG			0x50
1188 #define	ESS_PCI_DDMACONTROL		0x60
1189 
1190 static bool
1191 solo_init_hw(solo_dev_t *dev)
1192 {
1193 	uint32_t	data;
1194 
1195 	/*
1196 	 * Legacy audio register -- disable legacy audio.  We also
1197 	 * arrange for 16-bit I/O address decoding.
1198 	 */
1199 	/* this version disables the MPU, FM synthesis (Adlib), and Game Port */
1200 	pci_config_put16(dev->pcih, ESS_PCI_LEGACYCONTROL, 0x8041);
1201 
1202 	/*
1203 	 * Note that Solo-1 uses I/O space for all BARs, and hardwires
1204 	 * the upper 32-bits to zero.
1205 	 */
1206 	data = pci_config_get32(dev->pcih, PCI_CONF_BASE2);
1207 	data |= 1;
1208 	pci_config_put16(dev->pcih, ESS_PCI_DDMACONTROL, data & 0xffff);
1209 
1210 	/*
1211 	 * Make sure that legacy IRQ and DRQ are disbled.  We disable most
1212 	 * other legacy features too.
1213 	 */
1214 	pci_config_put16(dev->pcih, ESS_PCI_CONFIG, 0);
1215 
1216 	if (!solo_reset_dsp(dev))
1217 		return (false);
1218 
1219 	/* enable extended mode */
1220 	(void) solo_cmd(dev, 0xc6);
1221 
1222 
1223 	PORT_WR8(dev->io, 0x7, 0x30); /* enable audio irqs */
1224 
1225 	/* demand mode, 4 bytes/xfer */
1226 	solo_write(dev, 0xb9, 0x01);
1227 
1228 	/*
1229 	 * This sets Audio 2 (playback) to use its own independent
1230 	 * rate control, and gives us 48 kHz compatible divisors.  It
1231 	 * also bypasses the switched capacitor filter.
1232 	 */
1233 	solo_setmixer(dev, 0x71, 0x2a);
1234 
1235 	/* irq control */
1236 	solo_write(dev, 0xb1, (solo_read(dev, 0xb1) & 0x0f) | 0x50);
1237 	/* drq control */
1238 	solo_write(dev, 0xb2, (solo_read(dev, 0xb2) & 0x0f) | 0x50);
1239 
1240 	solo_setmixer(dev, 0, 0); /* reset mixer settings */
1241 
1242 	solo_configure_mixer(dev);
1243 	return (true);
1244 }
1245 
1246 static bool
1247 solo_alloc_engine(solo_dev_t *dev, int engno)
1248 {
1249 	size_t			rlen;
1250 	ddi_dma_attr_t		*dattr;
1251 	ddi_dma_cookie_t	c;
1252 	unsigned		ccnt;
1253 	unsigned		caps;
1254 	unsigned		dflags;
1255 	const char		*desc;
1256 	solo_engine_t		*e;
1257 
1258 	ASSERT((engno == 1) || (engno = 2));
1259 
1260 	switch (engno) {
1261 	case 1:	/* record */
1262 		e = &dev->rec;
1263 		desc = "record";
1264 		dattr = &dma_attr_audio1;
1265 		caps = ENGINE_INPUT_CAP;
1266 		dflags = DDI_DMA_READ | DDI_DMA_CONSISTENT;
1267 		e->syncdir = DDI_DMA_SYNC_FORKERNEL;
1268 		e->update = solo_aud1_update;
1269 		e->start = solo_aud1_start;
1270 		e->stop = solo_aud1_stop;
1271 		e->format = AUDIO_FORMAT_S16_BE;
1272 		e->swapped = true;
1273 		break;
1274 
1275 	case 2:	/* playback */
1276 		e = &dev->play;
1277 		desc = "playback";
1278 		dattr = &dma_attr_audio2;
1279 		caps = ENGINE_OUTPUT_CAP;
1280 		dflags = DDI_DMA_WRITE | DDI_DMA_CONSISTENT;
1281 		e->syncdir = DDI_DMA_SYNC_FORDEV;
1282 		e->update = solo_aud2_update;
1283 		e->start = solo_aud2_start;
1284 		e->stop = solo_aud2_stop;
1285 		e->format = AUDIO_FORMAT_S16_LE;
1286 		e->swapped = false;
1287 		break;
1288 
1289 	default:
1290 		audio_dev_warn(dev->adev, "bad engine number!");
1291 		return (false);
1292 	}
1293 
1294 	e->dev = dev;
1295 
1296 	if (ddi_dma_alloc_handle(dev->dip, dattr, DDI_DMA_SLEEP, NULL,
1297 	    &e->dmah) != DDI_SUCCESS) {
1298 		audio_dev_warn(dev->adev, "%s dma handle alloc failed", desc);
1299 		return (false);
1300 	}
1301 	if (ddi_dma_mem_alloc(e->dmah, SOLO_BUFSZ, &buf_attr,
1302 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &e->kaddr,
1303 	    &rlen, &e->acch) != DDI_SUCCESS) {
1304 		audio_dev_warn(dev->adev, "%s dma memory alloc failed", desc);
1305 		return (false);
1306 	}
1307 	/* ensure that the buffer is zeroed out properly */
1308 	bzero(e->kaddr, rlen);
1309 	if (ddi_dma_addr_bind_handle(e->dmah, NULL, e->kaddr, SOLO_BUFSZ,
1310 	    dflags, DDI_DMA_SLEEP, NULL, &c, &ccnt) != DDI_DMA_MAPPED) {
1311 		audio_dev_warn(dev->adev, "%s dma binding failed", desc);
1312 		return (false);
1313 	}
1314 	e->paddr = c.dmac_address;
1315 
1316 	/*
1317 	 * Allocate and configure audio engine.
1318 	 */
1319 	e->engine = audio_engine_alloc(&solo_engine_ops, caps);
1320 	if (e->engine == NULL) {
1321 		audio_dev_warn(dev->adev, "record audio_engine_alloc failed");
1322 		return (false);
1323 	}
1324 
1325 	audio_engine_set_private(e->engine, e);
1326 	audio_dev_add_engine(dev->adev, e->engine);
1327 
1328 	return (true);
1329 }
1330 
1331 
1332 static int
1333 solo_suspend(solo_dev_t *dev)
1334 {
1335 	mutex_enter(&dev->mutex);
1336 	/* play */
1337 	solo_aud2_stop(&dev->play);
1338 	solo_aud2_update(&dev->play);
1339 	/* record */
1340 	solo_aud1_stop(&dev->rec);
1341 	solo_aud1_update(&dev->rec);
1342 
1343 	dev->suspended = true;
1344 	mutex_exit(&dev->mutex);
1345 
1346 	return (DDI_SUCCESS);
1347 }
1348 
1349 static int
1350 solo_resume(solo_dev_t *dev)
1351 {
1352 	solo_engine_t	*e;
1353 	audio_engine_t	*prod = NULL;
1354 	audio_engine_t	*cons = NULL;
1355 
1356 	audio_engine_reset(dev->rec.engine);
1357 	audio_engine_reset(dev->play.engine);
1358 
1359 	mutex_enter(&dev->mutex);
1360 	if (!solo_init_hw(dev)) {
1361 		/* yikes! */
1362 		audio_dev_warn(dev->adev, "unable to resume audio!");
1363 		audio_dev_warn(dev->adev, "reboot or reload driver to reset");
1364 		mutex_exit(&dev->mutex);
1365 		return (DDI_SUCCESS);
1366 	}
1367 	dev->suspended = false;
1368 
1369 	/* record - audio 1 */
1370 	e = &dev->rec;
1371 	if (e->started) {
1372 		e->start(e);
1373 		prod = e->engine;
1374 	}
1375 
1376 	/* play - audio 2 */
1377 	e = &dev->play;
1378 	if (e->started) {
1379 		e->start(e);
1380 		cons = e->engine;
1381 	}
1382 
1383 	mutex_exit(&dev->mutex);
1384 
1385 	if (cons)
1386 		audio_engine_consume(cons);
1387 	if (prod)
1388 		audio_engine_produce(prod);
1389 
1390 	return (DDI_SUCCESS);
1391 }
1392 
1393 static int
1394 solo_attach(dev_info_t *dip)
1395 {
1396 	solo_dev_t	*dev;
1397 	uint32_t	data;
1398 
1399 	dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
1400 	dev->dip = dip;
1401 	ddi_set_driver_private(dip, dev);
1402 
1403 	dev->adev = audio_dev_alloc(dip, 0);
1404 	if (dev->adev == NULL)
1405 		goto no;
1406 
1407 	audio_dev_set_description(dev->adev, "ESS Solo-1 PCI AudioDrive");
1408 	audio_dev_set_version(dev->adev, "ES1938");
1409 
1410 	if (pci_config_setup(dip, &dev->pcih) != DDI_SUCCESS) {
1411 		audio_dev_warn(NULL, "pci_config_setup failed");
1412 		goto no;
1413 	}
1414 
1415 	data = pci_config_get16(dev->pcih, PCI_CONF_COMM);
1416 	data |= PCI_COMM_ME | PCI_COMM_IO;
1417 	pci_config_put16(dev->pcih, PCI_CONF_COMM, data);
1418 
1419 	if ((!solo_map_registers(dev)) ||
1420 	    (!solo_setup_interrupts(dev)) ||
1421 	    (!solo_alloc_engine(dev, 1)) ||
1422 	    (!solo_alloc_engine(dev, 2)) ||
1423 	    (!solo_add_controls(dev)) ||
1424 	    (!solo_init_hw(dev))) {
1425 		goto no;
1426 	}
1427 
1428 	if (audio_dev_register(dev->adev) != DDI_SUCCESS) {
1429 		audio_dev_warn(dev->adev,
1430 		    "unable to register with audio framework");
1431 		goto no;
1432 	}
1433 
1434 	(void) ddi_intr_enable(dev->ihandle);
1435 	ddi_report_dev(dip);
1436 
1437 	return (DDI_SUCCESS);
1438 
1439 no:
1440 	solo_release_resources(dev);
1441 	return (DDI_FAILURE);
1442 }
1443 
1444 static int
1445 solo_detach(solo_dev_t *dev)
1446 {
1447 	if (audio_dev_unregister(dev->adev) != DDI_SUCCESS) {
1448 		return (DDI_FAILURE);
1449 	}
1450 
1451 	solo_release_resources(dev);
1452 	return (DDI_SUCCESS);
1453 }
1454 
1455 static int
1456 solo_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1457 {
1458 	solo_dev_t *dev;
1459 
1460 	switch (cmd) {
1461 	case DDI_ATTACH:
1462 		return (solo_attach(dip));
1463 
1464 	case DDI_RESUME:
1465 		if ((dev = ddi_get_driver_private(dip)) == NULL) {
1466 			return (DDI_FAILURE);
1467 		}
1468 		return (solo_resume(dev));
1469 
1470 	default:
1471 		return (DDI_FAILURE);
1472 	}
1473 }
1474 
1475 static int
1476 solo_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1477 {
1478 	solo_dev_t *dev;
1479 
1480 	if ((dev = ddi_get_driver_private(dip)) == NULL) {
1481 		return (DDI_FAILURE);
1482 	}
1483 
1484 	switch (cmd) {
1485 	case DDI_DETACH:
1486 		return (solo_detach(dev));
1487 
1488 	case DDI_SUSPEND:
1489 		return (solo_suspend(dev));
1490 	default:
1491 		return (DDI_FAILURE);
1492 	}
1493 }
1494 
1495 static int
1496 solo_quiesce(dev_info_t *dip)
1497 {
1498 	solo_dev_t *dev;
1499 
1500 	dev = ddi_get_driver_private(dip);
1501 
1502 	solo_aud1_stop(&dev->rec);
1503 	solo_aud2_stop(&dev->play);
1504 
1505 	solo_setmixer(dev, 0, 0);
1506 	PORT_WR8(dev->io, 0x7, 0); /* disable all irqs */
1507 	return (0);
1508 }
1509 
1510 struct dev_ops solo_dev_ops = {
1511 	DEVO_REV,		/* rev */
1512 	0,			/* refcnt */
1513 	NULL,			/* getinfo */
1514 	nulldev,		/* identify */
1515 	nulldev,		/* probe */
1516 	solo_ddi_attach,	/* attach */
1517 	solo_ddi_detach,	/* detach */
1518 	nodev,			/* reset */
1519 	NULL,			/* cb_ops */
1520 	NULL,			/* bus_ops */
1521 	NULL,			/* power */
1522 	solo_quiesce,		/* quiesce */
1523 };
1524 
1525 static struct modldrv solo_modldrv = {
1526 	&mod_driverops,			/* drv_modops */
1527 	"ESS Solo-1 Audio",		/* linkinfo */
1528 	&solo_dev_ops,			/* dev_ops */
1529 };
1530 
1531 static struct modlinkage modlinkage = {
1532 	MODREV_1,
1533 	{ &solo_modldrv, NULL }
1534 };
1535 
1536 int
1537 _init(void)
1538 {
1539 	int	rv;
1540 
1541 	audio_init_ops(&solo_dev_ops, DRVNAME);
1542 	if ((rv = mod_install(&modlinkage)) != 0) {
1543 		audio_fini_ops(&solo_dev_ops);
1544 	}
1545 	return (rv);
1546 }
1547 
1548 int
1549 _fini(void)
1550 {
1551 	int	rv;
1552 
1553 	if ((rv = mod_remove(&modlinkage)) == 0) {
1554 		audio_fini_ops(&solo_dev_ops);
1555 	}
1556 	return (rv);
1557 }
1558 
1559 int
1560 _info(struct modinfo *modinfop)
1561 {
1562 	return (mod_info(&modlinkage, modinfop));
1563 }
1564