sb16.c revision 166904
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 3253465Scg#include <dev/sound/pcm/sound.h> 3329415Sjmg 3453465Scg#include <dev/sound/isa/sb.h> 3553553Stanimura#include <dev/sound/chip.h> 3629415Sjmg 37110499Snyan#include <isa/isavar.h> 38110499Snyan 3970134Scg#include "mixer_if.h" 4070134Scg 4182180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/isa/sb16.c 166904 2007-02-23 13:47:34Z netchild $"); 4282180Scg 4367803Scg#define SB16_BUFFSIZE 4096 4455706Scg#define PLAIN_SB16(x) ((((x)->bd_flags) & (BD_F_SB16|BD_F_SB16X)) == BD_F_SB16) 4555254Scg 4667803Scgstatic u_int32_t sb16_fmt8[] = { 4750723Scg AFMT_U8, 4850723Scg AFMT_STEREO | AFMT_U8, 4964881Scg 0 5050723Scg}; 5174763Scgstatic struct pcmchan_caps sb16_caps8 = {5000, 45000, sb16_fmt8, 0}; 5229415Sjmg 5367803Scgstatic u_int32_t sb16_fmt16[] = { 5464881Scg AFMT_S16_LE, 5550723Scg AFMT_STEREO | AFMT_S16_LE, 5664881Scg 0 5750723Scg}; 5874763Scgstatic struct pcmchan_caps sb16_caps16 = {5000, 45000, sb16_fmt16, 0}; 5929415Sjmg 6064881Scgstatic u_int32_t sb16x_fmt[] = { 6164881Scg AFMT_U8, 6264881Scg AFMT_STEREO | AFMT_U8, 6364881Scg AFMT_S16_LE, 6464881Scg AFMT_STEREO | AFMT_S16_LE, 6564881Scg 0 6654462Scg}; 6774763Scgstatic struct pcmchan_caps sb16x_caps = {5000, 49000, sb16x_fmt, 0}; 6854462Scg 6950723Scgstruct sb_info; 7029415Sjmg 7150723Scgstruct sb_chinfo { 7250723Scg struct sb_info *parent; 7374763Scg struct pcm_channel *channel; 7474763Scg struct snd_dbuf *buffer; 7567803Scg int dir, run, dch; 7667803Scg u_int32_t fmt, spd, blksz; 7750723Scg}; 7829415Sjmg 7950723Scgstruct sb_info { 8050723Scg struct resource *io_base; /* I/O address for the board */ 8150723Scg struct resource *irq; 8254462Scg struct resource *drq1; 8354462Scg struct resource *drq2; 8465644Scg void *ih; 8555706Scg bus_dma_tag_t parent_dmat; 8629415Sjmg 8784111Scg unsigned int bufsize; 8850723Scg int bd_id; 8950723Scg u_long bd_flags; /* board-specific flags */ 9070291Scg int prio, prio16; 9150723Scg struct sb_chinfo pch, rch; 9274763Scg device_t parent_dev; 9350723Scg}; 9450723Scg 9584111Scg#if 0 9674763Scgstatic void sb_lock(struct sb_info *sb); 9774763Scgstatic void sb_unlock(struct sb_info *sb); 9850723Scgstatic int sb_rd(struct sb_info *sb, int reg); 9950723Scgstatic void sb_wr(struct sb_info *sb, int reg, u_int8_t val); 10050723Scgstatic int sb_cmd(struct sb_info *sb, u_char val); 10167803Scg/* static int sb_cmd1(struct sb_info *sb, u_char cmd, int val); */ 10250723Scgstatic int sb_cmd2(struct sb_info *sb, u_char cmd, int val); 10350723Scgstatic u_int sb_get_byte(struct sb_info *sb); 10450723Scgstatic void sb_setmixer(struct sb_info *sb, u_int port, u_int value); 10550723Scgstatic int sb_getmixer(struct sb_info *sb, u_int port); 10654462Scgstatic int sb_reset_dsp(struct sb_info *sb); 10729415Sjmg 10850723Scgstatic void sb_intr(void *arg); 10984111Scg#endif 11050723Scg 11129415Sjmg/* 11250723Scg * Common code for the midi and pcm functions 11329415Sjmg * 11450723Scg * sb_cmd write a single byte to the CMD port. 11550723Scg * sb_cmd1 write a CMD + 1 byte arg 11650723Scg * sb_cmd2 write a CMD + 2 byte arg 11750723Scg * sb_get_byte returns a single byte from the DSP data port 11829415Sjmg */ 11929415Sjmg 12084111Scgstatic void 12184111Scgsb_lock(struct sb_info *sb) { 12284111Scg 12384111Scg sbc_lock(device_get_softc(sb->parent_dev)); 12484111Scg} 12584111Scg 12684111Scgstatic void 127129180Struckmansb_lockassert(struct sb_info *sb) { 128129180Struckman 129129180Struckman sbc_lockassert(device_get_softc(sb->parent_dev)); 130129180Struckman} 131129180Struckman 132129180Struckmanstatic void 13384111Scgsb_unlock(struct sb_info *sb) { 13484111Scg 13584111Scg sbc_unlock(device_get_softc(sb->parent_dev)); 13684111Scg} 13784111Scg 13829415Sjmgstatic int 13950723Scgport_rd(struct resource *port, int off) 14029415Sjmg{ 14167803Scg return bus_space_read_1(rman_get_bustag(port), rman_get_bushandle(port), off); 14250723Scg} 14329415Sjmg 14450723Scgstatic void 14550723Scgport_wr(struct resource *port, int off, u_int8_t data) 14650723Scg{ 147108064Ssemenu bus_space_write_1(rman_get_bustag(port), rman_get_bushandle(port), off, data); 14829415Sjmg} 14929415Sjmg 15029415Sjmgstatic int 15150723Scgsb_rd(struct sb_info *sb, int reg) 15229415Sjmg{ 15350723Scg return port_rd(sb->io_base, reg); 15450723Scg} 15529415Sjmg 15650723Scgstatic void 15750723Scgsb_wr(struct sb_info *sb, int reg, u_int8_t val) 15850723Scg{ 15950723Scg port_wr(sb->io_base, reg, val); 16029415Sjmg} 16129415Sjmg 16250723Scgstatic int 16350723Scgsb_dspwr(struct sb_info *sb, u_char val) 16429415Sjmg{ 16550723Scg int i; 16629415Sjmg 16750723Scg for (i = 0; i < 1000; i++) { 16870134Scg if ((sb_rd(sb, SBDSP_STATUS) & 0x80)) 16970134Scg DELAY((i > 100)? 1000 : 10); 17070134Scg else { 17170134Scg sb_wr(sb, SBDSP_CMD, val); 17270134Scg return 1; 17350723Scg } 17450723Scg } 17579116Sgreen#if __FreeBSD_version > 500000 17683366Sjulian if (curthread->td_intr_nesting_level == 0) 17774797Scg printf("sb_dspwr(0x%02x) timed out.\n", val); 17879090Sgreen#endif 17950723Scg return 0; 18050723Scg} 18129415Sjmg 18250723Scgstatic int 18350723Scgsb_cmd(struct sb_info *sb, u_char val) 18450723Scg{ 18550723Scg#if 0 18650723Scg printf("sb_cmd: %x\n", val); 18750723Scg#endif 18850723Scg return sb_dspwr(sb, val); 18950723Scg} 19029415Sjmg 19167803Scg/* 19250723Scgstatic int 19350723Scgsb_cmd1(struct sb_info *sb, u_char cmd, int val) 19450723Scg{ 19550723Scg#if 0 19650723Scg printf("sb_cmd1: %x, %x\n", cmd, val); 19750723Scg#endif 19850723Scg if (sb_dspwr(sb, cmd)) { 19950723Scg return sb_dspwr(sb, val & 0xff); 20050723Scg } else return 0; 20150723Scg} 20267803Scg*/ 20329415Sjmg 20450723Scgstatic int 20550723Scgsb_cmd2(struct sb_info *sb, u_char cmd, int val) 20650723Scg{ 20784111Scg int r; 20884111Scg 20950723Scg#if 0 21050723Scg printf("sb_cmd2: %x, %x\n", cmd, val); 21150723Scg#endif 212135115Struckman sb_lockassert(sb); 21384111Scg r = 0; 21450723Scg if (sb_dspwr(sb, cmd)) { 21584111Scg if (sb_dspwr(sb, val & 0xff)) { 21684111Scg if (sb_dspwr(sb, (val >> 8) & 0xff)) { 21784111Scg r = 1; 21884111Scg } 21984111Scg } 22084111Scg } 22184111Scg 22284111Scg return r; 22350723Scg} 22429415Sjmg 22550723Scg/* 22650723Scg * in the SB, there is a set of indirect "mixer" registers with 22750723Scg * address at offset 4, data at offset 5 22850723Scg */ 22950723Scgstatic void 23050723Scgsb_setmixer(struct sb_info *sb, u_int port, u_int value) 23150723Scg{ 23274763Scg sb_lock(sb); 23350723Scg sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ 23450723Scg DELAY(10); 23550723Scg sb_wr(sb, SB_MIX_DATA, (u_char) (value & 0xff)); 23650723Scg DELAY(10); 23774763Scg sb_unlock(sb); 23850723Scg} 23931361Sjmg 24050723Scgstatic int 24150723Scgsb_getmixer(struct sb_info *sb, u_int port) 24250723Scg{ 24350723Scg int val; 24429415Sjmg 245135115Struckman sb_lockassert(sb); 24650723Scg sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ 24750723Scg DELAY(10); 24850723Scg val = sb_rd(sb, SB_MIX_DATA); 24950723Scg DELAY(10); 25029415Sjmg 25150723Scg return val; 25250723Scg} 25329415Sjmg 25450723Scgstatic u_int 25550723Scgsb_get_byte(struct sb_info *sb) 25650723Scg{ 25750723Scg int i; 25829415Sjmg 25950723Scg for (i = 1000; i > 0; i--) { 26050723Scg if (sb_rd(sb, DSP_DATA_AVAIL) & 0x80) 26150723Scg return sb_rd(sb, DSP_READ); 26250723Scg else 26350723Scg DELAY(20); 26450723Scg } 26550723Scg return 0xffff; 26650723Scg} 26729415Sjmg 26850723Scgstatic int 26950723Scgsb_reset_dsp(struct sb_info *sb) 27029415Sjmg{ 27174763Scg u_char b; 27274763Scg 273129180Struckman sb_lockassert(sb); 27450723Scg sb_wr(sb, SBDSP_RST, 3); 27550723Scg DELAY(100); 27650723Scg sb_wr(sb, SBDSP_RST, 0); 27774763Scg b = sb_get_byte(sb); 27874763Scg if (b != 0xAA) { 27950723Scg DEB(printf("sb_reset_dsp 0x%lx failed\n", 28089774Sscottl rman_get_start(sb->io_base))); 28150723Scg return ENXIO; /* Sorry */ 28250723Scg } 28350723Scg return 0; 28429415Sjmg} 28529415Sjmg 28667803Scg/************************************************************/ 28767803Scg 28867803Scgstruct sb16_mixent { 28967803Scg int reg; 29067803Scg int bits; 29167803Scg int ofs; 29267803Scg int stereo; 29367803Scg}; 29467803Scg 29567803Scgstatic const struct sb16_mixent sb16_mixtab[32] = { 29667803Scg [SOUND_MIXER_VOLUME] = { 0x30, 5, 3, 1 }, 29767803Scg [SOUND_MIXER_PCM] = { 0x32, 5, 3, 1 }, 29867803Scg [SOUND_MIXER_SYNTH] = { 0x34, 5, 3, 1 }, 29967803Scg [SOUND_MIXER_CD] = { 0x36, 5, 3, 1 }, 30067803Scg [SOUND_MIXER_LINE] = { 0x38, 5, 3, 1 }, 30167803Scg [SOUND_MIXER_MIC] = { 0x3a, 5, 3, 0 }, 30267803Scg [SOUND_MIXER_SPEAKER] = { 0x3b, 5, 3, 0 }, 30367803Scg [SOUND_MIXER_IGAIN] = { 0x3f, 2, 6, 1 }, 30467803Scg [SOUND_MIXER_OGAIN] = { 0x41, 2, 6, 1 }, 30567803Scg [SOUND_MIXER_TREBLE] = { 0x44, 4, 4, 1 }, 30667803Scg [SOUND_MIXER_BASS] = { 0x46, 4, 4, 1 }, 307100953Ssobomax [SOUND_MIXER_LINE1] = { 0x52, 5, 3, 1 } 30867803Scg}; 30967803Scg 31067803Scgstatic int 31174763Scgsb16mix_init(struct snd_mixer *m) 31267803Scg{ 31367803Scg struct sb_info *sb = mix_getdevinfo(m); 31467803Scg 31567803Scg mix_setdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | 31667803Scg SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | 317100953Ssobomax SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | SOUND_MASK_LINE1 | 31867803Scg SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE); 31967803Scg 32067803Scg mix_setrecdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_LINE | 321100953Ssobomax SOUND_MASK_LINE1 | SOUND_MASK_MIC | SOUND_MASK_CD); 32267803Scg 32367803Scg sb_setmixer(sb, 0x3c, 0x1f); /* make all output active */ 32467803Scg 32567803Scg sb_setmixer(sb, 0x3d, 0); /* make all inputs-l off */ 32667803Scg sb_setmixer(sb, 0x3e, 0); /* make all inputs-r off */ 32767803Scg 32867803Scg return 0; 32967803Scg} 33067803Scg 33167803Scgstatic int 332130472Sjosefrel2abs_volume(int x, int max) 333130472Sjosef{ 334130472Sjosef int temp; 335130472Sjosef 336130472Sjosef temp = ((x * max) + 50) / 100; 337130472Sjosef if (temp > max) 338130472Sjosef temp = max; 339130472Sjosef else if (temp < 0) 340130472Sjosef temp = 0; 341130472Sjosef return (temp); 342130472Sjosef} 343130472Sjosef 344130472Sjosefstatic int 34574763Scgsb16mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 34667803Scg{ 34767803Scg struct sb_info *sb = mix_getdevinfo(m); 34867803Scg const struct sb16_mixent *e; 34967803Scg int max; 35067803Scg 35167803Scg e = &sb16_mixtab[dev]; 35267803Scg max = (1 << e->bits) - 1; 35367803Scg 354130472Sjosef left = rel2abs_volume(left, max); 355130472Sjosef right = rel2abs_volume(right, max); 35667803Scg 35767803Scg sb_setmixer(sb, e->reg, left << e->ofs); 35867803Scg if (e->stereo) 35968376Scg sb_setmixer(sb, e->reg + 1, right << e->ofs); 36067803Scg else 36167803Scg right = left; 36267803Scg 36367803Scg left = (left * 100) / max; 36467803Scg right = (right * 100) / max; 36567803Scg 36667803Scg return left | (right << 8); 36767803Scg} 36867803Scg 36967803Scgstatic int 37074763Scgsb16mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 37167803Scg{ 37267803Scg struct sb_info *sb = mix_getdevinfo(m); 373152418Sariff u_char recdev_l, recdev_r; 37467803Scg 375152418Sariff recdev_l = 0; 376152418Sariff recdev_r = 0; 377152418Sariff if (src & SOUND_MASK_MIC) { 378152418Sariff recdev_l |= 0x01; /* mono mic */ 379152418Sariff recdev_r |= 0x01; 380152418Sariff } 38167803Scg 382152418Sariff if (src & SOUND_MASK_CD) { 383152418Sariff recdev_l |= 0x04; /* l cd */ 384152418Sariff recdev_r |= 0x02; /* r cd */ 385152418Sariff } 38667803Scg 387152418Sariff if (src & SOUND_MASK_LINE) { 388152418Sariff recdev_l |= 0x10; /* l line */ 389152418Sariff recdev_r |= 0x08; /* r line */ 390152418Sariff } 39167803Scg 392152418Sariff if (src & SOUND_MASK_SYNTH) { 393152418Sariff recdev_l |= 0x40; /* l midi */ 394152418Sariff recdev_r |= 0x20; /* r midi */ 395152418Sariff } 39667803Scg 397152418Sariff sb_setmixer(sb, SB16_IMASK_L, recdev_l); 398152418Sariff sb_setmixer(sb, SB16_IMASK_R, recdev_r); 39967803Scg 400100953Ssobomax /* Switch on/off FM tuner source */ 401100953Ssobomax if (src & SOUND_MASK_LINE1) 402100953Ssobomax sb_setmixer(sb, 0x4a, 0x0c); 403100953Ssobomax else 404100953Ssobomax sb_setmixer(sb, 0x4a, 0x00); 405100953Ssobomax 40667803Scg /* 40767803Scg * since the same volume controls apply to the input and 40867803Scg * output sections, the best approach to have a consistent 40967803Scg * behaviour among cards would be to disable the output path 41067803Scg * on devices which are used to record. 41167803Scg * However, since users like to have feedback, we only disable 41267803Scg * the mic -- permanently. 41367803Scg */ 41467803Scg sb_setmixer(sb, SB16_OMASK, 0x1f & ~1); 41567803Scg 41667803Scg return src; 41767803Scg} 41867803Scg 41970134Scgstatic kobj_method_t sb16mix_mixer_methods[] = { 42070134Scg KOBJMETHOD(mixer_init, sb16mix_init), 42170134Scg KOBJMETHOD(mixer_set, sb16mix_set), 42270134Scg KOBJMETHOD(mixer_setrecsrc, sb16mix_setrecsrc), 42370134Scg { 0, 0 } 42470134Scg}; 42570134ScgMIXER_DECLARE(sb16mix_mixer); 42670134Scg 42767803Scg/************************************************************/ 42867803Scg 42929415Sjmgstatic void 43067803Scgsb16_release_resources(struct sb_info *sb, device_t dev) 43129415Sjmg{ 43250723Scg if (sb->irq) { 43365644Scg if (sb->ih) 43465644Scg bus_teardown_intr(dev, sb->irq, sb->ih); 43565644Scg bus_release_resource(dev, SYS_RES_IRQ, 0, sb->irq); 43650723Scg sb->irq = 0; 43750723Scg } 43884111Scg if (sb->drq2) { 43984111Scg if (sb->drq2 != sb->drq1) { 44084111Scg isa_dma_release(rman_get_start(sb->drq2)); 44184111Scg bus_release_resource(dev, SYS_RES_DRQ, 1, sb->drq2); 44284111Scg } 44370291Scg sb->drq2 = 0; 44470291Scg } 44570291Scg if (sb->drq1) { 44674364Scg isa_dma_release(rman_get_start(sb->drq1)); 44754462Scg bus_release_resource(dev, SYS_RES_DRQ, 0, sb->drq1); 44850723Scg sb->drq1 = 0; 44950723Scg } 45070291Scg if (sb->io_base) { 45154462Scg bus_release_resource(dev, SYS_RES_IOPORT, 0, sb->io_base); 45250723Scg sb->io_base = 0; 45350723Scg } 45465644Scg if (sb->parent_dmat) { 45565644Scg bus_dma_tag_destroy(sb->parent_dmat); 45665644Scg sb->parent_dmat = 0; 45765644Scg } 45865644Scg free(sb, M_DEVBUF); 45950723Scg} 46029415Sjmg 46150723Scgstatic int 46267803Scgsb16_alloc_resources(struct sb_info *sb, device_t dev) 46350723Scg{ 46454462Scg int rid; 46554462Scg 46654462Scg rid = 0; 46750723Scg if (!sb->io_base) 468127135Snjl sb->io_base = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 469127135Snjl &rid, RF_ACTIVE); 47067803Scg 47154462Scg rid = 0; 47250723Scg if (!sb->irq) 473127135Snjl sb->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 474127135Snjl RF_ACTIVE); 47567803Scg 47654462Scg rid = 0; 47750723Scg if (!sb->drq1) 478127135Snjl sb->drq1 = bus_alloc_resource_any(dev, SYS_RES_DRQ, &rid, 479127135Snjl RF_ACTIVE); 48067803Scg 48154462Scg rid = 1; 48258756Scg if (!sb->drq2) 483127135Snjl sb->drq2 = bus_alloc_resource_any(dev, SYS_RES_DRQ, &rid, 484127135Snjl RF_ACTIVE); 48529415Sjmg 48650723Scg if (sb->io_base && sb->drq1 && sb->irq) { 48754462Scg isa_dma_acquire(rman_get_start(sb->drq1)); 48884111Scg isa_dmainit(rman_get_start(sb->drq1), sb->bufsize); 48929415Sjmg 49050723Scg if (sb->drq2) { 49154462Scg isa_dma_acquire(rman_get_start(sb->drq2)); 49284111Scg isa_dmainit(rman_get_start(sb->drq2), sb->bufsize); 49370291Scg } else { 49470291Scg sb->drq2 = sb->drq1; 49570291Scg pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); 49654462Scg } 49750723Scg return 0; 49850723Scg } else return ENXIO; 49929415Sjmg} 50029415Sjmg 50174763Scg/* sbc does locking for us */ 50254462Scgstatic void 50367803Scgsb_intr(void *arg) 50429415Sjmg{ 50567803Scg struct sb_info *sb = (struct sb_info *)arg; 506149948Snetchild int reason, c; 50729415Sjmg 50867803Scg /* 50967803Scg * The Vibra16X has separate flags for 8 and 16 bit transfers, but 51067803Scg * I have no idea how to tell capture from playback interrupts... 51167803Scg */ 51229415Sjmg 51367803Scg reason = 0; 51474763Scg sb_lock(sb); 51567803Scg c = sb_getmixer(sb, IRQ_STAT); 51674763Scg if (c & 1) 51774763Scg sb_rd(sb, DSP_DATA_AVAIL); /* 8-bit int ack */ 51841653Sbrian 51974763Scg if (c & 2) 52074763Scg sb_rd(sb, DSP_DATA_AVL16); /* 16-bit int ack */ 52174763Scg sb_unlock(sb); 52274763Scg 52367803Scg /* 52467803Scg * this tells us if the source is 8-bit or 16-bit dma. We 52567803Scg * have to check the io channel to map it to read or write... 52667803Scg */ 52729415Sjmg 52868414Scg if (sb->bd_flags & BD_F_SB16X) { 52968414Scg if (c & 1) { /* 8-bit format */ 53068414Scg if (sb->pch.fmt & AFMT_8BIT) 53168414Scg reason |= 1; 53268414Scg if (sb->rch.fmt & AFMT_8BIT) 53368414Scg reason |= 2; 53468414Scg } 53568414Scg if (c & 2) { /* 16-bit format */ 53668414Scg if (sb->pch.fmt & AFMT_16BIT) 53768414Scg reason |= 1; 53868414Scg if (sb->rch.fmt & AFMT_16BIT) 53968414Scg reason |= 2; 54068414Scg } 54168414Scg } else { 54268414Scg if (c & 1) { /* 8-bit dma */ 54370291Scg if (sb->pch.dch == 1) 54468414Scg reason |= 1; 54570291Scg if (sb->rch.dch == 1) 54668414Scg reason |= 2; 54768414Scg } 54868414Scg if (c & 2) { /* 16-bit dma */ 54970291Scg if (sb->pch.dch == 2) 55068414Scg reason |= 1; 55170291Scg if (sb->rch.dch == 2) 55268414Scg reason |= 2; 55368414Scg } 55468414Scg } 55550723Scg#if 0 55650723Scg printf("sb_intr: reason=%d c=0x%x\n", reason, c); 55750723Scg#endif 55867803Scg if ((reason & 1) && (sb->pch.run)) 55950723Scg chn_intr(sb->pch.channel); 56067803Scg 56167803Scg if ((reason & 2) && (sb->rch.run)) 56250723Scg chn_intr(sb->rch.channel); 56350723Scg} 56431361Sjmg 56550723Scgstatic int 56667803Scgsb_setup(struct sb_info *sb) 56750723Scg{ 56867803Scg struct sb_chinfo *ch; 56967803Scg u_int8_t v; 57067803Scg int l, pprio; 57129415Sjmg 57274763Scg sb_lock(sb); 57367803Scg if (sb->bd_flags & BD_F_DMARUN) 574110499Snyan sndbuf_dma(sb->pch.buffer, PCMTRIG_STOP); 57567803Scg if (sb->bd_flags & BD_F_DMARUN2) 576110499Snyan sndbuf_dma(sb->rch.buffer, PCMTRIG_STOP); 57767803Scg sb->bd_flags &= ~(BD_F_DMARUN | BD_F_DMARUN2); 57829415Sjmg 57967803Scg sb_reset_dsp(sb); 58029415Sjmg 58167803Scg if (sb->bd_flags & BD_F_SB16X) { 582149948Snetchild /* full-duplex doesn't work! */ 58368414Scg pprio = sb->pch.run? 1 : 0; 584149948Snetchild sndbuf_dmasetup(sb->pch.buffer, pprio? sb->drq1 : sb->drq2); 58570291Scg sb->pch.dch = pprio? 1 : 0; 586110499Snyan sndbuf_dmasetup(sb->rch.buffer, pprio? sb->drq2 : sb->drq1); 58770291Scg sb->rch.dch = pprio? 2 : 1; 58867803Scg } else { 58967803Scg if (sb->pch.run && sb->rch.run) { 59067803Scg pprio = (sb->rch.fmt & AFMT_16BIT)? 0 : 1; 591110499Snyan sndbuf_dmasetup(sb->pch.buffer, pprio? sb->drq2 : sb->drq1); 59270291Scg sb->pch.dch = pprio? 2 : 1; 593110499Snyan sndbuf_dmasetup(sb->rch.buffer, pprio? sb->drq1 : sb->drq2); 59470291Scg sb->rch.dch = pprio? 1 : 2; 59567803Scg } else { 59667803Scg if (sb->pch.run) { 597110499Snyan sndbuf_dmasetup(sb->pch.buffer, (sb->pch.fmt & AFMT_16BIT)? sb->drq2 : sb->drq1); 59870291Scg sb->pch.dch = (sb->pch.fmt & AFMT_16BIT)? 2 : 1; 599110499Snyan sndbuf_dmasetup(sb->rch.buffer, (sb->pch.fmt & AFMT_16BIT)? sb->drq1 : sb->drq2); 60070291Scg sb->rch.dch = (sb->pch.fmt & AFMT_16BIT)? 1 : 2; 60167803Scg } else if (sb->rch.run) { 602110499Snyan sndbuf_dmasetup(sb->pch.buffer, (sb->rch.fmt & AFMT_16BIT)? sb->drq1 : sb->drq2); 60370291Scg sb->pch.dch = (sb->rch.fmt & AFMT_16BIT)? 1 : 2; 604110499Snyan sndbuf_dmasetup(sb->rch.buffer, (sb->rch.fmt & AFMT_16BIT)? sb->drq2 : sb->drq1); 60570291Scg sb->rch.dch = (sb->rch.fmt & AFMT_16BIT)? 2 : 1; 60667803Scg } 60750723Scg } 60867803Scg } 60929415Sjmg 610110499Snyan sndbuf_dmasetdir(sb->pch.buffer, PCMDIR_PLAY); 611110499Snyan sndbuf_dmasetdir(sb->rch.buffer, PCMDIR_REC); 61229415Sjmg 61367803Scg /* 61468414Scg printf("setup: [pch = %d, pfmt = %d, pgo = %d] [rch = %d, rfmt = %d, rgo = %d]\n", 61568414Scg sb->pch.dch, sb->pch.fmt, sb->pch.run, sb->rch.dch, sb->rch.fmt, sb->rch.run); 61667803Scg */ 61755706Scg 61867803Scg ch = &sb->pch; 61967803Scg if (ch->run) { 62067803Scg l = ch->blksz; 62167803Scg if (ch->fmt & AFMT_16BIT) 62267803Scg l >>= 1; 62367803Scg l--; 62455706Scg 62567803Scg /* play speed */ 62667803Scg RANGE(ch->spd, 5000, 45000); 62767803Scg sb_cmd(sb, DSP_CMD_OUT16); 62867803Scg sb_cmd(sb, ch->spd >> 8); 62967803Scg sb_cmd(sb, ch->spd & 0xff); 63067803Scg 63167803Scg /* play format, length */ 63267803Scg v = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_DAC; 63367803Scg v |= (ch->fmt & AFMT_16BIT)? DSP_DMA16 : DSP_DMA8; 63467803Scg sb_cmd(sb, v); 63567803Scg 63667803Scg v = (ch->fmt & AFMT_STEREO)? DSP_F16_STEREO : 0; 63767803Scg v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0; 63867803Scg sb_cmd2(sb, v, l); 639110499Snyan sndbuf_dma(ch->buffer, PCMTRIG_START); 64067803Scg sb->bd_flags |= BD_F_DMARUN; 64150723Scg } 64250723Scg 64367803Scg ch = &sb->rch; 64467803Scg if (ch->run) { 64567803Scg l = ch->blksz; 64667803Scg if (ch->fmt & AFMT_16BIT) 64767803Scg l >>= 1; 64867803Scg l--; 64929415Sjmg 65067803Scg /* record speed */ 65167803Scg RANGE(ch->spd, 5000, 45000); 65267803Scg sb_cmd(sb, DSP_CMD_IN16); 65367803Scg sb_cmd(sb, ch->spd >> 8); 65467803Scg sb_cmd(sb, ch->spd & 0xff); 65567803Scg 65667803Scg /* record format, length */ 65767803Scg v = DSP_F16_AUTO | DSP_F16_FIFO_ON | DSP_F16_ADC; 65867803Scg v |= (ch->fmt & AFMT_16BIT)? DSP_DMA16 : DSP_DMA8; 65967803Scg sb_cmd(sb, v); 66067803Scg 66167803Scg v = (ch->fmt & AFMT_STEREO)? DSP_F16_STEREO : 0; 66267803Scg v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0; 66367803Scg sb_cmd2(sb, v, l); 664110499Snyan sndbuf_dma(ch->buffer, PCMTRIG_START); 66567803Scg sb->bd_flags |= BD_F_DMARUN2; 66629415Sjmg } 66774763Scg sb_unlock(sb); 66867803Scg 66967803Scg return 0; 67050723Scg} 67129415Sjmg 67255706Scg/* channel interface */ 67355706Scgstatic void * 67474763Scgsb16chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 67555706Scg{ 67655706Scg struct sb_info *sb = devinfo; 67755706Scg struct sb_chinfo *ch = (dir == PCMDIR_PLAY)? &sb->pch : &sb->rch; 67855706Scg 67955706Scg ch->parent = sb; 68055706Scg ch->channel = c; 68155706Scg ch->buffer = b; 68267803Scg ch->dir = dir; 68367803Scg 684136469Syongari if (sndbuf_alloc(ch->buffer, sb->parent_dmat, sb->bufsize) != 0) 68555706Scg return NULL; 68667803Scg 68755706Scg return ch; 68855706Scg} 68955706Scg 69055706Scgstatic int 69170134Scgsb16chan_setformat(kobj_t obj, void *data, u_int32_t format) 69255706Scg{ 69355706Scg struct sb_chinfo *ch = data; 69467803Scg struct sb_info *sb = ch->parent; 69555706Scg 69667803Scg ch->fmt = format; 69767803Scg sb->prio = ch->dir; 69867803Scg sb->prio16 = (ch->fmt & AFMT_16BIT)? 1 : 0; 69967803Scg 70055706Scg return 0; 70155706Scg} 70255706Scg 70355706Scgstatic int 70470134Scgsb16chan_setspeed(kobj_t obj, void *data, u_int32_t speed) 70555706Scg{ 70655706Scg struct sb_chinfo *ch = data; 70755706Scg 70867803Scg ch->spd = speed; 70967803Scg return speed; 71055706Scg} 71155706Scg 71255706Scgstatic int 71370134Scgsb16chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 71455706Scg{ 71555706Scg struct sb_chinfo *ch = data; 71655706Scg 71767803Scg ch->blksz = blocksize; 71870291Scg return ch->blksz; 71955706Scg} 72055706Scg 72155706Scgstatic int 72270134Scgsb16chan_trigger(kobj_t obj, void *data, int go) 72355706Scg{ 72455706Scg struct sb_chinfo *ch = data; 72567803Scg struct sb_info *sb = ch->parent; 72655706Scg 72760958Scg if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 72855706Scg return 0; 72955706Scg 73055706Scg if (go == PCMTRIG_START) 73167803Scg ch->run = 1; 73255706Scg else 73367803Scg ch->run = 0; 73468376Scg 73567803Scg sb_setup(sb); 73667803Scg 73755706Scg return 0; 73855706Scg} 73955706Scg 74055706Scgstatic int 74170134Scgsb16chan_getptr(kobj_t obj, void *data) 74255706Scg{ 74355706Scg struct sb_chinfo *ch = data; 74455706Scg 745110499Snyan return sndbuf_dmaptr(ch->buffer); 74655706Scg} 74755706Scg 74874763Scgstatic struct pcmchan_caps * 74970134Scgsb16chan_getcaps(kobj_t obj, void *data) 75055706Scg{ 75155706Scg struct sb_chinfo *ch = data; 75267803Scg struct sb_info *sb = ch->parent; 75355706Scg 75467803Scg if ((sb->prio == 0) || (sb->prio == ch->dir)) 75555706Scg return &sb16x_caps; 75655706Scg else 75767803Scg return sb->prio16? &sb16_caps8 : &sb16_caps16; 75855706Scg} 75955706Scg 76029415Sjmgstatic int 76170134Scgsb16chan_resetdone(kobj_t obj, void *data) 76229415Sjmg{ 76367803Scg struct sb_chinfo *ch = data; 76467803Scg struct sb_info *sb = ch->parent; 76531361Sjmg 76667803Scg sb->prio = 0; 76729415Sjmg 76867803Scg return 0; 76929415Sjmg} 77029415Sjmg 77170134Scgstatic kobj_method_t sb16chan_methods[] = { 77270134Scg KOBJMETHOD(channel_init, sb16chan_init), 77370134Scg KOBJMETHOD(channel_resetdone, sb16chan_resetdone), 77470134Scg KOBJMETHOD(channel_setformat, sb16chan_setformat), 77570134Scg KOBJMETHOD(channel_setspeed, sb16chan_setspeed), 77670134Scg KOBJMETHOD(channel_setblocksize, sb16chan_setblocksize), 77770134Scg KOBJMETHOD(channel_trigger, sb16chan_trigger), 77870134Scg KOBJMETHOD(channel_getptr, sb16chan_getptr), 77970134Scg KOBJMETHOD(channel_getcaps, sb16chan_getcaps), 78070134Scg { 0, 0 } 78170134Scg}; 78270134ScgCHANNEL_DECLARE(sb16chan); 78370134Scg 78467803Scg/************************************************************/ 78529415Sjmg 78629415Sjmgstatic int 78767803Scgsb16_probe(device_t dev) 78853553Stanimura{ 78954462Scg char buf[64]; 79055092Sdfr uintptr_t func, ver, r, f; 79153553Stanimura 79253553Stanimura /* The parent device has already been probed. */ 79354462Scg r = BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func); 79454462Scg if (func != SCF_PCM) 79553553Stanimura return (ENXIO); 79653553Stanimura 79754462Scg r = BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver); 79854791Scg f = (ver & 0xffff0000) >> 16; 79954462Scg ver &= 0x0000ffff; 80067803Scg if (f & BD_F_SB16) { 80167803Scg snprintf(buf, sizeof buf, "SB16 DSP %d.%02d%s", (int) ver >> 8, (int) ver & 0xff, 80267803Scg (f & BD_F_SB16X)? " (ViBRA16X)" : ""); 80367803Scg device_set_desc_copy(dev, buf); 80467803Scg return 0; 80567803Scg } else 80658756Scg return (ENXIO); 80753553Stanimura} 80853553Stanimura 80953553Stanimurastatic int 81067803Scgsb16_attach(device_t dev) 81153553Stanimura{ 81253553Stanimura struct sb_info *sb; 81355092Sdfr uintptr_t ver; 81484111Scg char status[SND_STATUSLEN], status2[SND_STATUSLEN]; 81553553Stanimura 81678564Sgreid sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT | M_ZERO); 81755706Scg if (!sb) 81855706Scg return ENXIO; 81953553Stanimura 82074763Scg sb->parent_dev = device_get_parent(dev); 82174763Scg BUS_READ_IVAR(sb->parent_dev, dev, 1, &ver); 82254462Scg sb->bd_id = ver & 0x0000ffff; 82354462Scg sb->bd_flags = (ver & 0xffff0000) >> 16; 82484111Scg sb->bufsize = pcm_getbuffersize(dev, 4096, SB16_BUFFSIZE, 65536); 82554462Scg 826129181Struckman if (sb16_alloc_resources(sb, dev)) 82767803Scg goto no; 828129180Struckman sb_lock(sb); 829129180Struckman if (sb_reset_dsp(sb)) { 830129180Struckman sb_unlock(sb); 83167803Scg goto no; 832129180Struckman } 833129180Struckman sb_unlock(sb); 83470134Scg if (mixer_init(dev, &sb16mix_mixer_class, sb)) 83567803Scg goto no; 836128232Sgreen if (snd_setup_intr(dev, sb->irq, 0, sb_intr, sb, &sb->ih)) 83767803Scg goto no; 83867803Scg 83970291Scg if (sb->bd_flags & BD_F_SB16X) 84067803Scg pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); 84167803Scg 84267803Scg sb->prio = 0; 84367803Scg 844166904Snetchild if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, 845166904Snetchild /*boundary*/0, 84667803Scg /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, 84767803Scg /*highaddr*/BUS_SPACE_MAXADDR, 84867803Scg /*filter*/NULL, /*filterarg*/NULL, 84984111Scg /*maxsize*/sb->bufsize, /*nsegments*/1, 850117126Sscottl /*maxsegz*/0x3ffff, /*flags*/0, 851117126Sscottl /*lockfunc*/busdma_lock_mutex, /*lockarg*/&Giant, 852117126Sscottl &sb->parent_dmat) != 0) { 85367803Scg device_printf(dev, "unable to create dma tag\n"); 85467803Scg goto no; 85567803Scg } 85667803Scg 85770291Scg if (!(pcm_getflags(dev) & SD_F_SIMPLEX)) 85884111Scg snprintf(status2, SND_STATUSLEN, ":%ld", rman_get_start(sb->drq2)); 85984111Scg else 86084111Scg status2[0] = '\0'; 86167803Scg 862152418Sariff snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %u %s", 86384111Scg rman_get_start(sb->io_base), rman_get_start(sb->irq), 864126695Smatk rman_get_start(sb->drq1), status2, sb->bufsize, 865126695Smatk PCM_KLDSTRING(snd_sb16)); 86684111Scg 86767803Scg if (pcm_register(dev, sb, 1, 1)) 86867803Scg goto no; 86970134Scg pcm_addchan(dev, PCMDIR_REC, &sb16chan_class, sb); 87070134Scg pcm_addchan(dev, PCMDIR_PLAY, &sb16chan_class, sb); 87167803Scg 87267803Scg pcm_setstatus(dev, status); 87367803Scg 87467803Scg return 0; 87567803Scg 87667803Scgno: 87767803Scg sb16_release_resources(sb, dev); 87867803Scg return ENXIO; 87953553Stanimura} 88053553Stanimura 88167803Scgstatic int 88267803Scgsb16_detach(device_t dev) 88367803Scg{ 88467803Scg int r; 88567803Scg struct sb_info *sb; 88667803Scg 88767803Scg r = pcm_unregister(dev); 88867803Scg if (r) 88967803Scg return r; 89067803Scg 89167803Scg sb = pcm_getdevinfo(dev); 89267803Scg sb16_release_resources(sb, dev); 89367803Scg return 0; 89467803Scg} 89567803Scg 89667803Scgstatic device_method_t sb16_methods[] = { 89753553Stanimura /* Device interface */ 89867803Scg DEVMETHOD(device_probe, sb16_probe), 89967803Scg DEVMETHOD(device_attach, sb16_attach), 90067803Scg DEVMETHOD(device_detach, sb16_detach), 90153553Stanimura 90253553Stanimura { 0, 0 } 90353553Stanimura}; 90453553Stanimura 90567803Scgstatic driver_t sb16_driver = { 90653553Stanimura "pcm", 90767803Scg sb16_methods, 90882180Scg PCM_SOFTC_SIZE, 90953553Stanimura}; 91053553Stanimura 91167803ScgDRIVER_MODULE(snd_sb16, sbc, sb16_driver, pcm_devclass, 0, 0); 912132236StanimuraMODULE_DEPEND(snd_sb16, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 91374763ScgMODULE_DEPEND(snd_sb16, snd_sbc, 1, 1, 1); 91467803ScgMODULE_VERSION(snd_sb16, 1); 915