kernel tree reorganization stage 1: Major cvs repository work (not logged as
[dragonfly.git] / sys / dev / sound / pci / als4000.c
1 /*
2  * Copyright (c) 2001 Orion Hodson <oho@acm.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/dev/sound/pci/als4000.c,v 1.2.2.5 2002/04/22 15:49:31 cg Exp $
27  * $DragonFly: src/sys/dev/sound/pci/als4000.c,v 1.3 2003/08/07 21:17:13 dillon Exp $
28  */
29
30 /*
31  * als4000.c - driver for the Avance Logic ALS 4000 chipset.
32  *
33  * The ALS4000 is a effectively an SB16 with a PCI interface.
34  *
35  * This driver derives from ALS4000a.PDF, Bart Hartgers alsa driver, and
36  * SB16 register descriptions.
37  */
38
39 #include <dev/sound/pcm/sound.h>
40 #include <dev/sound/isa/sb.h>
41 #include <dev/sound/pci/als4000.h>
42
43 #include <bus/pci/pcireg.h>
44 #include <bus/pci/pcivar.h>
45
46 #include "mixer_if.h"
47
48 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/als4000.c,v 1.3 2003/08/07 21:17:13 dillon Exp $");
49
50 /* Debugging macro's */
51 #undef DEB
52 #ifndef DEB
53 #define DEB(x)  /* x */
54 #endif /* DEB */
55
56 #define ALS_DEFAULT_BUFSZ 16384
57
58 /* ------------------------------------------------------------------------- */
59 /* Structures */
60
61 struct sc_info;
62
63 struct sc_chinfo {
64         struct sc_info          *parent;
65         struct pcm_channel      *channel;
66         struct snd_dbuf         *buffer;
67         u_int32_t               format, speed, phys_buf, bps;
68         u_int32_t               dma_active:1, dma_was_active:1;
69         u_int8_t                gcr_fifo_status;
70         int                     dir;
71 };
72
73 struct sc_info {
74         device_t                dev;
75         bus_space_tag_t         st;
76         bus_space_handle_t      sh;
77         bus_dma_tag_t           parent_dmat;
78         struct resource         *reg, *irq;
79         int                     regid, irqid;
80         void                    *ih;
81
82         unsigned int            bufsz;
83         struct sc_chinfo        pch, rch;
84 };
85
86 /* Channel caps */
87
88 static u_int32_t als_format[] = {
89         AFMT_U8,
90         AFMT_STEREO | AFMT_U8,
91         AFMT_S16_LE,
92         AFMT_STEREO | AFMT_S16_LE,
93         0
94 };
95
96 static struct pcmchan_caps als_caps = { 4000, 48000, als_format, 0 };
97
98 /* ------------------------------------------------------------------------- */
99 /* Register Utilities */
100
101 static u_int32_t
102 als_gcr_rd(struct sc_info *sc, int index)
103 {
104         bus_space_write_1(sc->st, sc->sh, ALS_GCR_INDEX, index);
105         return bus_space_read_4(sc->st, sc->sh, ALS_GCR_DATA);
106 }
107
108 static void
109 als_gcr_wr(struct sc_info *sc, int index, int data)
110 {
111         bus_space_write_1(sc->st, sc->sh, ALS_GCR_INDEX, index);
112         bus_space_write_4(sc->st, sc->sh, ALS_GCR_DATA, data);
113 }
114
115 static u_int8_t
116 als_intr_rd(struct sc_info *sc)
117 {
118         return bus_space_read_1(sc->st, sc->sh, ALS_SB_MPU_IRQ);
119 }
120
121 static void
122 als_intr_wr(struct sc_info *sc, u_int8_t data)
123 {
124         bus_space_write_1(sc->st, sc->sh, ALS_SB_MPU_IRQ, data);
125 }
126
127 static u_int8_t
128 als_mix_rd(struct sc_info *sc, u_int8_t index)
129 {
130         bus_space_write_1(sc->st, sc->sh, ALS_MIXER_INDEX, index);
131         return bus_space_read_1(sc->st, sc->sh, ALS_MIXER_DATA);
132 }
133
134 static void
135 als_mix_wr(struct sc_info *sc, u_int8_t index, u_int8_t data)
136 {
137         bus_space_write_1(sc->st, sc->sh, ALS_MIXER_INDEX, index);
138         bus_space_write_1(sc->st, sc->sh, ALS_MIXER_DATA, data);
139 }
140
141 static void
142 als_esp_wr(struct sc_info *sc, u_int8_t data)
143 {
144         u_int32_t       tries, v;
145
146         tries = 1000;
147         do {
148                 v = bus_space_read_1(sc->st, sc->sh, ALS_ESP_WR_STATUS);
149                 if (~v & 0x80)
150                         break;
151                 DELAY(20);
152         } while (--tries != 0);
153
154         if (tries == 0)
155                 device_printf(sc->dev, "als_esp_wr timeout");
156
157         bus_space_write_1(sc->st, sc->sh, ALS_ESP_WR_DATA, data);
158 }
159
160 static int
161 als_esp_reset(struct sc_info *sc)
162 {
163         u_int32_t       tries, u, v;
164
165         bus_space_write_1(sc->st, sc->sh, ALS_ESP_RST, 1);
166         DELAY(10);
167         bus_space_write_1(sc->st, sc->sh, ALS_ESP_RST, 0);
168         DELAY(30);
169
170         tries = 1000;
171         do {
172                 u = bus_space_read_1(sc->st, sc->sh, ALS_ESP_RD_STATUS8);
173                 if (u & 0x80) {
174                         v = bus_space_read_1(sc->st, sc->sh, ALS_ESP_RD_DATA);
175                         if (v == 0xaa)
176                                 return 0;
177                         else
178                                 break;
179                 }
180                 DELAY(20);
181         } while (--tries != 0);
182
183         if (tries == 0)
184                 device_printf(sc->dev, "als_esp_reset timeout");
185         return 1;
186 }
187
188 static u_int8_t
189 als_ack_read(struct sc_info *sc, u_int8_t addr)
190 {
191         u_int8_t r = bus_space_read_1(sc->st, sc->sh, addr);
192         return r;
193 }
194
195 /* ------------------------------------------------------------------------- */
196 /* Common pcm channel implementation */
197
198 static void *
199 alschan_init(kobj_t obj, void *devinfo,
200              struct snd_dbuf *b, struct pcm_channel *c, int dir)
201 {
202         struct  sc_info *sc = devinfo;
203         struct  sc_chinfo *ch;
204
205         if (dir == PCMDIR_PLAY) {
206                 ch = &sc->pch;
207                 ch->gcr_fifo_status = ALS_GCR_FIFO0_STATUS;
208         } else {
209                 ch = &sc->rch;
210                 ch->gcr_fifo_status = ALS_GCR_FIFO1_STATUS;
211         }
212         ch->dir = dir;
213         ch->parent = sc;
214         ch->channel = c;
215         ch->bps = 1;
216         ch->format = AFMT_U8;
217         ch->speed = DSP_DEFAULT_SPEED;
218         ch->buffer = b;
219         if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0) {
220                 return NULL;
221         }
222         return ch;
223 }
224
225 static int
226 alschan_setformat(kobj_t obj, void *data, u_int32_t format)
227 {
228         struct  sc_chinfo *ch = data;
229
230         ch->format = format;
231         return 0;
232 }
233
234 static int
235 alschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
236 {
237         struct  sc_chinfo *ch = data, *other;
238         struct  sc_info *sc = ch->parent;
239
240         other = (ch->dir == PCMDIR_PLAY) ? &sc->rch : &sc->pch;
241
242         /* Deny request if other dma channel is active */
243         if (other->dma_active) {
244                 ch->speed = other->speed;
245                 return other->speed;
246         }
247
248         ch->speed = speed;
249         return speed;
250 }
251
252 static int
253 alschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
254 {
255         struct  sc_chinfo *ch = data;
256         struct  sc_info *sc = ch->parent;
257
258         if (blocksize > sc->bufsz / 2) {
259                 blocksize = sc->bufsz / 2;
260         }
261         sndbuf_resize(ch->buffer, 2, blocksize);
262         return blocksize;
263 }
264
265 static int
266 alschan_getptr(kobj_t obj, void *data)
267 {
268         struct sc_chinfo *ch = data;
269         int32_t pos, sz;
270
271         pos = als_gcr_rd(ch->parent, ch->gcr_fifo_status) & 0xffff;
272         sz  = sndbuf_getsize(ch->buffer);
273         return (2 * sz - pos - 1) % sz;
274 }
275
276 static struct pcmchan_caps*
277 alschan_getcaps(kobj_t obj, void *data)
278 {
279         return &als_caps;
280 }
281
282 static void
283 als_set_speed(struct sc_chinfo *ch)
284 {
285         struct sc_info *sc = ch->parent;
286         struct sc_chinfo *other;
287
288         other = (ch->dir == PCMDIR_PLAY) ? &sc->rch : &sc->pch;
289         if (other->dma_active == 0) {
290                 als_esp_wr(sc, ALS_ESP_SAMPLE_RATE);
291                 als_esp_wr(sc, ch->speed >> 8);
292                 als_esp_wr(sc, ch->speed & 0xff);
293         } else {
294                 DEB(printf("speed locked at %d (tried %d)\n",
295                            other->speed, ch->speed));
296         }
297 }
298
299 /* ------------------------------------------------------------------------- */
300 /* Playback channel implementation */
301
302 #define ALS_8BIT_CMD(x, y)  { (x), (y), DSP_DMA8,  DSP_CMD_DMAPAUSE_8  }
303 #define ALS_16BIT_CMD(x, y) { (x), (y), DSP_DMA16, DSP_CMD_DMAPAUSE_16 }
304
305 struct playback_command {
306         u_int32_t pcm_format;   /* newpcm format */
307         u_int8_t  format_val;   /* sb16 format value */
308         u_int8_t  dma_prog;     /* sb16 dma program */
309         u_int8_t  dma_stop;     /* sb16 stop register */
310 } static const playback_cmds[] = {
311         ALS_8BIT_CMD(AFMT_U8, DSP_MODE_U8MONO),
312         ALS_8BIT_CMD(AFMT_U8 | AFMT_STEREO, DSP_MODE_U8STEREO),
313         ALS_16BIT_CMD(AFMT_S16_LE, DSP_MODE_S16MONO),
314         ALS_16BIT_CMD(AFMT_S16_LE | AFMT_STEREO, DSP_MODE_S16STEREO),
315 };
316
317 static const struct playback_command*
318 als_get_playback_command(u_int32_t format)
319 {
320         u_int32_t i, n;
321
322         n = sizeof(playback_cmds) / sizeof(playback_cmds[0]);
323         for (i = 0; i < n; i++) {
324                 if (playback_cmds[i].pcm_format == format) {
325                         return &playback_cmds[i];
326                 }
327         }
328         DEB(printf("als_get_playback_command: invalid format 0x%08x\n",
329                    format));
330         return &playback_cmds[0];
331 }
332
333 static void
334 als_playback_start(struct sc_chinfo *ch)
335 {
336         const struct playback_command *p;
337         struct  sc_info *sc = ch->parent;
338         u_int32_t       buf, bufsz, count, dma_prog;
339
340         buf = vtophys(sndbuf_getbuf(ch->buffer));
341         bufsz = sndbuf_getsize(ch->buffer);
342         count = bufsz / 2;
343         if (ch->format & AFMT_16BIT)
344                 count /= 2;
345         count--;
346
347         als_esp_wr(sc, DSP_CMD_SPKON);
348         als_set_speed(ch);
349
350         als_gcr_wr(sc, ALS_GCR_DMA0_START, buf);
351         als_gcr_wr(sc, ALS_GCR_DMA0_MODE, (bufsz - 1) | 0x180000);
352
353         p = als_get_playback_command(ch->format);
354         dma_prog = p->dma_prog | DSP_F16_DAC | DSP_F16_AUTO | DSP_F16_FIFO_ON;
355
356         als_esp_wr(sc, dma_prog);
357         als_esp_wr(sc, p->format_val);
358         als_esp_wr(sc, count & 0xff);
359         als_esp_wr(sc, count >> 8);
360
361         ch->dma_active = 1;
362 }
363
364 static int
365 als_playback_stop(struct sc_chinfo *ch)
366 {
367         const struct playback_command *p;
368         struct sc_info *sc = ch->parent;
369         u_int32_t active;
370
371         active = ch->dma_active;
372         if (active) {
373                 p = als_get_playback_command(ch->format);
374                 als_esp_wr(sc, p->dma_stop);
375         }
376         ch->dma_active = 0;
377         return active;
378 }
379
380 static int
381 alspchan_trigger(kobj_t obj, void *data, int go)
382 {
383         struct  sc_chinfo *ch = data;
384
385         switch(go) {
386         case PCMTRIG_START:
387                 als_playback_start(ch);
388                 break;
389         case PCMTRIG_ABORT:
390                 als_playback_stop(ch);
391                 break;
392         }
393         return 0;
394 }
395
396 static kobj_method_t alspchan_methods[] = {
397         KOBJMETHOD(channel_init,                alschan_init),
398         KOBJMETHOD(channel_setformat,           alschan_setformat),
399         KOBJMETHOD(channel_setspeed,            alschan_setspeed),
400         KOBJMETHOD(channel_setblocksize,        alschan_setblocksize),
401         KOBJMETHOD(channel_trigger,             alspchan_trigger),
402         KOBJMETHOD(channel_getptr,              alschan_getptr),
403         KOBJMETHOD(channel_getcaps,             alschan_getcaps),
404         { 0, 0 }
405 };
406 CHANNEL_DECLARE(alspchan);
407
408 /* ------------------------------------------------------------------------- */
409 /* Capture channel implementation */
410
411 static u_int8_t
412 als_get_fifo_format(struct sc_info *sc, u_int32_t format)
413 {
414         switch (format) {
415         case AFMT_U8:
416                 return ALS_FIFO1_8BIT;
417         case AFMT_U8 | AFMT_STEREO:
418                 return ALS_FIFO1_8BIT | ALS_FIFO1_STEREO;
419         case AFMT_S16_LE:
420                 return ALS_FIFO1_SIGNED;
421         case AFMT_S16_LE | AFMT_STEREO:
422                 return ALS_FIFO1_SIGNED | ALS_FIFO1_STEREO;
423         }
424         device_printf(sc->dev, "format not found: 0x%08x\n", format);
425         return ALS_FIFO1_8BIT;
426 }
427
428 static void
429 als_capture_start(struct sc_chinfo *ch)
430 {
431         struct  sc_info *sc = ch->parent;
432         u_int32_t       buf, bufsz, count, dma_prog;
433
434         buf = vtophys(sndbuf_getbuf(ch->buffer));
435         bufsz = sndbuf_getsize(ch->buffer);
436         count = bufsz / 2;
437         if (ch->format & AFMT_16BIT)
438                 count /= 2;
439         count--;
440
441         als_esp_wr(sc, DSP_CMD_SPKON);
442         als_set_speed(ch);
443
444         als_gcr_wr(sc, ALS_GCR_FIFO1_START, buf);
445         als_gcr_wr(sc, ALS_GCR_FIFO1_COUNT, (bufsz - 1));
446
447         als_mix_wr(sc, ALS_FIFO1_LENGTH_LO, count & 0xff);
448         als_mix_wr(sc, ALS_FIFO1_LENGTH_HI, count >> 8);
449
450         dma_prog = ALS_FIFO1_RUN | als_get_fifo_format(sc, ch->format);
451         als_mix_wr(sc, ALS_FIFO1_CONTROL, dma_prog);
452
453         ch->dma_active = 1;
454 }
455
456 static int
457 als_capture_stop(struct sc_chinfo *ch)
458 {
459         struct sc_info *sc = ch->parent;
460         u_int32_t active;
461
462         active = ch->dma_active;
463         if (active) {
464                 als_mix_wr(sc, ALS_FIFO1_CONTROL, ALS_FIFO1_STOP);
465         }
466         ch->dma_active = 0;
467         return active;
468 }
469
470 static int
471 alsrchan_trigger(kobj_t obj, void *data, int go)
472 {
473         struct  sc_chinfo *ch = data;
474
475         switch(go) {
476         case PCMTRIG_START:
477                 als_capture_start(ch);
478                 break;
479         case PCMTRIG_ABORT:
480                 als_capture_stop(ch);
481                 break;
482         }
483         return 0;
484 }
485
486 static kobj_method_t alsrchan_methods[] = {
487         KOBJMETHOD(channel_init,                alschan_init),
488         KOBJMETHOD(channel_setformat,           alschan_setformat),
489         KOBJMETHOD(channel_setspeed,            alschan_setspeed),
490         KOBJMETHOD(channel_setblocksize,        alschan_setblocksize),
491         KOBJMETHOD(channel_trigger,             alsrchan_trigger),
492         KOBJMETHOD(channel_getptr,              alschan_getptr),
493         KOBJMETHOD(channel_getcaps,             alschan_getcaps),
494         { 0, 0 }
495 };
496 CHANNEL_DECLARE(alsrchan);
497
498 /* ------------------------------------------------------------------------- */
499 /* Mixer related */
500
501 /*
502  * ALS4000 has an sb16 mixer, with some additional controls that we do
503  * not yet a means to support.
504  */
505
506 struct sb16props {
507         u_int8_t lreg;
508         u_int8_t rreg;
509         u_int8_t bits;
510         u_int8_t oselect;
511         u_int8_t iselect; /* left input mask */
512 } static const amt[SOUND_MIXER_NRDEVICES] = {
513         [SOUND_MIXER_VOLUME]  = { 0x30, 0x31, 5, 0x00, 0x00 },
514         [SOUND_MIXER_PCM]     = { 0x32, 0x33, 5, 0x00, 0x00 },
515         [SOUND_MIXER_SYNTH]   = { 0x34, 0x35, 5, 0x60, 0x40 },
516         [SOUND_MIXER_CD]      = { 0x36, 0x37, 5, 0x06, 0x04 },
517         [SOUND_MIXER_LINE]    = { 0x38, 0x39, 5, 0x18, 0x10 },
518         [SOUND_MIXER_MIC]     = { 0x3a, 0x00, 5, 0x01, 0x01 },
519         [SOUND_MIXER_SPEAKER] = { 0x3b, 0x00, 2, 0x00, 0x00 },
520         [SOUND_MIXER_IGAIN]   = { 0x3f, 0x40, 2, 0x00, 0x00 },
521         [SOUND_MIXER_OGAIN]   = { 0x41, 0x42, 2, 0x00, 0x00 },
522         /* The following have register values but no h/w implementation */
523         [SOUND_MIXER_TREBLE]  = { 0x44, 0x45, 4, 0x00, 0x00 },
524         [SOUND_MIXER_BASS]    = { 0x46, 0x47, 4, 0x00, 0x00 }
525 };
526
527 static int
528 alsmix_init(struct snd_mixer *m)
529 {
530         u_int32_t i, v;
531
532         for (i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) {
533                 if (amt[i].bits) v |= 1 << i;
534         }
535         mix_setdevs(m, v);
536
537         for (i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) {
538                 if (amt[i].iselect) v |= 1 << i;
539         }
540         mix_setrecdevs(m, v);
541         return 0;
542 }
543
544 static int
545 alsmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
546 {
547         struct sc_info *sc = mix_getdevinfo(m);
548         u_int32_t r, l, v, mask;
549
550         /* Fill upper n bits in mask with 1's */
551         mask = ((1 << amt[dev].bits) - 1) << (8 - amt[dev].bits);
552
553         l = (left * mask / 100) & mask;
554         v = als_mix_rd(sc, amt[dev].lreg) & ~mask;
555         als_mix_wr(sc, amt[dev].lreg, l | v);
556
557         if (amt[dev].rreg) {
558                 r = (right * mask / 100) & mask;
559                 v = als_mix_rd(sc, amt[dev].rreg) & ~mask;
560                 als_mix_wr(sc, amt[dev].rreg, r | v);
561         } else {
562                 r = 0;
563         }
564
565         /* Zero gain does not mute channel from output, but this does. */
566         v = als_mix_rd(sc, SB16_OMASK);
567         if (l == 0 && r == 0) {
568                 v &= ~amt[dev].oselect;
569         } else {
570                 v |= amt[dev].oselect;
571         }
572         als_mix_wr(sc, SB16_OMASK, v);
573         return 0;
574 }
575
576 static int
577 alsmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
578 {
579         struct sc_info *sc = mix_getdevinfo(m);
580         u_int32_t i, l, r;
581
582         for (i = l = r = 0; i < SOUND_MIXER_NRDEVICES; i++) {
583                 if (src & (1 << i)) {
584                         l |= amt[i].iselect;
585                         r |= amt[i].iselect << 1;
586                 }
587         }
588
589         als_mix_wr(sc, SB16_IMASK_L, l);
590         als_mix_wr(sc, SB16_IMASK_R, r);
591         return src;
592 }
593
594 static kobj_method_t als_mixer_methods[] = {
595         KOBJMETHOD(mixer_init,          alsmix_init),
596         KOBJMETHOD(mixer_set,           alsmix_set),
597         KOBJMETHOD(mixer_setrecsrc,     alsmix_setrecsrc),
598         { 0, 0 }
599 };
600 MIXER_DECLARE(als_mixer);
601
602 /* ------------------------------------------------------------------------- */
603 /* Interrupt Handler */
604
605 static void
606 als_intr(void *p)
607 {
608         struct sc_info *sc = (struct sc_info *)p;
609         u_int8_t intr, sb_status;
610
611         intr = als_intr_rd(sc);
612
613         if (intr & 0x80)
614                 chn_intr(sc->pch.channel);
615
616         if (intr & 0x40)
617                 chn_intr(sc->rch.channel);
618
619         /* ACK interrupt in PCI core */
620         als_intr_wr(sc, intr);
621
622         /* ACK interrupt in SB core */
623         sb_status = als_mix_rd(sc, IRQ_STAT);
624
625         if (sb_status & ALS_IRQ_STATUS8)
626                 als_ack_read(sc, ALS_ESP_RD_STATUS8);
627         if (sb_status & ALS_IRQ_STATUS16)
628                 als_ack_read(sc, ALS_ESP_RD_STATUS16);
629         if (sb_status & ALS_IRQ_MPUIN)
630                 als_ack_read(sc, ALS_MIDI_DATA);
631         if (sb_status & ALS_IRQ_CR1E)
632                 als_ack_read(sc, ALS_CR1E_ACK_PORT);
633         return;
634 }
635
636 /* ------------------------------------------------------------------------- */
637 /* H/W initialization */
638
639 static int
640 als_init(struct sc_info *sc)
641 {
642         u_int32_t i, v;
643
644         /* Reset Chip */
645         if (als_esp_reset(sc)) {
646                 return 1;
647         }
648
649         /* Enable write on DMA_SETUP register */
650         v = als_mix_rd(sc, ALS_SB16_CONFIG);
651         als_mix_wr(sc, ALS_SB16_CONFIG, v | 0x80);
652
653         /* Select DMA0 */
654         als_mix_wr(sc, ALS_SB16_DMA_SETUP, 0x01);
655
656         /* Disable write on DMA_SETUP register */
657         als_mix_wr(sc, ALS_SB16_CONFIG, v & 0x7f);
658
659         /* Enable interrupts */
660         v  = als_gcr_rd(sc, ALS_GCR_MISC);
661         als_gcr_wr(sc, ALS_GCR_MISC, v | 0x28000);
662
663         /* Black out GCR DMA registers */
664         for (i = 0x91; i <= 0x96; i++) {
665                 als_gcr_wr(sc, i, 0);
666         }
667
668         /* Emulation mode */
669         v = als_gcr_rd(sc, ALS_GCR_DMA_EMULATION);
670         als_gcr_wr(sc, ALS_GCR_DMA_EMULATION, v);
671         DEB(printf("GCR_DMA_EMULATION 0x%08x\n", v));
672         return 0;
673 }
674
675 static void
676 als_uninit(struct sc_info *sc)
677 {
678         /* Disable interrupts */
679         als_gcr_wr(sc, ALS_GCR_MISC, 0);
680 }
681
682 /* ------------------------------------------------------------------------- */
683 /* Probe and attach card */
684
685 static int
686 als_pci_probe(device_t dev)
687 {
688         if (pci_get_devid(dev) == ALS_PCI_ID0) {
689                 device_set_desc(dev, "Avance Logic ALS4000");
690                 return 0;
691         }
692         return ENXIO;
693 }
694
695 static void
696 als_resource_free(device_t dev, struct sc_info *sc)
697 {
698         if (sc->reg) {
699                 bus_release_resource(dev, SYS_RES_IOPORT, sc->regid, sc->reg);
700                 sc->reg = 0;
701         }
702         if (sc->ih) {
703                 bus_teardown_intr(dev, sc->irq, sc->ih);
704                 sc->ih = 0;
705         }
706         if (sc->irq) {
707                 bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
708                 sc->irq = 0;
709         }
710         if (sc->parent_dmat) {
711                 bus_dma_tag_destroy(sc->parent_dmat);
712                 sc->parent_dmat = 0;
713         }
714 }
715
716 static int
717 als_resource_grab(device_t dev, struct sc_info *sc)
718 {
719         sc->regid = PCIR_MAPS;
720         sc->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->regid, 0, ~0,
721                                      ALS_CONFIG_SPACE_BYTES, RF_ACTIVE);
722         if (sc->reg == 0) {
723                 device_printf(dev, "unable to allocate register space\n");
724                 goto bad;
725         }
726         sc->st = rman_get_bustag(sc->reg);
727         sc->sh = rman_get_bushandle(sc->reg);
728
729         sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 0, ~0, 1,
730                                      RF_ACTIVE | RF_SHAREABLE);
731         if (sc->irq == 0) {
732                 device_printf(dev, "unable to allocate interrupt\n");
733                 goto bad;
734         }
735
736         if (bus_setup_intr(dev, sc->irq, INTR_TYPE_AV, als_intr,
737                            sc, &sc->ih)) {
738                 device_printf(dev, "unable to setup interrupt\n");
739                 goto bad;
740         }
741
742         sc->bufsz = pcm_getbuffersize(dev, 4096, ALS_DEFAULT_BUFSZ, 65536);
743
744         if (bus_dma_tag_create(/*parent*/NULL,
745                                /*alignment*/2, /*boundary*/0,
746                                /*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
747                                /*highaddr*/BUS_SPACE_MAXADDR,
748                                /*filter*/NULL, /*filterarg*/NULL,
749                                /*maxsize*/sc->bufsz,
750                                /*nsegments*/1, /*maxsegz*/0x3ffff,
751                                /*flags*/0, &sc->parent_dmat) != 0) {
752                 device_printf(dev, "unable to create dma tag\n");
753                 goto bad;
754         }
755         return 0;
756  bad:
757         als_resource_free(dev, sc);
758         return ENXIO;
759 }
760
761 static int
762 als_pci_attach(device_t dev)
763 {
764         struct sc_info *sc;
765         u_int32_t data;
766         char status[SND_STATUSLEN];
767
768         if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
769                 device_printf(dev, "cannot allocate softc\n");
770                 return ENXIO;
771         }
772
773         sc->dev = dev;
774
775         data = pci_read_config(dev, PCIR_COMMAND, 2);
776         data |= (PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
777         pci_write_config(dev, PCIR_COMMAND, data, 2);
778         /*
779          * By default the power to the various components on the
780          * ALS4000 is entirely controlled by the pci powerstate.  We
781          * could attempt finer grained control by setting GCR6.31.
782          */
783 #if __FreeBSD_version > 500000
784         if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
785                 /* Reset the power state. */
786                 device_printf(dev, "chip is in D%d power mode "
787                               "-- setting to D0\n", pci_get_powerstate(dev));
788                 pci_set_powerstate(dev, PCI_POWERSTATE_D0);
789         }
790 #else
791         data = pci_read_config(dev, ALS_PCI_POWERREG, 2);
792         if ((data & 0x03) != 0) {
793                 device_printf(dev, "chip is in D%d power mode "
794                               "-- setting to D0\n", data & 0x03);
795                 data &= ~0x03;
796                 pci_write_config(dev, ALS_PCI_POWERREG, data, 2);
797         }
798 #endif
799
800         if (als_resource_grab(dev, sc)) {
801                 device_printf(dev, "failed to allocate resources\n");
802                 goto bad_attach;
803         }
804
805         if (als_init(sc)) {
806                 device_printf(dev, "failed to initialize hardware\n");
807                 goto bad_attach;
808         }
809
810         if (mixer_init(dev, &als_mixer_class, sc)) {
811                 device_printf(dev, "failed to initialize mixer\n");
812                 goto bad_attach;
813         }
814
815         if (pcm_register(dev, sc, 1, 1)) {
816                 device_printf(dev, "failed to register pcm entries\n");
817                 goto bad_attach;
818         }
819
820         pcm_addchan(dev, PCMDIR_PLAY, &alspchan_class, sc);
821         pcm_addchan(dev, PCMDIR_REC,  &alsrchan_class, sc);
822
823         snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld",
824                  rman_get_start(sc->reg), rman_get_start(sc->irq));
825         pcm_setstatus(dev, status);
826         return 0;
827
828  bad_attach:
829         als_resource_free(dev, sc);
830         free(sc, M_DEVBUF);
831         return ENXIO;
832 }
833
834 static int
835 als_pci_detach(device_t dev)
836 {
837         struct sc_info *sc;
838         int r;
839
840         r = pcm_unregister(dev);
841         if (r)
842                 return r;
843
844         sc = pcm_getdevinfo(dev);
845         als_uninit(sc);
846         als_resource_free(dev, sc);
847         free(sc, M_DEVBUF);
848         return 0;
849 }
850
851 static int
852 als_pci_suspend(device_t dev)
853 {
854         struct sc_info *sc = pcm_getdevinfo(dev);
855
856         sc->pch.dma_was_active = als_playback_stop(&sc->pch);
857         sc->rch.dma_was_active = als_capture_stop(&sc->rch);
858         als_uninit(sc);
859         return 0;
860 }
861
862 static int
863 als_pci_resume(device_t dev)
864 {
865         struct sc_info *sc = pcm_getdevinfo(dev);
866
867         if (als_init(sc) != 0) {
868                 device_printf(dev, "unable to reinitialize the card\n");
869                 return ENXIO;
870         }
871
872         if (mixer_reinit(dev) != 0) {
873                 device_printf(dev, "unable to reinitialize the mixer\n");
874                 return ENXIO;
875         }
876
877         if (sc->pch.dma_was_active) {
878                 als_playback_start(&sc->pch);
879         }
880
881         if (sc->rch.dma_was_active) {
882                 als_capture_start(&sc->rch);
883         }
884         return 0;
885 }
886
887 static device_method_t als_methods[] = {
888         /* Device interface */
889         DEVMETHOD(device_probe,         als_pci_probe),
890         DEVMETHOD(device_attach,        als_pci_attach),
891         DEVMETHOD(device_detach,        als_pci_detach),
892         DEVMETHOD(device_suspend,       als_pci_suspend),
893         DEVMETHOD(device_resume,        als_pci_resume),
894         { 0, 0 }
895 };
896
897 static driver_t als_driver = {
898         "pcm",
899         als_methods,
900         PCM_SOFTC_SIZE,
901 };
902
903 DRIVER_MODULE(snd_als4000, pci, als_driver, pcm_devclass, 0, 0);
904 MODULE_DEPEND(snd_als4000, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER);
905 MODULE_VERSION(snd_als4000, 1);