1139749Simp/*- 275885Sorion * Copyright (c) 2001 Orion Hodson <oho@acm.org> 375885Sorion * All rights reserved. 475885Sorion * 575885Sorion * Redistribution and use in source and binary forms, with or without 675885Sorion * modification, are permitted provided that the following conditions 775885Sorion * are met: 875885Sorion * 1. Redistributions of source code must retain the above copyright 975885Sorion * notice, this list of conditions and the following disclaimer. 1075885Sorion * 2. Redistributions in binary form must reproduce the above copyright 1175885Sorion * notice, this list of conditions and the following disclaimer in the 1275885Sorion * documentation and/or other materials provided with the distribution. 1375885Sorion * 1475885Sorion * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1575885Sorion * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1675885Sorion * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1775885Sorion * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1875885Sorion * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1975885Sorion * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2075885Sorion * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2175885Sorion * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT 2275885Sorion * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2375885Sorion * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF 2475885Sorion * SUCH DAMAGE. 2575885Sorion */ 2675885Sorion 2775885Sorion/* 2875885Sorion * als4000.c - driver for the Avance Logic ALS 4000 chipset. 2975885Sorion * 30108533Sschweikh * The ALS4000 is effectively an SB16 with a PCI interface. 3175885Sorion * 3275885Sorion * This driver derives from ALS4000a.PDF, Bart Hartgers alsa driver, and 3378362Scg * SB16 register descriptions. 3475885Sorion */ 3575885Sorion 36193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 37193640Sariff#include "opt_snd.h" 38193640Sariff#endif 39193640Sariff 4075885Sorion#include <dev/sound/pcm/sound.h> 4175885Sorion#include <dev/sound/isa/sb.h> 4275885Sorion#include <dev/sound/pci/als4000.h> 4375885Sorion 44119287Simp#include <dev/pci/pcireg.h> 45119287Simp#include <dev/pci/pcivar.h> 4675885Sorion 4775885Sorion#include "mixer_if.h" 4875885Sorion 4982180ScgSND_DECLARE_FILE("$FreeBSD$"); 5082180Scg 5175885Sorion/* Debugging macro's */ 5275885Sorion#undef DEB 5375885Sorion#ifndef DEB 5475885Sorion#define DEB(x) /* x */ 5575885Sorion#endif /* DEB */ 5675885Sorion 5784771Sorion#define ALS_DEFAULT_BUFSZ 16384 5884771Sorion 5975885Sorion/* ------------------------------------------------------------------------- */ 6075885Sorion/* Structures */ 6175885Sorion 6275885Sorionstruct sc_info; 6375885Sorion 6475885Sorionstruct sc_chinfo { 6575885Sorion struct sc_info *parent; 6675885Sorion struct pcm_channel *channel; 6775885Sorion struct snd_dbuf *buffer; 6875885Sorion u_int32_t format, speed, phys_buf, bps; 6975885Sorion u_int32_t dma_active:1, dma_was_active:1; 7075885Sorion u_int8_t gcr_fifo_status; 7175885Sorion int dir; 7275885Sorion}; 7375885Sorion 7475885Sorionstruct sc_info { 7575885Sorion device_t dev; 7675885Sorion bus_space_tag_t st; 7775885Sorion bus_space_handle_t sh; 7875885Sorion bus_dma_tag_t parent_dmat; 7975885Sorion struct resource *reg, *irq; 8075885Sorion int regid, irqid; 8175885Sorion void *ih; 82148588Snetchild struct mtx *lock; 8384771Sorion 8484771Sorion unsigned int bufsz; 8575885Sorion struct sc_chinfo pch, rch; 8675885Sorion}; 8775885Sorion 8875885Sorion/* Channel caps */ 8975885Sorion 9075885Sorionstatic u_int32_t als_format[] = { 91193640Sariff SND_FORMAT(AFMT_U8, 1, 0), 92193640Sariff SND_FORMAT(AFMT_U8, 2, 0), 93193640Sariff SND_FORMAT(AFMT_S16_LE, 1, 0), 94193640Sariff SND_FORMAT(AFMT_S16_LE, 2, 0), 9575885Sorion 0 9675885Sorion}; 9775885Sorion 98148588Snetchild/* 99148588Snetchild * I don't believe this rotten soundcard can do 48k, really, 100148588Snetchild * trust me. 101148588Snetchild */ 102148588Snetchildstatic struct pcmchan_caps als_caps = { 4000, 44100, als_format, 0 }; 10375885Sorion 10475885Sorion/* ------------------------------------------------------------------------- */ 10575885Sorion/* Register Utilities */ 10675885Sorion 10778362Scgstatic u_int32_t 10875885Sorionals_gcr_rd(struct sc_info *sc, int index) 10975885Sorion{ 11075885Sorion bus_space_write_1(sc->st, sc->sh, ALS_GCR_INDEX, index); 11175885Sorion return bus_space_read_4(sc->st, sc->sh, ALS_GCR_DATA); 11275885Sorion} 11375885Sorion 11475885Sorionstatic void 11575885Sorionals_gcr_wr(struct sc_info *sc, int index, int data) 11675885Sorion{ 11775885Sorion bus_space_write_1(sc->st, sc->sh, ALS_GCR_INDEX, index); 11875885Sorion bus_space_write_4(sc->st, sc->sh, ALS_GCR_DATA, data); 11975885Sorion} 12075885Sorion 12175885Sorionstatic u_int8_t 12275885Sorionals_intr_rd(struct sc_info *sc) 12375885Sorion{ 12475885Sorion return bus_space_read_1(sc->st, sc->sh, ALS_SB_MPU_IRQ); 12575885Sorion} 12675885Sorion 12775885Sorionstatic void 12875885Sorionals_intr_wr(struct sc_info *sc, u_int8_t data) 12975885Sorion{ 13075885Sorion bus_space_write_1(sc->st, sc->sh, ALS_SB_MPU_IRQ, data); 13175885Sorion} 13275885Sorion 13375885Sorionstatic u_int8_t 13475885Sorionals_mix_rd(struct sc_info *sc, u_int8_t index) 13575885Sorion{ 13675885Sorion bus_space_write_1(sc->st, sc->sh, ALS_MIXER_INDEX, index); 13775885Sorion return bus_space_read_1(sc->st, sc->sh, ALS_MIXER_DATA); 13875885Sorion} 13975885Sorion 14075885Sorionstatic void 14175885Sorionals_mix_wr(struct sc_info *sc, u_int8_t index, u_int8_t data) 14275885Sorion{ 14375885Sorion bus_space_write_1(sc->st, sc->sh, ALS_MIXER_INDEX, index); 14475885Sorion bus_space_write_1(sc->st, sc->sh, ALS_MIXER_DATA, data); 14575885Sorion} 14675885Sorion 14775885Sorionstatic void 14875885Sorionals_esp_wr(struct sc_info *sc, u_int8_t data) 14975885Sorion{ 15075885Sorion u_int32_t tries, v; 15175885Sorion 15275885Sorion tries = 1000; 15375885Sorion do { 15475885Sorion v = bus_space_read_1(sc->st, sc->sh, ALS_ESP_WR_STATUS); 15575885Sorion if (~v & 0x80) 15675885Sorion break; 15775885Sorion DELAY(20); 15875885Sorion } while (--tries != 0); 15975885Sorion 16078362Scg if (tries == 0) 16175885Sorion device_printf(sc->dev, "als_esp_wr timeout"); 16278362Scg 16375885Sorion bus_space_write_1(sc->st, sc->sh, ALS_ESP_WR_DATA, data); 16475885Sorion} 16575885Sorion 16675885Sorionstatic int 16775885Sorionals_esp_reset(struct sc_info *sc) 16875885Sorion{ 16975885Sorion u_int32_t tries, u, v; 17075885Sorion 17175885Sorion bus_space_write_1(sc->st, sc->sh, ALS_ESP_RST, 1); 17275885Sorion DELAY(10); 17375885Sorion bus_space_write_1(sc->st, sc->sh, ALS_ESP_RST, 0); 17475885Sorion DELAY(30); 17575885Sorion 17675885Sorion tries = 1000; 17775885Sorion do { 17875885Sorion u = bus_space_read_1(sc->st, sc->sh, ALS_ESP_RD_STATUS8); 17975885Sorion if (u & 0x80) { 18075885Sorion v = bus_space_read_1(sc->st, sc->sh, ALS_ESP_RD_DATA); 18178362Scg if (v == 0xaa) 18275885Sorion return 0; 18378362Scg else 18475885Sorion break; 18575885Sorion } 18675885Sorion DELAY(20); 18775885Sorion } while (--tries != 0); 18875885Sorion 18978362Scg if (tries == 0) 19075885Sorion device_printf(sc->dev, "als_esp_reset timeout"); 19175885Sorion return 1; 19275885Sorion} 19375885Sorion 19475885Sorionstatic u_int8_t 19575885Sorionals_ack_read(struct sc_info *sc, u_int8_t addr) 19675885Sorion{ 19775885Sorion u_int8_t r = bus_space_read_1(sc->st, sc->sh, addr); 19875885Sorion return r; 19975885Sorion} 20075885Sorion 20175885Sorion/* ------------------------------------------------------------------------- */ 20275885Sorion/* Common pcm channel implementation */ 20375885Sorion 20475885Sorionstatic void * 20578362Scgalschan_init(kobj_t obj, void *devinfo, 20675885Sorion struct snd_dbuf *b, struct pcm_channel *c, int dir) 20775885Sorion{ 20875885Sorion struct sc_info *sc = devinfo; 20975885Sorion struct sc_chinfo *ch; 21075885Sorion 211148588Snetchild snd_mtxlock(sc->lock); 21275885Sorion if (dir == PCMDIR_PLAY) { 21375885Sorion ch = &sc->pch; 21475885Sorion ch->gcr_fifo_status = ALS_GCR_FIFO0_STATUS; 21575885Sorion } else { 21675885Sorion ch = &sc->rch; 21775885Sorion ch->gcr_fifo_status = ALS_GCR_FIFO1_STATUS; 21875885Sorion } 21975885Sorion ch->dir = dir; 22075885Sorion ch->parent = sc; 22175885Sorion ch->channel = c; 22275885Sorion ch->bps = 1; 223193640Sariff ch->format = SND_FORMAT(AFMT_U8, 1, 0); 22475885Sorion ch->speed = DSP_DEFAULT_SPEED; 22575885Sorion ch->buffer = b; 226148592Snetchild snd_mtxunlock(sc->lock); 227148592Snetchild 228168847Sariff if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0) 22975885Sorion return NULL; 230148592Snetchild 23175885Sorion return ch; 23275885Sorion} 23375885Sorion 23475885Sorionstatic int 23578362Scgalschan_setformat(kobj_t obj, void *data, u_int32_t format) 23675885Sorion{ 23775885Sorion struct sc_chinfo *ch = data; 23875885Sorion 23975885Sorion ch->format = format; 24075885Sorion return 0; 24175885Sorion} 24275885Sorion 243193640Sariffstatic u_int32_t 24475885Sorionalschan_setspeed(kobj_t obj, void *data, u_int32_t speed) 24575885Sorion{ 24675885Sorion struct sc_chinfo *ch = data, *other; 24775885Sorion struct sc_info *sc = ch->parent; 24875885Sorion 24975885Sorion other = (ch->dir == PCMDIR_PLAY) ? &sc->rch : &sc->pch; 25078362Scg 25175885Sorion /* Deny request if other dma channel is active */ 25275885Sorion if (other->dma_active) { 25375885Sorion ch->speed = other->speed; 25475885Sorion return other->speed; 25575885Sorion } 25675885Sorion 25775885Sorion ch->speed = speed; 25875885Sorion return speed; 25975885Sorion} 26075885Sorion 261193640Sariffstatic u_int32_t 26275885Sorionalschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 26375885Sorion{ 26475885Sorion struct sc_chinfo *ch = data; 26584771Sorion struct sc_info *sc = ch->parent; 26675885Sorion 26784771Sorion if (blocksize > sc->bufsz / 2) { 26884771Sorion blocksize = sc->bufsz / 2; 26975885Sorion } 27075885Sorion sndbuf_resize(ch->buffer, 2, blocksize); 27182834Sorion return blocksize; 27275885Sorion} 27375885Sorion 274193640Sariffstatic u_int32_t 27575885Sorionalschan_getptr(kobj_t obj, void *data) 27675885Sorion{ 27775885Sorion struct sc_chinfo *ch = data; 278150980Snetchild struct sc_info *sc = ch->parent; 27975885Sorion int32_t pos, sz; 28075885Sorion 281150980Snetchild snd_mtxlock(sc->lock); 28275885Sorion pos = als_gcr_rd(ch->parent, ch->gcr_fifo_status) & 0xffff; 283150980Snetchild snd_mtxunlock(sc->lock); 28475885Sorion sz = sndbuf_getsize(ch->buffer); 28575885Sorion return (2 * sz - pos - 1) % sz; 28675885Sorion} 28775885Sorion 28875885Sorionstatic struct pcmchan_caps* 28975885Sorionalschan_getcaps(kobj_t obj, void *data) 29075885Sorion{ 29175885Sorion return &als_caps; 29275885Sorion} 29375885Sorion 29475885Sorionstatic void 29575885Sorionals_set_speed(struct sc_chinfo *ch) 29675885Sorion{ 29775885Sorion struct sc_info *sc = ch->parent; 29875885Sorion struct sc_chinfo *other; 29975885Sorion 30075885Sorion other = (ch->dir == PCMDIR_PLAY) ? &sc->rch : &sc->pch; 30175885Sorion if (other->dma_active == 0) { 30275885Sorion als_esp_wr(sc, ALS_ESP_SAMPLE_RATE); 30375885Sorion als_esp_wr(sc, ch->speed >> 8); 30475885Sorion als_esp_wr(sc, ch->speed & 0xff); 30575885Sorion } else { 30678362Scg DEB(printf("speed locked at %d (tried %d)\n", 30775885Sorion other->speed, ch->speed)); 30875885Sorion } 30975885Sorion} 31075885Sorion 31175885Sorion/* ------------------------------------------------------------------------- */ 31275885Sorion/* Playback channel implementation */ 31375885Sorion 31475885Sorion#define ALS_8BIT_CMD(x, y) { (x), (y), DSP_DMA8, DSP_CMD_DMAPAUSE_8 } 31575885Sorion#define ALS_16BIT_CMD(x, y) { (x), (y), DSP_DMA16, DSP_CMD_DMAPAUSE_16 } 31675885Sorion 31775885Sorionstruct playback_command { 31875885Sorion u_int32_t pcm_format; /* newpcm format */ 31975885Sorion u_int8_t format_val; /* sb16 format value */ 32075885Sorion u_int8_t dma_prog; /* sb16 dma program */ 32175885Sorion u_int8_t dma_stop; /* sb16 stop register */ 32275885Sorion} static const playback_cmds[] = { 323193640Sariff ALS_8BIT_CMD(SND_FORMAT(AFMT_U8, 1, 0), DSP_MODE_U8MONO), 324193640Sariff ALS_8BIT_CMD(SND_FORMAT(AFMT_U8, 2, 0), DSP_MODE_U8STEREO), 325193640Sariff ALS_16BIT_CMD(SND_FORMAT(AFMT_S16_LE, 1, 0), DSP_MODE_S16MONO), 326193640Sariff ALS_16BIT_CMD(SND_FORMAT(AFMT_S16_LE, 2, 0), DSP_MODE_S16STEREO), 32775885Sorion}; 32875885Sorion 32975885Sorionstatic const struct playback_command* 33075885Sorionals_get_playback_command(u_int32_t format) 33175885Sorion{ 33275885Sorion u_int32_t i, n; 33375885Sorion 33475885Sorion n = sizeof(playback_cmds) / sizeof(playback_cmds[0]); 33575885Sorion for (i = 0; i < n; i++) { 33675885Sorion if (playback_cmds[i].pcm_format == format) { 33775885Sorion return &playback_cmds[i]; 33875885Sorion } 33975885Sorion } 34078362Scg DEB(printf("als_get_playback_command: invalid format 0x%08x\n", 34175885Sorion format)); 34275885Sorion return &playback_cmds[0]; 34375885Sorion} 34475885Sorion 34575885Sorionstatic void 34675885Sorionals_playback_start(struct sc_chinfo *ch) 34775885Sorion{ 34875885Sorion const struct playback_command *p; 34975885Sorion struct sc_info *sc = ch->parent; 35075885Sorion u_int32_t buf, bufsz, count, dma_prog; 35175885Sorion 352111183Scognet buf = sndbuf_getbufaddr(ch->buffer); 35375885Sorion bufsz = sndbuf_getsize(ch->buffer); 35475885Sorion count = bufsz / 2; 35575885Sorion if (ch->format & AFMT_16BIT) 35675885Sorion count /= 2; 35775885Sorion count--; 35875885Sorion 35975885Sorion als_esp_wr(sc, DSP_CMD_SPKON); 36075885Sorion als_set_speed(ch); 36175885Sorion 36275885Sorion als_gcr_wr(sc, ALS_GCR_DMA0_START, buf); 36375885Sorion als_gcr_wr(sc, ALS_GCR_DMA0_MODE, (bufsz - 1) | 0x180000); 36475885Sorion 36575885Sorion p = als_get_playback_command(ch->format); 36675885Sorion dma_prog = p->dma_prog | DSP_F16_DAC | DSP_F16_AUTO | DSP_F16_FIFO_ON; 36775885Sorion 36875885Sorion als_esp_wr(sc, dma_prog); 36975885Sorion als_esp_wr(sc, p->format_val); 37075885Sorion als_esp_wr(sc, count & 0xff); 37175885Sorion als_esp_wr(sc, count >> 8); 37278362Scg 37375885Sorion ch->dma_active = 1; 37475885Sorion} 37575885Sorion 37675885Sorionstatic int 37775885Sorionals_playback_stop(struct sc_chinfo *ch) 37875885Sorion{ 37975885Sorion const struct playback_command *p; 38075885Sorion struct sc_info *sc = ch->parent; 38175885Sorion u_int32_t active; 38275885Sorion 38375885Sorion active = ch->dma_active; 38475885Sorion if (active) { 38575885Sorion p = als_get_playback_command(ch->format); 38675885Sorion als_esp_wr(sc, p->dma_stop); 38775885Sorion } 38875885Sorion ch->dma_active = 0; 38975885Sorion return active; 39075885Sorion} 39175885Sorion 39275885Sorionstatic int 39375885Sorionalspchan_trigger(kobj_t obj, void *data, int go) 39475885Sorion{ 39575885Sorion struct sc_chinfo *ch = data; 396150980Snetchild struct sc_info *sc = ch->parent; 39775885Sorion 398170521Sariff if (!PCMTRIG_COMMON(go)) 399170521Sariff return 0; 400170521Sariff 401150980Snetchild snd_mtxlock(sc->lock); 40275885Sorion switch(go) { 40375885Sorion case PCMTRIG_START: 40475885Sorion als_playback_start(ch); 40575885Sorion break; 406170521Sariff case PCMTRIG_STOP: 40775885Sorion case PCMTRIG_ABORT: 40875885Sorion als_playback_stop(ch); 40975885Sorion break; 410170521Sariff default: 411170521Sariff break; 41275885Sorion } 413150980Snetchild snd_mtxunlock(sc->lock); 41475885Sorion return 0; 41575885Sorion} 41675885Sorion 41775885Sorionstatic kobj_method_t alspchan_methods[] = { 41875885Sorion KOBJMETHOD(channel_init, alschan_init), 41975885Sorion KOBJMETHOD(channel_setformat, alschan_setformat), 42075885Sorion KOBJMETHOD(channel_setspeed, alschan_setspeed), 42175885Sorion KOBJMETHOD(channel_setblocksize, alschan_setblocksize), 42275885Sorion KOBJMETHOD(channel_trigger, alspchan_trigger), 42375885Sorion KOBJMETHOD(channel_getptr, alschan_getptr), 42475885Sorion KOBJMETHOD(channel_getcaps, alschan_getcaps), 425193640Sariff KOBJMETHOD_END 42675885Sorion}; 42775885SorionCHANNEL_DECLARE(alspchan); 42875885Sorion 42975885Sorion/* ------------------------------------------------------------------------- */ 43075885Sorion/* Capture channel implementation */ 43175885Sorion 43275885Sorionstatic u_int8_t 43375885Sorionals_get_fifo_format(struct sc_info *sc, u_int32_t format) 43475885Sorion{ 43575885Sorion switch (format) { 436193640Sariff case SND_FORMAT(AFMT_U8, 1, 0): 43775885Sorion return ALS_FIFO1_8BIT; 438193640Sariff case SND_FORMAT(AFMT_U8, 2, 0): 43975885Sorion return ALS_FIFO1_8BIT | ALS_FIFO1_STEREO; 440193640Sariff case SND_FORMAT(AFMT_S16_LE, 1, 0): 44175885Sorion return ALS_FIFO1_SIGNED; 442193640Sariff case SND_FORMAT(AFMT_S16_LE, 2, 0): 44375885Sorion return ALS_FIFO1_SIGNED | ALS_FIFO1_STEREO; 44475885Sorion } 44575885Sorion device_printf(sc->dev, "format not found: 0x%08x\n", format); 44675885Sorion return ALS_FIFO1_8BIT; 44775885Sorion} 44875885Sorion 44975885Sorionstatic void 45075885Sorionals_capture_start(struct sc_chinfo *ch) 45175885Sorion{ 45275885Sorion struct sc_info *sc = ch->parent; 45375885Sorion u_int32_t buf, bufsz, count, dma_prog; 45475885Sorion 455111183Scognet buf = sndbuf_getbufaddr(ch->buffer); 45675885Sorion bufsz = sndbuf_getsize(ch->buffer); 45775885Sorion count = bufsz / 2; 45875885Sorion if (ch->format & AFMT_16BIT) 45975885Sorion count /= 2; 46075885Sorion count--; 46175885Sorion 46275885Sorion als_esp_wr(sc, DSP_CMD_SPKON); 46375885Sorion als_set_speed(ch); 46475885Sorion 46575885Sorion als_gcr_wr(sc, ALS_GCR_FIFO1_START, buf); 46675885Sorion als_gcr_wr(sc, ALS_GCR_FIFO1_COUNT, (bufsz - 1)); 46775885Sorion 46875885Sorion als_mix_wr(sc, ALS_FIFO1_LENGTH_LO, count & 0xff); 46975885Sorion als_mix_wr(sc, ALS_FIFO1_LENGTH_HI, count >> 8); 47075885Sorion 47175885Sorion dma_prog = ALS_FIFO1_RUN | als_get_fifo_format(sc, ch->format); 47275885Sorion als_mix_wr(sc, ALS_FIFO1_CONTROL, dma_prog); 47375885Sorion 47475885Sorion ch->dma_active = 1; 47575885Sorion} 47675885Sorion 47775885Sorionstatic int 47875885Sorionals_capture_stop(struct sc_chinfo *ch) 47975885Sorion{ 48075885Sorion struct sc_info *sc = ch->parent; 48175885Sorion u_int32_t active; 48275885Sorion 48375885Sorion active = ch->dma_active; 48475885Sorion if (active) { 48575885Sorion als_mix_wr(sc, ALS_FIFO1_CONTROL, ALS_FIFO1_STOP); 48675885Sorion } 48775885Sorion ch->dma_active = 0; 48875885Sorion return active; 48975885Sorion} 49075885Sorion 49175885Sorionstatic int 49275885Sorionalsrchan_trigger(kobj_t obj, void *data, int go) 49375885Sorion{ 49475885Sorion struct sc_chinfo *ch = data; 495150980Snetchild struct sc_info *sc = ch->parent; 49675885Sorion 497150980Snetchild snd_mtxlock(sc->lock); 49875885Sorion switch(go) { 49975885Sorion case PCMTRIG_START: 50075885Sorion als_capture_start(ch); 50175885Sorion break; 502170521Sariff case PCMTRIG_STOP: 50375885Sorion case PCMTRIG_ABORT: 50475885Sorion als_capture_stop(ch); 50575885Sorion break; 50675885Sorion } 507150980Snetchild snd_mtxunlock(sc->lock); 50875885Sorion return 0; 50975885Sorion} 51075885Sorion 51175885Sorionstatic kobj_method_t alsrchan_methods[] = { 51275885Sorion KOBJMETHOD(channel_init, alschan_init), 51375885Sorion KOBJMETHOD(channel_setformat, alschan_setformat), 51475885Sorion KOBJMETHOD(channel_setspeed, alschan_setspeed), 51575885Sorion KOBJMETHOD(channel_setblocksize, alschan_setblocksize), 51675885Sorion KOBJMETHOD(channel_trigger, alsrchan_trigger), 51775885Sorion KOBJMETHOD(channel_getptr, alschan_getptr), 51875885Sorion KOBJMETHOD(channel_getcaps, alschan_getcaps), 519193640Sariff KOBJMETHOD_END 52075885Sorion}; 52175885SorionCHANNEL_DECLARE(alsrchan); 52275885Sorion 52375885Sorion/* ------------------------------------------------------------------------- */ 52475885Sorion/* Mixer related */ 52575885Sorion 52675885Sorion/* 52775885Sorion * ALS4000 has an sb16 mixer, with some additional controls that we do 52875885Sorion * not yet a means to support. 52975885Sorion */ 53075885Sorion 53175885Sorionstruct sb16props { 53275885Sorion u_int8_t lreg; 53375885Sorion u_int8_t rreg; 53475885Sorion u_int8_t bits; 53575885Sorion u_int8_t oselect; 53675885Sorion u_int8_t iselect; /* left input mask */ 53775885Sorion} static const amt[SOUND_MIXER_NRDEVICES] = { 53875885Sorion [SOUND_MIXER_VOLUME] = { 0x30, 0x31, 5, 0x00, 0x00 }, 53975885Sorion [SOUND_MIXER_PCM] = { 0x32, 0x33, 5, 0x00, 0x00 }, 54075885Sorion [SOUND_MIXER_SYNTH] = { 0x34, 0x35, 5, 0x60, 0x40 }, 54175885Sorion [SOUND_MIXER_CD] = { 0x36, 0x37, 5, 0x06, 0x04 }, 54275885Sorion [SOUND_MIXER_LINE] = { 0x38, 0x39, 5, 0x18, 0x10 }, 54375885Sorion [SOUND_MIXER_MIC] = { 0x3a, 0x00, 5, 0x01, 0x01 }, 54475885Sorion [SOUND_MIXER_SPEAKER] = { 0x3b, 0x00, 2, 0x00, 0x00 }, 54575885Sorion [SOUND_MIXER_IGAIN] = { 0x3f, 0x40, 2, 0x00, 0x00 }, 54675885Sorion [SOUND_MIXER_OGAIN] = { 0x41, 0x42, 2, 0x00, 0x00 }, 54775885Sorion /* The following have register values but no h/w implementation */ 54875885Sorion [SOUND_MIXER_TREBLE] = { 0x44, 0x45, 4, 0x00, 0x00 }, 54975885Sorion [SOUND_MIXER_BASS] = { 0x46, 0x47, 4, 0x00, 0x00 } 55075885Sorion}; 55175885Sorion 55275885Sorionstatic int 55378362Scgalsmix_init(struct snd_mixer *m) 55475885Sorion{ 55575885Sorion u_int32_t i, v; 55675885Sorion 55775885Sorion for (i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) { 55875885Sorion if (amt[i].bits) v |= 1 << i; 55975885Sorion } 56075885Sorion mix_setdevs(m, v); 56175885Sorion 56275885Sorion for (i = v = 0; i < SOUND_MIXER_NRDEVICES; i++) { 56375885Sorion if (amt[i].iselect) v |= 1 << i; 56478362Scg } 56575885Sorion mix_setrecdevs(m, v); 56675885Sorion return 0; 56775885Sorion} 56875885Sorion 56975885Sorionstatic int 57075885Sorionalsmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 57175885Sorion{ 57275885Sorion struct sc_info *sc = mix_getdevinfo(m); 57375885Sorion u_int32_t r, l, v, mask; 57475885Sorion 57575885Sorion /* Fill upper n bits in mask with 1's */ 57675885Sorion mask = ((1 << amt[dev].bits) - 1) << (8 - amt[dev].bits); 57775885Sorion 57875885Sorion l = (left * mask / 100) & mask; 57975885Sorion v = als_mix_rd(sc, amt[dev].lreg) & ~mask; 58075885Sorion als_mix_wr(sc, amt[dev].lreg, l | v); 58178362Scg 58275885Sorion if (amt[dev].rreg) { 58375885Sorion r = (right * mask / 100) & mask; 58475885Sorion v = als_mix_rd(sc, amt[dev].rreg) & ~mask; 58575885Sorion als_mix_wr(sc, amt[dev].rreg, r | v); 58675885Sorion } else { 58775885Sorion r = 0; 58875885Sorion } 58978362Scg 59075885Sorion /* Zero gain does not mute channel from output, but this does. */ 59175885Sorion v = als_mix_rd(sc, SB16_OMASK); 59275885Sorion if (l == 0 && r == 0) { 59375885Sorion v &= ~amt[dev].oselect; 59475885Sorion } else { 59575885Sorion v |= amt[dev].oselect; 59675885Sorion } 59775885Sorion als_mix_wr(sc, SB16_OMASK, v); 59875885Sorion return 0; 59975885Sorion} 60075885Sorion 601193640Sariffstatic u_int32_t 60275885Sorionalsmix_setrecsrc(struct snd_mixer *m, u_int32_t src) 60375885Sorion{ 60475885Sorion struct sc_info *sc = mix_getdevinfo(m); 605152151Sariff u_int32_t i, l, r; 60675885Sorion 60775885Sorion for (i = l = r = 0; i < SOUND_MIXER_NRDEVICES; i++) { 60875885Sorion if (src & (1 << i)) { 609152151Sariff if (amt[i].iselect == 1) { /* microphone */ 610152151Sariff l |= amt[i].iselect; 611152151Sariff r |= amt[i].iselect; 612152151Sariff } else { 613152151Sariff l |= amt[i].iselect; 614152151Sariff r |= amt[i].iselect >> 1; 615152151Sariff } 61675885Sorion } 61775885Sorion } 61875885Sorion 619152151Sariff als_mix_wr(sc, SB16_IMASK_L, l); 620152151Sariff als_mix_wr(sc, SB16_IMASK_R, r); 62175885Sorion return src; 62275885Sorion} 62375885Sorion 62475885Sorionstatic kobj_method_t als_mixer_methods[] = { 62575885Sorion KOBJMETHOD(mixer_init, alsmix_init), 62675885Sorion KOBJMETHOD(mixer_set, alsmix_set), 62775885Sorion KOBJMETHOD(mixer_setrecsrc, alsmix_setrecsrc), 628193640Sariff KOBJMETHOD_END 62975885Sorion}; 63075885SorionMIXER_DECLARE(als_mixer); 63175885Sorion 63275885Sorion/* ------------------------------------------------------------------------- */ 63375885Sorion/* Interrupt Handler */ 63475885Sorion 63575885Sorionstatic void 63675885Sorionals_intr(void *p) 63775885Sorion{ 63875885Sorion struct sc_info *sc = (struct sc_info *)p; 63975885Sorion u_int8_t intr, sb_status; 64078362Scg 641148588Snetchild snd_mtxlock(sc->lock); 64275885Sorion intr = als_intr_rd(sc); 64378362Scg 644148588Snetchild if (intr & 0x80) { 645148588Snetchild snd_mtxunlock(sc->lock); 64675885Sorion chn_intr(sc->pch.channel); 647148588Snetchild snd_mtxlock(sc->lock); 648148588Snetchild } 64978362Scg 650148588Snetchild if (intr & 0x40) { 651148588Snetchild snd_mtxunlock(sc->lock); 65275885Sorion chn_intr(sc->rch.channel); 653148588Snetchild snd_mtxlock(sc->lock); 654148588Snetchild } 65578362Scg 65675885Sorion /* ACK interrupt in PCI core */ 65775885Sorion als_intr_wr(sc, intr); 65875885Sorion 65975885Sorion /* ACK interrupt in SB core */ 66075885Sorion sb_status = als_mix_rd(sc, IRQ_STAT); 66175885Sorion 66275885Sorion if (sb_status & ALS_IRQ_STATUS8) 66375885Sorion als_ack_read(sc, ALS_ESP_RD_STATUS8); 66475885Sorion if (sb_status & ALS_IRQ_STATUS16) 66575885Sorion als_ack_read(sc, ALS_ESP_RD_STATUS16); 66675885Sorion if (sb_status & ALS_IRQ_MPUIN) 66775885Sorion als_ack_read(sc, ALS_MIDI_DATA); 66875885Sorion if (sb_status & ALS_IRQ_CR1E) 66975885Sorion als_ack_read(sc, ALS_CR1E_ACK_PORT); 670148588Snetchild 671148588Snetchild snd_mtxunlock(sc->lock); 67275885Sorion return; 67378362Scg} 67475885Sorion 67575885Sorion/* ------------------------------------------------------------------------- */ 67675885Sorion/* H/W initialization */ 67775885Sorion 67875885Sorionstatic int 67975885Sorionals_init(struct sc_info *sc) 68075885Sorion{ 68175885Sorion u_int32_t i, v; 68275885Sorion 68375885Sorion /* Reset Chip */ 68475885Sorion if (als_esp_reset(sc)) { 68575885Sorion return 1; 68675885Sorion } 68775885Sorion 68875885Sorion /* Enable write on DMA_SETUP register */ 68975885Sorion v = als_mix_rd(sc, ALS_SB16_CONFIG); 69075885Sorion als_mix_wr(sc, ALS_SB16_CONFIG, v | 0x80); 69178362Scg 69275885Sorion /* Select DMA0 */ 69375885Sorion als_mix_wr(sc, ALS_SB16_DMA_SETUP, 0x01); 69478362Scg 69575885Sorion /* Disable write on DMA_SETUP register */ 69675885Sorion als_mix_wr(sc, ALS_SB16_CONFIG, v & 0x7f); 69775885Sorion 69875885Sorion /* Enable interrupts */ 69975885Sorion v = als_gcr_rd(sc, ALS_GCR_MISC); 70075885Sorion als_gcr_wr(sc, ALS_GCR_MISC, v | 0x28000); 70175885Sorion 70275885Sorion /* Black out GCR DMA registers */ 70375885Sorion for (i = 0x91; i <= 0x96; i++) { 70475885Sorion als_gcr_wr(sc, i, 0); 70575885Sorion } 70675885Sorion 70775885Sorion /* Emulation mode */ 70875885Sorion v = als_gcr_rd(sc, ALS_GCR_DMA_EMULATION); 70975885Sorion als_gcr_wr(sc, ALS_GCR_DMA_EMULATION, v); 71075885Sorion DEB(printf("GCR_DMA_EMULATION 0x%08x\n", v)); 71175885Sorion return 0; 71275885Sorion} 71375885Sorion 71475885Sorionstatic void 71575885Sorionals_uninit(struct sc_info *sc) 71675885Sorion{ 71775885Sorion /* Disable interrupts */ 71875885Sorion als_gcr_wr(sc, ALS_GCR_MISC, 0); 71975885Sorion} 72075885Sorion 72175885Sorion/* ------------------------------------------------------------------------- */ 72275885Sorion/* Probe and attach card */ 72375885Sorion 72475885Sorionstatic int 72575885Sorionals_pci_probe(device_t dev) 72675885Sorion{ 72775885Sorion if (pci_get_devid(dev) == ALS_PCI_ID0) { 72875885Sorion device_set_desc(dev, "Avance Logic ALS4000"); 729142890Simp return BUS_PROBE_DEFAULT; 73075885Sorion } 73175885Sorion return ENXIO; 73275885Sorion} 73375885Sorion 73475885Sorionstatic void 73575885Sorionals_resource_free(device_t dev, struct sc_info *sc) 73675885Sorion{ 73775885Sorion if (sc->reg) { 73875885Sorion bus_release_resource(dev, SYS_RES_IOPORT, sc->regid, sc->reg); 73975885Sorion sc->reg = 0; 74075885Sorion } 74175885Sorion if (sc->ih) { 74275885Sorion bus_teardown_intr(dev, sc->irq, sc->ih); 74375885Sorion sc->ih = 0; 74475885Sorion } 74575885Sorion if (sc->irq) { 74675885Sorion bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); 74775885Sorion sc->irq = 0; 74875885Sorion } 74975885Sorion if (sc->parent_dmat) { 75075885Sorion bus_dma_tag_destroy(sc->parent_dmat); 75175885Sorion sc->parent_dmat = 0; 75275885Sorion } 753148588Snetchild if (sc->lock) { 754148588Snetchild snd_mtxfree(sc->lock); 755148588Snetchild sc->lock = NULL; 756148588Snetchild } 75775885Sorion} 75875885Sorion 75975885Sorionstatic int 76075885Sorionals_resource_grab(device_t dev, struct sc_info *sc) 76175885Sorion{ 762119690Sjhb sc->regid = PCIR_BAR(0); 76375885Sorion sc->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->regid, 0, ~0, 76475885Sorion ALS_CONFIG_SPACE_BYTES, RF_ACTIVE); 76575885Sorion if (sc->reg == 0) { 76675885Sorion device_printf(dev, "unable to allocate register space\n"); 76775885Sorion goto bad; 76875885Sorion } 76975885Sorion sc->st = rman_get_bustag(sc->reg); 77075885Sorion sc->sh = rman_get_bushandle(sc->reg); 77175885Sorion 772127135Snjl sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, 773127135Snjl RF_ACTIVE | RF_SHAREABLE); 77475885Sorion if (sc->irq == 0) { 77575885Sorion device_printf(dev, "unable to allocate interrupt\n"); 77675885Sorion goto bad; 77775885Sorion } 77875885Sorion 779148588Snetchild if (snd_setup_intr(dev, sc->irq, INTR_MPSAFE, als_intr, 78075885Sorion sc, &sc->ih)) { 78175885Sorion device_printf(dev, "unable to setup interrupt\n"); 78275885Sorion goto bad; 78375885Sorion } 78475885Sorion 78584771Sorion sc->bufsz = pcm_getbuffersize(dev, 4096, ALS_DEFAULT_BUFSZ, 65536); 78684771Sorion 787166904Snetchild if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), 78875885Sorion /*alignment*/2, /*boundary*/0, 78975885Sorion /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, 79075885Sorion /*highaddr*/BUS_SPACE_MAXADDR, 79175885Sorion /*filter*/NULL, /*filterarg*/NULL, 79284771Sorion /*maxsize*/sc->bufsz, 79375885Sorion /*nsegments*/1, /*maxsegz*/0x3ffff, 794148588Snetchild /*flags*/0, /*lockfunc*/NULL, 795148588Snetchild /*lockarg*/NULL, &sc->parent_dmat) != 0) { 79675885Sorion device_printf(dev, "unable to create dma tag\n"); 79775885Sorion goto bad; 79875885Sorion } 79975885Sorion return 0; 80075885Sorion bad: 80175885Sorion als_resource_free(dev, sc); 80275885Sorion return ENXIO; 80375885Sorion} 80475885Sorion 80575885Sorionstatic int 80675885Sorionals_pci_attach(device_t dev) 80775885Sorion{ 80875885Sorion struct sc_info *sc; 80975885Sorion char status[SND_STATUSLEN]; 81075885Sorion 811170873Sariff sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); 812167608Sariff sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_als4000 softc"); 81375885Sorion sc->dev = dev; 81475885Sorion 815254306Sscottl pci_enable_busmaster(dev); 81675885Sorion /* 81775885Sorion * By default the power to the various components on the 81875885Sorion * ALS4000 is entirely controlled by the pci powerstate. We 81975885Sorion * could attempt finer grained control by setting GCR6.31. 82075885Sorion */ 82175885Sorion#if __FreeBSD_version > 500000 82275885Sorion if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { 82375885Sorion /* Reset the power state. */ 82475885Sorion device_printf(dev, "chip is in D%d power mode " 82575885Sorion "-- setting to D0\n", pci_get_powerstate(dev)); 82675885Sorion pci_set_powerstate(dev, PCI_POWERSTATE_D0); 82775885Sorion } 82875885Sorion#else 82975885Sorion data = pci_read_config(dev, ALS_PCI_POWERREG, 2); 83075885Sorion if ((data & 0x03) != 0) { 83175885Sorion device_printf(dev, "chip is in D%d power mode " 83275885Sorion "-- setting to D0\n", data & 0x03); 83375885Sorion data &= ~0x03; 83475885Sorion pci_write_config(dev, ALS_PCI_POWERREG, data, 2); 83575885Sorion } 83675885Sorion#endif 83775885Sorion 83875885Sorion if (als_resource_grab(dev, sc)) { 83975885Sorion device_printf(dev, "failed to allocate resources\n"); 84075885Sorion goto bad_attach; 84175885Sorion } 84275885Sorion 84375885Sorion if (als_init(sc)) { 84475885Sorion device_printf(dev, "failed to initialize hardware\n"); 84575885Sorion goto bad_attach; 84675885Sorion } 84775885Sorion 84875885Sorion if (mixer_init(dev, &als_mixer_class, sc)) { 84975885Sorion device_printf(dev, "failed to initialize mixer\n"); 85075885Sorion goto bad_attach; 85175885Sorion } 85275885Sorion 85375885Sorion if (pcm_register(dev, sc, 1, 1)) { 85475885Sorion device_printf(dev, "failed to register pcm entries\n"); 85575885Sorion goto bad_attach; 85675885Sorion } 85775885Sorion 85875885Sorion pcm_addchan(dev, PCMDIR_PLAY, &alspchan_class, sc); 85975885Sorion pcm_addchan(dev, PCMDIR_REC, &alsrchan_class, sc); 86075885Sorion 861126695Smatk snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", 862126695Smatk rman_get_start(sc->reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_als4000)); 86375885Sorion pcm_setstatus(dev, status); 86475885Sorion return 0; 86575885Sorion 86675885Sorion bad_attach: 86775885Sorion als_resource_free(dev, sc); 86875885Sorion free(sc, M_DEVBUF); 86975885Sorion return ENXIO; 87075885Sorion} 87175885Sorion 87275885Sorionstatic int 87375885Sorionals_pci_detach(device_t dev) 87475885Sorion{ 87575885Sorion struct sc_info *sc; 87675885Sorion int r; 87775885Sorion 87875885Sorion r = pcm_unregister(dev); 87975885Sorion if (r) 88075885Sorion return r; 88175885Sorion 88275885Sorion sc = pcm_getdevinfo(dev); 88375885Sorion als_uninit(sc); 88475885Sorion als_resource_free(dev, sc); 88575885Sorion free(sc, M_DEVBUF); 88675885Sorion return 0; 88775885Sorion} 88875885Sorion 88975885Sorionstatic int 89075885Sorionals_pci_suspend(device_t dev) 89175885Sorion{ 89275885Sorion struct sc_info *sc = pcm_getdevinfo(dev); 89375885Sorion 894150980Snetchild snd_mtxlock(sc->lock); 89575885Sorion sc->pch.dma_was_active = als_playback_stop(&sc->pch); 89675885Sorion sc->rch.dma_was_active = als_capture_stop(&sc->rch); 89775885Sorion als_uninit(sc); 898150980Snetchild snd_mtxunlock(sc->lock); 89975885Sorion return 0; 90075885Sorion} 90175885Sorion 90275885Sorionstatic int 90375885Sorionals_pci_resume(device_t dev) 90475885Sorion{ 90575885Sorion struct sc_info *sc = pcm_getdevinfo(dev); 90678362Scg 907148588Snetchild 908150980Snetchild snd_mtxlock(sc->lock); 90975885Sorion if (als_init(sc) != 0) { 91075885Sorion device_printf(dev, "unable to reinitialize the card\n"); 911150980Snetchild snd_mtxunlock(sc->lock); 91275885Sorion return ENXIO; 91378362Scg } 91475885Sorion 91575885Sorion if (mixer_reinit(dev) != 0) { 91675885Sorion device_printf(dev, "unable to reinitialize the mixer\n"); 917150980Snetchild snd_mtxunlock(sc->lock); 91875885Sorion return ENXIO; 91975885Sorion } 92075885Sorion 92175885Sorion if (sc->pch.dma_was_active) { 92275885Sorion als_playback_start(&sc->pch); 92375885Sorion } 92475885Sorion 92575885Sorion if (sc->rch.dma_was_active) { 92675885Sorion als_capture_start(&sc->rch); 92775885Sorion } 928150980Snetchild snd_mtxunlock(sc->lock); 929148588Snetchild 93075885Sorion return 0; 93175885Sorion} 93275885Sorion 93375885Sorionstatic device_method_t als_methods[] = { 93475885Sorion /* Device interface */ 93575885Sorion DEVMETHOD(device_probe, als_pci_probe), 93675885Sorion DEVMETHOD(device_attach, als_pci_attach), 93775885Sorion DEVMETHOD(device_detach, als_pci_detach), 93875885Sorion DEVMETHOD(device_suspend, als_pci_suspend), 93975885Sorion DEVMETHOD(device_resume, als_pci_resume), 94075885Sorion { 0, 0 } 94175885Sorion}; 94275885Sorion 94375885Sorionstatic driver_t als_driver = { 94475885Sorion "pcm", 94575885Sorion als_methods, 94682180Scg PCM_SOFTC_SIZE, 94775885Sorion}; 94875885Sorion 94985442SjhbDRIVER_MODULE(snd_als4000, pci, als_driver, pcm_devclass, 0, 0); 950132236StanimuraMODULE_DEPEND(snd_als4000, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 95185442SjhbMODULE_VERSION(snd_als4000, 1); 952