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