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