sb16.c revision 53126
1139749Simp/* 2156000Smjacob * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3101704Smjacob * Copyright 1997,1998 Luigi Rizzo. 4101704Smjacob * 5101704Smjacob * Derived from files in the Voxware 3.5 distribution, 6101704Smjacob * Copyright by Hannu Savolainen 1994, under the same copyright 7101704Smjacob * conditions. 8101704Smjacob * All rights reserved. 9101704Smjacob * 10101704Smjacob * Redistribution and use in source and binary forms, with or without 11101704Smjacob * modification, are permitted provided that the following conditions 12101704Smjacob * are met: 13101704Smjacob * 1. Redistributions of source code must retain the above copyright 14101704Smjacob * notice, this list of conditions and the following disclaimer. 15101704Smjacob * 2. Redistributions in binary form must reproduce the above copyright 16101704Smjacob * notice, this list of conditions and the following disclaimer in the 17101704Smjacob * documentation and/or other materials provided with the distribution. 18101704Smjacob * 19101704Smjacob * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20101704Smjacob * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21101704Smjacob * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22101704Smjacob * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23101704Smjacob * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24101704Smjacob * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25101704Smjacob * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26101704Smjacob * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27156000Smjacob * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28156000Smjacob * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29156000Smjacob * SUCH DAMAGE. 30156000Smjacob * 31156104Smjacob * $FreeBSD: head/sys/dev/sound/isa/sb16.c 53126 1999-11-13 18:31:31Z dfr $ 32156000Smjacob */ 33156000Smjacob 34156000Smjacob#include <dev/pcm/sound.h> 35156000Smjacob#if NPCM > 0 36156000Smjacob 37156000Smjacob#define __SB_MIXER_C__ /* XXX warning... */ 38156000Smjacob#include <dev/pcm/isa/sb.h> 39156000Smjacob 40156000Smjacob/* channel interface */ 41156000Smjacobstatic void *sbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); 42156000Smjacobstatic int sbchan_setdir(void *data, int dir); 43156000Smjacobstatic int sbchan_setformat(void *data, u_int32_t format); 44156000Smjacobstatic int sbchan_setspeed(void *data, u_int32_t speed); 45156104Smjacobstatic int sbchan_setblocksize(void *data, u_int32_t blocksize); 46156000Smjacobstatic int sbchan_trigger(void *data, int go); 47156000Smjacobstatic int sbchan_getptr(void *data); 48156000Smjacobstatic pcmchan_caps *sbchan_getcaps(void *data); 49156000Smjacob 50156000Smjacob/* channel interface for ESS */ 51156000Smjacob#ifdef notyet 52156000Smjacobstatic void *esschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); 53156000Smjacob#endif 54156000Smjacobstatic int esschan_setdir(void *data, int dir); 55156000Smjacobstatic int esschan_setformat(void *data, u_int32_t format); 56156000Smjacobstatic int esschan_setspeed(void *data, u_int32_t speed); 57147883Sscottlstatic int esschan_setblocksize(void *data, u_int32_t blocksize); 58156000Smjacobstatic int esschan_trigger(void *data, int go); 59156000Smjacobstatic int esschan_getptr(void *data); 60159052Smjacobstatic pcmchan_caps *esschan_getcaps(void *data); 61159052Smjacobstatic pcmchan_caps sb_playcaps = { 62159052Smjacob 4000, 22050, 63159052Smjacob AFMT_U8, 64101704Smjacob AFMT_U8 65156000Smjacob}; 66147883Sscottl 67147883Sscottlstatic pcmchan_caps sb_reccaps = { 68147883Sscottl 4000, 13000, 69147883Sscottl AFMT_U8, 70156104Smjacob AFMT_U8 71147883Sscottl}; 72147883Sscottl 73147883Sscottlstatic pcmchan_caps sbpro_playcaps = { 74147883Sscottl 4000, 45000, 75147883Sscottl AFMT_STEREO | AFMT_U8, 76147883Sscottl AFMT_STEREO | AFMT_U8 77147883Sscottl}; 78147883Sscottl 79147883Sscottlstatic pcmchan_caps sbpro_reccaps = { 80147883Sscottl 4000, 15000, 81148679Sgibbs AFMT_STEREO | AFMT_U8, 82148679Sgibbs AFMT_STEREO | AFMT_U8 83148679Sgibbs}; 84156104Smjacob 85147883Sscottlstatic pcmchan_caps sb16_playcaps = { 86147883Sscottl 5000, 45000, 87147883Sscottl AFMT_STEREO | AFMT_S16_LE, 88147883Sscottl AFMT_STEREO | AFMT_S16_LE 89147883Sscottl}; 90147883Sscottl 91147883Sscottlstatic pcmchan_caps sb16_reccaps = { 92147883Sscottl 5000, 45000, 93147883Sscottl AFMT_STEREO | AFMT_U8, 94147883Sscottl AFMT_STEREO | AFMT_U8 95147883Sscottl}; 96101704Smjacob 97101704Smjacobstatic pcmchan_caps ess_playcaps = { 98134123Sobrien 5000, 49000, 99134123Sobrien AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, 100134123Sobrien AFMT_STEREO | AFMT_S16_LE 101147883Sscottl}; 102147883Sscottl 103147883Sscottlstatic pcmchan_caps ess_reccaps = { 104102199Smjacob 5000, 49000, 105147883Sscottl AFMT_STEREO | AFMT_U8 | AFMT_S16_LE, 106147883Sscottl AFMT_STEREO | AFMT_S16_LE 107157117Smjacob}; 108157117Smjacob 109147883Sscottlstatic pcm_channel sb_chantemplate = { 110147883Sscottl sbchan_init, 111147883Sscottl sbchan_setdir, 112101704Smjacob sbchan_setformat, 113101704Smjacob sbchan_setspeed, 114101704Smjacob sbchan_setblocksize, 115101704Smjacob sbchan_trigger, 116101704Smjacob sbchan_getptr, 117101704Smjacob sbchan_getcaps, 118101704Smjacob}; 119162133Smjacob 120147883Sscottlstatic pcm_channel ess_chantemplate = { 121101704Smjacob sbchan_init, 122147883Sscottl esschan_setdir, 123147883Sscottl esschan_setformat, 124147883Sscottl esschan_setspeed, 125147883Sscottl esschan_setblocksize, 126147883Sscottl esschan_trigger, 127147883Sscottl esschan_getptr, 128155521Smjacob esschan_getcaps, 129147883Sscottl}; 130147883Sscottl#define PLAIN_SB16(x) ((((x)->bd_flags) & (BD_F_SB16|BD_F_SB16X)) == BD_F_SB16) 131209960Smarius 132209599Skenstruct sb_info; 133164990Smjacob 134157117Smjacobstruct sb_chinfo { 135147883Sscottl struct sb_info *parent; 136147883Sscottl pcm_channel *channel; 137147883Sscottl snd_dbuf *buffer; 138147883Sscottl int dir; 139147883Sscottl u_int32_t fmt; 140147883Sscottl int ess_dma_started; 141147883Sscottl}; 142147883Sscottl 143147883Sscottlstruct sb_info { 144147883Sscottl struct resource *io_base; /* I/O address for the board */ 145147883Sscottl int io_rid; 146147883Sscottl struct resource *irq; 147147883Sscottl int irq_rid; 148147883Sscottl struct resource *drq1; /* play */ 149101704Smjacob int drq1_rid; 150147883Sscottl struct resource *drq2; /* rec */ 151147883Sscottl int drq2_rid; 152147883Sscottl bus_dma_tag_t parent_dmat; 153147883Sscottl 154147883Sscottl int dma16, dma8; 155147883Sscottl int bd_id; 156147883Sscottl u_long bd_flags; /* board-specific flags */ 157147883Sscottl struct sb_chinfo pch, rch; 158147883Sscottl}; 159147883Sscottl 160147883Sscottlstatic int sb_rd(struct sb_info *sb, int reg); 161157117Smjacobstatic void sb_wr(struct sb_info *sb, int reg, u_int8_t val); 162147883Sscottlstatic int sb_dspready(struct sb_info *sb); 163147883Sscottlstatic int sb_cmd(struct sb_info *sb, u_char val); 164147883Sscottlstatic int sb_cmd1(struct sb_info *sb, u_char cmd, int val); 165147883Sscottlstatic int sb_cmd2(struct sb_info *sb, u_char cmd, int val); 166147883Sscottlstatic u_int sb_get_byte(struct sb_info *sb); 167147883Sscottlstatic int ess_write(struct sb_info *sb, u_char reg, int val); 168147883Sscottlstatic int ess_read(struct sb_info *sb, u_char reg); 169147883Sscottl 170147883Sscottl/* 171147883Sscottl * in the SB, there is a set of indirect "mixer" registers with 172147883Sscottl * address at offset 4, data at offset 5 173147883Sscottl */ 174147883Sscottlstatic void sb_setmixer(struct sb_info *sb, u_int port, u_int value); 175147883Sscottlstatic int sb_getmixer(struct sb_info *sb, u_int port); 176147883Sscottl 177147883Sscottlstatic void sb_intr(void *arg); 178147883Sscottlstatic void ess_intr(void *arg); 179147883Sscottlstatic int sb_init(device_t dev, struct sb_info *sb); 180147883Sscottlstatic int sb_reset_dsp(struct sb_info *sb); 181147883Sscottl 182147883Sscottlstatic int sb_format(struct sb_chinfo *ch, u_int32_t format); 183147883Sscottlstatic int sb_speed(struct sb_chinfo *ch, int speed); 184147883Sscottlstatic int sb_start(struct sb_chinfo *ch); 185147883Sscottlstatic int sb_stop(struct sb_chinfo *ch); 186147883Sscottl 187147883Sscottlstatic int ess_format(struct sb_chinfo *ch, u_int32_t format); 188147883Sscottlstatic int ess_speed(struct sb_chinfo *ch, int speed); 189147883Sscottlstatic int ess_start(struct sb_chinfo *ch); 190157117Smjacobstatic int ess_stop(struct sb_chinfo *ch); 191162133Smjacobstatic int ess_abort(struct sb_chinfo *ch); 192147883Sscottlstatic int sbmix_init(snd_mixer *m); 193147883Sscottlstatic int sbmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right); 194147883Sscottlstatic int sbmix_setrecsrc(snd_mixer *m, u_int32_t src); 195147883Sscottl 196147883Sscottlstatic snd_mixer sb_mixer = { 197147883Sscottl "SoundBlaster mixer", 198147883Sscottl sbmix_init, 199147883Sscottl sbmix_set, 200147883Sscottl sbmix_setrecsrc, 201147883Sscottl}; 202157117Smjacob 203162133Smjacobstatic devclass_t pcm_devclass; 204147883Sscottl 205147883Sscottl/* 206147883Sscottl * Common code for the midi and pcm functions 207147883Sscottl * 208147883Sscottl * sb_cmd write a single byte to the CMD port. 209147883Sscottl * sb_cmd1 write a CMD + 1 byte arg 210147883Sscottl * sb_cmd2 write a CMD + 2 byte arg 211147883Sscottl * sb_get_byte returns a single byte from the DSP data port 212147883Sscottl * 213157117Smjacob * ess_write is actually sb_cmd1 214147883Sscottl * ess_read access ext. regs via sb_cmd(0xc0, reg) followed by sb_get_byte 215147883Sscottl */ 216147883Sscottl 217147883Sscottlstatic int 218147883Sscottlport_rd(struct resource *port, int off) 219147883Sscottl{ 220147883Sscottl return bus_space_read_1(rman_get_bustag(port), 221147883Sscottl rman_get_bushandle(port), 222147883Sscottl off); 223177808Sscottl} 224177808Sscottl 225147883Sscottlstatic void 226147883Sscottlport_wr(struct resource *port, int off, u_int8_t data) 227147883Sscottl{ 228147883Sscottl return bus_space_write_1(rman_get_bustag(port), 229147883Sscottl rman_get_bushandle(port), 230147883Sscottl off, data); 231147883Sscottl} 232147883Sscottl 233147883Sscottlstatic int 234147883Sscottlsb_rd(struct sb_info *sb, int reg) 235147883Sscottl{ 236147883Sscottl return port_rd(sb->io_base, reg); 237147883Sscottl} 238147883Sscottl 239147883Sscottlstatic void 240147883Sscottlsb_wr(struct sb_info *sb, int reg, u_int8_t val) 241147883Sscottl{ 242147883Sscottl port_wr(sb->io_base, reg, val); 243157117Smjacob} 244147883Sscottl 245147883Sscottlstatic int 246147883Sscottlsb_dspready(struct sb_info *sb) 247147883Sscottl{ 248147883Sscottl return ((sb_rd(sb, SBDSP_STATUS) & 0x80) == 0); 249147883Sscottl} 250147883Sscottl 251147883Sscottlstatic int 252147883Sscottlsb_dspwr(struct sb_info *sb, u_char val) 253147883Sscottl{ 254147883Sscottl int i; 255147883Sscottl 256147883Sscottl for (i = 0; i < 1000; i++) { 257147883Sscottl if (sb_dspready(sb)) { 258147883Sscottl sb_wr(sb, SBDSP_CMD, val); 259147883Sscottl return 1; 260147883Sscottl } 261147883Sscottl if (i > 10) DELAY((i > 100)? 1000 : 10); 262147883Sscottl } 263147883Sscottl printf("sb_dspwr(0x%02x) timed out.\n", val); 264147883Sscottl return 0; 265147883Sscottl} 266147883Sscottl 267147883Sscottlstatic int 268147883Sscottlsb_cmd(struct sb_info *sb, u_char val) 269147883Sscottl{ 270147883Sscottl#if 0 271147883Sscottl printf("sb_cmd: %x\n", val); 272147883Sscottl#endif 273147883Sscottl return sb_dspwr(sb, val); 274147883Sscottl} 275147883Sscottl 276147883Sscottlstatic int 277147883Sscottlsb_cmd1(struct sb_info *sb, u_char cmd, int val) 278147883Sscottl{ 279147883Sscottl#if 0 280147883Sscottl printf("sb_cmd1: %x, %x\n", cmd, val); 281147883Sscottl#endif 282147883Sscottl if (sb_dspwr(sb, cmd)) { 283147883Sscottl return sb_dspwr(sb, val & 0xff); 284147883Sscottl } else return 0; 285147883Sscottl} 286147883Sscottl 287147883Sscottlstatic int 288147883Sscottlsb_cmd2(struct sb_info *sb, u_char cmd, int val) 289157117Smjacob{ 290147883Sscottl#if 0 291147883Sscottl printf("sb_cmd2: %x, %x\n", cmd, val); 292157117Smjacob#endif 293147883Sscottl if (sb_dspwr(sb, cmd)) { 294147883Sscottl return sb_dspwr(sb, val & 0xff) && 295147883Sscottl sb_dspwr(sb, (val >> 8) & 0xff); 296147883Sscottl } else return 0; 297147883Sscottl} 298147883Sscottl 299147883Sscottl/* 300147883Sscottl * in the SB, there is a set of indirect "mixer" registers with 301147883Sscottl * address at offset 4, data at offset 5 302147883Sscottl */ 303147883Sscottlstatic void 304147883Sscottlsb_setmixer(struct sb_info *sb, u_int port, u_int value) 305147883Sscottl{ 306147883Sscottl u_long flags; 307220945Smarius 308147883Sscottl flags = spltty(); 309147883Sscottl sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ 310147883Sscottl DELAY(10); 311147883Sscottl sb_wr(sb, SB_MIX_DATA, (u_char) (value & 0xff)); 312147883Sscottl DELAY(10); 313147883Sscottl splx(flags); 314220945Smarius} 315147883Sscottl 316147883Sscottlstatic int 317147883Sscottlsb_getmixer(struct sb_info *sb, u_int port) 318147883Sscottl{ 319147883Sscottl int val; 320147883Sscottl u_long flags; 321220945Smarius 322147883Sscottl flags = spltty(); 323147883Sscottl sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ 324147883Sscottl DELAY(10); 325147883Sscottl val = sb_rd(sb, SB_MIX_DATA); 326157117Smjacob DELAY(10); 327157117Smjacob splx(flags); 328220945Smarius 329157117Smjacob return val; 330157117Smjacob} 331157117Smjacob 332162133Smjacobstatic u_int 333162133Smjacobsb_get_byte(struct sb_info *sb) 334162133Smjacob{ 335162133Smjacob int i; 336162133Smjacob 337162133Smjacob for (i = 1000; i > 0; i--) { 338157117Smjacob if (sb_rd(sb, DSP_DATA_AVAIL) & 0x80) 339155521Smjacob return sb_rd(sb, DSP_READ); 340147883Sscottl else 341155521Smjacob DELAY(20); 342147883Sscottl } 343147883Sscottl return 0xffff; 344147883Sscottl} 345147883Sscottl 346147883Sscottlstatic int 347147883Sscottless_write(struct sb_info *sb, u_char reg, int val) 348147883Sscottl{ 349147883Sscottl return sb_cmd1(sb, reg, val); 350147883Sscottl} 351147883Sscottl 352147883Sscottlstatic int 353147883Sscottless_read(struct sb_info *sb, u_char reg) 354147883Sscottl{ 355147883Sscottl return (sb_cmd(sb, 0xc0) && sb_cmd(sb, reg))? sb_get_byte(sb) : 0xffff; 356147883Sscottl} 357147883Sscottl 358147883Sscottlstatic int 359147883Sscottlsb_reset_dsp(struct sb_info *sb) 360147883Sscottl{ 361147883Sscottl sb_wr(sb, SBDSP_RST, 3); 362147883Sscottl DELAY(100); 363147883Sscottl sb_wr(sb, SBDSP_RST, 0); 364220945Smarius if (sb_get_byte(sb) != 0xAA) { 365147883Sscottl DEB(printf("sb_reset_dsp 0x%lx failed\n", 366147883Sscottl rman_get_start(d->io_base))); 367147883Sscottl return ENXIO; /* Sorry */ 368162133Smjacob } 369162133Smjacob if (sb->bd_flags & BD_F_ESS) sb_cmd(sb, 0xc6); 370162133Smjacob return 0; 371162133Smjacob} 372162133Smjacob 373162133Smjacobstatic void 374162133Smjacobsb_release_resources(struct sb_info *sb, device_t dev) 375162133Smjacob{ 376162133Smjacob /* should we bus_teardown_intr here? */ 377162133Smjacob if (sb->irq) { 378162133Smjacob bus_release_resource(dev, SYS_RES_IRQ, sb->irq_rid, sb->irq); 379162133Smjacob sb->irq = 0; 380162133Smjacob } 381162133Smjacob if (sb->drq1) { 382162133Smjacob bus_release_resource(dev, SYS_RES_DRQ, sb->drq1_rid, sb->drq1); 383162133Smjacob sb->drq1 = 0; 384162133Smjacob } 385162133Smjacob if (sb->drq2) { 386162133Smjacob bus_release_resource(dev, SYS_RES_DRQ, sb->drq2_rid, sb->drq2); 387147883Sscottl sb->drq2 = 0; 388147883Sscottl } 389147883Sscottl if (sb->io_base) { 390147883Sscottl bus_release_resource(dev, SYS_RES_IOPORT, sb->io_rid, 391147883Sscottl sb->io_base); 392147883Sscottl sb->io_base = 0; 393147883Sscottl } 394147883Sscottl free(sb, M_DEVBUF); 395147883Sscottl} 396147883Sscottl 397147883Sscottlstatic int 398147883Sscottlsb_alloc_resources(struct sb_info *sb, device_t dev) 399147883Sscottl{ 400147883Sscottl if (!sb->io_base) 401147883Sscottl sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, 402147883Sscottl &sb->io_rid, 0, ~0, 1, 403147883Sscottl RF_ACTIVE); 404147883Sscottl if (!sb->irq) 405147883Sscottl sb->irq = bus_alloc_resource(dev, SYS_RES_IRQ, 406147883Sscottl &sb->irq_rid, 0, ~0, 1, 407147883Sscottl RF_ACTIVE); 408147883Sscottl if (!sb->drq1) 409147883Sscottl sb->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, 410147883Sscottl &sb->drq1_rid, 0, ~0, 1, 411147883Sscottl RF_ACTIVE); 412147883Sscottl if (!sb->drq2 && sb->drq2_rid > 0) 413147883Sscottl sb->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, 414147883Sscottl &sb->drq2_rid, 0, ~0, 1, 415147883Sscottl RF_ACTIVE); 416147883Sscottl 417147883Sscottl if (sb->io_base && sb->drq1 && sb->irq) { 418147883Sscottl sb->dma8 = rman_get_start(sb->drq1); 419147883Sscottl isa_dma_acquire(sb->dma8); 420147883Sscottl isa_dmainit(sb->dma8, DSP_BUFFSIZE); 421147883Sscottl 422147883Sscottl if (sb->drq2) { 423147883Sscottl sb->dma16 = rman_get_start(sb->drq2); 424147883Sscottl isa_dma_acquire(sb->dma16); 425147883Sscottl isa_dmainit(sb->dma16, DSP_BUFFSIZE); 426147883Sscottl } else sb->dma16 = sb->dma8; 427147883Sscottl 428147883Sscottl if (sb->dma8 > sb->dma16) { 429147883Sscottl int tmp = sb->dma16; 430147883Sscottl sb->dma16 = sb->dma8; 431147883Sscottl sb->dma8 = tmp; 432147883Sscottl } 433147883Sscottl return 0; 434157117Smjacob } else return ENXIO; 435157117Smjacob} 436147883Sscottl 437147883Sscottlstatic int 438147883Sscottlsb_identify_board(device_t dev, struct sb_info *sb) 439157117Smjacob{ 440147883Sscottl char *fmt = NULL; 441157117Smjacob static char buf[64]; 442147883Sscottl int essver = 0; 443147883Sscottl 444147883Sscottl sb_cmd(sb, DSP_CMD_GETVER); /* Get version */ 445147883Sscottl sb->bd_id = (sb_get_byte(sb) << 8) | sb_get_byte(sb); 446147883Sscottl 447147883Sscottl switch (sb->bd_id >> 8) { 448147883Sscottl case 1: /* old sound blaster has nothing... */ 449147883Sscottl case 2: 450147883Sscottl fmt = "SoundBlaster %d.%d" ; /* default */ 451147883Sscottl break; 452147883Sscottl 453147883Sscottl case 3: 454147883Sscottl fmt = "SoundBlaster Pro %d.%d"; 455147883Sscottl if (sb->bd_id == 0x301) { 456147883Sscottl int rev; 457147883Sscottl 458147883Sscottl /* Try to detect ESS chips. */ 459147883Sscottl sb_cmd(sb, DSP_CMD_GETID); /* Return ident. bytes. */ 460147883Sscottl essver = (sb_get_byte(sb) << 8) | sb_get_byte(sb); 461147883Sscottl rev = essver & 0x000f; 462147883Sscottl essver &= 0xfff0; 463147883Sscottl if (essver == 0x4880) { 464147883Sscottl /* the ESS488 can be treated as an SBPRO */ 465147883Sscottl fmt = "SoundBlaster Pro (ESS488 rev %d)"; 466147883Sscottl } else if (essver == 0x6880) { 467147883Sscottl if (rev < 8) fmt = "ESS688 rev %d"; 468147883Sscottl else fmt = "ESS1868 rev %d"; 469147883Sscottl sb->bd_flags |= BD_F_ESS; 470147883Sscottl } else return ENXIO; 471147883Sscottl sb->bd_id &= 0xff00; 472147883Sscottl sb->bd_id |= ((essver & 0xf000) >> 8) | rev; 473147883Sscottl } 474147883Sscottl break; 475147883Sscottl 476147883Sscottl case 4: 477147883Sscottl sb->bd_flags |= BD_F_SB16; 478147883Sscottl if (sb->bd_flags & BD_F_SB16X) fmt = "SB16 ViBRA16X %d.%d"; 479157117Smjacob else fmt = "SoundBlaster 16 %d.%d"; 480147883Sscottl break; 481157117Smjacob 482157117Smjacob default: 483157117Smjacob device_printf(dev, "failed to get SB version (%x)\n", 484147883Sscottl sb->bd_id); 485147883Sscottl return ENXIO; 486147883Sscottl } 487147883Sscottl if (essver) snprintf(buf, sizeof buf, fmt, sb->bd_id & 0x000f); 488157117Smjacob else snprintf(buf, sizeof buf, fmt, sb->bd_id >> 8, sb->bd_id & 0xff); 489147883Sscottl device_set_desc_copy(dev, buf); 490147883Sscottl return sb_reset_dsp(sb); 491147883Sscottl} 492147883Sscottl 493147883Sscottlstatic int 494147883Sscottlsb_init(device_t dev, struct sb_info *sb) 495157117Smjacob{ 496147883Sscottl int x, irq; 497147883Sscottl 498147883Sscottl sb->bd_flags &= ~BD_F_MIX_MASK; 499147883Sscottl /* do various initializations depending on board id. */ 500147883Sscottl switch (sb->bd_id >> 8) { 501147883Sscottl case 1: /* old sound blaster has nothing... */ 502147883Sscottl break; 503147883Sscottl 504147883Sscottl case 2: 505147883Sscottl sb->bd_flags |= BD_F_DUP_MIDI; 506147883Sscottl if (sb->bd_id > 0x200) sb->bd_flags |= BD_F_MIX_CT1335; 507147883Sscottl break; 508170252Sscottl 509170252Sscottl case 3: 510147883Sscottl sb->bd_flags |= BD_F_DUP_MIDI | BD_F_MIX_CT1345; 511147883Sscottl break; 512147883Sscottl 513147883Sscottl case 4: 514157354Smjacob sb->bd_flags |= BD_F_SB16 | BD_F_MIX_CT1745; 515147883Sscottl if (sb->dma16 != sb->dma8) sb->bd_flags |= BD_F_DUPLEX; 516160396Smjacob 517160396Smjacob /* soft irq/dma configuration */ 518160396Smjacob x = -1; 519160396Smjacob irq = rman_get_start(sb->irq); 520160396Smjacob if (irq == 5) x = 2; 521157354Smjacob else if (irq == 7) x = 4; 522147883Sscottl else if (irq == 9) x = 1; 523147883Sscottl else if (irq == 10) x = 8; 524157354Smjacob if (x == -1) device_printf(dev, 525147883Sscottl "bad irq %d (5/7/9/10 valid)\n", 526147883Sscottl irq); 527147883Sscottl else sb_setmixer(sb, IRQ_NR, x); 528147883Sscottl sb_setmixer(sb, DMA_NR, (1 << sb->dma16) | (1 << sb->dma8)); 529157117Smjacob break; 530147883Sscottl } 531147883Sscottl return 0; 532157354Smjacob} 533147883Sscottl 534147883Sscottlstatic int 535147883Sscottlsb_probe(device_t dev) 536147883Sscottl{ 537157117Smjacob snddev_info *d = device_get_softc(dev); 538147883Sscottl struct sb_info *sb; 539147883Sscottl int allocated, i; 540147883Sscottl int error; 541157354Smjacob 542157354Smjacob if (isa_get_vendorid(dev)) return ENXIO; /* not yet */ 543147883Sscottl 544147883Sscottl device_set_desc(dev, "SoundBlaster"); 545147883Sscottl bzero(d, sizeof *d); 546147883Sscottl sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT); 547147883Sscottl if (!sb) return ENXIO; 548147883Sscottl bzero(sb, sizeof *sb); 549147883Sscottl 550147883Sscottl allocated = 0; 551147883Sscottl sb->io_rid = 0; 552147883Sscottl sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &sb->io_rid, 553147883Sscottl 0, ~0, 16, RF_ACTIVE); 554164315Sjb if (!sb->io_base) { 555164315Sjb BVDDB(printf("sb_probe: no addr, trying (0x220, 0x240)\n")); 556164315Sjb allocated = 1; 557164315Sjb sb->io_rid = 0; 558147883Sscottl sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, 559147883Sscottl &sb->io_rid, 0x220, 0x22f, 560147883Sscottl 16, RF_ACTIVE); 561155521Smjacob if (!sb->io_base) { 562156301Smjacob sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, 563155521Smjacob &sb->io_rid, 0x240, 564155521Smjacob 0x24f, 16, RF_ACTIVE); 565155521Smjacob } 566155521Smjacob } 567173303Sscottl if (!sb->io_base) return ENXIO; 568173303Sscottl 569155521Smjacob error = sb_reset_dsp(sb); 570155521Smjacob if (error) goto no; 571155521Smjacob error = sb_identify_board(dev, sb); 572155521Smjacob if (error) goto no; 573147883Sscottlno: 574147883Sscottl i = sb->io_rid; 575147883Sscottl sb_release_resources(sb, dev); 576147883Sscottl if (allocated) bus_delete_resource(dev, SYS_RES_IOPORT, i); 577147883Sscottl return error; 578164315Sjb} 579157354Smjacob 580147883Sscottlstatic int 581147883Sscottlsb_doattach(device_t dev, struct sb_info *sb) 582147883Sscottl{ 583147883Sscottl snddev_info *d = device_get_softc(dev); 584147883Sscottl void *ih; 585147883Sscottl int error; 586147883Sscottl char status[SND_STATUSLEN]; 587147883Sscottl 588147883Sscottl sb->irq_rid = 0; 589147883Sscottl sb->drq1_rid = 0; 590157354Smjacob sb->drq2_rid = 1; 591157354Smjacob if (sb_alloc_resources(sb, dev)) goto no; 592157354Smjacob error = sb_reset_dsp(sb); 593157354Smjacob if (error) goto no; 594147883Sscottl error = sb_identify_board(dev, sb); 595147883Sscottl if (error) goto no; 596147883Sscottl 597147883Sscottl sb_init(dev, sb); 598157354Smjacob mixer_init(d, &sb_mixer, sb); 599147883Sscottl if (sb->bd_flags & BD_F_ESS) 600147883Sscottl bus_setup_intr(dev, sb->irq, INTR_TYPE_TTY, ess_intr, sb, &ih); 601147883Sscottl else 602147883Sscottl bus_setup_intr(dev, sb->irq, INTR_TYPE_TTY, sb_intr, sb, &ih); 603157354Smjacob 604147883Sscottl if (sb->bd_flags & BD_F_SB16) 605147883Sscottl pcm_setflags(dev, pcm_getflags(dev) | SD_F_EVILSB16); 606147883Sscottl if (sb->dma16 == sb->dma8) 607147883Sscottl pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); 608157354Smjacob if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, 609157354Smjacob /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, 610157354Smjacob /*highaddr*/BUS_SPACE_MAXADDR, 611157354Smjacob /*filter*/NULL, /*filterarg*/NULL, 612157354Smjacob /*maxsize*/DSP_BUFFSIZE, /*nsegments*/1, 613157354Smjacob /*maxsegz*/0x3ffff, 614157354Smjacob /*flags*/0, &sb->parent_dmat) != 0) { 615157354Smjacob device_printf(dev, "unable to create dma tag\n"); 616157354Smjacob goto no; 617157354Smjacob } 618157354Smjacob 619157354Smjacob snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %d", 620157354Smjacob rman_get_start(sb->io_base), rman_get_start(sb->irq), 621147883Sscottl sb->dma8); 622157354Smjacob if (sb->dma16 != sb->dma8) snprintf(status + strlen(status), 623157354Smjacob SND_STATUSLEN - strlen(status), ":%d", sb->dma16); 624157354Smjacob 625157354Smjacob if (pcm_register(dev, sb, 1, 1)) goto no; 626157354Smjacob if (sb->bd_flags & BD_F_ESS) { 627157354Smjacob pcm_addchan(dev, PCMDIR_REC, &ess_chantemplate, sb); 628157354Smjacob pcm_addchan(dev, PCMDIR_PLAY, &ess_chantemplate, sb); 629157354Smjacob } else { 630157354Smjacob pcm_addchan(dev, PCMDIR_REC, &sb_chantemplate, sb); 631157354Smjacob pcm_addchan(dev, PCMDIR_PLAY, &sb_chantemplate, sb); 632157354Smjacob } 633157354Smjacob pcm_setstatus(dev, status); 634157354Smjacob 635157354Smjacob return 0; 636157354Smjacob 637157354Smjacobno: 638157354Smjacob sb_release_resources(sb, dev); 639157354Smjacob return ENXIO; 640157354Smjacob} 641147883Sscottl 642157354Smjacobstatic int 643147883Sscottlsb_attach(device_t dev) 644147883Sscottl{ 645147883Sscottl struct sb_info *sb; 646147883Sscottl int flags = device_get_flags(dev); 647147883Sscottl 648147883Sscottl if (flags & DV_F_DUAL_DMA) { 649147883Sscottl bus_set_resource(dev, SYS_RES_DRQ, 1, 650147883Sscottl flags & DV_F_DRQ_MASK, 1); 651147883Sscottl } 652147883Sscottl sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT); 653155521Smjacob if (!sb) return ENXIO; 654155521Smjacob bzero(sb, sizeof *sb); 655147883Sscottl 656147883Sscottl /* XXX in probe should set io resource to right val instead of this */ 657147883Sscottl sb->io_rid = 0; 658147883Sscottl sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &sb->io_rid, 659147883Sscottl 0, ~0, 16, RF_ACTIVE); 660147883Sscottl if (!sb->io_base) { 661147883Sscottl BVDDB(printf("sb_probe: no addr, trying (0x220, 0x240)\n")); 662220945Smarius sb->io_rid = 0; 663147883Sscottl sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, 664147883Sscottl &sb->io_rid, 0x220, 0x22f, 665147883Sscottl 16, RF_ACTIVE); 666147883Sscottl if (!sb->io_base) { 667147883Sscottl sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, 668147883Sscottl &sb->io_rid, 0x240, 669147883Sscottl 0x24f, 16, RF_ACTIVE); 670147883Sscottl } 671147883Sscottl } 672147883Sscottl if (!sb->io_base) return ENXIO; 673147883Sscottl 674147883Sscottl return sb_doattach(dev, sb); 675147883Sscottl} 676147883Sscottl 677155521Smjacobstatic device_method_t sb_methods[] = { 678155521Smjacob /* Device interface */ 679147883Sscottl DEVMETHOD(device_probe, sb_probe), 680157354Smjacob DEVMETHOD(device_attach, sb_attach), 681147883Sscottl 682147883Sscottl { 0, 0 } 683157354Smjacob}; 684147883Sscottl 685147883Sscottlstatic driver_t sb_driver = { 686147883Sscottl "pcm", 687147883Sscottl sb_methods, 688147883Sscottl sizeof(snddev_info), 689147883Sscottl}; 690147883Sscottl 691147883SscottlDRIVER_MODULE(sb, isa, sb_driver, pcm_devclass, 0, 0); 692147883Sscottl 693157354Smjacobstatic void 694147883Sscottlsb_intr(void *arg) 695164315Sjb{ 696164315Sjb struct sb_info *sb = (struct sb_info *)arg; 697164315Sjb int reason = 3, c; 698147883Sscottl 699147883Sscottl /* 700147883Sscottl * SB < 4.0 is half duplex and has only 1 bit for int source, 701147883Sscottl * so we fake it. SB 4.x (SB16) has the int source in a separate 702147883Sscottl * register. 703147883Sscottl * The Vibra16X has separate flags for 8 and 16 bit transfers, but 704147883Sscottl * I have no idea how to tell capture from playback interrupts... 705147883Sscottl */ 706147883Sscottl if (sb->bd_flags & BD_F_SB16) { 707157117Smjacob c = sb_getmixer(sb, IRQ_STAT); 708157117Smjacob /* this tells us if the source is 8-bit or 16-bit dma. We 709147883Sscottl * have to check the io channel to map it to read or write... 710147883Sscottl */ 711157662Smjacob reason = 0; 712169293Smjacob if (c & 1) { /* 8-bit dma */ 713169293Smjacob if (sb->pch.fmt & AFMT_U8) reason |= 1; 714147883Sscottl if (sb->rch.fmt & AFMT_U8) reason |= 2; 715147883Sscottl } 716147883Sscottl if (c & 2) { /* 16-bit dma */ 717147883Sscottl if (sb->pch.fmt & AFMT_S16_LE) reason |= 1; 718157117Smjacob if (sb->rch.fmt & AFMT_S16_LE) reason |= 2; 719147883Sscottl } 720147883Sscottl } else c = 1; 721219335Smarius#if 0 722147883Sscottl printf("sb_intr: reason=%d c=0x%x\n", reason, c); 723147883Sscottl#endif 724147883Sscottl if ((reason & 1) && (sb->pch.buffer->dl > 0)) 725147883Sscottl chn_intr(sb->pch.channel); 726147883Sscottl if ((reason & 2) && (sb->rch.buffer->dl > 0)) 727219335Smarius chn_intr(sb->rch.channel); 728147883Sscottl if (c & 1) sb_rd(sb, DSP_DATA_AVAIL); /* 8-bit int ack */ 729147883Sscottl if (c & 2) sb_rd(sb, DSP_DATA_AVL16); /* 16-bit int ack */ 730219335Smarius} 731147883Sscottl 732157354Smjacobstatic void 733147883Sscottless_intr(void *arg) 734157117Smjacob{ 735157117Smjacob struct sb_info *sb = (struct sb_info *)arg; 736157117Smjacob sb_rd(sb, DSP_DATA_AVAIL); /* int ack */ 737147883Sscottl#ifdef notyet 738157117Smjacob /* 739157117Smjacob * XXX 740157117Smjacob * for full-duplex mode: 741157117Smjacob * should read port 0x6 to identify where interrupt came from. 742157117Smjacob */ 743157117Smjacob#endif 744157117Smjacob /* 745157117Smjacob * We are transferring data in DSP normal mode, 746157117Smjacob * so clear the dl to indicate the DMA is stopped. 747157117Smjacob */ 748157117Smjacob if (sb->pch.buffer->dl > 0) { 749157117Smjacob sb->pch.buffer->dl = -1; 750157117Smjacob chn_intr(sb->pch.channel); 751157117Smjacob } 752157117Smjacob if (sb->rch.buffer->dl > 0) { 753157117Smjacob sb->rch.buffer->dl = -1; 754157117Smjacob chn_intr(sb->rch.channel); 755157117Smjacob } 756157117Smjacob} 757157117Smjacob 758157117Smjacobstatic int 759157117Smjacobsb_format(struct sb_chinfo *ch, u_int32_t format) 760157117Smjacob{ 761157117Smjacob ch->fmt = format; 762157117Smjacob return 0; 763157117Smjacob} 764157117Smjacob 765157117Smjacobstatic int 766157117Smjacobsb_speed(struct sb_chinfo *ch, int speed) 767157117Smjacob{ 768157117Smjacob struct sb_info *sb = ch->parent; 769157117Smjacob int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; 770157117Smjacob int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; 771157117Smjacob 772157117Smjacob if (sb->bd_flags & BD_F_SB16) { 773157117Smjacob RANGE(speed, 5000, 45000); 774157117Smjacob sb_cmd(sb, 0x42 - play); 775157117Smjacob sb_cmd(sb, speed >> 8); 776157117Smjacob sb_cmd(sb, speed & 0xff); 777157117Smjacob } else { 778157117Smjacob u_char tconst; 779157117Smjacob int max_speed = 45000, tmp; 780157117Smjacob u_long flags; 781157117Smjacob 782157117Smjacob /* here enforce speed limitations - max 22050 on sb 1.x*/ 783157117Smjacob if (sb->bd_id <= 0x200) max_speed = 22050; 784157117Smjacob 785157117Smjacob /* 786157117Smjacob * SB models earlier than SB Pro have low limit for the 787157117Smjacob * input rate. Note that this is only for input, but since 788157117Smjacob * we do not support separate values for rec & play.... 789157117Smjacob */ 790157117Smjacob if (!play) { 791157117Smjacob if (sb->bd_id <= 0x200) max_speed = 13000; 792157117Smjacob else if (sb->bd_id < 0x300) max_speed = 15000; 793157117Smjacob } 794157117Smjacob RANGE(speed, 4000, max_speed); 795157117Smjacob if (stereo) speed <<= 1; 796157117Smjacob 797157117Smjacob /* 798157117Smjacob * Now the speed should be valid. Compute the value to be 799157117Smjacob * programmed into the board. 800157117Smjacob */ 801147883Sscottl if (speed > 22050) { /* High speed mode on 2.01/3.xx */ 802157117Smjacob tconst = (u_char) 803157117Smjacob ((65536 - ((256000000 + speed / 2) / speed)) 804157117Smjacob >> 8); 805157117Smjacob sb->bd_flags |= BD_F_HISPEED; 806147883Sscottl tmp = 65536 - (tconst << 8); 807157354Smjacob speed = (256000000 + tmp / 2) / tmp; 808157354Smjacob } else { 809157354Smjacob sb->bd_flags &= ~BD_F_HISPEED; 810157117Smjacob tconst = (256 - ((1000000 + speed / 2) / speed)) & 0xff; 811147883Sscottl tmp = 256 - tconst; 812219335Smarius speed = (1000000 + tmp / 2) / tmp; 813219335Smarius } 814157117Smjacob flags = spltty(); 815157117Smjacob sb_cmd1(sb, 0x40, tconst); /* set time constant */ 816147883Sscottl splx(flags); 817157354Smjacob if (stereo) speed >>= 1; 818219335Smarius } 819219335Smarius return speed; 820219335Smarius} 821147883Sscottl 822157354Smjacobstatic int 823157117Smjacobsb_start(struct sb_chinfo *ch) 824157117Smjacob{ 825157117Smjacob struct sb_info *sb = ch->parent; 826157117Smjacob int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; 827157117Smjacob int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0; 828157117Smjacob int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; 829157117Smjacob int l = ch->buffer->dl; 830157117Smjacob u_char i1, i2 = 0; 831157117Smjacob 832157117Smjacob if (b16) l >>= 1; 833157117Smjacob l--; 834147883Sscottl if (play) sb_cmd(sb, DSP_CMD_SPKON); 835157662Smjacob if (sb->bd_flags & BD_F_SB16) { 836147883Sscottl i1 = DSP_F16_AUTO | DSP_F16_FIFO_ON | 837147883Sscottl (play? DSP_F16_DAC : DSP_F16_ADC); 838147883Sscottl i1 |= (b16 && (sb->bd_flags & BD_F_DUPLEX))? DSP_DMA16 : DSP_DMA8; 839147883Sscottl i2 = (stereo? DSP_F16_STEREO : 0) | (b16? DSP_F16_SIGNED : 0); 840147883Sscottl sb_cmd(sb, i1); 841147883Sscottl sb_cmd2(sb, i2, l); 842147883Sscottl } else { 843147883Sscottl if (sb->bd_flags & BD_F_HISPEED) i1 = play? 0x90 : 0x98; 844147883Sscottl else i1 = play? 0x1c : 0x2c; 845147883Sscottl sb_setmixer(sb, 0x0e, stereo? 2 : 0); 846157354Smjacob /* an ESS extension -- they can do 16 bits */ 847147883Sscottl if (b16) i1 |= 1; 848156104Smjacob sb_cmd2(sb, 0x48, l); 849147883Sscottl sb_cmd(sb, i1); 850147883Sscottl } 851147883Sscottl sb->bd_flags |= BD_F_DMARUN << b16; 852157354Smjacob return 0; 853219335Smarius} 854219335Smarius 855147883Sscottlstatic int 856156104Smjacobsb_stop(struct sb_chinfo *ch) 857156104Smjacob{ 858147883Sscottl struct sb_info *sb = ch->parent; 859157117Smjacob int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; 860157117Smjacob int b16 = (ch->fmt & AFMT_S16_LE)? 1 : 0; 861213105Smarius 862213105Smarius if (sb->bd_flags & BD_F_HISPEED) sb_reset_dsp(sb); 863147883Sscottl else { 864147883Sscottl sb_cmd(sb, b16? DSP_CMD_DMAPAUSE_16 : DSP_CMD_DMAPAUSE_8); 865147883Sscottl /* 866147883Sscottl * The above seems to have the undocumented side effect of 867147883Sscottl * blocking the other side as well. If the other 868147883Sscottl * channel was active (SB16) I have to re-enable it :( 869147883Sscottl */ 870147883Sscottl if (sb->bd_flags & (BD_F_DMARUN << (1 - b16))) 871147883Sscottl sb_cmd(sb, b16? 0xd4 : 0xd6 ); 872147883Sscottl } 873147883Sscottl if (play) sb_cmd(sb, DSP_CMD_SPKOFF); /* speaker off */ 874147883Sscottl sb->bd_flags &= ~(BD_F_DMARUN << b16); 875147883Sscottl return 0; 876147883Sscottl} 877147883Sscottl 878147883Sscottl/* utility functions for ESS */ 879147883Sscottlstatic int 880147883Sscottless_format(struct sb_chinfo *ch, u_int32_t format) 881147883Sscottl{ 882147883Sscottl struct sb_info *sb = ch->parent; 883147883Sscottl int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; 884101704Smjacob int b16 = (format & AFMT_S16_LE)? 1 : 0; 885101704Smjacob int stereo = (format & AFMT_STEREO)? 1 : 0; 886101704Smjacob u_char c; 887147883Sscottl ch->fmt = format; 888147883Sscottl sb_reset_dsp(sb); 889101704Smjacob /* normal DMA mode */ 890101704Smjacob ess_write(sb, 0xb8, play ? 0x00 : 0x0a); 891101704Smjacob /* mono/stereo */ 892101704Smjacob c = (ess_read(sb, 0xa8) & ~0x03) | 1; 893101704Smjacob if (!stereo) c++; 894101704Smjacob ess_write(sb, 0xa8, c); 895147883Sscottl /* demand mode, 4 bytes/xfer */ 896101704Smjacob ess_write(sb, 0xb9, 2); 897101704Smjacob /* setup dac/adc */ 898101704Smjacob if (play) ess_write(sb, 0xb6, b16? 0x00 : 0x80); 899101704Smjacob ess_write(sb, 0xb7, 0x51 | (b16? 0x20 : 0x00)); 900101704Smjacob ess_write(sb, 0xb7, 0x98 + (b16? 0x24 : 0x00) + (stereo? 0x00 : 0x38)); 901157117Smjacob /* irq/drq control */ 902101704Smjacob ess_write(sb, 0xb1, (ess_read(sb, 0xb1) & 0x0f) | 0x50); 903157117Smjacob ess_write(sb, 0xb2, (ess_read(sb, 0xb2) & 0x0f) | 0x50); 904101704Smjacob return 0; 905157117Smjacob} 906101704Smjacob 907101704Smjacobstatic int 908101704Smjacobess_speed(struct sb_chinfo *ch, int speed) 909101704Smjacob{ 910147883Sscottl struct sb_info *sb = ch->parent; 911101704Smjacob int t; 912101704Smjacob RANGE (speed, 5000, 49000); 913164837Smjacob if (speed > 22000) { 914101704Smjacob t = (795500 + speed / 2) / speed; 915101704Smjacob speed = (795500 + t / 2) / t; 916101704Smjacob t = (256 - t ) | 0x80; 917101704Smjacob } else { 918101704Smjacob t = (397700 + speed / 2) / speed; 919101704Smjacob speed = (397700 + t / 2) / t; 920157117Smjacob t = 128 - t; 921101704Smjacob } 922101704Smjacob ess_write(sb, 0xa1, t); /* set time constant */ 923101704Smjacob#if 0 924101704Smjacob d->play_speed = d->rec_speed = speed; 925147883Sscottl speed = (speed * 9 ) / 20; 926101704Smjacob#endif 927147883Sscottl t = 256 - 7160000 / ((speed * 9 / 20) * 82); 928101704Smjacob ess_write(sb, 0xa2, t); 929147883Sscottl return speed; 930101704Smjacob} 931101704Smjacob 932101704Smjacobstatic int 933101704Smjacobess_start(struct sb_chinfo *ch) 934101704Smjacob{ 935101704Smjacob struct sb_info *sb = ch->parent; 936147883Sscottl int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; 937101704Smjacob short c = - ch->buffer->dl; 938101704Smjacob u_char c1; 939101704Smjacob /* 940101704Smjacob * clear bit 0 of register B8h 941147883Sscottl */ 942101704Smjacob#if 1 943101704Smjacob c1 = play ? 0x00 : 0x0a; 944101704Smjacob ess_write(sb, 0xb8, c1++); 945101704Smjacob#else 946101704Smjacob c1 = ess_read(sb, 0xb8) & 0xfe; 947101704Smjacob ess_write(sb, 0xb8, c1++); 948101704Smjacob#endif 949101704Smjacob /* 950101704Smjacob * update ESS Transfer Count Register 951101704Smjacob */ 952147883Sscottl ess_write(sb, 0xa4, (u_char)((u_short)c & 0xff)); 953147883Sscottl ess_write(sb, 0xa5, (u_char)(((u_short)c >> 8) & 0xff)); 954147883Sscottl /* 955101704Smjacob * set bit 0 of register B8h 956147883Sscottl */ 957147883Sscottl ess_write(sb, 0xb8, c1); 958101704Smjacob if (play) 959147883Sscottl sb_cmd(sb, DSP_CMD_SPKON); 960101704Smjacob return 0; 961101704Smjacob} 962101704Smjacob 963147883Sscottlstatic int 964157117Smjacobess_stop(struct sb_chinfo *ch) 965101704Smjacob{ 966101704Smjacob struct sb_info *sb = ch->parent; 967101704Smjacob /* 968101704Smjacob * no need to send a stop command if the DMA has already stopped. 969101704Smjacob */ 970101704Smjacob if (ch->buffer->dl > 0) { 971101704Smjacob sb_cmd(sb, DSP_CMD_DMAPAUSE_8); /* pause dma. */ 972147883Sscottl } 973157117Smjacob return 0; 974101704Smjacob} 975101704Smjacob 976101704Smjacobstatic int 977101704Smjacobess_abort(struct sb_chinfo *ch) 978101704Smjacob{ 979101704Smjacob struct sb_info *sb = ch->parent; 980147883Sscottl int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; 981157117Smjacob if (play) sb_cmd(sb, DSP_CMD_SPKOFF); /* speaker off */ 982101704Smjacob sb_reset_dsp(sb); 983101704Smjacob ess_format(ch, ch->fmt); 984101704Smjacob ess_speed(ch, ch->channel->speed); 985101704Smjacob return 0; 986147883Sscottl} 987157117Smjacob 988101704Smjacob/* channel interface */ 989101704Smjacobstatic void * 990101704Smjacobsbchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) 991101704Smjacob{ 992101704Smjacob struct sb_info *sb = devinfo; 993147883Sscottl struct sb_chinfo *ch = (dir == PCMDIR_PLAY)? &sb->pch : &sb->rch; 994147883Sscottl 995147883Sscottl ch->parent = sb; 996147883Sscottl ch->channel = c; 997147883Sscottl ch->buffer = b; 998147883Sscottl ch->buffer->bufsize = DSP_BUFFSIZE; 999147883Sscottl if (chn_allocbuf(ch->buffer, sb->parent_dmat) == -1) return NULL; 1000147883Sscottl ch->buffer->chan = (dir == PCMDIR_PLAY)? sb->dma16 : sb->dma8; 1001147883Sscottl return ch; 1002147883Sscottl} 1003147883Sscottl 1004147883Sscottlstatic int 1005147883Sscottlsbchan_setdir(void *data, int dir) 1006147883Sscottl{ 1007147883Sscottl struct sb_chinfo *ch = data; 1008147883Sscottl ch->dir = dir; 1009147883Sscottl return 0; 1010147883Sscottl} 1011147883Sscottl 1012147883Sscottlstatic int 1013147883Sscottlsbchan_setformat(void *data, u_int32_t format) 1014147883Sscottl{ 1015147883Sscottl struct sb_chinfo *ch = data; 1016147883Sscottl sb_format(ch, format); 1017147883Sscottl return 0; 1018147883Sscottl} 1019147883Sscottl 1020147883Sscottlstatic int 1021147883Sscottlsbchan_setspeed(void *data, u_int32_t speed) 1022147883Sscottl{ 1023147883Sscottl struct sb_chinfo *ch = data; 1024147883Sscottl return sb_speed(ch, speed); 1025101704Smjacob} 1026156104Smjacob 1027101704Smjacobstatic int 1028147883Sscottlsbchan_setblocksize(void *data, u_int32_t blocksize) 1029147883Sscottl{ 1030101704Smjacob return blocksize; 1031147883Sscottl} 1032147883Sscottl 1033147883Sscottlstatic int 1034147883Sscottlsbchan_trigger(void *data, int go) 1035147883Sscottl{ 1036147883Sscottl struct sb_chinfo *ch = data; 1037147883Sscottl buf_isadma(ch->buffer, go); 1038147883Sscottl if (go == PCMTRIG_START) sb_start(ch); else sb_stop(ch); 1039147883Sscottl return 0; 1040147883Sscottl} 1041101704Smjacob 1042101704Smjacobstatic int 1043147883Sscottlsbchan_getptr(void *data) 1044101704Smjacob{ 1045147883Sscottl struct sb_chinfo *ch = data; 1046147883Sscottl return buf_isadmaptr(ch->buffer); 1047147883Sscottl} 1048147883Sscottl 1049147883Sscottlstatic pcmchan_caps * 1050147883Sscottlsbchan_getcaps(void *data) 1051147883Sscottl{ 1052101704Smjacob struct sb_chinfo *ch = data; 1053147883Sscottl int p = (ch->dir == PCMDIR_PLAY)? 1 : 0; 1054101704Smjacob if (ch->parent->bd_id <= 0x200) 1055147883Sscottl return p? &sb_playcaps : &sb_reccaps; 1056147883Sscottl else if (ch->parent->bd_id >= 0x400) 1057147883Sscottl return p? &sb16_playcaps : &sb16_reccaps; 1058147883Sscottl else 1059147883Sscottl return p? &sbpro_playcaps : &sbpro_reccaps; 1060147883Sscottl} 1061147883Sscottl/* channel interface for ESS18xx */ 1062147883Sscottl#ifdef notyet 1063147883Sscottlstatic void * 1064147883Sscottlesschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) 1065147883Sscottl{ 1066101704Smjacob /* the same as sbchan_init()? */ 1067147883Sscottl} 1068147883Sscottl#endif 1069147883Sscottl 1070147883Sscottlstatic int 1071101704Smjacobesschan_setdir(void *data, int dir) 1072147883Sscottl{ 1073147883Sscottl struct sb_chinfo *ch = data; 1074147883Sscottl ch->dir = dir; 1075147883Sscottl return 0; 1076147883Sscottl} 1077147883Sscottl 1078147883Sscottlstatic int 1079147883Sscottlesschan_setformat(void *data, u_int32_t format) 1080147883Sscottl{ 1081147883Sscottl struct sb_chinfo *ch = data; 1082147883Sscottl ess_format(ch, format); 1083147883Sscottl return 0; 1084101704Smjacob} 1085101704Smjacob 1086147883Sscottlstatic int 1087147883Sscottlesschan_setspeed(void *data, u_int32_t speed) 1088147883Sscottl{ 1089147883Sscottl struct sb_chinfo *ch = data; 1090147883Sscottl return ess_speed(ch, speed); 1091147883Sscottl} 1092101704Smjacob 1093101704Smjacobstatic int 1094147883Sscottlesschan_setblocksize(void *data, u_int32_t blocksize) 1095147883Sscottl{ 1096147883Sscottl return blocksize; 1097147883Sscottl} 1098147883Sscottl 1099147883Sscottlstatic int 1100147883Sscottlesschan_trigger(void *data, int go) 1101147883Sscottl{ 1102147883Sscottl struct sb_chinfo *ch = data; 1103147883Sscottl switch (go) { 1104147883Sscottl case PCMTRIG_START: 1105147883Sscottl if (!ch->ess_dma_started) 1106101704Smjacob buf_isadma(ch->buffer, go); 1107101704Smjacob ch->ess_dma_started = 1; 1108101704Smjacob ess_start(ch); 1109101704Smjacob break; 1110101704Smjacob case PCMTRIG_STOP: 1111101704Smjacob if (ch->buffer->dl >= 0) { 1112101704Smjacob buf_isadma(ch->buffer, go); 1113147883Sscottl ch->ess_dma_started = 0; 1114101704Smjacob ess_stop(ch); 1115147883Sscottl } 1116147883Sscottl break; 1117156104Smjacob case PCMTRIG_ABORT: 1118101704Smjacob default: 1119156104Smjacob ch->ess_dma_started = 0; 1120156104Smjacob ess_abort(ch); 1121156104Smjacob buf_isadma(ch->buffer, go); 1122156104Smjacob break; 1123101704Smjacob } 1124156104Smjacob return 0; 1125156104Smjacob} 1126156104Smjacob 1127156104Smjacobstatic int 1128101704Smjacobesschan_getptr(void *data) 1129156104Smjacob{ 1130156104Smjacob struct sb_chinfo *ch = data; 1131156104Smjacob return buf_isadmaptr(ch->buffer); 1132156104Smjacob} 1133156104Smjacob 1134156104Smjacobstatic pcmchan_caps * 1135156104Smjacobesschan_getcaps(void *data) 1136156104Smjacob{ 1137156104Smjacob struct sb_chinfo *ch = data; 1138156104Smjacob return (ch->dir == PCMDIR_PLAY)? &ess_playcaps : &ess_reccaps; 1139156104Smjacob} 1140156104Smjacob 1141156104Smjacob/************************************************************/ 1142156104Smjacob 1143156104Smjacobstatic int 1144156104Smjacobsbmix_init(snd_mixer *m) 1145156104Smjacob{ 1146156104Smjacob struct sb_info *sb = mix_getdevinfo(m); 1147101704Smjacob 1148101704Smjacob switch (sb->bd_flags & BD_F_MIX_MASK) { 1149156104Smjacob case BD_F_MIX_CT1345: /* SB 3.0 has 1345 mixer */ 1150156104Smjacob mix_setdevs(m, SBPRO_MIXER_DEVICES); 1151156104Smjacob mix_setrecdevs(m, SBPRO_RECORDING_DEVICES); 1152156104Smjacob sb_setmixer(sb, 0, 1); /* reset mixer */ 1153156104Smjacob sb_setmixer(sb, MIC_VOL, 0x6); /* mic volume max */ 1154156104Smjacob sb_setmixer(sb, RECORD_SRC, 0x0); /* mic source */ 1155156104Smjacob sb_setmixer(sb, FM_VOL, 0x0); /* no midi */ 1156156104Smjacob break; 1157156104Smjacob 1158156104Smjacob case BD_F_MIX_CT1745: /* SB16 mixer ... */ 1159147883Sscottl mix_setdevs(m, SB16_MIXER_DEVICES); 1160157354Smjacob mix_setrecdevs(m, SB16_RECORDING_DEVICES); 1161157117Smjacob sb_setmixer(sb, 0x3c, 0x1f); /* make all output active */ 1162156104Smjacob sb_setmixer(sb, 0x3d, 0); /* make all inputs-l off */ 1163156104Smjacob sb_setmixer(sb, 0x3e, 0); /* make all inputs-r off */ 1164156104Smjacob } 1165156104Smjacob return 0; 1166156104Smjacob} 1167156104Smjacob 1168156104Smjacobstatic int 1169101704Smjacobsbmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) 1170101704Smjacob{ 1171101704Smjacob struct sb_info *sb = mix_getdevinfo(m); 1172101704Smjacob int regoffs; 1173101704Smjacob u_char val; 1174147883Sscottl mixer_tab *iomap; 1175101704Smjacob 1176155521Smjacob switch (sb->bd_flags & BD_F_MIX_MASK) { 1177147883Sscottl case BD_F_MIX_CT1345: 1178219335Smarius if (sb->bd_flags & BD_F_ESS) 1179147883Sscottl iomap = &ess_mix; 1180103871Smjacob else 1181101704Smjacob iomap = &sbpro_mix; 1182101704Smjacob break; 1183101704Smjacob 1184155521Smjacob case BD_F_MIX_CT1745: 1185155521Smjacob iomap = &sb16_mix; 1186155521Smjacob break; 1187155521Smjacob 1188157354Smjacob default: 1189157354Smjacob return -1; 1190169293Smjacob /* XXX how about the SG NX Pro, iomap = sgnxpro_mix */ 1191157662Smjacob } 1192157662Smjacob 1193157662Smjacob /* Change left channel */ 1194157662Smjacob regoffs = (*iomap)[dev][LEFT_CHN].regno; 1195157662Smjacob if (regoffs != 0) { 1196157662Smjacob val = sb_getmixer(sb, regoffs); 1197157662Smjacob change_bits(iomap, &val, dev, LEFT_CHN, left); 1198157662Smjacob sb_setmixer(sb, regoffs, val); 1199157662Smjacob } 1200157354Smjacob 1201101704Smjacob /* Change right channel */ 1202147883Sscottl regoffs = (*iomap)[dev][RIGHT_CHN].regno; 1203157117Smjacob if (regoffs != 0) { 1204157117Smjacob val = sb_getmixer(sb, regoffs); /* Read the new one */ 1205157117Smjacob change_bits(iomap, &val, dev, RIGHT_CHN, right); 1206157354Smjacob sb_setmixer(sb, regoffs, val); 1207157354Smjacob } else 1208157662Smjacob right = left; 1209157662Smjacob 1210157662Smjacob return left | (right << 8); 1211157117Smjacob} 1212147883Sscottl 1213147883Sscottlstatic int 1214147883Sscottlsbmix_setrecsrc(snd_mixer *m, u_int32_t src) 1215147883Sscottl{ 1216147883Sscottl struct sb_info *sb = mix_getdevinfo(m); 1217147883Sscottl u_char recdev; 1218147883Sscottl 1219147883Sscottl switch (sb->bd_flags & BD_F_MIX_MASK) { 1220147883Sscottl case BD_F_MIX_CT1345: 1221147883Sscottl if (src == SOUND_MASK_LINE) recdev = 0x06; 1222147883Sscottl else if (src == SOUND_MASK_CD) recdev = 0x02; 1223147883Sscottl else { /* default: mic */ 1224157354Smjacob src = SOUND_MASK_MIC; 1225157662Smjacob recdev = 0; 1226147883Sscottl } 1227219335Smarius sb_setmixer(sb, RECORD_SRC, recdev | 1228219335Smarius (sb_getmixer(sb, RECORD_SRC) & ~0x07)); 1229219335Smarius break; 1230219335Smarius 1231147883Sscottl case BD_F_MIX_CT1745: /* sb16 */ 1232101704Smjacob recdev = 0; 1233101704Smjacob if (src & SOUND_MASK_MIC) recdev |= 0x01; /* mono mic */ 1234101704Smjacob if (src & SOUND_MASK_CD) recdev |= 0x06; /* l+r cd */ 1235101704Smjacob if (src & SOUND_MASK_LINE) recdev |= 0x18; /* l+r line */ 1236147883Sscottl if (src & SOUND_MASK_SYNTH) recdev |= 0x60; /* l+r midi */ 1237101704Smjacob sb_setmixer(sb, SB16_IMASK_L, recdev); 1238101704Smjacob sb_setmixer(sb, SB16_IMASK_R, recdev); 1239147883Sscottl /* 1240147883Sscottl * since the same volume controls apply to the input and 1241169293Smjacob * output sections, the best approach to have a consistent 1242147883Sscottl * behaviour among cards would be to disable the output path 1243101704Smjacob * on devices which are used to record. 1244147883Sscottl * However, since users like to have feedback, we only disable 1245147883Sscottl * the mic -- permanently. 1246157354Smjacob */ 1247157662Smjacob sb_setmixer(sb, SB16_OMASK, 0x1f & ~1); 1248157662Smjacob break; 1249157662Smjacob } 1250147883Sscottl return src; 1251147883Sscottl} 1252155521Smjacob 1253157662Smjacob#if NPNP > 0 1254147883Sscottlstatic int 1255147883Sscottlsbpnp_probe(device_t dev) 1256147883Sscottl{ 1257147883Sscottl char *s = NULL; 1258101704Smjacob u_int32_t logical_id = isa_get_logicalid(dev); 1259157354Smjacob 1260101704Smjacob switch(logical_id) { 1261101704Smjacob case 0x43008c0e: /* CTL0043 */ 1262101704Smjacob case 0x01008c0e: /* CTL0001 */ 1263101704Smjacob s = "Vibra16X"; 1264147883Sscottl break; 1265101704Smjacob 1266158982Smjacob case 0x31008c0e: /* CTL0031 */ 1267158932Smjacob case 0x41008c0e: /* CTL0041 */ 1268155521Smjacob case 0x42008c0e: /* CTL0042 */ 1269101704Smjacob s = "SB16 PnP"; 1270219335Smarius break; 1271147883Sscottl 1272157662Smjacob case 0x44008c0e: /* CTL0044 */ 1273157662Smjacob s = "Creative SB AWE64 Gold"; 1274157662Smjacob break; 1275157662Smjacob 1276157662Smjacob case 0x45008c0e: /* CTL0045 */ 1277157662Smjacob s = "Creative AWE64 PnP"; 1278147883Sscottl break; 1279147883Sscottl 1280101704Smjacob case 0x01100000: /* @@@1001 */ 1281101704Smjacob s = "Avance Asound 110"; 1282101704Smjacob break; 1283147883Sscottl 1284147883Sscottl case 0x01200000: /* @@@2001 */ 1285147883Sscottl s = "Avance Logic ALS120"; 1286147883Sscottl break; 1287147883Sscottl 1288147883Sscottl case 0x68187316: /* ESS1868 */ 1289147883Sscottl s = "ESS1868"; 1290147883Sscottl break; 1291147883Sscottl 1292147883Sscottl case 0x69187316: /* ESS1869 */ 1293147883Sscottl s = "ESS1869"; 1294101704Smjacob break; 1295147883Sscottl 1296147883Sscottl case 0x88187316: /* ESS1888 */ 1297147883Sscottl s = "ESS1888"; 1298147883Sscottl break; 1299101704Smjacob } 1300147883Sscottl if (s) { 1301147883Sscottl device_set_desc(dev, s); 1302147883Sscottl return 0; 1303101704Smjacob } 1304147883Sscottl return ENXIO; 1305147883Sscottl} 1306147883Sscottl 1307147883Sscottlstatic int 1308147883Sscottlsbpnp_attach(device_t dev) 1309157117Smjacob{ 1310147883Sscottl struct sb_info *sb; 1311157117Smjacob u_int32_t vend_id = isa_get_vendorid(dev); 1312147883Sscottl 1313157117Smjacob sb = (struct sb_info *)malloc(sizeof *sb, M_DEVBUF, M_NOWAIT); 1314147883Sscottl if (!sb) return ENXIO; 1315147883Sscottl bzero(sb, sizeof *sb); 1316155521Smjacob 1317157117Smjacob switch(vend_id) { 1318147883Sscottl case 0xf0008c0e: 1319147883Sscottl case 0x10019305: 1320147883Sscottl case 0x20019305: 1321147883Sscottl /* XXX add here the vend_id for other vibra16X cards... */ 1322147883Sscottl sb->bd_flags = BD_F_SB16X; 1323147883Sscottl } 1324147883Sscottl return sb_doattach(dev, sb); 1325147883Sscottl} 1326147883Sscottl 1327147883Sscottlstatic device_method_t sbpnp_methods[] = { 1328147883Sscottl /* Device interface */ 1329147883Sscottl DEVMETHOD(device_probe, sbpnp_probe), 1330147883Sscottl DEVMETHOD(device_attach, sbpnp_attach), 1331147883Sscottl 1332147883Sscottl { 0, 0 } 1333157117Smjacob}; 1334147883Sscottl 1335157117Smjacobstatic driver_t sbpnp_driver = { 1336157117Smjacob "pcm", 1337157117Smjacob sbpnp_methods, 1338160396Smjacob sizeof(snddev_info), 1339157117Smjacob}; 1340147883Sscottl 1341157117SmjacobDRIVER_MODULE(sbpnp, isa, sbpnp_driver, pcm_devclass, 0, 0); 1342147883Sscottl 1343101704Smjacob#endif /* NPNP > 0 */ 1344101704Smjacob 1345101704Smjacob#endif /* NPCM > 0 */ 1346101704Smjacob 1347101704Smjacob 1348101704Smjacob