- Add codec id for Realtek ALC268.
[dragonfly.git] / sys / dev / sound / pci / als4000.c
blob2a11be5134c65c3925a95f88170ce809bcd5041d
1 /*-
2 * Copyright (c) 2001 Orion Hodson <oho@acm.org>
3 * All rights reserved.
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.
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.
26 * $FreeBSD: src/sys/dev/sound/pci/als4000.c,v 1.18.2.1 2005/12/30 19:55:53 netchild Exp $
27 * $DragonFly: src/sys/dev/sound/pci/als4000.c,v 1.11 2007/06/16 20:07:19 dillon Exp $
31 * als4000.c - driver for the Avance Logic ALS 4000 chipset.
33 * The ALS4000 is effectively an SB16 with a PCI interface.
35 * This driver derives from ALS4000a.PDF, Bart Hartgers alsa driver, and
36 * SB16 register descriptions.
39 #include <dev/sound/pcm/sound.h>
40 #include <dev/sound/isa/sb.h>
41 #include <dev/sound/pci/als4000.h>
43 #include <bus/pci/pcireg.h>
44 #include <bus/pci/pcivar.h>
46 #include "mixer_if.h"
48 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/als4000.c,v 1.11 2007/06/16 20:07:19 dillon Exp $");
50 /* Debugging macro's */
51 #undef DEB
52 #ifndef DEB
53 #define DEB(x) /* x */
54 #endif /* DEB */
56 #define ALS_DEFAULT_BUFSZ 16384
58 /* ------------------------------------------------------------------------- */
59 /* Structures */
61 struct sc_info;
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;
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 sndlock_t lock;
83 unsigned int bufsz;
84 struct sc_chinfo pch, rch;
87 /* Channel caps */
89 static u_int32_t als_format[] = {
90 AFMT_U8,
91 AFMT_STEREO | AFMT_U8,
92 AFMT_S16_LE,
93 AFMT_STEREO | AFMT_S16_LE,
98 * I don't believe this rotten soundcard can do 48k, really,
99 * trust me.
101 static struct pcmchan_caps als_caps = { 4000, 44100, als_format, 0 };
103 /* ------------------------------------------------------------------------- */
104 /* Register Utilities */
106 static u_int32_t
107 als_gcr_rd(struct sc_info *sc, int index)
109 bus_space_write_1(sc->st, sc->sh, ALS_GCR_INDEX, index);
110 return bus_space_read_4(sc->st, sc->sh, ALS_GCR_DATA);
113 static void
114 als_gcr_wr(struct sc_info *sc, int index, int data)
116 bus_space_write_1(sc->st, sc->sh, ALS_GCR_INDEX, index);
117 bus_space_write_4(sc->st, sc->sh, ALS_GCR_DATA, data);
120 static u_int8_t
121 als_intr_rd(struct sc_info *sc)
123 return bus_space_read_1(sc->st, sc->sh, ALS_SB_MPU_IRQ);
126 static void
127 als_intr_wr(struct sc_info *sc, u_int8_t data)
129 bus_space_write_1(sc->st, sc->sh, ALS_SB_MPU_IRQ, data);
132 static u_int8_t
133 als_mix_rd(struct sc_info *sc, u_int8_t index)
135 bus_space_write_1(sc->st, sc->sh, ALS_MIXER_INDEX, index);
136 return bus_space_read_1(sc->st, sc->sh, ALS_MIXER_DATA);
139 static void
140 als_mix_wr(struct sc_info *sc, u_int8_t index, u_int8_t data)
142 bus_space_write_1(sc->st, sc->sh, ALS_MIXER_INDEX, index);
143 bus_space_write_1(sc->st, sc->sh, ALS_MIXER_DATA, data);
146 static void
147 als_esp_wr(struct sc_info *sc, u_int8_t data)
149 u_int32_t tries, v;
151 tries = 1000;
152 do {
153 v = bus_space_read_1(sc->st, sc->sh, ALS_ESP_WR_STATUS);
154 if (~v & 0x80)
155 break;
156 DELAY(20);
157 } while (--tries != 0);
159 if (tries == 0)
160 device_printf(sc->dev, "als_esp_wr timeout");
162 bus_space_write_1(sc->st, sc->sh, ALS_ESP_WR_DATA, data);
165 static int
166 als_esp_reset(struct sc_info *sc)
168 u_int32_t tries, u, v;
170 bus_space_write_1(sc->st, sc->sh, ALS_ESP_RST, 1);
171 DELAY(10);
172 bus_space_write_1(sc->st, sc->sh, ALS_ESP_RST, 0);
173 DELAY(30);
175 tries = 1000;
176 do {
177 u = bus_space_read_1(sc->st, sc->sh, ALS_ESP_RD_STATUS8);
178 if (u & 0x80) {
179 v = bus_space_read_1(sc->st, sc->sh, ALS_ESP_RD_DATA);
180 if (v == 0xaa)
181 return 0;
182 else
183 break;
185 DELAY(20);
186 } while (--tries != 0);
188 if (tries == 0)
189 device_printf(sc->dev, "als_esp_reset timeout");
190 return 1;
193 static u_int8_t
194 als_ack_read(struct sc_info *sc, u_int8_t addr)
196 u_int8_t r = bus_space_read_1(sc->st, sc->sh, addr);
197 return r;
200 /* ------------------------------------------------------------------------- */
201 /* Common pcm channel implementation */
203 static void *
204 alschan_init(kobj_t obj, void *devinfo,
205 struct snd_dbuf *b, struct pcm_channel *c, int dir)
207 struct sc_info *sc = devinfo;
208 struct sc_chinfo *ch;
210 snd_mtxlock(sc->lock);
211 if (dir == PCMDIR_PLAY) {
212 ch = &sc->pch;
213 ch->gcr_fifo_status = ALS_GCR_FIFO0_STATUS;
214 } else {
215 ch = &sc->rch;
216 ch->gcr_fifo_status = ALS_GCR_FIFO1_STATUS;
218 ch->dir = dir;
219 ch->parent = sc;
220 ch->channel = c;
221 ch->bps = 1;
222 ch->format = AFMT_U8;
223 ch->speed = DSP_DEFAULT_SPEED;
224 ch->buffer = b;
225 snd_mtxunlock(sc->lock);
227 if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0)
228 return NULL;
230 return ch;
233 static int
234 alschan_setformat(kobj_t obj, void *data, u_int32_t format)
236 struct sc_chinfo *ch = data;
238 ch->format = format;
239 return 0;
242 static int
243 alschan_setspeed(kobj_t obj, void *data, u_int32_t speed)
245 struct sc_chinfo *ch = data, *other;
246 struct sc_info *sc = ch->parent;
248 other = (ch->dir == PCMDIR_PLAY) ? &sc->rch : &sc->pch;
250 /* Deny request if other dma channel is active */
251 if (other->dma_active) {
252 ch->speed = other->speed;
253 return other->speed;
256 ch->speed = speed;
257 return speed;
260 static int
261 alschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
263 struct sc_chinfo *ch = data;
264 struct sc_info *sc = ch->parent;
266 if (blocksize > sc->bufsz / 2) {
267 blocksize = sc->bufsz / 2;
269 sndbuf_resize(ch->buffer, 2, blocksize);
270 return blocksize;
273 static int
274 alschan_getptr(kobj_t obj, void *data)
276 struct sc_chinfo *ch = data;
277 struct sc_info *sc = ch->parent;
278 int32_t pos, sz;
280 snd_mtxlock(sc->lock);
281 pos = als_gcr_rd(ch->parent, ch->gcr_fifo_status) & 0xffff;
282 snd_mtxunlock(sc->lock);
283 sz = sndbuf_getsize(ch->buffer);
284 return (2 * sz - pos - 1) % sz;
287 static struct pcmchan_caps*
288 alschan_getcaps(kobj_t obj, void *data)
290 return &als_caps;
293 static void
294 als_set_speed(struct sc_chinfo *ch)
296 struct sc_info *sc = ch->parent;
297 struct sc_chinfo *other;
299 other = (ch->dir == PCMDIR_PLAY) ? &sc->rch : &sc->pch;
300 if (other->dma_active == 0) {
301 als_esp_wr(sc, ALS_ESP_SAMPLE_RATE);
302 als_esp_wr(sc, ch->speed >> 8);
303 als_esp_wr(sc, ch->speed & 0xff);
304 } else {
305 DEB(kprintf("speed locked at %d (tried %d)\n",
306 other->speed, ch->speed));
310 /* ------------------------------------------------------------------------- */
311 /* Playback channel implementation */
313 #define ALS_8BIT_CMD(x, y) { (x), (y), DSP_DMA8, DSP_CMD_DMAPAUSE_8 }
314 #define ALS_16BIT_CMD(x, y) { (x), (y), DSP_DMA16, DSP_CMD_DMAPAUSE_16 }
316 struct playback_command {
317 u_int32_t pcm_format; /* newpcm format */
318 u_int8_t format_val; /* sb16 format value */
319 u_int8_t dma_prog; /* sb16 dma program */
320 u_int8_t dma_stop; /* sb16 stop register */
321 } static const playback_cmds[] = {
322 ALS_8BIT_CMD(AFMT_U8, DSP_MODE_U8MONO),
323 ALS_8BIT_CMD(AFMT_U8 | AFMT_STEREO, DSP_MODE_U8STEREO),
324 ALS_16BIT_CMD(AFMT_S16_LE, DSP_MODE_S16MONO),
325 ALS_16BIT_CMD(AFMT_S16_LE | AFMT_STEREO, DSP_MODE_S16STEREO),
328 static const struct playback_command*
329 als_get_playback_command(u_int32_t format)
331 u_int32_t i, n;
333 n = sizeof(playback_cmds) / sizeof(playback_cmds[0]);
334 for (i = 0; i < n; i++) {
335 if (playback_cmds[i].pcm_format == format) {
336 return &playback_cmds[i];
339 DEB(kprintf("als_get_playback_command: invalid format 0x%08x\n",
340 format));
341 return &playback_cmds[0];
344 static void
345 als_playback_start(struct sc_chinfo *ch)
347 const struct playback_command *p;
348 struct sc_info *sc = ch->parent;
349 u_int32_t buf, bufsz, count, dma_prog;
351 buf = sndbuf_getbufaddr(ch->buffer);
352 bufsz = sndbuf_getsize(ch->buffer);
353 count = bufsz / 2;
354 if (ch->format & AFMT_16BIT)
355 count /= 2;
356 count--;
358 als_esp_wr(sc, DSP_CMD_SPKON);
359 als_set_speed(ch);
361 als_gcr_wr(sc, ALS_GCR_DMA0_START, buf);
362 als_gcr_wr(sc, ALS_GCR_DMA0_MODE, (bufsz - 1) | 0x180000);
364 p = als_get_playback_command(ch->format);
365 dma_prog = p->dma_prog | DSP_F16_DAC | DSP_F16_AUTO | DSP_F16_FIFO_ON;
367 als_esp_wr(sc, dma_prog);
368 als_esp_wr(sc, p->format_val);
369 als_esp_wr(sc, count & 0xff);
370 als_esp_wr(sc, count >> 8);
372 ch->dma_active = 1;
375 static int
376 als_playback_stop(struct sc_chinfo *ch)
378 const struct playback_command *p;
379 struct sc_info *sc = ch->parent;
380 u_int32_t active;
382 active = ch->dma_active;
383 if (active) {
384 p = als_get_playback_command(ch->format);
385 als_esp_wr(sc, p->dma_stop);
387 ch->dma_active = 0;
388 return active;
391 static int
392 alspchan_trigger(kobj_t obj, void *data, int go)
394 struct sc_chinfo *ch = data;
395 struct sc_info *sc = ch->parent;
397 snd_mtxlock(sc->lock);
398 switch(go) {
399 case PCMTRIG_START:
400 als_playback_start(ch);
401 break;
402 case PCMTRIG_ABORT:
403 als_playback_stop(ch);
404 break;
406 snd_mtxunlock(sc->lock);
407 return 0;
410 static kobj_method_t alspchan_methods[] = {
411 KOBJMETHOD(channel_init, alschan_init),
412 KOBJMETHOD(channel_setformat, alschan_setformat),
413 KOBJMETHOD(channel_setspeed, alschan_setspeed),
414 KOBJMETHOD(channel_setblocksize, alschan_setblocksize),
415 KOBJMETHOD(channel_trigger, alspchan_trigger),
416 KOBJMETHOD(channel_getptr, alschan_getptr),
417 KOBJMETHOD(channel_getcaps, alschan_getcaps),
418 { 0, 0 }
420 CHANNEL_DECLARE(alspchan);
422 /* ------------------------------------------------------------------------- */
423 /* Capture channel implementation */
425 static u_int8_t
426 als_get_fifo_format(struct sc_info *sc, u_int32_t format)
428 switch (format) {
429 case AFMT_U8:
430 return ALS_FIFO1_8BIT;
431 case AFMT_U8 | AFMT_STEREO:
432 return ALS_FIFO1_8BIT | ALS_FIFO1_STEREO;
433 case AFMT_S16_LE:
434 return ALS_FIFO1_SIGNED;
435 case AFMT_S16_LE | AFMT_STEREO:
436 return ALS_FIFO1_SIGNED | ALS_FIFO1_STEREO;
438 device_printf(sc->dev, "format not found: 0x%08x\n", format);
439 return ALS_FIFO1_8BIT;
442 static void
443 als_capture_start(struct sc_chinfo *ch)
445 struct sc_info *sc = ch->parent;
446 u_int32_t buf, bufsz, count, dma_prog;
448 buf = sndbuf_getbufaddr(ch->buffer);
449 bufsz = sndbuf_getsize(ch->buffer);
450 count = bufsz / 2;
451 if (ch->format & AFMT_16BIT)
452 count /= 2;
453 count--;
455 als_esp_wr(sc, DSP_CMD_SPKON);
456 als_set_speed(ch);
458 als_gcr_wr(sc, ALS_GCR_FIFO1_START, buf);
459 als_gcr_wr(sc, ALS_GCR_FIFO1_COUNT, (bufsz - 1));
461 als_mix_wr(sc, ALS_FIFO1_LENGTH_LO, count & 0xff);
462 als_mix_wr(sc, ALS_FIFO1_LENGTH_HI, count >> 8);
464 dma_prog = ALS_FIFO1_RUN | als_get_fifo_format(sc, ch->format);
465 als_mix_wr(sc, ALS_FIFO1_CONTROL, dma_prog);
467 ch->dma_active = 1;
470 static int
471 als_capture_stop(struct sc_chinfo *ch)
473 struct sc_info *sc = ch->parent;
474 u_int32_t active;
476 active = ch->dma_active;
477 if (active) {
478 als_mix_wr(sc, ALS_FIFO1_CONTROL, ALS_FIFO1_STOP);
480 ch->dma_active = 0;
481 return active;
484 static int
485 alsrchan_trigger(kobj_t obj, void *data, int go)
487 struct sc_chinfo *ch = data;
488 struct sc_info *sc = ch->parent;
490 snd_mtxlock(sc->lock);
491 switch(go) {
492 case PCMTRIG_START:
493 als_capture_start(ch);
494 break;
495 case PCMTRIG_ABORT:
496 als_capture_stop(ch);
497 break;
499 snd_mtxunlock(sc->lock);
500 return 0;
503 static kobj_method_t alsrchan_methods[] = {
504 KOBJMETHOD(channel_init, alschan_init),
505 KOBJMETHOD(channel_setformat, alschan_setformat),
506 KOBJMETHOD(channel_setspeed, alschan_setspeed),
507 KOBJMETHOD(channel_setblocksize, alschan_setblocksize),
508 KOBJMETHOD(channel_trigger, alsrchan_trigger),
509 KOBJMETHOD(channel_getptr, alschan_getptr),
510 KOBJMETHOD(channel_getcaps, alschan_getcaps),
511 { 0, 0 }
513 CHANNEL_DECLARE(alsrchan);
515 /* ------------------------------------------------------------------------- */
516 /* Mixer related */
519 * ALS4000 has an sb16 mixer, with some additional controls that we do
520 * not yet a means to support.
523 struct sb16props {
524 u_int8_t lreg;
525 u_int8_t rreg;
526 u_int8_t bits;
527 u_int8_t oselect;
528 u_int8_t iselect; /* left input mask */
529 } static const amt[SOUND_MIXER_NRDEVICES] = {
530 [SOUND_MIXER_VOLUME] = { 0x30, 0x31, 5, 0x00, 0x00 },
531 [SOUND_MIXER_PCM] = { 0x32, 0x33, 5, 0x00, 0x00 },
532 [SOUND_MIXER_SYNTH] = { 0x34, 0x35, 5, 0x60, 0x40 },
533 [SOUND_MIXER_CD] = { 0x36, 0x37, 5, 0x06, 0x04 },
534 [SOUND_MIXER_LINE] = { 0x38, 0x39, 5, 0x18, 0x10 },
535 [SOUND_MIXER_MIC] = { 0x3a, 0x00, 5, 0x01, 0x01 },
536 [SOUND_MIXER_SPEAKER] = { 0x3b, 0x00, 2, 0x00, 0x00 },
537 [SOUND_MIXER_IGAIN] = { 0x3f, 0x40, 2, 0x00, 0x00 },
538 [SOUND_MIXER_OGAIN] = { 0x41, 0x42, 2, 0x00, 0x00 },
539 /* The following have register values but no h/w implementation */
540 [SOUND_MIXER_TREBLE] = { 0x44, 0x45, 4, 0x00, 0x00 },
541 [SOUND_MIXER_BASS] = { 0x46, 0x47, 4, 0x00, 0x00 }
544 static int
545 alsmix_init(struct snd_mixer *m)
547 u_int32_t i, v;
549 for (i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) {
550 if (amt[i].bits) v |= 1 << i;
552 mix_setdevs(m, v);
554 for (i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) {
555 if (amt[i].iselect) v |= 1 << i;
557 mix_setrecdevs(m, v);
558 return 0;
561 static int
562 alsmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
564 struct sc_info *sc = mix_getdevinfo(m);
565 u_int32_t r, l, v, mask;
567 /* Fill upper n bits in mask with 1's */
568 mask = ((1 << amt[dev].bits) - 1) << (8 - amt[dev].bits);
570 l = (left * mask / 100) & mask;
571 v = als_mix_rd(sc, amt[dev].lreg) & ~mask;
572 als_mix_wr(sc, amt[dev].lreg, l | v);
574 if (amt[dev].rreg) {
575 r = (right * mask / 100) & mask;
576 v = als_mix_rd(sc, amt[dev].rreg) & ~mask;
577 als_mix_wr(sc, amt[dev].rreg, r | v);
578 } else {
579 r = 0;
582 /* Zero gain does not mute channel from output, but this does. */
583 v = als_mix_rd(sc, SB16_OMASK);
584 if (l == 0 && r == 0) {
585 v &= ~amt[dev].oselect;
586 } else {
587 v |= amt[dev].oselect;
589 als_mix_wr(sc, SB16_OMASK, v);
590 return 0;
593 static int
594 alsmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
596 struct sc_info *sc = mix_getdevinfo(m);
597 u_int32_t i, l, r;
599 for (i = l = r = 0; i < SOUND_MIXER_NRDEVICES; i++) {
600 if (src & (1 << i)) {
601 if (amt[i].iselect == 1) { /* microphone */
602 l |= amt[i].iselect;
603 r |= amt[i].iselect;
604 } else {
605 l |= amt[i].iselect;
606 r |= amt[i].iselect >> 1;
611 als_mix_wr(sc, SB16_IMASK_L, l);
612 als_mix_wr(sc, SB16_IMASK_R, r);
613 return src;
616 static kobj_method_t als_mixer_methods[] = {
617 KOBJMETHOD(mixer_init, alsmix_init),
618 KOBJMETHOD(mixer_set, alsmix_set),
619 KOBJMETHOD(mixer_setrecsrc, alsmix_setrecsrc),
620 { 0, 0 }
622 MIXER_DECLARE(als_mixer);
624 /* ------------------------------------------------------------------------- */
625 /* Interrupt Handler */
627 static void
628 als_intr(void *p)
630 struct sc_info *sc = (struct sc_info *)p;
631 u_int8_t intr, sb_status;
633 snd_mtxlock(sc->lock);
634 intr = als_intr_rd(sc);
636 if (intr & 0x80) {
637 snd_mtxunlock(sc->lock);
638 chn_intr(sc->pch.channel);
639 snd_mtxlock(sc->lock);
642 if (intr & 0x40) {
643 snd_mtxunlock(sc->lock);
644 chn_intr(sc->rch.channel);
645 snd_mtxlock(sc->lock);
648 /* ACK interrupt in PCI core */
649 als_intr_wr(sc, intr);
651 /* ACK interrupt in SB core */
652 sb_status = als_mix_rd(sc, IRQ_STAT);
654 if (sb_status & ALS_IRQ_STATUS8)
655 als_ack_read(sc, ALS_ESP_RD_STATUS8);
656 if (sb_status & ALS_IRQ_STATUS16)
657 als_ack_read(sc, ALS_ESP_RD_STATUS16);
658 if (sb_status & ALS_IRQ_MPUIN)
659 als_ack_read(sc, ALS_MIDI_DATA);
660 if (sb_status & ALS_IRQ_CR1E)
661 als_ack_read(sc, ALS_CR1E_ACK_PORT);
663 snd_mtxunlock(sc->lock);
664 return;
667 /* ------------------------------------------------------------------------- */
668 /* H/W initialization */
670 static int
671 als_init(struct sc_info *sc)
673 u_int32_t i, v;
675 /* Reset Chip */
676 if (als_esp_reset(sc)) {
677 return 1;
680 /* Enable write on DMA_SETUP register */
681 v = als_mix_rd(sc, ALS_SB16_CONFIG);
682 als_mix_wr(sc, ALS_SB16_CONFIG, v | 0x80);
684 /* Select DMA0 */
685 als_mix_wr(sc, ALS_SB16_DMA_SETUP, 0x01);
687 /* Disable write on DMA_SETUP register */
688 als_mix_wr(sc, ALS_SB16_CONFIG, v & 0x7f);
690 /* Enable interrupts */
691 v = als_gcr_rd(sc, ALS_GCR_MISC);
692 als_gcr_wr(sc, ALS_GCR_MISC, v | 0x28000);
694 /* Black out GCR DMA registers */
695 for (i = 0x91; i <= 0x96; i++) {
696 als_gcr_wr(sc, i, 0);
699 /* Emulation mode */
700 v = als_gcr_rd(sc, ALS_GCR_DMA_EMULATION);
701 als_gcr_wr(sc, ALS_GCR_DMA_EMULATION, v);
702 DEB(kprintf("GCR_DMA_EMULATION 0x%08x\n", v));
703 return 0;
706 static void
707 als_uninit(struct sc_info *sc)
709 /* Disable interrupts */
710 als_gcr_wr(sc, ALS_GCR_MISC, 0);
713 /* ------------------------------------------------------------------------- */
714 /* Probe and attach card */
716 static int
717 als_pci_probe(device_t dev)
719 if (pci_get_devid(dev) == ALS_PCI_ID0) {
720 device_set_desc(dev, "Avance Logic ALS4000");
721 return BUS_PROBE_DEFAULT;
723 return ENXIO;
726 static void
727 als_resource_free(device_t dev, struct sc_info *sc)
729 if (sc->reg) {
730 bus_release_resource(dev, SYS_RES_IOPORT, sc->regid, sc->reg);
731 sc->reg = 0;
733 if (sc->ih) {
734 bus_teardown_intr(dev, sc->irq, sc->ih);
735 sc->ih = 0;
737 if (sc->irq) {
738 bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq);
739 sc->irq = 0;
741 if (sc->parent_dmat) {
742 bus_dma_tag_destroy(sc->parent_dmat);
743 sc->parent_dmat = 0;
745 if (sc->lock) {
746 snd_mtxfree(sc->lock);
747 sc->lock = NULL;
751 static int
752 als_resource_grab(device_t dev, struct sc_info *sc)
754 sc->regid = PCIR_BAR(0);
755 sc->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->regid, 0, ~0,
756 ALS_CONFIG_SPACE_BYTES, RF_ACTIVE);
757 if (sc->reg == 0) {
758 device_printf(dev, "unable to allocate register space\n");
759 goto bad;
761 sc->st = rman_get_bustag(sc->reg);
762 sc->sh = rman_get_bushandle(sc->reg);
764 sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid,
765 RF_ACTIVE | RF_SHAREABLE);
766 if (sc->irq == 0) {
767 device_printf(dev, "unable to allocate interrupt\n");
768 goto bad;
771 if (snd_setup_intr(dev, sc->irq, INTR_MPSAFE, als_intr,
772 sc, &sc->ih)) {
773 device_printf(dev, "unable to setup interrupt\n");
774 goto bad;
777 sc->bufsz = pcm_getbuffersize(dev, 4096, ALS_DEFAULT_BUFSZ, 65536);
779 if (bus_dma_tag_create(/*parent*/NULL,
780 /*alignment*/2, /*boundary*/0,
781 /*lowaddr*/BUS_SPACE_MAXADDR_24BIT,
782 /*highaddr*/BUS_SPACE_MAXADDR,
783 /*filter*/NULL, /*filterarg*/NULL,
784 /*maxsize*/sc->bufsz,
785 /*nsegments*/1, /*maxsegz*/0x3ffff,
786 /*flags*/0,
787 &sc->parent_dmat) != 0) {
788 device_printf(dev, "unable to create dma tag\n");
789 goto bad;
791 return 0;
792 bad:
793 als_resource_free(dev, sc);
794 return ENXIO;
797 static int
798 als_pci_attach(device_t dev)
800 struct sc_info *sc;
801 u_int32_t data;
802 char status[SND_STATUSLEN];
804 if ((sc = kmalloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) {
805 device_printf(dev, "cannot allocate softc\n");
806 return ENXIO;
809 sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc");
810 sc->dev = dev;
812 data = pci_read_config(dev, PCIR_COMMAND, 2);
813 data |= (PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
814 pci_write_config(dev, PCIR_COMMAND, data, 2);
816 * By default the power to the various components on the
817 * ALS4000 is entirely controlled by the pci powerstate. We
818 * could attempt finer grained control by setting GCR6.31.
820 #if __FreeBSD_version > 500000
821 if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) {
822 /* Reset the power state. */
823 device_printf(dev, "chip is in D%d power mode "
824 "-- setting to D0\n", pci_get_powerstate(dev));
825 pci_set_powerstate(dev, PCI_POWERSTATE_D0);
827 #else
828 data = pci_read_config(dev, ALS_PCI_POWERREG, 2);
829 if ((data & 0x03) != 0) {
830 device_printf(dev, "chip is in D%d power mode "
831 "-- setting to D0\n", data & 0x03);
832 data &= ~0x03;
833 pci_write_config(dev, ALS_PCI_POWERREG, data, 2);
835 #endif
837 if (als_resource_grab(dev, sc)) {
838 device_printf(dev, "failed to allocate resources\n");
839 goto bad_attach;
842 if (als_init(sc)) {
843 device_printf(dev, "failed to initialize hardware\n");
844 goto bad_attach;
847 if (mixer_init(dev, &als_mixer_class, sc)) {
848 device_printf(dev, "failed to initialize mixer\n");
849 goto bad_attach;
852 if (pcm_register(dev, sc, 1, 1)) {
853 device_printf(dev, "failed to register pcm entries\n");
854 goto bad_attach;
857 pcm_addchan(dev, PCMDIR_PLAY, &alspchan_class, sc);
858 pcm_addchan(dev, PCMDIR_REC, &alsrchan_class, sc);
860 ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s",
861 rman_get_start(sc->reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_als4000));
862 pcm_setstatus(dev, status);
863 return 0;
865 bad_attach:
866 als_resource_free(dev, sc);
867 kfree(sc, M_DEVBUF);
868 return ENXIO;
871 static int
872 als_pci_detach(device_t dev)
874 struct sc_info *sc;
875 int r;
877 r = pcm_unregister(dev);
878 if (r)
879 return r;
881 sc = pcm_getdevinfo(dev);
882 als_uninit(sc);
883 als_resource_free(dev, sc);
884 kfree(sc, M_DEVBUF);
885 return 0;
888 static int
889 als_pci_suspend(device_t dev)
891 struct sc_info *sc = pcm_getdevinfo(dev);
893 snd_mtxlock(sc->lock);
894 sc->pch.dma_was_active = als_playback_stop(&sc->pch);
895 sc->rch.dma_was_active = als_capture_stop(&sc->rch);
896 als_uninit(sc);
897 snd_mtxunlock(sc->lock);
898 return 0;
901 static int
902 als_pci_resume(device_t dev)
904 struct sc_info *sc = pcm_getdevinfo(dev);
907 snd_mtxlock(sc->lock);
908 if (als_init(sc) != 0) {
909 device_printf(dev, "unable to reinitialize the card\n");
910 snd_mtxunlock(sc->lock);
911 return ENXIO;
914 if (mixer_reinit(dev) != 0) {
915 device_printf(dev, "unable to reinitialize the mixer\n");
916 snd_mtxunlock(sc->lock);
917 return ENXIO;
920 if (sc->pch.dma_was_active) {
921 als_playback_start(&sc->pch);
924 if (sc->rch.dma_was_active) {
925 als_capture_start(&sc->rch);
927 snd_mtxunlock(sc->lock);
929 return 0;
932 static device_method_t als_methods[] = {
933 /* Device interface */
934 DEVMETHOD(device_probe, als_pci_probe),
935 DEVMETHOD(device_attach, als_pci_attach),
936 DEVMETHOD(device_detach, als_pci_detach),
937 DEVMETHOD(device_suspend, als_pci_suspend),
938 DEVMETHOD(device_resume, als_pci_resume),
939 { 0, 0 }
942 static driver_t als_driver = {
943 "pcm",
944 als_methods,
945 PCM_SOFTC_SIZE,
948 DRIVER_MODULE(snd_als4000, pci, als_driver, pcm_devclass, 0, 0);
949 MODULE_DEPEND(snd_als4000, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
950 MODULE_VERSION(snd_als4000, 1);