1139749Simp/*- 2119853Scg * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 3166426Sjoel * Copyright (c) 1997,1998 Luigi Rizzo 429415Sjmg * 529415Sjmg * Derived from files in the Voxware 3.5 distribution, 629415Sjmg * Copyright by Hannu Savolainen 1994, under the same copyright 729415Sjmg * conditions. 850723Scg * All rights reserved. 950723Scg * 1029415Sjmg * Redistribution and use in source and binary forms, with or without 1129415Sjmg * modification, are permitted provided that the following conditions 1230869Sjmg * are met: 1330869Sjmg * 1. Redistributions of source code must retain the above copyright 1430869Sjmg * notice, this list of conditions and the following disclaimer. 1530869Sjmg * 2. Redistributions in binary form must reproduce the above copyright 1650723Scg * notice, this list of conditions and the following disclaimer in the 1750723Scg * documentation and/or other materials provided with the distribution. 1830869Sjmg * 1950723Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2050723Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2150723Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2250723Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2350723Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2450723Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2550723Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2650723Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2750723Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2850723Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2950723Scg * SUCH DAMAGE. 3029415Sjmg */ 3129415Sjmg 32193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 33193640Sariff#include "opt_snd.h" 34193640Sariff#endif 35193640Sariff 3653465Scg#include <dev/sound/pcm/sound.h> 3729415Sjmg 3853465Scg#include <dev/sound/isa/sb.h> 3953553Stanimura#include <dev/sound/chip.h> 4029415Sjmg 41110499Snyan#include <isa/isavar.h> 42110499Snyan 4370134Scg#include "mixer_if.h" 4470134Scg 4582180ScgSND_DECLARE_FILE("$FreeBSD: releng/11.0/sys/dev/sound/isa/sb16.c 297862 2016-04-12 17:23:03Z pfg $"); 4682180Scg 4767803Scg#define SB16_BUFFSIZE 4096 4855706Scg#define PLAIN_SB16(x) ((((x)->bd_flags) & (BD_F_SB16|BD_F_SB16X)) == BD_F_SB16) 4955254Scg 5067803Scgstatic u_int32_t sb16_fmt8[] = { 51193640Sariff SND_FORMAT(AFMT_U8, 1, 0), 52193640Sariff SND_FORMAT(AFMT_U8, 2, 0), 5364881Scg 0 5450723Scg}; 5574763Scgstatic struct pcmchan_caps sb16_caps8 = {5000, 45000, sb16_fmt8, 0}; 5629415Sjmg 5767803Scgstatic u_int32_t sb16_fmt16[] = { 58193640Sariff SND_FORMAT(AFMT_S16_LE, 1, 0), 59193640Sariff SND_FORMAT(AFMT_S16_LE, 2, 0), 6064881Scg 0 6150723Scg}; 6274763Scgstatic struct pcmchan_caps sb16_caps16 = {5000, 45000, sb16_fmt16, 0}; 6329415Sjmg 6464881Scgstatic u_int32_t sb16x_fmt[] = { 65193640Sariff SND_FORMAT(AFMT_U8, 1, 0), 66193640Sariff SND_FORMAT(AFMT_U8, 2, 0), 67193640Sariff SND_FORMAT(AFMT_S16_LE, 1, 0), 68193640Sariff SND_FORMAT(AFMT_S16_LE, 2, 0), 6964881Scg 0 7054462Scg}; 7174763Scgstatic struct pcmchan_caps sb16x_caps = {5000, 49000, sb16x_fmt, 0}; 7254462Scg 7350723Scgstruct sb_info; 7429415Sjmg 7550723Scgstruct sb_chinfo { 7650723Scg struct sb_info *parent; 7774763Scg struct pcm_channel *channel; 7874763Scg struct snd_dbuf *buffer; 7967803Scg int dir, run, dch; 8067803Scg u_int32_t fmt, spd, blksz; 8150723Scg}; 8229415Sjmg 8350723Scgstruct sb_info { 8450723Scg struct resource *io_base; /* I/O address for the board */ 8550723Scg struct resource *irq; 8654462Scg struct resource *drq1; 8754462Scg struct resource *drq2; 8865644Scg void *ih; 8955706Scg bus_dma_tag_t parent_dmat; 9029415Sjmg 9184111Scg unsigned int bufsize; 9250723Scg int bd_id; 9350723Scg u_long bd_flags; /* board-specific flags */ 9470291Scg int prio, prio16; 9550723Scg struct sb_chinfo pch, rch; 9674763Scg device_t parent_dev; 9750723Scg}; 9850723Scg 9984111Scg#if 0 10074763Scgstatic void sb_lock(struct sb_info *sb); 10174763Scgstatic void sb_unlock(struct sb_info *sb); 10250723Scgstatic int sb_rd(struct sb_info *sb, int reg); 10350723Scgstatic void sb_wr(struct sb_info *sb, int reg, u_int8_t val); 10450723Scgstatic int sb_cmd(struct sb_info *sb, u_char val); 10567803Scg/* static int sb_cmd1(struct sb_info *sb, u_char cmd, int val); */ 10650723Scgstatic int sb_cmd2(struct sb_info *sb, u_char cmd, int val); 10750723Scgstatic u_int sb_get_byte(struct sb_info *sb); 10850723Scgstatic void sb_setmixer(struct sb_info *sb, u_int port, u_int value); 10950723Scgstatic int sb_getmixer(struct sb_info *sb, u_int port); 11054462Scgstatic int sb_reset_dsp(struct sb_info *sb); 11129415Sjmg 11250723Scgstatic void sb_intr(void *arg); 11384111Scg#endif 11450723Scg 11529415Sjmg/* 11650723Scg * Common code for the midi and pcm functions 11729415Sjmg * 11850723Scg * sb_cmd write a single byte to the CMD port. 11950723Scg * sb_cmd1 write a CMD + 1 byte arg 12050723Scg * sb_cmd2 write a CMD + 2 byte arg 12150723Scg * sb_get_byte returns a single byte from the DSP data port 12229415Sjmg */ 12329415Sjmg 12484111Scgstatic void 12584111Scgsb_lock(struct sb_info *sb) { 12684111Scg 12784111Scg sbc_lock(device_get_softc(sb->parent_dev)); 12884111Scg} 12984111Scg 13084111Scgstatic void 131129180Struckmansb_lockassert(struct sb_info *sb) { 132129180Struckman 133129180Struckman sbc_lockassert(device_get_softc(sb->parent_dev)); 134129180Struckman} 135129180Struckman 136129180Struckmanstatic void 13784111Scgsb_unlock(struct sb_info *sb) { 13884111Scg 13984111Scg sbc_unlock(device_get_softc(sb->parent_dev)); 14084111Scg} 14184111Scg 14229415Sjmgstatic int 14350723Scgport_rd(struct resource *port, int off) 14429415Sjmg{ 14567803Scg return bus_space_read_1(rman_get_bustag(port), rman_get_bushandle(port), off); 14650723Scg} 14729415Sjmg 14850723Scgstatic void 14950723Scgport_wr(struct resource *port, int off, u_int8_t data) 15050723Scg{ 151108064Ssemenu bus_space_write_1(rman_get_bustag(port), rman_get_bushandle(port), off, data); 15229415Sjmg} 15329415Sjmg 15429415Sjmgstatic int 15550723Scgsb_rd(struct sb_info *sb, int reg) 15629415Sjmg{ 15750723Scg return port_rd(sb->io_base, reg); 15850723Scg} 15929415Sjmg 16050723Scgstatic void 16150723Scgsb_wr(struct sb_info *sb, int reg, u_int8_t val) 16250723Scg{ 16350723Scg port_wr(sb->io_base, reg, val); 16429415Sjmg} 16529415Sjmg 16650723Scgstatic int 16750723Scgsb_dspwr(struct sb_info *sb, u_char val) 16829415Sjmg{ 16950723Scg int i; 17029415Sjmg 17150723Scg for (i = 0; i < 1000; i++) { 17270134Scg if ((sb_rd(sb, SBDSP_STATUS) & 0x80)) 17370134Scg DELAY((i > 100)? 1000 : 10); 17470134Scg else { 17570134Scg sb_wr(sb, SBDSP_CMD, val); 17670134Scg return 1; 17750723Scg } 17850723Scg } 17983366Sjulian if (curthread->td_intr_nesting_level == 0) 18074797Scg printf("sb_dspwr(0x%02x) timed out.\n", val); 18150723Scg return 0; 18250723Scg} 18329415Sjmg 18450723Scgstatic int 18550723Scgsb_cmd(struct sb_info *sb, u_char val) 18650723Scg{ 18750723Scg#if 0 18850723Scg printf("sb_cmd: %x\n", val); 18950723Scg#endif 19050723Scg return sb_dspwr(sb, val); 19150723Scg} 19229415Sjmg 19367803Scg/* 19450723Scgstatic int 19550723Scgsb_cmd1(struct sb_info *sb, u_char cmd, int val) 19650723Scg{ 19750723Scg#if 0 19850723Scg printf("sb_cmd1: %x, %x\n", cmd, val); 19950723Scg#endif 20050723Scg if (sb_dspwr(sb, cmd)) { 20150723Scg return sb_dspwr(sb, val & 0xff); 20250723Scg } else return 0; 20350723Scg} 20467803Scg*/ 20529415Sjmg 20650723Scgstatic int 20750723Scgsb_cmd2(struct sb_info *sb, u_char cmd, int val) 20850723Scg{ 20984111Scg int r; 21084111Scg 21150723Scg#if 0 21250723Scg printf("sb_cmd2: %x, %x\n", cmd, val); 21350723Scg#endif 214135115Struckman sb_lockassert(sb); 21584111Scg r = 0; 21650723Scg if (sb_dspwr(sb, cmd)) { 21784111Scg if (sb_dspwr(sb, val & 0xff)) { 21884111Scg if (sb_dspwr(sb, (val >> 8) & 0xff)) { 21984111Scg r = 1; 22084111Scg } 22184111Scg } 22284111Scg } 22384111Scg 22484111Scg return r; 22550723Scg} 22629415Sjmg 22750723Scg/* 22850723Scg * in the SB, there is a set of indirect "mixer" registers with 22950723Scg * address at offset 4, data at offset 5 23050723Scg */ 23150723Scgstatic void 23250723Scgsb_setmixer(struct sb_info *sb, u_int port, u_int value) 23350723Scg{ 23474763Scg sb_lock(sb); 23550723Scg sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ 23650723Scg DELAY(10); 23750723Scg sb_wr(sb, SB_MIX_DATA, (u_char) (value & 0xff)); 23850723Scg DELAY(10); 23974763Scg sb_unlock(sb); 24050723Scg} 24131361Sjmg 24250723Scgstatic int 24350723Scgsb_getmixer(struct sb_info *sb, u_int port) 24450723Scg{ 24550723Scg int val; 24629415Sjmg 247135115Struckman sb_lockassert(sb); 24850723Scg sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ 24950723Scg DELAY(10); 25050723Scg val = sb_rd(sb, SB_MIX_DATA); 25150723Scg DELAY(10); 25229415Sjmg 25350723Scg return val; 25450723Scg} 25529415Sjmg 25650723Scgstatic u_int 25750723Scgsb_get_byte(struct sb_info *sb) 25850723Scg{ 25950723Scg int i; 26029415Sjmg 26150723Scg for (i = 1000; i > 0; i--) { 26250723Scg if (sb_rd(sb, DSP_DATA_AVAIL) & 0x80) 26350723Scg return sb_rd(sb, DSP_READ); 26450723Scg else 26550723Scg DELAY(20); 26650723Scg } 26750723Scg return 0xffff; 26850723Scg} 26929415Sjmg 27050723Scgstatic int 27150723Scgsb_reset_dsp(struct sb_info *sb) 27229415Sjmg{ 27374763Scg u_char b; 27474763Scg 275129180Struckman sb_lockassert(sb); 27650723Scg sb_wr(sb, SBDSP_RST, 3); 27750723Scg DELAY(100); 27850723Scg sb_wr(sb, SBDSP_RST, 0); 27974763Scg b = sb_get_byte(sb); 28074763Scg if (b != 0xAA) { 28150723Scg DEB(printf("sb_reset_dsp 0x%lx failed\n", 28289774Sscottl rman_get_start(sb->io_base))); 28350723Scg return ENXIO; /* Sorry */ 28450723Scg } 28550723Scg return 0; 28629415Sjmg} 28729415Sjmg 28867803Scg/************************************************************/ 28967803Scg 29067803Scgstruct sb16_mixent { 29167803Scg int reg; 29267803Scg int bits; 29367803Scg int ofs; 29467803Scg int stereo; 29567803Scg}; 29667803Scg 29767803Scgstatic const struct sb16_mixent sb16_mixtab[32] = { 29867803Scg [SOUND_MIXER_VOLUME] = { 0x30, 5, 3, 1 }, 29967803Scg [SOUND_MIXER_PCM] = { 0x32, 5, 3, 1 }, 30067803Scg [SOUND_MIXER_SYNTH] = { 0x34, 5, 3, 1 }, 30167803Scg [SOUND_MIXER_CD] = { 0x36, 5, 3, 1 }, 30267803Scg [SOUND_MIXER_LINE] = { 0x38, 5, 3, 1 }, 30367803Scg [SOUND_MIXER_MIC] = { 0x3a, 5, 3, 0 }, 30467803Scg [SOUND_MIXER_SPEAKER] = { 0x3b, 5, 3, 0 }, 30567803Scg [SOUND_MIXER_IGAIN] = { 0x3f, 2, 6, 1 }, 30667803Scg [SOUND_MIXER_OGAIN] = { 0x41, 2, 6, 1 }, 30767803Scg [SOUND_MIXER_TREBLE] = { 0x44, 4, 4, 1 }, 30867803Scg [SOUND_MIXER_BASS] = { 0x46, 4, 4, 1 }, 309100953Ssobomax [SOUND_MIXER_LINE1] = { 0x52, 5, 3, 1 } 31067803Scg}; 31167803Scg 31267803Scgstatic int 31374763Scgsb16mix_init(struct snd_mixer *m) 31467803Scg{ 31567803Scg struct sb_info *sb = mix_getdevinfo(m); 31667803Scg 31767803Scg mix_setdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | 31867803Scg SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | 319100953Ssobomax SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | SOUND_MASK_LINE1 | 32067803Scg SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE); 32167803Scg 32267803Scg mix_setrecdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_LINE | 323100953Ssobomax SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_CD); 32467803Scg 32567803Scg sb_setmixer(sb, 0x3c, 0x1f); /* make all output active */ 32667803Scg 32767803Scg sb_setmixer(sb, 0x3d, 0); /* make all inputs-l off */ 32867803Scg sb_setmixer(sb, 0x3e, 0); /* make all inputs-r off */ 32967803Scg 33067803Scg return 0; 33167803Scg} 33267803Scg 33367803Scgstatic int 334130472Sjosefrel2abs_volume(int x, int max) 335130472Sjosef{ 336130472Sjosef int temp; 337130472Sjosef 338130472Sjosef temp = ((x * max) + 50) / 100; 339130472Sjosef if (temp > max) 340130472Sjosef temp = max; 341130472Sjosef else if (temp < 0) 342130472Sjosef temp = 0; 343130472Sjosef return (temp); 344130472Sjosef} 345130472Sjosef 346130472Sjosefstatic int 34774763Scgsb16mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 34867803Scg{ 34967803Scg struct sb_info *sb = mix_getdevinfo(m); 35067803Scg const struct sb16_mixent *e; 35167803Scg int max; 35267803Scg 35367803Scg e = &sb16_mixtab[dev]; 35467803Scg max = (1 << e->bits) - 1; 35567803Scg 356130472Sjosef left = rel2abs_volume(left, max); 357130472Sjosef right = rel2abs_volume(right, max); 35867803Scg 35967803Scg sb_setmixer(sb, e->reg, left << e->ofs); 36067803Scg if (e->stereo) 36168376Scg sb_setmixer(sb, e->reg + 1, right << e->ofs); 36267803Scg else 36367803Scg right = left; 36467803Scg 36567803Scg left = (left * 100) / max; 36667803Scg right = (right * 100) / max; 36767803Scg 36867803Scg return left | (right << 8); 36967803Scg} 37067803Scg 371193640Sariffstatic u_int32_t 37274763Scgsb16mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 37367803Scg{ 37467803Scg struct sb_info *sb = mix_getdevinfo(m); 375152418Sariff u_char recdev_l, recdev_r; 37667803Scg 377152418Sariff recdev_l = 0; 378152418Sariff recdev_r = 0; 379152418Sariff if (src & SOUND_MASK_MIC) { 380152418Sariff recdev_l |= 0x01; /* mono mic */ 381152418Sariff recdev_r |= 0x01; 382152418Sariff } 38367803Scg 384152418Sariff if (src & SOUND_MASK_CD) { 385152418Sariff recdev_l |= 0x04; /* l cd */ 386152418Sariff recdev_r |= 0x02; /* r cd */ 387152418Sariff } 38867803Scg 389152418Sariff if (src & SOUND_MASK_LINE) { 390152418Sariff recdev_l |= 0x10; /* l line */ 391152418Sariff recdev_r |= 0x08; /* r line */ 392152418Sariff } 39367803Scg 394152418Sariff if (src & SOUND_MASK_SYNTH) { 395152418Sariff recdev_l |= 0x40; /* l midi */ 396152418Sariff recdev_r |= 0x20; /* r midi */ 397152418Sariff } 39867803Scg 399152418Sariff sb_setmixer(sb, SB16_IMASK_L, recdev_l); 400152418Sariff sb_setmixer(sb, SB16_IMASK_R, recdev_r); 40167803Scg 402100953Ssobomax /* Switch on/off FM tuner source */ 403100953Ssobomax if (src & SOUND_MASK_LINE1) 404100953Ssobomax sb_setmixer(sb, 0x4a, 0x0c); 405100953Ssobomax else 406100953Ssobomax sb_setmixer(sb, 0x4a, 0x00); 407100953Ssobomax 40867803Scg /* 40967803Scg * since the same volume controls apply to the input and 41067803Scg * output sections, the best approach to have a consistent 41167803Scg * behaviour among cards would be to disable the output path 41267803Scg * on devices which are used to record. 41367803Scg * However, since users like to have feedback, we only disable 41467803Scg * the mic -- permanently. 41567803Scg */ 41667803Scg sb_setmixer(sb, SB16_OMASK, 0x1f & ~1); 41767803Scg 41867803Scg return src; 41967803Scg} 42067803Scg 42170134Scgstatic kobj_method_t sb16mix_mixer_methods[] = { 42270134Scg KOBJMETHOD(mixer_init, sb16mix_init), 42370134Scg KOBJMETHOD(mixer_set, sb16mix_set), 42470134Scg KOBJMETHOD(mixer_setrecsrc, sb16mix_setrecsrc), 425193640Sariff KOBJMETHOD_END 42670134Scg}; 42770134ScgMIXER_DECLARE(sb16mix_mixer); 42870134Scg 42967803Scg/************************************************************/ 43067803Scg 43129415Sjmgstatic void 43267803Scgsb16_release_resources(struct sb_info *sb, device_t dev) 43329415Sjmg{ 43450723Scg if (sb->irq) { 43565644Scg if (sb->ih) 43665644Scg bus_teardown_intr(dev, sb->irq, sb->ih); 43765644Scg bus_release_resource(dev, SYS_RES_IRQ, 0, sb->irq); 438297862Spfg sb->irq = NULL; 43950723Scg } 44084111Scg if (sb->drq2) { 44184111Scg if (sb->drq2 != sb->drq1) { 44284111Scg isa_dma_release(rman_get_start(sb->drq2)); 44384111Scg bus_release_resource(dev, SYS_RES_DRQ, 1, sb->drq2); 44484111Scg } 445297862Spfg sb->drq2 = NULL; 44670291Scg } 44770291Scg if (sb->drq1) { 44874364Scg isa_dma_release(rman_get_start(sb->drq1)); 44954462Scg bus_release_resource(dev, SYS_RES_DRQ, 0, sb->drq1); 450297862Spfg sb->drq1 = NULL; 45150723Scg } 45270291Scg if (sb->io_base) { 45354462Scg bus_release_resource(dev, SYS_RES_IOPORT, 0, sb->io_base); 454297862Spfg sb->io_base = NULL; 45550723Scg } 45665644Scg if (sb->parent_dmat) { 45765644Scg bus_dma_tag_destroy(sb->parent_dmat); 45865644Scg sb->parent_dmat = 0; 45965644Scg } 46065644Scg free(sb, M_DEVBUF); 46150723Scg} 46229415Sjmg 46350723Scgstatic int 46467803Scgsb16_alloc_resources(struct sb_info *sb, device_t dev) 46550723Scg{ 46654462Scg int rid; 46754462Scg 46854462Scg rid = 0; 46950723Scg if (!sb->io_base) 470127135Snjl sb->io_base = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 471127135Snjl &rid, RF_ACTIVE); 47267803Scg 47354462Scg rid = 0; 47450723Scg if (!sb->irq) 475127135Snjl sb->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 476127135Snjl RF_ACTIVE); 47767803Scg 47854462Scg rid = 0; 47950723Scg if (!sb->drq1) 480127135Snjl sb->drq1 = bus_alloc_resource_any(dev, SYS_RES_DRQ, &rid, 481127135Snjl RF_ACTIVE); 48267803Scg 48354462Scg rid = 1; 48458756Scg if (!sb->drq2) 485127135Snjl sb->drq2 = bus_alloc_resource_any(dev, SYS_RES_DRQ, &rid, 486127135Snjl RF_ACTIVE); 48729415Sjmg 48850723Scg if (sb->io_base && sb->drq1 && sb->irq) { 48954462Scg isa_dma_acquire(rman_get_start(sb->drq1)); 49084111Scg isa_dmainit(rman_get_start(sb->drq1), sb->bufsize); 49129415Sjmg 49250723Scg if (sb->drq2) { 49354462Scg isa_dma_acquire(rman_get_start(sb->drq2)); 49484111Scg isa_dmainit(rman_get_start(sb->drq2), sb->bufsize); 49570291Scg } else { 49670291Scg sb->drq2 = sb->drq1; 49770291Scg pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); 49854462Scg } 49950723Scg return 0; 50050723Scg } else return ENXIO; 50129415Sjmg} 50229415Sjmg 50374763Scg/* sbc does locking for us */ 50454462Scgstatic void 50567803Scgsb_intr(void *arg) 50629415Sjmg{ 50767803Scg struct sb_info *sb = (struct sb_info *)arg; 508149948Snetchild int reason, c; 50929415Sjmg 51067803Scg /* 51167803Scg * The Vibra16X has separate flags for 8 and 16 bit transfers, but 51267803Scg * I have no idea how to tell capture from playback interrupts... 51367803Scg */ 51429415Sjmg 51567803Scg reason = 0; 51674763Scg sb_lock(sb); 51767803Scg c = sb_getmixer(sb, IRQ_STAT); 51874763Scg if (c & 1) 51974763Scg sb_rd(sb, DSP_DATA_AVAIL); /* 8-bit int ack */ 52041653Sbrian 52174763Scg if (c & 2) 52274763Scg sb_rd(sb, DSP_DATA_AVL16); /* 16-bit int ack */ 52374763Scg sb_unlock(sb); 52474763Scg 52567803Scg /* 52667803Scg * this tells us if the source is 8-bit or 16-bit dma. We 52767803Scg * have to check the io channel to map it to read or write... 52867803Scg */ 52929415Sjmg 53068414Scg if (sb->bd_flags & BD_F_SB16X) { 53168414Scg if (c & 1) { /* 8-bit format */ 53268414Scg if (sb->pch.fmt & AFMT_8BIT) 53368414Scg reason |= 1; 53468414Scg if (sb->rch.fmt & AFMT_8BIT) 53568414Scg reason |= 2; 53668414Scg } 53768414Scg if (c & 2) { /* 16-bit format */ 53868414Scg if (sb->pch.fmt & AFMT_16BIT) 53968414Scg reason |= 1; 54068414Scg if (sb->rch.fmt & AFMT_16BIT) 54168414Scg reason |= 2; 54268414Scg } 54368414Scg } else { 54468414Scg if (c & 1) { /* 8-bit dma */ 54570291Scg if (sb->pch.dch == 1) 54668414Scg reason |= 1; 54770291Scg if (sb->rch.dch == 1) 54868414Scg reason |= 2; 54968414Scg } 55068414Scg if (c & 2) { /* 16-bit dma */ 55170291Scg if (sb->pch.dch == 2) 55268414Scg reason |= 1; 55370291Scg if (sb->rch.dch == 2) 55468414Scg reason |= 2; 55568414Scg } 55668414Scg } 55750723Scg#if 0 55850723Scg printf("sb_intr: reason=%d c=0x%x\n", reason, c); 55950723Scg#endif 56067803Scg if ((reason & 1) && (sb->pch.run)) 56150723Scg chn_intr(sb->pch.channel); 56267803Scg 56367803Scg if ((reason & 2) && (sb->rch.run)) 56450723Scg chn_intr(sb->rch.channel); 56550723Scg} 56631361Sjmg 56750723Scgstatic int 56867803Scgsb_setup(struct sb_info *sb) 56950723Scg{ 57067803Scg struct sb_chinfo *ch; 57167803Scg u_int8_t v; 57267803Scg int l, pprio; 57329415Sjmg 57474763Scg sb_lock(sb); 57567803Scg if (sb->bd_flags & BD_F_DMARUN) 576110499Snyan sndbuf_dma(sb->pch.buffer, PCMTRIG_STOP); 57767803Scg if (sb->bd_flags & BD_F_DMARUN2) 578110499Snyan sndbuf_dma(sb->rch.buffer, PCMTRIG_STOP); 57967803Scg sb->bd_flags &= ~(BD_F_DMARUN | BD_F_DMARUN2); 58029415Sjmg 58167803Scg sb_reset_dsp(sb); 58229415Sjmg 58367803Scg if (sb->bd_flags & BD_F_SB16X) { 584149948Snetchild /* full-duplex doesn't work! */ 58568414Scg pprio = sb->pch.run? 1 : 0; 586149948Snetchild sndbuf_dmasetup(sb->pch.buffer, pprio? sb->drq1 : sb->drq2); 58770291Scg sb->pch.dch = pprio? 1 : 0; 588110499Snyan sndbuf_dmasetup(sb->rch.buffer, pprio? sb->drq2 : sb->drq1); 58970291Scg sb->rch.dch = pprio? 2 : 1; 59067803Scg } else { 59167803Scg if (sb->pch.run && sb->rch.run) { 59267803Scg pprio = (sb->rch.fmt & AFMT_16BIT)? 0 : 1; 593110499Snyan sndbuf_dmasetup(sb->pch.buffer, pprio? sb->drq2 : sb->drq1); 59470291Scg sb->pch.dch = pprio? 2 : 1; 595110499Snyan sndbuf_dmasetup(sb->rch.buffer, pprio? sb->drq1 : sb->drq2); 59670291Scg sb->rch.dch = pprio? 1 : 2; 59767803Scg } else { 59867803Scg if (sb->pch.run) { 599110499Snyan sndbuf_dmasetup(sb->pch.buffer, (sb->pch.fmt & AFMT_16BIT)? sb->drq2 : sb->drq1); 60070291Scg sb->pch.dch = (sb->pch.fmt & AFMT_16BIT)? 2 : 1; 601110499Snyan sndbuf_dmasetup(sb->rch.buffer, (sb->pch.fmt & AFMT_16BIT)? sb->drq1 : sb->drq2); 60270291Scg sb->rch.dch = (sb->pch.fmt & AFMT_16BIT)? 1 : 2; 60367803Scg } else if (sb->rch.run) { 604110499Snyan sndbuf_dmasetup(sb->pch.buffer, (sb->rch.fmt & AFMT_16BIT)? sb->drq1 : sb->drq2); 60570291Scg sb->pch.dch = (sb->rch.fmt & AFMT_16BIT)? 1 : 2; 606110499Snyan sndbuf_dmasetup(sb->rch.buffer, (sb->rch.fmt & AFMT_16BIT)? sb->drq2 : sb->drq1); 60770291Scg sb->rch.dch = (sb->rch.fmt & AFMT_16BIT)? 2 : 1; 60867803Scg } 60950723Scg } 61067803Scg } 61129415Sjmg 612110499Snyan sndbuf_dmasetdir(sb->pch.buffer, PCMDIR_PLAY); 613110499Snyan sndbuf_dmasetdir(sb->rch.buffer, PCMDIR_REC); 61429415Sjmg 61567803Scg /* 61668414Scg printf("setup: [pch = %d, pfmt = %d, pgo = %d] [rch = %d, rfmt = %d, rgo = %d]\n", 61768414Scg sb->pch.dch, sb->pch.fmt, sb->pch.run, sb->rch.dch, sb->rch.fmt, sb->rch.run); 61867803Scg */ 61955706Scg 62067803Scg ch = &sb->pch; 62167803Scg if (ch->run) { 62267803Scg l = ch->blksz; 62367803Scg if (ch->fmt & AFMT_16BIT) 62467803Scg l >>= 1; 62567803Scg l--; 62655706Scg 62767803Scg /* play speed */ 62867803Scg RANGE(ch->spd, 5000, 45000); 62967803Scg sb_cmd(sb, DSP_CMD_OUT16); 63067803Scg sb_cmd(sb, ch->spd >> 8); 63167803Scg sb_cmd(sb, ch->spd & 0xff); 63267803Scg 63367803Scg /* play format, length */ 63467803Scg v = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_DAC; 63567803Scg v |= (ch->fmt & AFMT_16BIT)? DSP_DMA16 : DSP_DMA8; 63667803Scg sb_cmd(sb, v); 63767803Scg 638193640Sariff v = (AFMT_CHANNEL(ch->fmt) > 1)? DSP_F16_STEREO : 0; 63967803Scg v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0; 64067803Scg sb_cmd2(sb, v, l); 641110499Snyan sndbuf_dma(ch->buffer, PCMTRIG_START); 64267803Scg sb->bd_flags |= BD_F_DMARUN; 64350723Scg } 64450723Scg 64567803Scg ch = &sb->rch; 64667803Scg if (ch->run) { 64767803Scg l = ch->blksz; 64867803Scg if (ch->fmt & AFMT_16BIT) 64967803Scg l >>= 1; 65067803Scg l--; 65129415Sjmg 65267803Scg /* record speed */ 65367803Scg RANGE(ch->spd, 5000, 45000); 65467803Scg sb_cmd(sb, DSP_CMD_IN16); 65567803Scg sb_cmd(sb, ch->spd >> 8); 65667803Scg sb_cmd(sb, ch->spd & 0xff); 65767803Scg 65867803Scg /* record format, length */ 65967803Scg v = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_ADC; 66067803Scg v |= (ch->fmt & AFMT_16BIT)? DSP_DMA16 : DSP_DMA8; 66167803Scg sb_cmd(sb, v); 66267803Scg 663193640Sariff v = (AFMT_CHANNEL(ch->fmt) > 1)? DSP_F16_STEREO : 0; 66467803Scg v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0; 66567803Scg sb_cmd2(sb, v, l); 666110499Snyan sndbuf_dma(ch->buffer, PCMTRIG_START); 66767803Scg sb->bd_flags |= BD_F_DMARUN2; 66829415Sjmg } 66974763Scg sb_unlock(sb); 67067803Scg 67167803Scg return 0; 67250723Scg} 67329415Sjmg 67455706Scg/* channel interface */ 67555706Scgstatic void * 67674763Scgsb16chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 67755706Scg{ 67855706Scg struct sb_info *sb = devinfo; 67955706Scg struct sb_chinfo *ch = (dir == PCMDIR_PLAY)? &sb->pch : &sb->rch; 68055706Scg 68155706Scg ch->parent = sb; 68255706Scg ch->channel = c; 68355706Scg ch->buffer = b; 68467803Scg ch->dir = dir; 68567803Scg 686168847Sariff if (sndbuf_alloc(ch->buffer, sb->parent_dmat, 0, sb->bufsize) != 0) 68755706Scg return NULL; 68867803Scg 68955706Scg return ch; 69055706Scg} 69155706Scg 69255706Scgstatic int 69370134Scgsb16chan_setformat(kobj_t obj, void *data, u_int32_t format) 69455706Scg{ 69555706Scg struct sb_chinfo *ch = data; 69667803Scg struct sb_info *sb = ch->parent; 69755706Scg 69867803Scg ch->fmt = format; 69967803Scg sb->prio = ch->dir; 70067803Scg sb->prio16 = (ch->fmt & AFMT_16BIT)? 1 : 0; 70167803Scg 70255706Scg return 0; 70355706Scg} 70455706Scg 705193640Sariffstatic u_int32_t 70670134Scgsb16chan_setspeed(kobj_t obj, void *data, u_int32_t speed) 70755706Scg{ 70855706Scg struct sb_chinfo *ch = data; 70955706Scg 71067803Scg ch->spd = speed; 71167803Scg return speed; 71255706Scg} 71355706Scg 714193640Sariffstatic u_int32_t 71570134Scgsb16chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 71655706Scg{ 71755706Scg struct sb_chinfo *ch = data; 71855706Scg 71967803Scg ch->blksz = blocksize; 72070291Scg return ch->blksz; 72155706Scg} 72255706Scg 72355706Scgstatic int 72470134Scgsb16chan_trigger(kobj_t obj, void *data, int go) 72555706Scg{ 72655706Scg struct sb_chinfo *ch = data; 72767803Scg struct sb_info *sb = ch->parent; 72855706Scg 729170521Sariff if (!PCMTRIG_COMMON(go)) 73055706Scg return 0; 73155706Scg 73255706Scg if (go == PCMTRIG_START) 73367803Scg ch->run = 1; 73455706Scg else 73567803Scg ch->run = 0; 73668376Scg 73767803Scg sb_setup(sb); 73867803Scg 73955706Scg return 0; 74055706Scg} 74155706Scg 742193640Sariffstatic u_int32_t 74370134Scgsb16chan_getptr(kobj_t obj, void *data) 74455706Scg{ 74555706Scg struct sb_chinfo *ch = data; 74655706Scg 747110499Snyan return sndbuf_dmaptr(ch->buffer); 74855706Scg} 74955706Scg 75074763Scgstatic struct pcmchan_caps * 75170134Scgsb16chan_getcaps(kobj_t obj, void *data) 75255706Scg{ 75355706Scg struct sb_chinfo *ch = data; 75467803Scg struct sb_info *sb = ch->parent; 75555706Scg 75667803Scg if ((sb->prio == 0) || (sb->prio == ch->dir)) 75755706Scg return &sb16x_caps; 75855706Scg else 75967803Scg return sb->prio16? &sb16_caps8 : &sb16_caps16; 76055706Scg} 76155706Scg 76229415Sjmgstatic int 76370134Scgsb16chan_resetdone(kobj_t obj, void *data) 76429415Sjmg{ 76567803Scg struct sb_chinfo *ch = data; 76667803Scg struct sb_info *sb = ch->parent; 76731361Sjmg 76867803Scg sb->prio = 0; 76929415Sjmg 77067803Scg return 0; 77129415Sjmg} 77229415Sjmg 77370134Scgstatic kobj_method_t sb16chan_methods[] = { 77470134Scg KOBJMETHOD(channel_init, sb16chan_init), 77570134Scg KOBJMETHOD(channel_resetdone, sb16chan_resetdone), 77670134Scg KOBJMETHOD(channel_setformat, sb16chan_setformat), 77770134Scg KOBJMETHOD(channel_setspeed, sb16chan_setspeed), 77870134Scg KOBJMETHOD(channel_setblocksize, sb16chan_setblocksize), 77970134Scg KOBJMETHOD(channel_trigger, sb16chan_trigger), 78070134Scg KOBJMETHOD(channel_getptr, sb16chan_getptr), 78170134Scg KOBJMETHOD(channel_getcaps, sb16chan_getcaps), 782193640Sariff KOBJMETHOD_END 78370134Scg}; 78470134ScgCHANNEL_DECLARE(sb16chan); 78570134Scg 78667803Scg/************************************************************/ 78729415Sjmg 78829415Sjmgstatic int 78967803Scgsb16_probe(device_t dev) 79053553Stanimura{ 79154462Scg char buf[64]; 79255092Sdfr uintptr_t func, ver, r, f; 79353553Stanimura 79453553Stanimura /* The parent device has already been probed. */ 79554462Scg r = BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func); 79654462Scg if (func != SCF_PCM) 79753553Stanimura return (ENXIO); 79853553Stanimura 79954462Scg r = BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver); 80054791Scg f = (ver & 0xffff0000) >> 16; 80154462Scg ver &= 0x0000ffff; 80267803Scg if (f & BD_F_SB16) { 80367803Scg snprintf(buf, sizeof buf, "SB16 DSP %d.%02d%s", (int) ver >> 8, (int) ver & 0xff, 80467803Scg (f & BD_F_SB16X)? " (ViBRA16X)" : ""); 80567803Scg device_set_desc_copy(dev, buf); 80667803Scg return 0; 80767803Scg } else 80858756Scg return (ENXIO); 80953553Stanimura} 81053553Stanimura 81153553Stanimurastatic int 81267803Scgsb16_attach(device_t dev) 81353553Stanimura{ 81453553Stanimura struct sb_info *sb; 81555092Sdfr uintptr_t ver; 81684111Scg char status[SND_STATUSLEN], status2[SND_STATUSLEN]; 81753553Stanimura 818170873Sariff sb = malloc(sizeof(*sb), M_DEVBUF, M_WAITOK | M_ZERO); 81974763Scg sb->parent_dev = device_get_parent(dev); 82074763Scg BUS_READ_IVAR(sb->parent_dev, dev, 1, &ver); 82154462Scg sb->bd_id = ver & 0x0000ffff; 82254462Scg sb->bd_flags = (ver & 0xffff0000) >> 16; 82384111Scg sb->bufsize = pcm_getbuffersize(dev, 4096, SB16_BUFFSIZE, 65536); 82454462Scg 825129181Struckman if (sb16_alloc_resources(sb, dev)) 82667803Scg goto no; 827129180Struckman sb_lock(sb); 828129180Struckman if (sb_reset_dsp(sb)) { 829129180Struckman sb_unlock(sb); 83067803Scg goto no; 831129180Struckman } 832129180Struckman sb_unlock(sb); 83370134Scg if (mixer_init(dev, &sb16mix_mixer_class, sb)) 83467803Scg goto no; 835128232Sgreen if (snd_setup_intr(dev, sb->irq, 0, sb_intr, sb, &sb->ih)) 83667803Scg goto no; 83767803Scg 83870291Scg if (sb->bd_flags & BD_F_SB16X) 83967803Scg pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); 84067803Scg 84167803Scg sb->prio = 0; 84267803Scg 843166904Snetchild if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, 844166904Snetchild /*boundary*/0, 84567803Scg /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, 84667803Scg /*highaddr*/BUS_SPACE_MAXADDR, 84767803Scg /*filter*/NULL, /*filterarg*/NULL, 84884111Scg /*maxsize*/sb->bufsize, /*nsegments*/1, 849117126Sscottl /*maxsegz*/0x3ffff, /*flags*/0, 850117126Sscottl /*lockfunc*/busdma_lock_mutex, /*lockarg*/&Giant, 851117126Sscottl &sb->parent_dmat) != 0) { 85267803Scg device_printf(dev, "unable to create dma tag\n"); 85367803Scg goto no; 85467803Scg } 85567803Scg 85670291Scg if (!(pcm_getflags(dev) & SD_F_SIMPLEX)) 857297000Sjhibbits snprintf(status2, SND_STATUSLEN, ":%jd", rman_get_start(sb->drq2)); 85884111Scg else 85984111Scg status2[0] = '\0'; 86067803Scg 861297000Sjhibbits snprintf(status, SND_STATUSLEN, "at io 0x%jx irq %jd drq %jd%s bufsz %u %s", 86284111Scg rman_get_start(sb->io_base), rman_get_start(sb->irq), 863126695Smatk rman_get_start(sb->drq1), status2, sb->bufsize, 864126695Smatk PCM_KLDSTRING(snd_sb16)); 86584111Scg 86667803Scg if (pcm_register(dev, sb, 1, 1)) 86767803Scg goto no; 86870134Scg pcm_addchan(dev, PCMDIR_REC, &sb16chan_class, sb); 86970134Scg pcm_addchan(dev, PCMDIR_PLAY, &sb16chan_class, sb); 87067803Scg 87167803Scg pcm_setstatus(dev, status); 87267803Scg 87367803Scg return 0; 87467803Scg 87567803Scgno: 87667803Scg sb16_release_resources(sb, dev); 87767803Scg return ENXIO; 87853553Stanimura} 87953553Stanimura 88067803Scgstatic int 88167803Scgsb16_detach(device_t dev) 88267803Scg{ 88367803Scg int r; 88467803Scg struct sb_info *sb; 88567803Scg 88667803Scg r = pcm_unregister(dev); 88767803Scg if (r) 88867803Scg return r; 88967803Scg 89067803Scg sb = pcm_getdevinfo(dev); 89167803Scg sb16_release_resources(sb, dev); 89267803Scg return 0; 89367803Scg} 89467803Scg 89567803Scgstatic device_method_t sb16_methods[] = { 89653553Stanimura /* Device interface */ 89767803Scg DEVMETHOD(device_probe, sb16_probe), 89867803Scg DEVMETHOD(device_attach, sb16_attach), 89967803Scg DEVMETHOD(device_detach, sb16_detach), 90053553Stanimura 90153553Stanimura { 0, 0 } 90253553Stanimura}; 90353553Stanimura 90467803Scgstatic driver_t sb16_driver = { 90553553Stanimura "pcm", 90667803Scg sb16_methods, 90782180Scg PCM_SOFTC_SIZE, 90853553Stanimura}; 90953553Stanimura 91067803ScgDRIVER_MODULE(snd_sb16, sbc, sb16_driver, pcm_devclass, 0, 0); 911132236StanimuraMODULE_DEPEND(snd_sb16, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 91274763ScgMODULE_DEPEND(snd_sb16, snd_sbc, 1, 1, 1); 91367803ScgMODULE_VERSION(snd_sb16, 1); 914