1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
5 * Copyright (c) 1997,1998 Luigi Rizzo
6 *
7 * Derived from files in the Voxware 3.5 distribution,
8 * Copyright by Hannu Savolainen 1994, under the same copyright
9 * conditions.
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifdef HAVE_KERNEL_OPTION_HEADERS
35#include "opt_snd.h"
36#endif
37
38#include <dev/sound/pcm/sound.h>
39
40#include  <dev/sound/isa/sb.h>
41#include  <dev/sound/chip.h>
42
43#include <isa/isavar.h>
44
45#include "mixer_if.h"
46
47SND_DECLARE_FILE("$FreeBSD$");
48
49#define ESS_BUFFSIZE (4096)
50#define ABS(x) (((x) < 0)? -(x) : (x))
51
52/* audio2 never generates irqs and sounds very noisy */
53#undef ESS18XX_DUPLEX
54
55/* more accurate clocks and split audio1/audio2 rates */
56#define ESS18XX_NEWSPEED
57
58static u_int32_t ess_pfmt[] = {
59	SND_FORMAT(AFMT_U8, 1, 0),
60	SND_FORMAT(AFMT_U8, 2, 0),
61	SND_FORMAT(AFMT_S8, 1, 0),
62	SND_FORMAT(AFMT_S8, 2, 0),
63	SND_FORMAT(AFMT_S16_LE, 1, 0),
64	SND_FORMAT(AFMT_S16_LE, 2, 0),
65	SND_FORMAT(AFMT_U16_LE, 1, 0),
66	SND_FORMAT(AFMT_U16_LE, 2, 0),
67	0
68};
69
70static struct pcmchan_caps ess_playcaps = {6000, 48000, ess_pfmt, 0};
71
72static u_int32_t ess_rfmt[] = {
73	SND_FORMAT(AFMT_U8, 1, 0),
74	SND_FORMAT(AFMT_U8, 2, 0),
75	SND_FORMAT(AFMT_S8, 1, 0),
76	SND_FORMAT(AFMT_S8, 2, 0),
77	SND_FORMAT(AFMT_S16_LE, 1, 0),
78	SND_FORMAT(AFMT_S16_LE, 2, 0),
79	SND_FORMAT(AFMT_U16_LE, 1, 0),
80	SND_FORMAT(AFMT_U16_LE, 2, 0),
81	0
82};
83
84static struct pcmchan_caps ess_reccaps = {6000, 48000, ess_rfmt, 0};
85
86struct ess_info;
87
88struct ess_chinfo {
89	struct ess_info *parent;
90	struct pcm_channel *channel;
91	struct snd_dbuf *buffer;
92	int dir, hwch, stopping, run;
93	u_int32_t fmt, spd, blksz;
94};
95
96struct ess_info {
97	device_t parent_dev;
98    	struct resource *io_base;	/* I/O address for the board */
99    	struct resource *irq;
100   	struct resource *drq1;
101    	struct resource *drq2;
102    	void *ih;
103    	bus_dma_tag_t parent_dmat;
104
105	unsigned int bufsize;
106    	int type;
107	unsigned int duplex:1, newspeed:1;
108    	u_long bd_flags;       /* board-specific flags */
109    	struct ess_chinfo pch, rch;
110};
111
112#if 0
113static int ess_rd(struct ess_info *sc, int reg);
114static void ess_wr(struct ess_info *sc, int reg, u_int8_t val);
115static int ess_dspready(struct ess_info *sc);
116static int ess_cmd(struct ess_info *sc, u_char val);
117static int ess_cmd1(struct ess_info *sc, u_char cmd, int val);
118static int ess_get_byte(struct ess_info *sc);
119static void ess_setmixer(struct ess_info *sc, u_int port, u_int value);
120static int ess_getmixer(struct ess_info *sc, u_int port);
121static int ess_reset_dsp(struct ess_info *sc);
122
123static int ess_write(struct ess_info *sc, u_char reg, int val);
124static int ess_read(struct ess_info *sc, u_char reg);
125
126static void ess_intr(void *arg);
127static int ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int len);
128static int ess_start(struct ess_chinfo *ch);
129static int ess_stop(struct ess_chinfo *ch);
130#endif
131
132/*
133 * Common code for the midi and pcm functions
134 *
135 * ess_cmd write a single byte to the CMD port.
136 * ess_cmd1 write a CMD + 1 byte arg
137 * ess_cmd2 write a CMD + 2 byte arg
138 * ess_get_byte returns a single byte from the DSP data port
139 *
140 * ess_write is actually ess_cmd1
141 * ess_read access ext. regs via ess_cmd(0xc0, reg) followed by ess_get_byte
142 */
143
144static void
145ess_lock(struct ess_info *sc) {
146
147	sbc_lock(device_get_softc(sc->parent_dev));
148}
149
150static void
151ess_unlock(struct ess_info *sc) {
152
153	sbc_unlock(device_get_softc(sc->parent_dev));
154}
155
156static int
157port_rd(struct resource *port, int off)
158{
159	return bus_space_read_1(rman_get_bustag(port),
160				rman_get_bushandle(port),
161				off);
162}
163
164static void
165port_wr(struct resource *port, int off, u_int8_t data)
166{
167	bus_space_write_1(rman_get_bustag(port),
168			  rman_get_bushandle(port),
169			  off, data);
170}
171
172static int
173ess_rd(struct ess_info *sc, int reg)
174{
175	return port_rd(sc->io_base, reg);
176}
177
178static void
179ess_wr(struct ess_info *sc, int reg, u_int8_t val)
180{
181	port_wr(sc->io_base, reg, val);
182}
183
184static int
185ess_dspready(struct ess_info *sc)
186{
187	return ((ess_rd(sc, SBDSP_STATUS) & 0x80) == 0);
188}
189
190static int
191ess_dspwr(struct ess_info *sc, u_char val)
192{
193    	int  i;
194
195    	for (i = 0; i < 1000; i++) {
196		if (ess_dspready(sc)) {
197	    		ess_wr(sc, SBDSP_CMD, val);
198	    		return 1;
199		}
200		if (i > 10) DELAY((i > 100)? 1000 : 10);
201    	}
202    	printf("ess_dspwr(0x%02x) timed out.\n", val);
203    	return 0;
204}
205
206static int
207ess_cmd(struct ess_info *sc, u_char val)
208{
209#if 0
210	printf("ess_cmd: %x\n", val);
211#endif
212    	return ess_dspwr(sc, val);
213}
214
215static int
216ess_cmd1(struct ess_info *sc, u_char cmd, int val)
217{
218#if 0
219    	printf("ess_cmd1: %x, %x\n", cmd, val);
220#endif
221    	if (ess_dspwr(sc, cmd)) {
222		return ess_dspwr(sc, val & 0xff);
223    	} else return 0;
224}
225
226static void
227ess_setmixer(struct ess_info *sc, u_int port, u_int value)
228{
229	DEB(printf("ess_setmixer: reg=%x, val=%x\n", port, value);)
230    	ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
231    	DELAY(10);
232    	ess_wr(sc, SB_MIX_DATA, (u_char) (value & 0xff));
233    	DELAY(10);
234}
235
236static int
237ess_getmixer(struct ess_info *sc, u_int port)
238{
239    	int val;
240    	ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */
241    	DELAY(10);
242    	val = ess_rd(sc, SB_MIX_DATA);
243    	DELAY(10);
244
245    	return val;
246}
247
248static int
249ess_get_byte(struct ess_info *sc)
250{
251    	int i;
252
253    	for (i = 1000; i > 0; i--) {
254		if (ess_rd(sc, DSP_DATA_AVAIL) & 0x80)
255			return ess_rd(sc, DSP_READ);
256		else
257			DELAY(20);
258    	}
259    	return -1;
260}
261
262static int
263ess_write(struct ess_info *sc, u_char reg, int val)
264{
265    	return ess_cmd1(sc, reg, val);
266}
267
268static int
269ess_read(struct ess_info *sc, u_char reg)
270{
271    	return (ess_cmd(sc, 0xc0) && ess_cmd(sc, reg))? ess_get_byte(sc) : -1;
272}
273
274static int
275ess_reset_dsp(struct ess_info *sc)
276{
277    	ess_wr(sc, SBDSP_RST, 3);
278    	DELAY(100);
279    	ess_wr(sc, SBDSP_RST, 0);
280    	if (ess_get_byte(sc) != 0xAA) {
281        	DEB(printf("ess_reset_dsp 0x%lx failed\n",
282			   rman_get_start(sc->io_base)));
283		return ENXIO;	/* Sorry */
284    	}
285    	ess_cmd(sc, 0xc6);
286    	return 0;
287}
288
289static void
290ess_release_resources(struct ess_info *sc, device_t dev)
291{
292    	if (sc->irq) {
293    		if (sc->ih)
294			bus_teardown_intr(dev, sc->irq, sc->ih);
295		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq);
296		sc->irq = NULL;
297    	}
298    	if (sc->drq1) {
299		isa_dma_release(rman_get_start(sc->drq1));
300		bus_release_resource(dev, SYS_RES_DRQ, 0, sc->drq1);
301		sc->drq1 = NULL;
302    	}
303    	if (sc->drq2) {
304		isa_dma_release(rman_get_start(sc->drq2));
305		bus_release_resource(dev, SYS_RES_DRQ, 1, sc->drq2);
306		sc->drq2 = NULL;
307    	}
308    	if (sc->io_base) {
309		bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->io_base);
310		sc->io_base = NULL;
311    	}
312    	if (sc->parent_dmat) {
313		bus_dma_tag_destroy(sc->parent_dmat);
314		sc->parent_dmat = 0;
315    	}
316     	free(sc, M_DEVBUF);
317}
318
319static int
320ess_alloc_resources(struct ess_info *sc, device_t dev)
321{
322	int rid;
323
324	rid = 0;
325	if (!sc->io_base)
326    		sc->io_base = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
327						     &rid, RF_ACTIVE);
328	rid = 0;
329	if (!sc->irq)
330    		sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
331						 &rid, RF_ACTIVE);
332	rid = 0;
333	if (!sc->drq1)
334    		sc->drq1 = bus_alloc_resource_any(dev, SYS_RES_DRQ,
335						  &rid, RF_ACTIVE);
336	rid = 1;
337	if (!sc->drq2)
338        	sc->drq2 = bus_alloc_resource_any(dev, SYS_RES_DRQ,
339						  &rid, RF_ACTIVE);
340
341    	if (sc->io_base && sc->drq1 && sc->irq) {
342  		isa_dma_acquire(rman_get_start(sc->drq1));
343		isa_dmainit(rman_get_start(sc->drq1), sc->bufsize);
344
345		if (sc->drq2) {
346			isa_dma_acquire(rman_get_start(sc->drq2));
347			isa_dmainit(rman_get_start(sc->drq2), sc->bufsize);
348		}
349
350		return 0;
351	} else return ENXIO;
352}
353
354static void
355ess_intr(void *arg)
356{
357    	struct ess_info *sc = (struct ess_info *)arg;
358	int src, pirq, rirq;
359
360	ess_lock(sc);
361	src = 0;
362	if (ess_getmixer(sc, 0x7a) & 0x80)
363		src |= 2;
364	if (ess_rd(sc, 0x0c) & 0x01)
365		src |= 1;
366
367	pirq = (src & sc->pch.hwch)? 1 : 0;
368	rirq = (src & sc->rch.hwch)? 1 : 0;
369
370	if (pirq) {
371		if (sc->pch.run) {
372			ess_unlock(sc);
373			chn_intr(sc->pch.channel);
374			ess_lock(sc);
375		}
376		if (sc->pch.stopping) {
377			sc->pch.run = 0;
378			sndbuf_dma(sc->pch.buffer, PCMTRIG_STOP);
379			sc->pch.stopping = 0;
380			if (sc->pch.hwch == 1)
381				ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01);
382			else
383				ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) & ~0x03);
384		}
385	}
386
387	if (rirq) {
388		if (sc->rch.run) {
389			ess_unlock(sc);
390			chn_intr(sc->rch.channel);
391			ess_lock(sc);
392		}
393		if (sc->rch.stopping) {
394			sc->rch.run = 0;
395			sndbuf_dma(sc->rch.buffer, PCMTRIG_STOP);
396			sc->rch.stopping = 0;
397			/* XXX: will this stop audio2? */
398			ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01);
399		}
400	}
401
402	if (src & 2)
403		ess_setmixer(sc, 0x7a, ess_getmixer(sc, 0x7a) & ~0x80);
404	if (src & 1)
405    		ess_rd(sc, DSP_DATA_AVAIL);
406	ess_unlock(sc);
407}
408
409/* utility functions for ESS */
410static u_int8_t
411ess_calcspeed8(int *spd)
412{
413	int speed = *spd;
414	u_int32_t t;
415
416	if (speed > 22000) {
417		t = (795500 + speed / 2) / speed;
418		speed = (795500 + t / 2) / t;
419		t = (256 - t) | 0x80;
420	} else {
421		t = (397700 + speed / 2) / speed;
422		speed = (397700 + t / 2) / t;
423		t = 128 - t;
424	}
425	*spd = speed;
426	return t & 0x000000ff;
427}
428
429static u_int8_t
430ess_calcspeed9(int *spd)
431{
432	int speed, s0, s1, use0;
433	u_int8_t t0, t1;
434
435	/* rate = source / (256 - divisor) */
436	/* divisor = 256 - (source / rate) */
437	speed = *spd;
438	t0 = 128 - (793800 / speed);
439	s0 = 793800 / (128 - t0);
440
441	t1 = 128 - (768000 / speed);
442	s1 = 768000 / (128 - t1);
443	t1 |= 0x80;
444
445	use0 = (ABS(speed - s0) < ABS(speed - s1))? 1 : 0;
446
447	*spd = use0? s0 : s1;
448	return use0? t0 : t1;
449}
450
451static u_int8_t
452ess_calcfilter(int spd)
453{
454	int cutoff;
455
456	/* cutoff = 7160000 / (256 - divisor) */
457	/* divisor = 256 - (7160000 / cutoff) */
458	cutoff = (spd * 9 * 82) / 20;
459	return (256 - (7160000 / cutoff));
460}
461
462static int
463ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int len)
464{
465	int play = (dir == PCMDIR_PLAY)? 1 : 0;
466	int b16 = (fmt & AFMT_16BIT)? 1 : 0;
467	int stereo = (AFMT_CHANNEL(fmt) > 1)? 1 : 0;
468	int unsign = (fmt == AFMT_U8 || fmt == AFMT_U16_LE)? 1 : 0;
469	u_int8_t spdval, fmtval;
470
471
472	spdval = (sc->newspeed)? ess_calcspeed9(&spd) : ess_calcspeed8(&spd);
473	len = -len;
474
475	if (ch == 1) {
476		KASSERT((dir == PCMDIR_PLAY) || (dir == PCMDIR_REC), ("ess_setupch: dir1 bad"));
477		/* transfer length low */
478		ess_write(sc, 0xa4, len & 0x00ff);
479		/* transfer length high */
480		ess_write(sc, 0xa5, (len & 0xff00) >> 8);
481		/* autoinit, dma dir */
482		ess_write(sc, 0xb8, 0x04 | (play? 0x00 : 0x0a));
483		/* mono/stereo */
484		ess_write(sc, 0xa8, (ess_read(sc, 0xa8) & ~0x03) | (stereo? 0x01 : 0x02));
485		/* demand mode, 4 bytes/xfer */
486		ess_write(sc, 0xb9, 0x02);
487		/* sample rate */
488        	ess_write(sc, 0xa1, spdval);
489		/* filter cutoff */
490		ess_write(sc, 0xa2, ess_calcfilter(spd));
491		/* setup dac/adc */
492		if (play)
493			ess_write(sc, 0xb6, unsign? 0x80 : 0x00);
494		/* mono, b16: signed, load signal */
495		ess_write(sc, 0xb7, 0x51 | (unsign? 0x00 : 0x20));
496		/* setup fifo */
497		ess_write(sc, 0xb7, 0x90 | (unsign? 0x00 : 0x20) |
498					   (b16? 0x04 : 0x00) |
499					   (stereo? 0x08 : 0x40));
500		/* irq control */
501		ess_write(sc, 0xb1, (ess_read(sc, 0xb1) & 0x0f) | 0x50);
502		/* drq control */
503		ess_write(sc, 0xb2, (ess_read(sc, 0xb2) & 0x0f) | 0x50);
504	} else if (ch == 2) {
505		KASSERT(dir == PCMDIR_PLAY, ("ess_setupch: dir2 bad"));
506		/* transfer length low */
507		ess_setmixer(sc, 0x74, len & 0x00ff);
508		/* transfer length high */
509		ess_setmixer(sc, 0x76, (len & 0xff00) >> 8);
510		/* autoinit, 4 bytes/req */
511		ess_setmixer(sc, 0x78, 0x90);
512		fmtval = b16 | (stereo << 1) | (unsign << 2);
513		/* enable irq, set format */
514		ess_setmixer(sc, 0x7a, 0x40 | fmtval);
515		if (sc->newspeed) {
516			/* sample rate */
517			ess_setmixer(sc, 0x70, spdval);
518			/* filter cutoff */
519			ess_setmixer(sc, 0x72, ess_calcfilter(spd));
520		}
521	}
522
523	return 0;
524}
525static int
526ess_start(struct ess_chinfo *ch)
527{
528	struct ess_info *sc = ch->parent;
529    	int play = (ch->dir == PCMDIR_PLAY)? 1 : 0;
530
531	ess_lock(sc);
532	ess_setupch(sc, ch->hwch, ch->dir, ch->spd, ch->fmt, ch->blksz);
533	ch->stopping = 0;
534	if (ch->hwch == 1)
535		ess_write(sc, 0xb8, ess_read(sc, 0xb8) | 0x01);
536	else
537		ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) | 0x03);
538	if (play)
539		ess_cmd(sc, DSP_CMD_SPKON);
540	ess_unlock(sc);
541	return 0;
542}
543
544static int
545ess_stop(struct ess_chinfo *ch)
546{
547	struct ess_info *sc = ch->parent;
548    	int play = (ch->dir == PCMDIR_PLAY)? 1 : 0;
549
550	ess_lock(sc);
551	ch->stopping = 1;
552	if (ch->hwch == 1)
553		ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x04);
554	else
555		ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) & ~0x10);
556	if (play)
557		ess_cmd(sc, DSP_CMD_SPKOFF);
558	ess_unlock(sc);
559	return 0;
560}
561
562/* -------------------------------------------------------------------- */
563/* channel interface for ESS18xx */
564static void *
565esschan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
566{
567	struct ess_info *sc = devinfo;
568	struct ess_chinfo *ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch;
569
570	ch->parent = sc;
571	ch->channel = c;
572	ch->buffer = b;
573	if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsize) != 0)
574		return NULL;
575	ch->dir = dir;
576	ch->hwch = 1;
577	if ((dir == PCMDIR_PLAY) && (sc->duplex))
578		ch->hwch = 2;
579	sndbuf_dmasetup(ch->buffer, (ch->hwch == 1)? sc->drq1 : sc->drq2);
580	return ch;
581}
582
583static int
584esschan_setformat(kobj_t obj, void *data, u_int32_t format)
585{
586	struct ess_chinfo *ch = data;
587
588	ch->fmt = format;
589	return 0;
590}
591
592static u_int32_t
593esschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
594{
595	struct ess_chinfo *ch = data;
596	struct ess_info *sc = ch->parent;
597
598	ch->spd = speed;
599	if (sc->newspeed)
600		ess_calcspeed9(&ch->spd);
601	else
602		ess_calcspeed8(&ch->spd);
603	return ch->spd;
604}
605
606static u_int32_t
607esschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
608{
609	struct ess_chinfo *ch = data;
610
611	ch->blksz = blocksize;
612	return ch->blksz;
613}
614
615static int
616esschan_trigger(kobj_t obj, void *data, int go)
617{
618	struct ess_chinfo *ch = data;
619
620	if (!PCMTRIG_COMMON(go))
621		return 0;
622
623	switch (go) {
624	case PCMTRIG_START:
625		ch->run = 1;
626		sndbuf_dma(ch->buffer, go);
627		ess_start(ch);
628		break;
629
630	case PCMTRIG_STOP:
631	case PCMTRIG_ABORT:
632	default:
633		ess_stop(ch);
634		break;
635	}
636	return 0;
637}
638
639static u_int32_t
640esschan_getptr(kobj_t obj, void *data)
641{
642	struct ess_chinfo *ch = data;
643
644	return sndbuf_dmaptr(ch->buffer);
645}
646
647static struct pcmchan_caps *
648esschan_getcaps(kobj_t obj, void *data)
649{
650	struct ess_chinfo *ch = data;
651
652	return (ch->dir == PCMDIR_PLAY)? &ess_playcaps : &ess_reccaps;
653}
654
655static kobj_method_t esschan_methods[] = {
656    	KOBJMETHOD(channel_init,		esschan_init),
657    	KOBJMETHOD(channel_setformat,		esschan_setformat),
658    	KOBJMETHOD(channel_setspeed,		esschan_setspeed),
659    	KOBJMETHOD(channel_setblocksize,	esschan_setblocksize),
660    	KOBJMETHOD(channel_trigger,		esschan_trigger),
661    	KOBJMETHOD(channel_getptr,		esschan_getptr),
662    	KOBJMETHOD(channel_getcaps,		esschan_getcaps),
663	KOBJMETHOD_END
664};
665CHANNEL_DECLARE(esschan);
666
667/************************************************************/
668
669static int
670essmix_init(struct snd_mixer *m)
671{
672    	struct ess_info *sc = mix_getdevinfo(m);
673
674	mix_setrecdevs(m, SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE |
675			  SOUND_MASK_IMIX);
676
677	mix_setdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE |
678		       SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME |
679		       SOUND_MASK_LINE1 | SOUND_MASK_SPEAKER);
680
681	ess_setmixer(sc, 0, 0); /* reset */
682
683	return 0;
684}
685
686static int
687essmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
688{
689    	struct ess_info *sc = mix_getdevinfo(m);
690    	int preg = 0, rreg = 0, l, r;
691
692	l = (left * 15) / 100;
693	r = (right * 15) / 100;
694	switch (dev) {
695	case SOUND_MIXER_SYNTH:
696		preg = 0x36;
697		rreg = 0x6b;
698		break;
699
700	case SOUND_MIXER_PCM:
701		preg = 0x14;
702		rreg = 0x7c;
703		break;
704
705	case SOUND_MIXER_LINE:
706		preg = 0x3e;
707		rreg = 0x6e;
708		break;
709
710	case SOUND_MIXER_MIC:
711		preg = 0x1a;
712		rreg = 0x68;
713		break;
714
715	case SOUND_MIXER_LINE1:
716		preg = 0x3a;
717		rreg = 0x6c;
718		break;
719
720	case SOUND_MIXER_CD:
721		preg = 0x38;
722		rreg = 0x6a;
723		break;
724
725	case SOUND_MIXER_SPEAKER:
726		preg = 0x3c;
727		break;
728
729 	case SOUND_MIXER_VOLUME:
730		l = left? (left * 63) / 100 : 64;
731		r = right? (right * 63) / 100 : 64;
732		ess_setmixer(sc, 0x60, l);
733		ess_setmixer(sc, 0x62, r);
734		left = (l == 64)? 0 : (l * 100) / 63;
735		right = (r == 64)? 0 : (r * 100) / 63;
736    		return left | (right << 8);
737	}
738
739	if (preg)
740		ess_setmixer(sc, preg, (l << 4) | r);
741	if (rreg)
742		ess_setmixer(sc, rreg, (l << 4) | r);
743
744	left = (l * 100) / 15;
745	right = (r * 100) / 15;
746
747    	return left | (right << 8);
748}
749
750static u_int32_t
751essmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
752{
753    	struct ess_info *sc = mix_getdevinfo(m);
754    	u_char recdev;
755
756    	switch (src) {
757	case SOUND_MASK_CD:
758		recdev = 0x02;
759		break;
760
761	case SOUND_MASK_LINE:
762		recdev = 0x06;
763		break;
764
765	case SOUND_MASK_IMIX:
766		recdev = 0x05;
767		break;
768
769	case SOUND_MASK_MIC:
770	default:
771		recdev = 0x00;
772		src = SOUND_MASK_MIC;
773		break;
774	}
775
776	ess_setmixer(sc, 0x1c, recdev);
777
778	return src;
779}
780
781static kobj_method_t essmixer_methods[] = {
782    	KOBJMETHOD(mixer_init,		essmix_init),
783    	KOBJMETHOD(mixer_set,		essmix_set),
784    	KOBJMETHOD(mixer_setrecsrc,	essmix_setrecsrc),
785	KOBJMETHOD_END
786};
787MIXER_DECLARE(essmixer);
788
789/************************************************************/
790
791static int
792ess_probe(device_t dev)
793{
794	uintptr_t func, ver, r, f;
795
796	/* The parent device has already been probed. */
797	r = BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func);
798	if (func != SCF_PCM)
799		return (ENXIO);
800
801	r = BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver);
802	f = (ver & 0xffff0000) >> 16;
803	if (!(f & BD_F_ESS))
804		return (ENXIO);
805
806    	device_set_desc(dev, "ESS 18xx DSP");
807
808	return 0;
809}
810
811static int
812ess_attach(device_t dev)
813{
814    	struct ess_info *sc;
815    	char status[SND_STATUSLEN], buf[64];
816	int ver;
817
818    	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
819	sc->parent_dev = device_get_parent(dev);
820	sc->bufsize = pcm_getbuffersize(dev, 4096, ESS_BUFFSIZE, 65536);
821    	if (ess_alloc_resources(sc, dev))
822		goto no;
823    	if (ess_reset_dsp(sc))
824		goto no;
825    	if (mixer_init(dev, &essmixer_class, sc))
826		goto no;
827
828	sc->duplex = 0;
829	sc->newspeed = 0;
830	ver = (ess_getmixer(sc, 0x40) << 8) | ess_rd(sc, SB_MIX_DATA);
831	snprintf(buf, sizeof buf, "ESS %x DSP", ver);
832	device_set_desc_copy(dev, buf);
833	if (bootverbose)
834		device_printf(dev, "ESS%x detected", ver);
835
836	switch (ver) {
837	case 0x1869:
838	case 0x1879:
839#ifdef ESS18XX_DUPLEX
840		sc->duplex = sc->drq2? 1 : 0;
841#endif
842#ifdef ESS18XX_NEWSPEED
843		sc->newspeed = 1;
844#endif
845		break;
846	}
847	if (bootverbose)
848		printf("%s%s\n", sc->duplex? ", duplex" : "",
849				 sc->newspeed? ", newspeed" : "");
850
851	if (sc->newspeed)
852		ess_setmixer(sc, 0x71, 0x22);
853
854	snd_setup_intr(dev, sc->irq, 0, ess_intr, sc, &sc->ih);
855    	if (!sc->duplex)
856		pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX);
857
858    	if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2,
859			/*boundary*/0,
860			/*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
861			/*highaddr*/BUS_SPACE_MAXADDR,
862			/*filter*/NULL, /*filterarg*/NULL,
863			/*maxsize*/sc->bufsize, /*nsegments*/1,
864			/*maxsegz*/0x3ffff,
865			/*flags*/0, /*lockfunc*/busdma_lock_mutex,
866			/*lockarg*/&Giant, &sc->parent_dmat) != 0) {
867		device_printf(dev, "unable to create dma tag\n");
868		goto no;
869    	}
870
871    	if (sc->drq2)
872		snprintf(buf, SND_STATUSLEN, ":%jd", rman_get_start(sc->drq2));
873	else
874		buf[0] = '\0';
875
876    	snprintf(status, SND_STATUSLEN, "at io 0x%jx irq %jd drq %jd%s bufsz %u %s",
877		rman_get_start(sc->io_base), rman_get_start(sc->irq),
878		rman_get_start(sc->drq1), buf, sc->bufsize,
879		PCM_KLDSTRING(snd_ess));
880
881    	if (pcm_register(dev, sc, 1, 1))
882		goto no;
883      	pcm_addchan(dev, PCMDIR_REC, &esschan_class, sc);
884	pcm_addchan(dev, PCMDIR_PLAY, &esschan_class, sc);
885	pcm_setstatus(dev, status);
886
887    	return 0;
888
889no:
890    	ess_release_resources(sc, dev);
891    	return ENXIO;
892}
893
894static int
895ess_detach(device_t dev)
896{
897	int r;
898	struct ess_info *sc;
899
900	r = pcm_unregister(dev);
901	if (r)
902		return r;
903
904	sc = pcm_getdevinfo(dev);
905    	ess_release_resources(sc, dev);
906	return 0;
907}
908
909static int
910ess_resume(device_t dev)
911{
912	struct ess_info *sc;
913
914	sc = pcm_getdevinfo(dev);
915
916	if (ess_reset_dsp(sc)) {
917		device_printf(dev, "unable to reset DSP at resume\n");
918		return ENXIO;
919	}
920
921	if (mixer_reinit(dev)) {
922		device_printf(dev, "unable to reinitialize mixer at resume\n");
923		return ENXIO;
924	}
925
926	return 0;
927}
928
929static device_method_t ess_methods[] = {
930	/* Device interface */
931	DEVMETHOD(device_probe,		ess_probe),
932	DEVMETHOD(device_attach,	ess_attach),
933	DEVMETHOD(device_detach,	ess_detach),
934	DEVMETHOD(device_resume,	ess_resume),
935
936	{ 0, 0 }
937};
938
939static driver_t ess_driver = {
940	"pcm",
941	ess_methods,
942	PCM_SOFTC_SIZE,
943};
944
945DRIVER_MODULE(snd_ess, sbc, ess_driver, pcm_devclass, 0, 0);
946MODULE_DEPEND(snd_ess, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
947MODULE_DEPEND(snd_ess, snd_sbc, 1, 1, 1);
948MODULE_VERSION(snd_ess, 1);
949
950/************************************************************/
951
952static devclass_t esscontrol_devclass;
953
954static struct isa_pnp_id essc_ids[] = {
955	{0x06007316, "ESS Control"},
956	{0}
957};
958
959static int
960esscontrol_probe(device_t dev)
961{
962	int i;
963
964	i = ISA_PNP_PROBE(device_get_parent(dev), dev, essc_ids);
965	if (i == 0)
966		device_quiet(dev);
967	return i;
968}
969
970static int
971esscontrol_attach(device_t dev)
972{
973#ifdef notyet
974    	struct resource *io;
975	int rid, i, x;
976
977	rid = 0;
978    	io = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE);
979	x = 0;
980	for (i = 0; i < 0x100; i++) {
981		port_wr(io, 0, i);
982		x = port_rd(io, 1);
983		if ((i & 0x0f) == 0)
984			printf("%3.3x: ", i);
985		printf("%2.2x ", x);
986		if ((i & 0x0f) == 0x0f)
987			printf("\n");
988	}
989	bus_release_resource(dev, SYS_RES_IOPORT, 0, io);
990	io = NULL;
991#endif
992
993    	return 0;
994}
995
996static int
997esscontrol_detach(device_t dev)
998{
999	return 0;
1000}
1001
1002static device_method_t esscontrol_methods[] = {
1003	/* Device interface */
1004	DEVMETHOD(device_probe,		esscontrol_probe),
1005	DEVMETHOD(device_attach,	esscontrol_attach),
1006	DEVMETHOD(device_detach,	esscontrol_detach),
1007
1008	{ 0, 0 }
1009};
1010
1011static driver_t esscontrol_driver = {
1012	"esscontrol",
1013	esscontrol_methods,
1014	1,
1015};
1016
1017DRIVER_MODULE(esscontrol, isa, esscontrol_driver, esscontrol_devclass, 0, 0);
1018DRIVER_MODULE(esscontrol, acpi, esscontrol_driver, esscontrol_devclass, 0, 0);
1019ISA_PNP_INFO(essc_ids);
1020