1fd1aaeccSCameron Grant /* 2fd1aaeccSCameron Grant * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3fd1aaeccSCameron Grant * 4fd1aaeccSCameron Grant * Redistribution and use in source and binary forms, with or without 5fd1aaeccSCameron Grant * modification, are permitted provided that the following conditions 6fd1aaeccSCameron Grant * are met: 7fd1aaeccSCameron Grant * 1. Redistributions of source code must retain the above copyright 8fd1aaeccSCameron Grant * notice, this list of conditions and the following disclaimer. 9fd1aaeccSCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 10fd1aaeccSCameron Grant * notice, this list of conditions and the following disclaimer in the 11fd1aaeccSCameron Grant * documentation and/or other materials provided with the distribution. 12fd1aaeccSCameron Grant * 13fd1aaeccSCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14fd1aaeccSCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15fd1aaeccSCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16fd1aaeccSCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17fd1aaeccSCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18fd1aaeccSCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19fd1aaeccSCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20fd1aaeccSCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21fd1aaeccSCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22fd1aaeccSCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23fd1aaeccSCameron Grant * SUCH DAMAGE. 24fd1aaeccSCameron Grant * 25fd1aaeccSCameron Grant * $FreeBSD$ 26fd1aaeccSCameron Grant */ 27fd1aaeccSCameron Grant 28fd1aaeccSCameron Grant #include <dev/sound/pcm/sound.h> 29fd1aaeccSCameron Grant 30fd1aaeccSCameron Grant #include <pci/pcireg.h> 31fd1aaeccSCameron Grant #include <pci/pcivar.h> 32fd1aaeccSCameron Grant 33fd1aaeccSCameron Grant #include <dev/sound/isa/sb.h> 34fd1aaeccSCameron Grant #include <dev/sound/chip.h> 35fd1aaeccSCameron Grant 363ac1ca33SNick Sayer #define ESS_BUFFSIZE (16384) 37fd1aaeccSCameron Grant #define ABS(x) (((x) < 0)? -(x) : (x)) 38fd1aaeccSCameron Grant 393ac1ca33SNick Sayer /* if defined, playback always uses the 2nd channel and full duplex works */ 40fd1aaeccSCameron Grant #undef ESS18XX_DUPLEX 41fd1aaeccSCameron Grant 42fd1aaeccSCameron Grant /* more accurate clocks and split audio1/audio2 rates */ 43fd1aaeccSCameron Grant #define ESS18XX_NEWSPEED 44fd1aaeccSCameron Grant 45fd1aaeccSCameron Grant /* channel interface for ESS */ 46fd1aaeccSCameron Grant static void *esschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); 47fd1aaeccSCameron Grant static int esschan_setdir(void *data, int dir); 48fd1aaeccSCameron Grant static int esschan_setformat(void *data, u_int32_t format); 49fd1aaeccSCameron Grant static int esschan_setspeed(void *data, u_int32_t speed); 50fd1aaeccSCameron Grant static int esschan_setblocksize(void *data, u_int32_t blocksize); 51fd1aaeccSCameron Grant static int esschan_trigger(void *data, int go); 52fd1aaeccSCameron Grant static int esschan_getptr(void *data); 53fd1aaeccSCameron Grant static pcmchan_caps *esschan_getcaps(void *data); 54fd1aaeccSCameron Grant 55fd1aaeccSCameron Grant static pcmchan_caps ess_playcaps = { 56fd1aaeccSCameron Grant 5000, 49000, 57fd1aaeccSCameron Grant AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_U16_LE | AFMT_S16_LE, 58fd1aaeccSCameron Grant AFMT_STEREO | AFMT_S16_LE 59fd1aaeccSCameron Grant }; 60fd1aaeccSCameron Grant 613ac1ca33SNick Sayer /* 623ac1ca33SNick Sayer * Recording output is byte-swapped 633ac1ca33SNick Sayer */ 64fd1aaeccSCameron Grant static pcmchan_caps ess_reccaps = { 65fd1aaeccSCameron Grant 5000, 49000, 663ac1ca33SNick Sayer AFMT_STEREO | AFMT_U8 | AFMT_S8 | AFMT_U16_BE | AFMT_S16_BE, 673ac1ca33SNick Sayer AFMT_STEREO | AFMT_S16_BE 68fd1aaeccSCameron Grant }; 69fd1aaeccSCameron Grant 70fd1aaeccSCameron Grant static pcm_channel ess_chantemplate = { 71fd1aaeccSCameron Grant esschan_init, 72fd1aaeccSCameron Grant esschan_setdir, 73fd1aaeccSCameron Grant esschan_setformat, 74fd1aaeccSCameron Grant esschan_setspeed, 75fd1aaeccSCameron Grant esschan_setblocksize, 76fd1aaeccSCameron Grant esschan_trigger, 77fd1aaeccSCameron Grant esschan_getptr, 78fd1aaeccSCameron Grant esschan_getcaps, 79fd1aaeccSCameron Grant }; 80fd1aaeccSCameron Grant 81fd1aaeccSCameron Grant struct ess_info; 82fd1aaeccSCameron Grant 83fd1aaeccSCameron Grant struct ess_chinfo { 84fd1aaeccSCameron Grant struct ess_info *parent; 85fd1aaeccSCameron Grant pcm_channel *channel; 86fd1aaeccSCameron Grant snd_dbuf *buffer; 87fd1aaeccSCameron Grant int dir, hwch, stopping; 88fd1aaeccSCameron Grant u_int32_t fmt, spd; 89fd1aaeccSCameron Grant }; 90fd1aaeccSCameron Grant 91fd1aaeccSCameron Grant struct ess_info { 92fd1aaeccSCameron Grant struct resource *io, *sb, *vc, *mpu, *gp; /* I/O address for the board */ 93fd1aaeccSCameron Grant struct resource *irq; 94fd1aaeccSCameron Grant bus_dma_tag_t parent_dmat; 95fd1aaeccSCameron Grant 963ac1ca33SNick Sayer int simplex_dir, type, duplex:1, newspeed:1, dmasz[2]; 97fd1aaeccSCameron Grant struct ess_chinfo pch, rch; 98fd1aaeccSCameron Grant }; 99fd1aaeccSCameron Grant 100fd1aaeccSCameron Grant static int ess_rd(struct ess_info *sc, int reg); 101fd1aaeccSCameron Grant static void ess_wr(struct ess_info *sc, int reg, u_int8_t val); 102fd1aaeccSCameron Grant static int ess_dspready(struct ess_info *sc); 103fd1aaeccSCameron Grant static int ess_cmd(struct ess_info *sc, u_char val); 104fd1aaeccSCameron Grant static int ess_cmd1(struct ess_info *sc, u_char cmd, int val); 105fd1aaeccSCameron Grant static int ess_get_byte(struct ess_info *sc); 106fd1aaeccSCameron Grant static void ess_setmixer(struct ess_info *sc, u_int port, u_int value); 107fd1aaeccSCameron Grant static int ess_getmixer(struct ess_info *sc, u_int port); 108fd1aaeccSCameron Grant static int ess_reset_dsp(struct ess_info *sc); 109fd1aaeccSCameron Grant 110fd1aaeccSCameron Grant static int ess_write(struct ess_info *sc, u_char reg, int val); 111fd1aaeccSCameron Grant static int ess_read(struct ess_info *sc, u_char reg); 112fd1aaeccSCameron Grant 113fd1aaeccSCameron Grant static void ess_intr(void *arg); 114fd1aaeccSCameron Grant static int ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int len); 115fd1aaeccSCameron Grant static int ess_start(struct ess_chinfo *ch); 116fd1aaeccSCameron Grant static int ess_stop(struct ess_chinfo *ch); 117fd1aaeccSCameron Grant 118fd1aaeccSCameron Grant static int ess_dmasetup(struct ess_info *sc, int ch, u_int32_t base, u_int16_t cnt, int dir); 119fd1aaeccSCameron Grant static int ess_dmapos(struct ess_info *sc, int ch); 120fd1aaeccSCameron Grant static int ess_dmatrigger(struct ess_info *sc, int ch, int go); 121fd1aaeccSCameron Grant 122fd1aaeccSCameron Grant static int essmix_init(snd_mixer *m); 123fd1aaeccSCameron Grant static int essmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right); 124fd1aaeccSCameron Grant static int essmix_setrecsrc(snd_mixer *m, u_int32_t src); 125fd1aaeccSCameron Grant 126fd1aaeccSCameron Grant static snd_mixer ess_mixer = { 127fd1aaeccSCameron Grant "ESS mixer", 128fd1aaeccSCameron Grant essmix_init, 129fd1aaeccSCameron Grant essmix_set, 130fd1aaeccSCameron Grant essmix_setrecsrc, 131fd1aaeccSCameron Grant }; 132fd1aaeccSCameron Grant 133fd1aaeccSCameron Grant static devclass_t pcm_devclass; 134fd1aaeccSCameron Grant 135fd1aaeccSCameron Grant /* 136fd1aaeccSCameron Grant * Common code for the midi and pcm functions 137fd1aaeccSCameron Grant * 138fd1aaeccSCameron Grant * ess_cmd write a single byte to the CMD port. 139fd1aaeccSCameron Grant * ess_cmd1 write a CMD + 1 byte arg 140fd1aaeccSCameron Grant * ess_cmd2 write a CMD + 2 byte arg 141fd1aaeccSCameron Grant * ess_get_byte returns a single byte from the DSP data port 142fd1aaeccSCameron Grant * 143fd1aaeccSCameron Grant * ess_write is actually ess_cmd1 144fd1aaeccSCameron Grant * ess_read access ext. regs via ess_cmd(0xc0, reg) followed by ess_get_byte 145fd1aaeccSCameron Grant */ 146fd1aaeccSCameron Grant 147fd1aaeccSCameron Grant static int 148fd1aaeccSCameron Grant port_rd(struct resource *port, int regno, int size) 149fd1aaeccSCameron Grant { 150fd1aaeccSCameron Grant bus_space_tag_t st = rman_get_bustag(port); 151fd1aaeccSCameron Grant bus_space_handle_t sh = rman_get_bushandle(port); 152fd1aaeccSCameron Grant 153fd1aaeccSCameron Grant switch (size) { 154fd1aaeccSCameron Grant case 1: 155fd1aaeccSCameron Grant return bus_space_read_1(st, sh, regno); 156fd1aaeccSCameron Grant case 2: 157fd1aaeccSCameron Grant return bus_space_read_2(st, sh, regno); 158fd1aaeccSCameron Grant case 4: 159fd1aaeccSCameron Grant return bus_space_read_4(st, sh, regno); 160fd1aaeccSCameron Grant default: 161fd1aaeccSCameron Grant return 0xffffffff; 162fd1aaeccSCameron Grant } 163fd1aaeccSCameron Grant } 164fd1aaeccSCameron Grant 165fd1aaeccSCameron Grant static void 166fd1aaeccSCameron Grant port_wr(struct resource *port, int regno, u_int32_t data, int size) 167fd1aaeccSCameron Grant { 168fd1aaeccSCameron Grant bus_space_tag_t st = rman_get_bustag(port); 169fd1aaeccSCameron Grant bus_space_handle_t sh = rman_get_bushandle(port); 170fd1aaeccSCameron Grant 171fd1aaeccSCameron Grant switch (size) { 172fd1aaeccSCameron Grant case 1: 173fd1aaeccSCameron Grant bus_space_write_1(st, sh, regno, data); 174fd1aaeccSCameron Grant break; 175fd1aaeccSCameron Grant case 2: 176fd1aaeccSCameron Grant bus_space_write_2(st, sh, regno, data); 177fd1aaeccSCameron Grant break; 178fd1aaeccSCameron Grant case 4: 179fd1aaeccSCameron Grant bus_space_write_4(st, sh, regno, data); 180fd1aaeccSCameron Grant break; 181fd1aaeccSCameron Grant } 182fd1aaeccSCameron Grant } 183fd1aaeccSCameron Grant 184fd1aaeccSCameron Grant static int 185fd1aaeccSCameron Grant ess_rd(struct ess_info *sc, int reg) 186fd1aaeccSCameron Grant { 187fd1aaeccSCameron Grant return port_rd(sc->sb, reg, 1); 188fd1aaeccSCameron Grant } 189fd1aaeccSCameron Grant 190fd1aaeccSCameron Grant static void 191fd1aaeccSCameron Grant ess_wr(struct ess_info *sc, int reg, u_int8_t val) 192fd1aaeccSCameron Grant { 193fd1aaeccSCameron Grant port_wr(sc->sb, reg, val, 1); 194fd1aaeccSCameron Grant } 195fd1aaeccSCameron Grant 196fd1aaeccSCameron Grant static int 197fd1aaeccSCameron Grant ess_dspready(struct ess_info *sc) 198fd1aaeccSCameron Grant { 199fd1aaeccSCameron Grant return ((ess_rd(sc, SBDSP_STATUS) & 0x80) == 0); 200fd1aaeccSCameron Grant } 201fd1aaeccSCameron Grant 202fd1aaeccSCameron Grant static int 203fd1aaeccSCameron Grant ess_dspwr(struct ess_info *sc, u_char val) 204fd1aaeccSCameron Grant { 205fd1aaeccSCameron Grant int i; 206fd1aaeccSCameron Grant 207fd1aaeccSCameron Grant for (i = 0; i < 1000; i++) { 208fd1aaeccSCameron Grant if (ess_dspready(sc)) { 209fd1aaeccSCameron Grant ess_wr(sc, SBDSP_CMD, val); 210fd1aaeccSCameron Grant return 1; 211fd1aaeccSCameron Grant } 212fd1aaeccSCameron Grant if (i > 10) DELAY((i > 100)? 1000 : 10); 213fd1aaeccSCameron Grant } 214fd1aaeccSCameron Grant printf("ess_dspwr(0x%02x) timed out.\n", val); 215fd1aaeccSCameron Grant return 0; 216fd1aaeccSCameron Grant } 217fd1aaeccSCameron Grant 218fd1aaeccSCameron Grant static int 219fd1aaeccSCameron Grant ess_cmd(struct ess_info *sc, u_char val) 220fd1aaeccSCameron Grant { 221a7e11506SNick Sayer DEB(printf("ess_cmd: %x\n", val)); 222fd1aaeccSCameron Grant return ess_dspwr(sc, val); 223fd1aaeccSCameron Grant } 224fd1aaeccSCameron Grant 225fd1aaeccSCameron Grant static int 226fd1aaeccSCameron Grant ess_cmd1(struct ess_info *sc, u_char cmd, int val) 227fd1aaeccSCameron Grant { 228a7e11506SNick Sayer DEB(printf("ess_cmd1: %x, %x\n", cmd, val)); 229fd1aaeccSCameron Grant if (ess_dspwr(sc, cmd)) { 230fd1aaeccSCameron Grant return ess_dspwr(sc, val & 0xff); 231fd1aaeccSCameron Grant } else return 0; 232fd1aaeccSCameron Grant } 233fd1aaeccSCameron Grant 234fd1aaeccSCameron Grant static void 235fd1aaeccSCameron Grant ess_setmixer(struct ess_info *sc, u_int port, u_int value) 236fd1aaeccSCameron Grant { 237fd1aaeccSCameron Grant u_long flags; 238fd1aaeccSCameron Grant 239fd1aaeccSCameron Grant DEB(printf("ess_setmixer: reg=%x, val=%x\n", port, value);) 240fd1aaeccSCameron Grant flags = spltty(); 241fd1aaeccSCameron Grant ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ 242fd1aaeccSCameron Grant DELAY(10); 243fd1aaeccSCameron Grant ess_wr(sc, SB_MIX_DATA, (u_char) (value & 0xff)); 244fd1aaeccSCameron Grant DELAY(10); 245fd1aaeccSCameron Grant splx(flags); 246fd1aaeccSCameron Grant } 247fd1aaeccSCameron Grant 248fd1aaeccSCameron Grant static int 249fd1aaeccSCameron Grant ess_getmixer(struct ess_info *sc, u_int port) 250fd1aaeccSCameron Grant { 251fd1aaeccSCameron Grant int val; 252fd1aaeccSCameron Grant u_long flags; 253fd1aaeccSCameron Grant 254fd1aaeccSCameron Grant flags = spltty(); 255fd1aaeccSCameron Grant ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ 256fd1aaeccSCameron Grant DELAY(10); 257fd1aaeccSCameron Grant val = ess_rd(sc, SB_MIX_DATA); 258fd1aaeccSCameron Grant DELAY(10); 259fd1aaeccSCameron Grant splx(flags); 260fd1aaeccSCameron Grant 261fd1aaeccSCameron Grant return val; 262fd1aaeccSCameron Grant } 263fd1aaeccSCameron Grant 264fd1aaeccSCameron Grant static int 265fd1aaeccSCameron Grant ess_get_byte(struct ess_info *sc) 266fd1aaeccSCameron Grant { 267fd1aaeccSCameron Grant int i; 268fd1aaeccSCameron Grant 269fd1aaeccSCameron Grant for (i = 1000; i > 0; i--) { 2700edeb3dcSNick Sayer if (ess_rd(sc, 0xc) & 0x40) 271fd1aaeccSCameron Grant return ess_rd(sc, DSP_READ); 272fd1aaeccSCameron Grant else 273fd1aaeccSCameron Grant DELAY(20); 274fd1aaeccSCameron Grant } 275fd1aaeccSCameron Grant return -1; 276fd1aaeccSCameron Grant } 277fd1aaeccSCameron Grant 278fd1aaeccSCameron Grant static int 279fd1aaeccSCameron Grant ess_write(struct ess_info *sc, u_char reg, int val) 280fd1aaeccSCameron Grant { 281fd1aaeccSCameron Grant return ess_cmd1(sc, reg, val); 282fd1aaeccSCameron Grant } 283fd1aaeccSCameron Grant 284fd1aaeccSCameron Grant static int 285fd1aaeccSCameron Grant ess_read(struct ess_info *sc, u_char reg) 286fd1aaeccSCameron Grant { 287fd1aaeccSCameron Grant return (ess_cmd(sc, 0xc0) && ess_cmd(sc, reg))? ess_get_byte(sc) : -1; 288fd1aaeccSCameron Grant } 289fd1aaeccSCameron Grant 290fd1aaeccSCameron Grant static int 291fd1aaeccSCameron Grant ess_reset_dsp(struct ess_info *sc) 292fd1aaeccSCameron Grant { 293a7e11506SNick Sayer DEB(printf("ess_reset_dsp\n")); 294fd1aaeccSCameron Grant ess_wr(sc, SBDSP_RST, 3); 295fd1aaeccSCameron Grant DELAY(100); 296fd1aaeccSCameron Grant ess_wr(sc, SBDSP_RST, 0); 297fd1aaeccSCameron Grant if (ess_get_byte(sc) != 0xAA) { 298a7e11506SNick Sayer DEB(printf("ess_reset_dsp failed\n")); 299a7e11506SNick Sayer /* 300fd1aaeccSCameron Grant rman_get_start(d->io_base))); 301a7e11506SNick Sayer */ 302fd1aaeccSCameron Grant return ENXIO; /* Sorry */ 303fd1aaeccSCameron Grant } 304fd1aaeccSCameron Grant ess_cmd(sc, 0xc6); 305fd1aaeccSCameron Grant return 0; 306fd1aaeccSCameron Grant } 307fd1aaeccSCameron Grant 308fd1aaeccSCameron Grant static void 309fd1aaeccSCameron Grant ess_intr(void *arg) 310fd1aaeccSCameron Grant { 311fd1aaeccSCameron Grant struct ess_info *sc = (struct ess_info *)arg; 3123ac1ca33SNick Sayer int src, pirq = 0, rirq = 0; 313fd1aaeccSCameron Grant 314fd1aaeccSCameron Grant src = 0; 315fd1aaeccSCameron Grant if (ess_getmixer(sc, 0x7a) & 0x80) 316fd1aaeccSCameron Grant src |= 2; 317fd1aaeccSCameron Grant if (ess_rd(sc, 0x0c) & 0x01) 318fd1aaeccSCameron Grant src |= 1; 319fd1aaeccSCameron Grant 3204e77c048SCameron Grant if (src == 0) 3214e77c048SCameron Grant return; 3224e77c048SCameron Grant 3233ac1ca33SNick Sayer if (sc->duplex) { 324fd1aaeccSCameron Grant pirq = (src & sc->pch.hwch)? 1 : 0; 325fd1aaeccSCameron Grant rirq = (src & sc->rch.hwch)? 1 : 0; 3263ac1ca33SNick Sayer } else { 3273ac1ca33SNick Sayer if (sc->simplex_dir == PCMDIR_PLAY) 3283ac1ca33SNick Sayer pirq = 1; 3293ac1ca33SNick Sayer if (sc->simplex_dir == PCMDIR_REC) 3303ac1ca33SNick Sayer rirq = 1; 3313ac1ca33SNick Sayer if (!pirq && !rirq) 3323ac1ca33SNick Sayer printf("solo: IRQ neither playback nor rec!\n"); 3333ac1ca33SNick Sayer } 334fd1aaeccSCameron Grant 335a7e11506SNick Sayer DEB(printf("ess_intr: pirq:%d rirq:%d\n",pirq,rirq)); 336a7e11506SNick Sayer 337fd1aaeccSCameron Grant if (pirq) { 338fd1aaeccSCameron Grant if (sc->pch.stopping) { 339fd1aaeccSCameron Grant ess_dmatrigger(sc, sc->pch.hwch, 0); 340fd1aaeccSCameron Grant sc->pch.stopping = 0; 341fd1aaeccSCameron Grant if (sc->pch.hwch == 1) 342fd1aaeccSCameron Grant ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01); 343fd1aaeccSCameron Grant else 344fd1aaeccSCameron Grant ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) & ~0x03); 345fd1aaeccSCameron Grant } 346fd1aaeccSCameron Grant chn_intr(sc->pch.channel); 347fd1aaeccSCameron Grant } 348fd1aaeccSCameron Grant 349fd1aaeccSCameron Grant if (rirq) { 350fd1aaeccSCameron Grant if (sc->rch.stopping) { 351fd1aaeccSCameron Grant ess_dmatrigger(sc, sc->rch.hwch, 0); 352fd1aaeccSCameron Grant sc->rch.stopping = 0; 353fd1aaeccSCameron Grant /* XXX: will this stop audio2? */ 354fd1aaeccSCameron Grant ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01); 355fd1aaeccSCameron Grant } 356fd1aaeccSCameron Grant chn_intr(sc->rch.channel); 357fd1aaeccSCameron Grant } 358fd1aaeccSCameron Grant 359fd1aaeccSCameron Grant if (src & 2) 360fd1aaeccSCameron Grant ess_setmixer(sc, 0x7a, ess_getmixer(sc, 0x7a) & ~0x80); 361fd1aaeccSCameron Grant if (src & 1) 362fd1aaeccSCameron Grant ess_rd(sc, DSP_DATA_AVAIL); 363fd1aaeccSCameron Grant } 364fd1aaeccSCameron Grant 365fd1aaeccSCameron Grant /* utility functions for ESS */ 366fd1aaeccSCameron Grant static u_int8_t 367fd1aaeccSCameron Grant ess_calcspeed8(int *spd) 368fd1aaeccSCameron Grant { 369fd1aaeccSCameron Grant int speed = *spd; 370fd1aaeccSCameron Grant u_int32_t t; 371fd1aaeccSCameron Grant 372fd1aaeccSCameron Grant if (speed > 22000) { 373fd1aaeccSCameron Grant t = (795500 + speed / 2) / speed; 374fd1aaeccSCameron Grant speed = (795500 + t / 2) / t; 375fd1aaeccSCameron Grant t = (256 - t) | 0x80; 376fd1aaeccSCameron Grant } else { 377fd1aaeccSCameron Grant t = (397700 + speed / 2) / speed; 378fd1aaeccSCameron Grant speed = (397700 + t / 2) / t; 379fd1aaeccSCameron Grant t = 128 - t; 380fd1aaeccSCameron Grant } 381fd1aaeccSCameron Grant *spd = speed; 382fd1aaeccSCameron Grant return t & 0x000000ff; 383fd1aaeccSCameron Grant } 384fd1aaeccSCameron Grant 385fd1aaeccSCameron Grant static u_int8_t 386fd1aaeccSCameron Grant ess_calcspeed9(int *spd) 387fd1aaeccSCameron Grant { 388fd1aaeccSCameron Grant int speed, s0, s1, use0; 389fd1aaeccSCameron Grant u_int8_t t0, t1; 390fd1aaeccSCameron Grant 391fd1aaeccSCameron Grant /* rate = source / (256 - divisor) */ 392fd1aaeccSCameron Grant /* divisor = 256 - (source / rate) */ 393fd1aaeccSCameron Grant speed = *spd; 394fd1aaeccSCameron Grant t0 = 128 - (793800 / speed); 395fd1aaeccSCameron Grant s0 = 793800 / (128 - t0); 396fd1aaeccSCameron Grant 397fd1aaeccSCameron Grant t1 = 128 - (768000 / speed); 398fd1aaeccSCameron Grant s1 = 768000 / (128 - t1); 399fd1aaeccSCameron Grant t1 |= 0x80; 400fd1aaeccSCameron Grant 401fd1aaeccSCameron Grant use0 = (ABS(speed - s0) < ABS(speed - s1))? 1 : 0; 402fd1aaeccSCameron Grant 403fd1aaeccSCameron Grant *spd = use0? s0 : s1; 404fd1aaeccSCameron Grant return use0? t0 : t1; 405fd1aaeccSCameron Grant } 406fd1aaeccSCameron Grant 407fd1aaeccSCameron Grant static u_int8_t 408fd1aaeccSCameron Grant ess_calcfilter(int spd) 409fd1aaeccSCameron Grant { 410fd1aaeccSCameron Grant int cutoff; 411fd1aaeccSCameron Grant 412fd1aaeccSCameron Grant /* cutoff = 7160000 / (256 - divisor) */ 413fd1aaeccSCameron Grant /* divisor = 256 - (7160000 / cutoff) */ 414fd1aaeccSCameron Grant cutoff = (spd * 9 * 82) / 20; 415fd1aaeccSCameron Grant return (256 - (7160000 / cutoff)); 416fd1aaeccSCameron Grant } 417fd1aaeccSCameron Grant 418fd1aaeccSCameron Grant static int 419fd1aaeccSCameron Grant ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int len) 420fd1aaeccSCameron Grant { 421fd1aaeccSCameron Grant int play = (dir == PCMDIR_PLAY)? 1 : 0; 422fd1aaeccSCameron Grant int b16 = (fmt & AFMT_16BIT)? 1 : 0; 423fd1aaeccSCameron Grant int stereo = (fmt & AFMT_STEREO)? 1 : 0; 4243ac1ca33SNick Sayer int unsign = (fmt == AFMT_U8 || fmt == AFMT_U16_LE || fmt == AFMT_U16_BE)? 1 : 0; 425fd1aaeccSCameron Grant u_int8_t spdval, fmtval; 426fd1aaeccSCameron Grant 427a7e11506SNick Sayer DEB(printf("ess_setupch\n")); 428fd1aaeccSCameron Grant spdval = (sc->newspeed)? ess_calcspeed9(&spd) : ess_calcspeed8(&spd); 429fd1aaeccSCameron Grant 4303ac1ca33SNick Sayer sc->simplex_dir = play ? PCMDIR_PLAY : PCMDIR_REC ; 4313ac1ca33SNick Sayer 432fd1aaeccSCameron Grant if (ch == 1) { 433fd1aaeccSCameron Grant KASSERT((dir == PCMDIR_PLAY) || (dir == PCMDIR_REC), ("ess_setupch: dir1 bad")); 434fd1aaeccSCameron Grant len = -len; 435fd1aaeccSCameron Grant /* transfer length low */ 436fd1aaeccSCameron Grant ess_write(sc, 0xa4, len & 0x00ff); 437fd1aaeccSCameron Grant /* transfer length high */ 438fd1aaeccSCameron Grant ess_write(sc, 0xa5, (len & 0xff00) >> 8); 439fd1aaeccSCameron Grant /* autoinit, dma dir */ 440a7e11506SNick Sayer ess_write(sc, 0xb8, 0x04 | (play? 0x00 : 0x0a)); 441fd1aaeccSCameron Grant /* mono/stereo */ 442fd1aaeccSCameron Grant ess_write(sc, 0xa8, (ess_read(sc, 0xa8) & ~0x03) | (stereo? 0x01 : 0x02)); 443fd1aaeccSCameron Grant /* demand mode, 4 bytes/xfer */ 444fd1aaeccSCameron Grant ess_write(sc, 0xb9, 0x02); 445fd1aaeccSCameron Grant /* sample rate */ 446fd1aaeccSCameron Grant ess_write(sc, 0xa1, spdval); 447fd1aaeccSCameron Grant /* filter cutoff */ 448fd1aaeccSCameron Grant ess_write(sc, 0xa2, ess_calcfilter(spd)); 449fd1aaeccSCameron Grant /* setup dac/adc */ 450fd1aaeccSCameron Grant /* 451fd1aaeccSCameron Grant if (play) 452fd1aaeccSCameron Grant ess_write(sc, 0xb6, unsign? 0x80 : 0x00); 453fd1aaeccSCameron Grant */ 454fd1aaeccSCameron Grant /* mono, b16: signed, load signal */ 455fd1aaeccSCameron Grant /* 456fd1aaeccSCameron Grant ess_write(sc, 0xb7, 0x51 | (unsign? 0x00 : 0x20)); 457fd1aaeccSCameron Grant */ 458fd1aaeccSCameron Grant /* setup fifo */ 459a7e11506SNick Sayer ess_write(sc, 0xb7, 0x91 | (unsign? 0x00 : 0x20) | 460fd1aaeccSCameron Grant (b16? 0x04 : 0x00) | 461fd1aaeccSCameron Grant (stereo? 0x08 : 0x40)); 462fd1aaeccSCameron Grant /* irq control */ 463fd1aaeccSCameron Grant ess_write(sc, 0xb1, (ess_read(sc, 0xb1) & 0x0f) | 0x50); 464fd1aaeccSCameron Grant /* drq control */ 465fd1aaeccSCameron Grant ess_write(sc, 0xb2, (ess_read(sc, 0xb2) & 0x0f) | 0x50); 466fd1aaeccSCameron Grant } else if (ch == 2) { 467fd1aaeccSCameron Grant KASSERT(dir == PCMDIR_PLAY, ("ess_setupch: dir2 bad")); 468fd1aaeccSCameron Grant len >>= 1; 469fd1aaeccSCameron Grant len = -len; 470fd1aaeccSCameron Grant /* transfer length low */ 471fd1aaeccSCameron Grant ess_setmixer(sc, 0x74, len & 0x00ff); 472fd1aaeccSCameron Grant /* transfer length high */ 473fd1aaeccSCameron Grant ess_setmixer(sc, 0x76, (len & 0xff00) >> 8); 474fd1aaeccSCameron Grant /* autoinit, 4 bytes/req */ 475fd1aaeccSCameron Grant ess_setmixer(sc, 0x78, 0x10); 47680a8e065SNick Sayer fmtval = b16 | (stereo << 1) | ((!unsign) << 2); 477fd1aaeccSCameron Grant /* enable irq, set format */ 478fd1aaeccSCameron Grant ess_setmixer(sc, 0x7a, 0x40 | fmtval); 479fd1aaeccSCameron Grant if (sc->newspeed) { 480fd1aaeccSCameron Grant /* sample rate */ 481fd1aaeccSCameron Grant ess_setmixer(sc, 0x70, spdval); 482fd1aaeccSCameron Grant /* filter cutoff */ 483fd1aaeccSCameron Grant ess_setmixer(sc, 0x72, ess_calcfilter(spd)); 484fd1aaeccSCameron Grant } 485fd1aaeccSCameron Grant 486fd1aaeccSCameron Grant } 487fd1aaeccSCameron Grant return 0; 488fd1aaeccSCameron Grant } 489fd1aaeccSCameron Grant static int 490fd1aaeccSCameron Grant ess_start(struct ess_chinfo *ch) 491fd1aaeccSCameron Grant { 492fd1aaeccSCameron Grant struct ess_info *sc = ch->parent; 493fd1aaeccSCameron Grant 494a7e11506SNick Sayer DEB(printf("ess_start\n");); 495fd1aaeccSCameron Grant ess_setupch(sc, ch->hwch, ch->dir, ch->spd, ch->fmt, ch->buffer->dl); 496fd1aaeccSCameron Grant ch->stopping = 0; 49719a0702eSNick Sayer if (ch->hwch == 1) { 498fd1aaeccSCameron Grant ess_write(sc, 0xb8, ess_read(sc, 0xb8) | 0x01); 49919a0702eSNick Sayer if (ch->dir == PCMDIR_PLAY) { 5000edeb3dcSNick Sayer #if 0 50119a0702eSNick Sayer DELAY(100000); /* 100 ms */ 5020edeb3dcSNick Sayer #endif 50319a0702eSNick Sayer ess_cmd(sc, 0xd1); 50419a0702eSNick Sayer } 50519a0702eSNick Sayer } else 506fd1aaeccSCameron Grant ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) | 0x03); 507fd1aaeccSCameron Grant return 0; 508fd1aaeccSCameron Grant } 509fd1aaeccSCameron Grant 510fd1aaeccSCameron Grant static int 511fd1aaeccSCameron Grant ess_stop(struct ess_chinfo *ch) 512fd1aaeccSCameron Grant { 513fd1aaeccSCameron Grant struct ess_info *sc = ch->parent; 514fd1aaeccSCameron Grant 515a7e11506SNick Sayer DEB(printf("ess_stop\n")); 516fd1aaeccSCameron Grant ch->stopping = 1; 517fd1aaeccSCameron Grant if (ch->hwch == 1) 518fd1aaeccSCameron Grant ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x04); 519fd1aaeccSCameron Grant else 520fd1aaeccSCameron Grant ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) & ~0x10); 521a7e11506SNick Sayer DEB(printf("done with stop\n")); 522fd1aaeccSCameron Grant return 0; 523fd1aaeccSCameron Grant } 524fd1aaeccSCameron Grant 525fd1aaeccSCameron Grant /* channel interface for ESS18xx */ 526fd1aaeccSCameron Grant static void * 527fd1aaeccSCameron Grant esschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) 528fd1aaeccSCameron Grant { 529fd1aaeccSCameron Grant struct ess_info *sc = devinfo; 530fd1aaeccSCameron Grant struct ess_chinfo *ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch; 531fd1aaeccSCameron Grant 532a7e11506SNick Sayer DEB(printf("esschan_init\n")); 533fd1aaeccSCameron Grant ch->parent = sc; 534fd1aaeccSCameron Grant ch->channel = c; 535fd1aaeccSCameron Grant ch->buffer = b; 536fd1aaeccSCameron Grant ch->buffer->bufsize = ESS_BUFFSIZE; 537fd1aaeccSCameron Grant if (chn_allocbuf(ch->buffer, sc->parent_dmat) == -1) 538fd1aaeccSCameron Grant return NULL; 539fd1aaeccSCameron Grant ch->hwch = 1; 540fd1aaeccSCameron Grant if ((dir == PCMDIR_PLAY) && (sc->duplex)) 541fd1aaeccSCameron Grant ch->hwch = 2; 542fd1aaeccSCameron Grant return ch; 543fd1aaeccSCameron Grant } 544fd1aaeccSCameron Grant 545fd1aaeccSCameron Grant static int 546fd1aaeccSCameron Grant esschan_setdir(void *data, int dir) 547fd1aaeccSCameron Grant { 548fd1aaeccSCameron Grant struct ess_chinfo *ch = data; 549fd1aaeccSCameron Grant 550fd1aaeccSCameron Grant ch->dir = dir; 551fd1aaeccSCameron Grant return 0; 552fd1aaeccSCameron Grant } 553fd1aaeccSCameron Grant 554fd1aaeccSCameron Grant static int 555fd1aaeccSCameron Grant esschan_setformat(void *data, u_int32_t format) 556fd1aaeccSCameron Grant { 557fd1aaeccSCameron Grant struct ess_chinfo *ch = data; 558fd1aaeccSCameron Grant 559fd1aaeccSCameron Grant ch->fmt = format; 560fd1aaeccSCameron Grant return 0; 561fd1aaeccSCameron Grant } 562fd1aaeccSCameron Grant 563fd1aaeccSCameron Grant static int 564fd1aaeccSCameron Grant esschan_setspeed(void *data, u_int32_t speed) 565fd1aaeccSCameron Grant { 566fd1aaeccSCameron Grant struct ess_chinfo *ch = data; 567fd1aaeccSCameron Grant struct ess_info *sc = ch->parent; 568fd1aaeccSCameron Grant 569fd1aaeccSCameron Grant ch->spd = speed; 570fd1aaeccSCameron Grant if (sc->newspeed) 571fd1aaeccSCameron Grant ess_calcspeed9(&ch->spd); 572fd1aaeccSCameron Grant else 573fd1aaeccSCameron Grant ess_calcspeed8(&ch->spd); 574fd1aaeccSCameron Grant return ch->spd; 575fd1aaeccSCameron Grant } 576fd1aaeccSCameron Grant 577fd1aaeccSCameron Grant static int 578fd1aaeccSCameron Grant esschan_setblocksize(void *data, u_int32_t blocksize) 579fd1aaeccSCameron Grant { 580fd1aaeccSCameron Grant return blocksize; 581fd1aaeccSCameron Grant } 582fd1aaeccSCameron Grant 583fd1aaeccSCameron Grant static int 584fd1aaeccSCameron Grant esschan_trigger(void *data, int go) 585fd1aaeccSCameron Grant { 586fd1aaeccSCameron Grant struct ess_chinfo *ch = data; 587fd1aaeccSCameron Grant struct ess_info *sc = ch->parent; 588fd1aaeccSCameron Grant 589a7e11506SNick Sayer DEB(printf("esschan_trigger: %d\n",go)); 590fd1aaeccSCameron Grant if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) 591fd1aaeccSCameron Grant return 0; 592fd1aaeccSCameron Grant 593fd1aaeccSCameron Grant switch (go) { 594fd1aaeccSCameron Grant case PCMTRIG_START: 595fd1aaeccSCameron Grant ess_dmasetup(sc, ch->hwch, vtophys(ch->buffer->buf), ch->buffer->bufsize, ch->dir); 596fd1aaeccSCameron Grant ess_dmatrigger(sc, ch->hwch, 1); 597fd1aaeccSCameron Grant ess_start(ch); 598fd1aaeccSCameron Grant break; 599fd1aaeccSCameron Grant 600fd1aaeccSCameron Grant case PCMTRIG_STOP: 601fd1aaeccSCameron Grant case PCMTRIG_ABORT: 602fd1aaeccSCameron Grant default: 603fd1aaeccSCameron Grant ess_stop(ch); 604fd1aaeccSCameron Grant break; 605fd1aaeccSCameron Grant } 606fd1aaeccSCameron Grant return 0; 607fd1aaeccSCameron Grant } 608fd1aaeccSCameron Grant 609fd1aaeccSCameron Grant static int 610fd1aaeccSCameron Grant esschan_getptr(void *data) 611fd1aaeccSCameron Grant { 612fd1aaeccSCameron Grant struct ess_chinfo *ch = data; 613fd1aaeccSCameron Grant struct ess_info *sc = ch->parent; 614fd1aaeccSCameron Grant 615fd1aaeccSCameron Grant return ess_dmapos(sc, ch->hwch); 616fd1aaeccSCameron Grant } 617fd1aaeccSCameron Grant 618fd1aaeccSCameron Grant static pcmchan_caps * 619fd1aaeccSCameron Grant esschan_getcaps(void *data) 620fd1aaeccSCameron Grant { 621fd1aaeccSCameron Grant struct ess_chinfo *ch = data; 622fd1aaeccSCameron Grant 623fd1aaeccSCameron Grant return (ch->dir == PCMDIR_PLAY)? &ess_playcaps : &ess_reccaps; 624fd1aaeccSCameron Grant } 625fd1aaeccSCameron Grant 626fd1aaeccSCameron Grant /************************************************************/ 627fd1aaeccSCameron Grant 628fd1aaeccSCameron Grant static int 629fd1aaeccSCameron Grant essmix_init(snd_mixer *m) 630fd1aaeccSCameron Grant { 631fd1aaeccSCameron Grant struct ess_info *sc = mix_getdevinfo(m); 632fd1aaeccSCameron Grant 633fd1aaeccSCameron Grant mix_setrecdevs(m, SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE | 634fd1aaeccSCameron Grant SOUND_MASK_IMIX); 635fd1aaeccSCameron Grant 636fd1aaeccSCameron Grant mix_setdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | 637fd1aaeccSCameron Grant SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME | 638fd1aaeccSCameron Grant SOUND_MASK_LINE1); 639fd1aaeccSCameron Grant 640fd1aaeccSCameron Grant ess_setmixer(sc, 0, 0); /* reset */ 641fd1aaeccSCameron Grant 642fd1aaeccSCameron Grant return 0; 643fd1aaeccSCameron Grant } 644fd1aaeccSCameron Grant 645fd1aaeccSCameron Grant static int 646fd1aaeccSCameron Grant essmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) 647fd1aaeccSCameron Grant { 648fd1aaeccSCameron Grant struct ess_info *sc = mix_getdevinfo(m); 649fd1aaeccSCameron Grant int preg = 0, rreg = 0, l, r; 650fd1aaeccSCameron Grant 651fd1aaeccSCameron Grant l = (left * 15) / 100; 652fd1aaeccSCameron Grant r = (right * 15) / 100; 653fd1aaeccSCameron Grant switch (dev) { 654fd1aaeccSCameron Grant case SOUND_MIXER_SYNTH: 655fd1aaeccSCameron Grant preg = 0x36; 656fd1aaeccSCameron Grant rreg = 0x6b; 657fd1aaeccSCameron Grant break; 658fd1aaeccSCameron Grant 659fd1aaeccSCameron Grant case SOUND_MIXER_PCM: 660fd1aaeccSCameron Grant preg = 0x14; 661fd1aaeccSCameron Grant rreg = 0x7c; 662fd1aaeccSCameron Grant break; 663fd1aaeccSCameron Grant 664fd1aaeccSCameron Grant case SOUND_MIXER_LINE: 665fd1aaeccSCameron Grant preg = 0x3e; 666fd1aaeccSCameron Grant rreg = 0x6e; 667fd1aaeccSCameron Grant break; 668fd1aaeccSCameron Grant 669fd1aaeccSCameron Grant case SOUND_MIXER_MIC: 670fd1aaeccSCameron Grant preg = 0x1a; 671fd1aaeccSCameron Grant rreg = 0x68; 672fd1aaeccSCameron Grant break; 673fd1aaeccSCameron Grant 674fd1aaeccSCameron Grant case SOUND_MIXER_LINE1: 675fd1aaeccSCameron Grant preg = 0x3a; 676fd1aaeccSCameron Grant rreg = 0x6c; 677fd1aaeccSCameron Grant break; 678fd1aaeccSCameron Grant 679fd1aaeccSCameron Grant case SOUND_MIXER_CD: 680fd1aaeccSCameron Grant preg = 0x38; 681fd1aaeccSCameron Grant rreg = 0x6a; 682fd1aaeccSCameron Grant break; 683fd1aaeccSCameron Grant 684fd1aaeccSCameron Grant case SOUND_MIXER_VOLUME: 685fd1aaeccSCameron Grant l = left? (left * 63) / 100 : 64; 686fd1aaeccSCameron Grant r = right? (right * 63) / 100 : 64; 687fd1aaeccSCameron Grant ess_setmixer(sc, 0x60, l); 688fd1aaeccSCameron Grant ess_setmixer(sc, 0x62, r); 689fd1aaeccSCameron Grant left = (l == 64)? 0 : (l * 100) / 63; 690fd1aaeccSCameron Grant right = (r == 64)? 0 : (r * 100) / 63; 691fd1aaeccSCameron Grant return left | (right << 8); 692fd1aaeccSCameron Grant } 693fd1aaeccSCameron Grant 694fd1aaeccSCameron Grant if (preg) 695fd1aaeccSCameron Grant ess_setmixer(sc, preg, (l << 4) | r); 696fd1aaeccSCameron Grant if (rreg) 697fd1aaeccSCameron Grant ess_setmixer(sc, rreg, (l << 4) | r); 698fd1aaeccSCameron Grant 699fd1aaeccSCameron Grant left = (l * 100) / 15; 700fd1aaeccSCameron Grant right = (r * 100) / 15; 701fd1aaeccSCameron Grant 702fd1aaeccSCameron Grant return left | (right << 8); 703fd1aaeccSCameron Grant } 704fd1aaeccSCameron Grant 705fd1aaeccSCameron Grant static int 706fd1aaeccSCameron Grant essmix_setrecsrc(snd_mixer *m, u_int32_t src) 707fd1aaeccSCameron Grant { 708fd1aaeccSCameron Grant struct ess_info *sc = mix_getdevinfo(m); 709fd1aaeccSCameron Grant u_char recdev; 710fd1aaeccSCameron Grant 711fd1aaeccSCameron Grant switch (src) { 712fd1aaeccSCameron Grant case SOUND_MASK_CD: 713fd1aaeccSCameron Grant recdev = 0x02; 714fd1aaeccSCameron Grant break; 715fd1aaeccSCameron Grant 716fd1aaeccSCameron Grant case SOUND_MASK_LINE: 717fd1aaeccSCameron Grant recdev = 0x06; 718fd1aaeccSCameron Grant break; 719fd1aaeccSCameron Grant 720fd1aaeccSCameron Grant case SOUND_MASK_IMIX: 721fd1aaeccSCameron Grant recdev = 0x05; 722fd1aaeccSCameron Grant break; 723fd1aaeccSCameron Grant 724fd1aaeccSCameron Grant case SOUND_MASK_MIC: 725fd1aaeccSCameron Grant default: 726fd1aaeccSCameron Grant recdev = 0x00; 727fd1aaeccSCameron Grant src = SOUND_MASK_MIC; 728fd1aaeccSCameron Grant break; 729fd1aaeccSCameron Grant } 730fd1aaeccSCameron Grant 731fd1aaeccSCameron Grant ess_setmixer(sc, 0x1c, recdev); 732fd1aaeccSCameron Grant 733fd1aaeccSCameron Grant return src; 734fd1aaeccSCameron Grant } 735fd1aaeccSCameron Grant 736fd1aaeccSCameron Grant /************************************************************/ 737fd1aaeccSCameron Grant 738fd1aaeccSCameron Grant static int 739fd1aaeccSCameron Grant ess_dmasetup(struct ess_info *sc, int ch, u_int32_t base, u_int16_t cnt, int dir) 740fd1aaeccSCameron Grant { 741fd1aaeccSCameron Grant KASSERT(ch == 1 || ch == 2, ("bad ch")); 742fd1aaeccSCameron Grant sc->dmasz[ch - 1] = cnt; 743fd1aaeccSCameron Grant if (ch == 1) { 74419a0702eSNick Sayer port_wr(sc->vc, 0x8, 0xc4, 1); /* command */ 745fd1aaeccSCameron Grant port_wr(sc->vc, 0xd, 0xff, 1); /* reset */ 746fd1aaeccSCameron Grant port_wr(sc->vc, 0xf, 0x01, 1); /* mask */ 74719a0702eSNick Sayer port_wr(sc->vc, 0xb, dir == PCMDIR_PLAY? 0x58 : 0x54, 1); /* mode */ 748fd1aaeccSCameron Grant port_wr(sc->vc, 0x0, base, 4); 749bb7f26c3SNick Sayer port_wr(sc->vc, 0x4, cnt - 1, 2); 750fd1aaeccSCameron Grant 751fd1aaeccSCameron Grant } else if (ch == 2) { 752fd1aaeccSCameron Grant port_wr(sc->io, 0x6, 0x08, 1); /* autoinit */ 753fd1aaeccSCameron Grant port_wr(sc->io, 0x0, base, 4); 754fd1aaeccSCameron Grant port_wr(sc->io, 0x4, cnt, 2); 755fd1aaeccSCameron Grant } 756fd1aaeccSCameron Grant return 0; 757fd1aaeccSCameron Grant } 758fd1aaeccSCameron Grant 759fd1aaeccSCameron Grant static int 760fd1aaeccSCameron Grant ess_dmapos(struct ess_info *sc, int ch) 761fd1aaeccSCameron Grant { 7626ba60b3cSNick Sayer int p = 0, i = 0, j = 0; 7630edeb3dcSNick Sayer u_long flags; 764fd1aaeccSCameron Grant 765fd1aaeccSCameron Grant KASSERT(ch == 1 || ch == 2, ("bad ch")); 7660edeb3dcSNick Sayer flags = spltty(); 7670edeb3dcSNick Sayer if (ch == 1) { 7680edeb3dcSNick Sayer 7690edeb3dcSNick Sayer /* 7700edeb3dcSNick Sayer * During recording, this register is known to give back 7710edeb3dcSNick Sayer * garbage if it's not quiescent while being read. That's 7726ba60b3cSNick Sayer * why we spl, stop the DMA, and try over and over until 7736ba60b3cSNick Sayer * adjacent reads are "close", in the right order and not 7746ba60b3cSNick Sayer * bigger than is otherwise possible. 7750edeb3dcSNick Sayer */ 7760edeb3dcSNick Sayer ess_dmatrigger(sc, ch, 0); 7770edeb3dcSNick Sayer DELAY(20); 7786ba60b3cSNick Sayer do { 7796ba60b3cSNick Sayer DELAY(10); 7806ba60b3cSNick Sayer if (j > 1) 7816ba60b3cSNick Sayer printf("DMA count reg bogus: %04x & %04x\n", 7826ba60b3cSNick Sayer i, p); 7836ba60b3cSNick Sayer i = port_rd(sc->vc, 0x4, 2) + 1; 784fd1aaeccSCameron Grant p = port_rd(sc->vc, 0x4, 2) + 1; 7856ba60b3cSNick Sayer } while ((p > sc->dmasz[ch -1 ] || i < p || (p - i) > 0x8) && j++ < 1000); 7860edeb3dcSNick Sayer ess_dmatrigger(sc, ch, 1); 7870edeb3dcSNick Sayer } 788fd1aaeccSCameron Grant else if (ch == 2) 789fd1aaeccSCameron Grant p = port_rd(sc->io, 0x4, 2); 7900edeb3dcSNick Sayer splx(flags); 791fd1aaeccSCameron Grant return sc->dmasz[ch - 1] - p; 792fd1aaeccSCameron Grant } 793fd1aaeccSCameron Grant 794fd1aaeccSCameron Grant static int 795fd1aaeccSCameron Grant ess_dmatrigger(struct ess_info *sc, int ch, int go) 796fd1aaeccSCameron Grant { 797fd1aaeccSCameron Grant KASSERT(ch == 1 || ch == 2, ("bad ch")); 798fd1aaeccSCameron Grant if (ch == 1) 7998eb3acc9SNick Sayer port_wr(sc->vc, 0xf, go? 0x00 : 0x01, 1); /* mask */ 800fd1aaeccSCameron Grant else if (ch == 2) 801fd1aaeccSCameron Grant port_wr(sc->io, 0x6, 0x08 | (go? 0x02 : 0x00), 1); /* autoinit */ 802fd1aaeccSCameron Grant return 0; 803fd1aaeccSCameron Grant } 804fd1aaeccSCameron Grant 805fd1aaeccSCameron Grant static void 806fd1aaeccSCameron Grant ess_release_resources(struct ess_info *sc, device_t dev) 807fd1aaeccSCameron Grant { 808fd1aaeccSCameron Grant /* should we bus_teardown_intr here? */ 809fd1aaeccSCameron Grant if (sc->irq) { 810fd1aaeccSCameron Grant bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); 811fd1aaeccSCameron Grant sc->irq = 0; 812fd1aaeccSCameron Grant } 813fd1aaeccSCameron Grant if (sc->io) { 814fd1aaeccSCameron Grant bus_release_resource(dev, SYS_RES_IOPORT, 0 * 4 + PCIR_MAPS, sc->io); 815fd1aaeccSCameron Grant sc->io = 0; 816fd1aaeccSCameron Grant } 817fd1aaeccSCameron Grant 818fd1aaeccSCameron Grant if (sc->sb) { 819fd1aaeccSCameron Grant bus_release_resource(dev, SYS_RES_IOPORT, 1 * 4 + PCIR_MAPS, sc->sb); 820fd1aaeccSCameron Grant sc->sb = 0; 821fd1aaeccSCameron Grant } 822fd1aaeccSCameron Grant 823fd1aaeccSCameron Grant if (sc->vc) { 824fd1aaeccSCameron Grant bus_release_resource(dev, SYS_RES_IOPORT, 2 * 4 + PCIR_MAPS, sc->vc); 825fd1aaeccSCameron Grant sc->vc = 0; 826fd1aaeccSCameron Grant } 827fd1aaeccSCameron Grant 828fd1aaeccSCameron Grant if (sc->mpu) { 829fd1aaeccSCameron Grant bus_release_resource(dev, SYS_RES_IOPORT, 3 * 4 + PCIR_MAPS, sc->mpu); 830fd1aaeccSCameron Grant sc->mpu = 0; 831fd1aaeccSCameron Grant } 832fd1aaeccSCameron Grant 833fd1aaeccSCameron Grant if (sc->gp) { 834fd1aaeccSCameron Grant bus_release_resource(dev, SYS_RES_IOPORT, 4 * 4 + PCIR_MAPS, sc->gp); 835fd1aaeccSCameron Grant sc->gp = 0; 836fd1aaeccSCameron Grant } 837fd1aaeccSCameron Grant 838fd1aaeccSCameron Grant free(sc, M_DEVBUF); 839fd1aaeccSCameron Grant } 840fd1aaeccSCameron Grant 841fd1aaeccSCameron Grant static int 842fd1aaeccSCameron Grant ess_alloc_resources(struct ess_info *sc, device_t dev) 843fd1aaeccSCameron Grant { 844fd1aaeccSCameron Grant int rid; 845fd1aaeccSCameron Grant 846fd1aaeccSCameron Grant rid = 0 * 4 + PCIR_MAPS; 847fd1aaeccSCameron Grant sc->io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); 848fd1aaeccSCameron Grant 849fd1aaeccSCameron Grant rid = 1 * 4 + PCIR_MAPS; 850fd1aaeccSCameron Grant sc->sb = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); 851fd1aaeccSCameron Grant 852fd1aaeccSCameron Grant rid = 2 * 4 + PCIR_MAPS; 853fd1aaeccSCameron Grant sc->vc = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); 854fd1aaeccSCameron Grant 855fd1aaeccSCameron Grant rid = 3 * 4 + PCIR_MAPS; 856fd1aaeccSCameron Grant sc->mpu = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); 857fd1aaeccSCameron Grant 858fd1aaeccSCameron Grant rid = 4 * 4 + PCIR_MAPS; 859fd1aaeccSCameron Grant sc->gp = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); 860fd1aaeccSCameron Grant 861fd1aaeccSCameron Grant rid = 0; 862fd1aaeccSCameron Grant sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 863fd1aaeccSCameron Grant 864fd1aaeccSCameron Grant return (sc->irq && sc->io && sc->sb && sc->vc && sc->mpu && sc->gp)? 0 : ENXIO; 865fd1aaeccSCameron Grant } 866fd1aaeccSCameron Grant 867fd1aaeccSCameron Grant static int 868fd1aaeccSCameron Grant ess_probe(device_t dev) 869fd1aaeccSCameron Grant { 870fd1aaeccSCameron Grant char *s = NULL; 871fd1aaeccSCameron Grant u_int32_t subdev; 872fd1aaeccSCameron Grant 873fd1aaeccSCameron Grant subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev); 874fd1aaeccSCameron Grant switch (pci_get_devid(dev)) { 875fd1aaeccSCameron Grant case 0x1969125d: 876fd1aaeccSCameron Grant if (subdev == 0x8888125d) 877fd1aaeccSCameron Grant s = "ESS Solo-1E"; 878fd1aaeccSCameron Grant else if (subdev == 0x1818125d) 879fd1aaeccSCameron Grant s = "ESS Solo-1"; 880fd1aaeccSCameron Grant else 881fd1aaeccSCameron Grant s = "ESS Solo-1 (unknown vendor)"; 882fd1aaeccSCameron Grant break; 883fd1aaeccSCameron Grant } 884fd1aaeccSCameron Grant 885fd1aaeccSCameron Grant if (s) 886fd1aaeccSCameron Grant device_set_desc(dev, s); 887fd1aaeccSCameron Grant return s? 0 : ENXIO; 888fd1aaeccSCameron Grant } 889fd1aaeccSCameron Grant 890fd1aaeccSCameron Grant #define PCI_LEGACYCONTROL 0x40 891fd1aaeccSCameron Grant #define PCI_CONFIG 0x50 892fd1aaeccSCameron Grant #define PCI_DDMACONTROL 0x60 893fd1aaeccSCameron Grant 894fd1aaeccSCameron Grant static int 895fd1aaeccSCameron Grant ess_attach(device_t dev) 896fd1aaeccSCameron Grant { 897fd1aaeccSCameron Grant snddev_info *d = device_get_softc(dev); 898fd1aaeccSCameron Grant struct ess_info *sc; 899fd1aaeccSCameron Grant void *ih; 900fd1aaeccSCameron Grant char status[SND_STATUSLEN]; 901fd1aaeccSCameron Grant u_int16_t ddma; 902fd1aaeccSCameron Grant u_int32_t data; 903fd1aaeccSCameron Grant 904fd1aaeccSCameron Grant sc = (struct ess_info *)malloc(sizeof *sc, M_DEVBUF, M_NOWAIT); 905fd1aaeccSCameron Grant if (!sc) 906fd1aaeccSCameron Grant return ENXIO; 907fd1aaeccSCameron Grant bzero(sc, sizeof *sc); 908fd1aaeccSCameron Grant 909fd1aaeccSCameron Grant data = pci_read_config(dev, PCIR_COMMAND, 2); 910fd1aaeccSCameron Grant data |= PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN; 911fd1aaeccSCameron Grant pci_write_config(dev, PCIR_COMMAND, data, 2); 912fd1aaeccSCameron Grant data = pci_read_config(dev, PCIR_COMMAND, 2); 913fd1aaeccSCameron Grant 914fd1aaeccSCameron Grant if (ess_alloc_resources(sc, dev)) 915fd1aaeccSCameron Grant goto no; 916fd1aaeccSCameron Grant 917fd1aaeccSCameron Grant ddma = rman_get_start(sc->vc) | 1; 918fd1aaeccSCameron Grant pci_write_config(dev, PCI_LEGACYCONTROL, 0x805f, 2); 919fd1aaeccSCameron Grant pci_write_config(dev, PCI_DDMACONTROL, ddma, 2); 920fd1aaeccSCameron Grant pci_write_config(dev, PCI_CONFIG, 0, 2); 921fd1aaeccSCameron Grant 922fd1aaeccSCameron Grant if (ess_reset_dsp(sc)) 923fd1aaeccSCameron Grant goto no; 924fd1aaeccSCameron Grant mixer_init(d, &ess_mixer, sc); 925fd1aaeccSCameron Grant 926fd1aaeccSCameron Grant port_wr(sc->io, 0x7, 0xb0, 1); /* enable irqs */ 9273ac1ca33SNick Sayer #ifdef ESS18XX_DUPLEX 928fd1aaeccSCameron Grant sc->duplex = 1; 9293ac1ca33SNick Sayer #else 9303ac1ca33SNick Sayer sc->duplex = 0; 9313ac1ca33SNick Sayer #endif 932fd1aaeccSCameron Grant 9333ac1ca33SNick Sayer #ifdef ESS18XX_NEWSPEED 9343ac1ca33SNick Sayer sc->newspeed = 1; 9353ac1ca33SNick Sayer #else 9363ac1ca33SNick Sayer sc->newspeed = 0; 9373ac1ca33SNick Sayer #endif 938fd1aaeccSCameron Grant if (sc->newspeed) 939fd1aaeccSCameron Grant ess_setmixer(sc, 0x71, 0x2a); 940fd1aaeccSCameron Grant 941fd1aaeccSCameron Grant bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, ess_intr, sc, &ih); 942fd1aaeccSCameron Grant if (!sc->duplex) 943fd1aaeccSCameron Grant pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); 944fd1aaeccSCameron Grant 945bb7f26c3SNick Sayer if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/65536, /*boundary*/0, 946fd1aaeccSCameron Grant /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, 947fd1aaeccSCameron Grant /*highaddr*/BUS_SPACE_MAXADDR, 948fd1aaeccSCameron Grant /*filter*/NULL, /*filterarg*/NULL, 949fd1aaeccSCameron Grant /*maxsize*/ESS_BUFFSIZE, /*nsegments*/1, 950fd1aaeccSCameron Grant /*maxsegz*/0x3ffff, 951fd1aaeccSCameron Grant /*flags*/0, &sc->parent_dmat) != 0) { 952fd1aaeccSCameron Grant device_printf(dev, "unable to create dma tag\n"); 953fd1aaeccSCameron Grant goto no; 954fd1aaeccSCameron Grant } 955fd1aaeccSCameron Grant 956fd1aaeccSCameron Grant snprintf(status, SND_STATUSLEN, "at io 0x%lx,0x%lx,0x%lx irq %ld", 957fd1aaeccSCameron Grant rman_get_start(sc->io), rman_get_start(sc->sb), rman_get_start(sc->vc), 958fd1aaeccSCameron Grant rman_get_start(sc->irq)); 959fd1aaeccSCameron Grant 960fd1aaeccSCameron Grant if (pcm_register(dev, sc, 1, 1)) 961fd1aaeccSCameron Grant goto no; 962fd1aaeccSCameron Grant pcm_addchan(dev, PCMDIR_REC, &ess_chantemplate, sc); 963fd1aaeccSCameron Grant pcm_addchan(dev, PCMDIR_PLAY, &ess_chantemplate, sc); 964fd1aaeccSCameron Grant pcm_setstatus(dev, status); 965fd1aaeccSCameron Grant 966fd1aaeccSCameron Grant return 0; 967fd1aaeccSCameron Grant 968fd1aaeccSCameron Grant no: 969fd1aaeccSCameron Grant ess_release_resources(sc, dev); 970fd1aaeccSCameron Grant return ENXIO; 971fd1aaeccSCameron Grant } 972fd1aaeccSCameron Grant 973fd1aaeccSCameron Grant static device_method_t ess_methods[] = { 974fd1aaeccSCameron Grant /* Device interface */ 975fd1aaeccSCameron Grant DEVMETHOD(device_probe, ess_probe), 976fd1aaeccSCameron Grant DEVMETHOD(device_attach, ess_attach), 977fd1aaeccSCameron Grant 978fd1aaeccSCameron Grant { 0, 0 } 979fd1aaeccSCameron Grant }; 980fd1aaeccSCameron Grant 981fd1aaeccSCameron Grant static driver_t ess_driver = { 982fd1aaeccSCameron Grant "pcm", 983fd1aaeccSCameron Grant ess_methods, 984fd1aaeccSCameron Grant sizeof(snddev_info), 985fd1aaeccSCameron Grant }; 986fd1aaeccSCameron Grant 987fd1aaeccSCameron Grant DRIVER_MODULE(snd_solo, pci, ess_driver, pcm_devclass, 0, 0); 988fd1aaeccSCameron Grant MODULE_DEPEND(snd_solo, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); 989fd1aaeccSCameron Grant MODULE_VERSION(snd_solo, 1); 990fd1aaeccSCameron Grant 991fd1aaeccSCameron Grant 992fd1aaeccSCameron Grant 993