1098ca2bdSWarner Losh /*- 23f225978SCameron Grant * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 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 2690da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS 2790da2b28SAriff Abdullah #include "opt_snd.h" 2890da2b28SAriff Abdullah #endif 2990da2b28SAriff Abdullah 30fd1aaeccSCameron Grant #include <dev/sound/pcm/sound.h> 31fd1aaeccSCameron Grant 3290cf0136SWarner Losh #include <dev/pci/pcireg.h> 3390cf0136SWarner Losh #include <dev/pci/pcivar.h> 34fd1aaeccSCameron Grant 35fd1aaeccSCameron Grant #include <dev/sound/isa/sb.h> 36fd1aaeccSCameron Grant #include <dev/sound/chip.h> 37fd1aaeccSCameron Grant 380f55ac6cSCameron Grant #include "mixer_if.h" 390f55ac6cSCameron Grant 4067b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 4167b1dce3SCameron Grant 4239dbd126SCameron Grant #define SOLO_DEFAULT_BUFSZ 16384 43fd1aaeccSCameron Grant #define ABS(x) (((x) < 0)? -(x) : (x)) 44fd1aaeccSCameron Grant 453ac1ca33SNick Sayer /* if defined, playback always uses the 2nd channel and full duplex works */ 4686b391b2SAriff Abdullah #define ESS18XX_DUPLEX 1 47fd1aaeccSCameron Grant 48fd1aaeccSCameron Grant /* more accurate clocks and split audio1/audio2 rates */ 49fd1aaeccSCameron Grant #define ESS18XX_NEWSPEED 50fd1aaeccSCameron Grant 518b6d3fe1SAriff Abdullah /* 1 = INTR_MPSAFE, 0 = GIANT */ 528b6d3fe1SAriff Abdullah #define ESS18XX_MPSAFE 1 538b6d3fe1SAriff Abdullah 54513693beSCameron Grant static u_int32_t ess_playfmt[] = { 5590da2b28SAriff Abdullah SND_FORMAT(AFMT_U8, 1, 0), 5690da2b28SAriff Abdullah SND_FORMAT(AFMT_U8, 2, 0), 5790da2b28SAriff Abdullah SND_FORMAT(AFMT_S8, 1, 0), 5890da2b28SAriff Abdullah SND_FORMAT(AFMT_S8, 2, 0), 5990da2b28SAriff Abdullah SND_FORMAT(AFMT_S16_LE, 1, 0), 6090da2b28SAriff Abdullah SND_FORMAT(AFMT_S16_LE, 2, 0), 6190da2b28SAriff Abdullah SND_FORMAT(AFMT_U16_LE, 1, 0), 6290da2b28SAriff Abdullah SND_FORMAT(AFMT_U16_LE, 2, 0), 63513693beSCameron Grant 0 64fd1aaeccSCameron Grant }; 651333b4a4SAriff Abdullah static struct pcmchan_caps ess_playcaps = {6000, 48000, ess_playfmt, 0}; 66fd1aaeccSCameron Grant 673ac1ca33SNick Sayer /* 683ac1ca33SNick Sayer * Recording output is byte-swapped 693ac1ca33SNick Sayer */ 70513693beSCameron Grant static u_int32_t ess_recfmt[] = { 7190da2b28SAriff Abdullah SND_FORMAT(AFMT_U8, 1, 0), 7290da2b28SAriff Abdullah SND_FORMAT(AFMT_U8, 2, 0), 7390da2b28SAriff Abdullah SND_FORMAT(AFMT_S8, 1, 0), 7490da2b28SAriff Abdullah SND_FORMAT(AFMT_S8, 2, 0), 7590da2b28SAriff Abdullah SND_FORMAT(AFMT_S16_BE, 1, 0), 7690da2b28SAriff Abdullah SND_FORMAT(AFMT_S16_BE, 2, 0), 7790da2b28SAriff Abdullah SND_FORMAT(AFMT_U16_BE, 1, 0), 7890da2b28SAriff Abdullah SND_FORMAT(AFMT_U16_BE, 2, 0), 79513693beSCameron Grant 0 80fd1aaeccSCameron Grant }; 811333b4a4SAriff Abdullah static struct pcmchan_caps ess_reccaps = {6000, 48000, ess_recfmt, 0}; 82fd1aaeccSCameron Grant 83fd1aaeccSCameron Grant struct ess_info; 84fd1aaeccSCameron Grant 85fd1aaeccSCameron Grant struct ess_chinfo { 86fd1aaeccSCameron Grant struct ess_info *parent; 8766ef8af5SCameron Grant struct pcm_channel *channel; 8866ef8af5SCameron Grant struct snd_dbuf *buffer; 89fd1aaeccSCameron Grant int dir, hwch, stopping; 90350a5fafSCameron Grant u_int32_t fmt, spd, blksz; 91fd1aaeccSCameron Grant }; 92fd1aaeccSCameron Grant 93fd1aaeccSCameron Grant struct ess_info { 94fd1aaeccSCameron Grant struct resource *io, *sb, *vc, *mpu, *gp; /* I/O address for the board */ 95fd1aaeccSCameron Grant struct resource *irq; 96306f91b6SCameron Grant void *ih; 97fd1aaeccSCameron Grant bus_dma_tag_t parent_dmat; 98fd1aaeccSCameron Grant 9979462204SAriff Abdullah int simplex_dir, type, dmasz[2]; 10079462204SAriff Abdullah unsigned int duplex:1, newspeed:1; 10139dbd126SCameron Grant unsigned int bufsz; 10239dbd126SCameron Grant 103fd1aaeccSCameron Grant struct ess_chinfo pch, rch; 1048b6d3fe1SAriff Abdullah #if ESS18XX_MPSAFE == 1 1058b6d3fe1SAriff Abdullah struct mtx *lock; 1068b6d3fe1SAriff Abdullah #endif 107fd1aaeccSCameron Grant }; 108fd1aaeccSCameron Grant 1098b6d3fe1SAriff Abdullah #if ESS18XX_MPSAFE == 1 1108b6d3fe1SAriff Abdullah #define ess_lock(_ess) snd_mtxlock((_ess)->lock) 1118b6d3fe1SAriff Abdullah #define ess_unlock(_ess) snd_mtxunlock((_ess)->lock) 1128b6d3fe1SAriff Abdullah #define ess_lock_assert(_ess) snd_mtxassert((_ess)->lock) 1138b6d3fe1SAriff Abdullah #else 1148b6d3fe1SAriff Abdullah #define ess_lock(_ess) 1158b6d3fe1SAriff Abdullah #define ess_unlock(_ess) 1168b6d3fe1SAriff Abdullah #define ess_lock_assert(_ess) 1178b6d3fe1SAriff Abdullah #endif 1188b6d3fe1SAriff Abdullah 119fd1aaeccSCameron Grant static int ess_rd(struct ess_info *sc, int reg); 120fd1aaeccSCameron Grant static void ess_wr(struct ess_info *sc, int reg, u_int8_t val); 121fd1aaeccSCameron Grant static int ess_dspready(struct ess_info *sc); 122fd1aaeccSCameron Grant static int ess_cmd(struct ess_info *sc, u_char val); 123fd1aaeccSCameron Grant static int ess_cmd1(struct ess_info *sc, u_char cmd, int val); 124fd1aaeccSCameron Grant static int ess_get_byte(struct ess_info *sc); 125fd1aaeccSCameron Grant static void ess_setmixer(struct ess_info *sc, u_int port, u_int value); 126fd1aaeccSCameron Grant static int ess_getmixer(struct ess_info *sc, u_int port); 127fd1aaeccSCameron Grant static int ess_reset_dsp(struct ess_info *sc); 128fd1aaeccSCameron Grant 129fd1aaeccSCameron Grant static int ess_write(struct ess_info *sc, u_char reg, int val); 130fd1aaeccSCameron Grant static int ess_read(struct ess_info *sc, u_char reg); 131fd1aaeccSCameron Grant 132fd1aaeccSCameron Grant static void ess_intr(void *arg); 133fd1aaeccSCameron Grant static int ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int len); 134fd1aaeccSCameron Grant static int ess_start(struct ess_chinfo *ch); 135fd1aaeccSCameron Grant static int ess_stop(struct ess_chinfo *ch); 136fd1aaeccSCameron Grant 137fd1aaeccSCameron Grant static int ess_dmasetup(struct ess_info *sc, int ch, u_int32_t base, u_int16_t cnt, int dir); 138fd1aaeccSCameron Grant static int ess_dmapos(struct ess_info *sc, int ch); 139fd1aaeccSCameron Grant static int ess_dmatrigger(struct ess_info *sc, int ch, int go); 140fd1aaeccSCameron Grant 141fd1aaeccSCameron Grant /* 142fd1aaeccSCameron Grant * Common code for the midi and pcm functions 143fd1aaeccSCameron Grant * 144fd1aaeccSCameron Grant * ess_cmd write a single byte to the CMD port. 145fd1aaeccSCameron Grant * ess_cmd1 write a CMD + 1 byte arg 146fd1aaeccSCameron Grant * ess_cmd2 write a CMD + 2 byte arg 147fd1aaeccSCameron Grant * ess_get_byte returns a single byte from the DSP data port 148fd1aaeccSCameron Grant * 149fd1aaeccSCameron Grant * ess_write is actually ess_cmd1 150fd1aaeccSCameron Grant * ess_read access ext. regs via ess_cmd(0xc0, reg) followed by ess_get_byte 151fd1aaeccSCameron Grant */ 152fd1aaeccSCameron Grant 153fd1aaeccSCameron Grant static int 154fd1aaeccSCameron Grant port_rd(struct resource *port, int regno, int size) 155fd1aaeccSCameron Grant { 156fd1aaeccSCameron Grant bus_space_tag_t st = rman_get_bustag(port); 157fd1aaeccSCameron Grant bus_space_handle_t sh = rman_get_bushandle(port); 158fd1aaeccSCameron Grant 159fd1aaeccSCameron Grant switch (size) { 160fd1aaeccSCameron Grant case 1: 161fd1aaeccSCameron Grant return bus_space_read_1(st, sh, regno); 162fd1aaeccSCameron Grant case 2: 163fd1aaeccSCameron Grant return bus_space_read_2(st, sh, regno); 164fd1aaeccSCameron Grant case 4: 165fd1aaeccSCameron Grant return bus_space_read_4(st, sh, regno); 166fd1aaeccSCameron Grant default: 167fd1aaeccSCameron Grant return 0xffffffff; 168fd1aaeccSCameron Grant } 169fd1aaeccSCameron Grant } 170fd1aaeccSCameron Grant 171fd1aaeccSCameron Grant static void 172fd1aaeccSCameron Grant port_wr(struct resource *port, int regno, u_int32_t data, int size) 173fd1aaeccSCameron Grant { 174fd1aaeccSCameron Grant bus_space_tag_t st = rman_get_bustag(port); 175fd1aaeccSCameron Grant bus_space_handle_t sh = rman_get_bushandle(port); 176fd1aaeccSCameron Grant 177fd1aaeccSCameron Grant switch (size) { 178fd1aaeccSCameron Grant case 1: 179fd1aaeccSCameron Grant bus_space_write_1(st, sh, regno, data); 180fd1aaeccSCameron Grant break; 181fd1aaeccSCameron Grant case 2: 182fd1aaeccSCameron Grant bus_space_write_2(st, sh, regno, data); 183fd1aaeccSCameron Grant break; 184fd1aaeccSCameron Grant case 4: 185fd1aaeccSCameron Grant bus_space_write_4(st, sh, regno, data); 186fd1aaeccSCameron Grant break; 187fd1aaeccSCameron Grant } 188fd1aaeccSCameron Grant } 189fd1aaeccSCameron Grant 190fd1aaeccSCameron Grant static int 191fd1aaeccSCameron Grant ess_rd(struct ess_info *sc, int reg) 192fd1aaeccSCameron Grant { 193fd1aaeccSCameron Grant return port_rd(sc->sb, reg, 1); 194fd1aaeccSCameron Grant } 195fd1aaeccSCameron Grant 196fd1aaeccSCameron Grant static void 197fd1aaeccSCameron Grant ess_wr(struct ess_info *sc, int reg, u_int8_t val) 198fd1aaeccSCameron Grant { 199fd1aaeccSCameron Grant port_wr(sc->sb, reg, val, 1); 200fd1aaeccSCameron Grant } 201fd1aaeccSCameron Grant 202fd1aaeccSCameron Grant static int 203fd1aaeccSCameron Grant ess_dspready(struct ess_info *sc) 204fd1aaeccSCameron Grant { 205fd1aaeccSCameron Grant return ((ess_rd(sc, SBDSP_STATUS) & 0x80) == 0); 206fd1aaeccSCameron Grant } 207fd1aaeccSCameron Grant 208fd1aaeccSCameron Grant static int 209fd1aaeccSCameron Grant ess_dspwr(struct ess_info *sc, u_char val) 210fd1aaeccSCameron Grant { 211fd1aaeccSCameron Grant int i; 212fd1aaeccSCameron Grant 213fd1aaeccSCameron Grant for (i = 0; i < 1000; i++) { 214fd1aaeccSCameron Grant if (ess_dspready(sc)) { 215fd1aaeccSCameron Grant ess_wr(sc, SBDSP_CMD, val); 216fd1aaeccSCameron Grant return 1; 217fd1aaeccSCameron Grant } 218fd1aaeccSCameron Grant if (i > 10) DELAY((i > 100)? 1000 : 10); 219fd1aaeccSCameron Grant } 220fd1aaeccSCameron Grant printf("ess_dspwr(0x%02x) timed out.\n", val); 221fd1aaeccSCameron Grant return 0; 222fd1aaeccSCameron Grant } 223fd1aaeccSCameron Grant 224fd1aaeccSCameron Grant static int 225fd1aaeccSCameron Grant ess_cmd(struct ess_info *sc, u_char val) 226fd1aaeccSCameron Grant { 227a7e11506SNick Sayer DEB(printf("ess_cmd: %x\n", val)); 228fd1aaeccSCameron Grant return ess_dspwr(sc, val); 229fd1aaeccSCameron Grant } 230fd1aaeccSCameron Grant 231fd1aaeccSCameron Grant static int 232fd1aaeccSCameron Grant ess_cmd1(struct ess_info *sc, u_char cmd, int val) 233fd1aaeccSCameron Grant { 234a7e11506SNick Sayer DEB(printf("ess_cmd1: %x, %x\n", cmd, val)); 235fd1aaeccSCameron Grant if (ess_dspwr(sc, cmd)) { 236fd1aaeccSCameron Grant return ess_dspwr(sc, val & 0xff); 237fd1aaeccSCameron Grant } else return 0; 238fd1aaeccSCameron Grant } 239fd1aaeccSCameron Grant 240fd1aaeccSCameron Grant static void 241fd1aaeccSCameron Grant ess_setmixer(struct ess_info *sc, u_int port, u_int value) 242fd1aaeccSCameron Grant { 243fd1aaeccSCameron Grant DEB(printf("ess_setmixer: reg=%x, val=%x\n", port, value);) 244fd1aaeccSCameron Grant ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ 245fd1aaeccSCameron Grant DELAY(10); 246fd1aaeccSCameron Grant ess_wr(sc, SB_MIX_DATA, (u_char) (value & 0xff)); 247fd1aaeccSCameron Grant DELAY(10); 248fd1aaeccSCameron Grant } 249fd1aaeccSCameron Grant 250fd1aaeccSCameron Grant static int 251fd1aaeccSCameron Grant ess_getmixer(struct ess_info *sc, u_int port) 252fd1aaeccSCameron Grant { 253fd1aaeccSCameron Grant int val; 254fd1aaeccSCameron Grant 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 260fd1aaeccSCameron Grant return val; 261fd1aaeccSCameron Grant } 262fd1aaeccSCameron Grant 263fd1aaeccSCameron Grant static int 264fd1aaeccSCameron Grant ess_get_byte(struct ess_info *sc) 265fd1aaeccSCameron Grant { 266fd1aaeccSCameron Grant int i; 267fd1aaeccSCameron Grant 268fd1aaeccSCameron Grant for (i = 1000; i > 0; i--) { 2690edeb3dcSNick Sayer if (ess_rd(sc, 0xc) & 0x40) 270fd1aaeccSCameron Grant return ess_rd(sc, DSP_READ); 271fd1aaeccSCameron Grant else 272fd1aaeccSCameron Grant DELAY(20); 273fd1aaeccSCameron Grant } 274fd1aaeccSCameron Grant return -1; 275fd1aaeccSCameron Grant } 276fd1aaeccSCameron Grant 277fd1aaeccSCameron Grant static int 278fd1aaeccSCameron Grant ess_write(struct ess_info *sc, u_char reg, int val) 279fd1aaeccSCameron Grant { 280fd1aaeccSCameron Grant return ess_cmd1(sc, reg, val); 281fd1aaeccSCameron Grant } 282fd1aaeccSCameron Grant 283fd1aaeccSCameron Grant static int 284fd1aaeccSCameron Grant ess_read(struct ess_info *sc, u_char reg) 285fd1aaeccSCameron Grant { 286fd1aaeccSCameron Grant return (ess_cmd(sc, 0xc0) && ess_cmd(sc, reg))? ess_get_byte(sc) : -1; 287fd1aaeccSCameron Grant } 288fd1aaeccSCameron Grant 289fd1aaeccSCameron Grant static int 290fd1aaeccSCameron Grant ess_reset_dsp(struct ess_info *sc) 291fd1aaeccSCameron Grant { 292a7e11506SNick Sayer DEB(printf("ess_reset_dsp\n")); 293fd1aaeccSCameron Grant ess_wr(sc, SBDSP_RST, 3); 294fd1aaeccSCameron Grant DELAY(100); 295fd1aaeccSCameron Grant ess_wr(sc, SBDSP_RST, 0); 296fd1aaeccSCameron Grant if (ess_get_byte(sc) != 0xAA) { 297a7e11506SNick Sayer DEB(printf("ess_reset_dsp failed\n")); 298a7e11506SNick Sayer /* 299fd1aaeccSCameron Grant rman_get_start(d->io_base))); 300a7e11506SNick Sayer */ 301fd1aaeccSCameron Grant return ENXIO; /* Sorry */ 302fd1aaeccSCameron Grant } 303fd1aaeccSCameron Grant ess_cmd(sc, 0xc6); 304fd1aaeccSCameron Grant return 0; 305fd1aaeccSCameron Grant } 306fd1aaeccSCameron Grant 307fd1aaeccSCameron Grant static void 308fd1aaeccSCameron Grant ess_intr(void *arg) 309fd1aaeccSCameron Grant { 310fd1aaeccSCameron Grant struct ess_info *sc = (struct ess_info *)arg; 3113ac1ca33SNick Sayer int src, pirq = 0, rirq = 0; 312fd1aaeccSCameron Grant 3138b6d3fe1SAriff Abdullah ess_lock(sc); 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 3208d934d50SPyun YongHyeon if (src == 0) { 3218d934d50SPyun YongHyeon ess_unlock(sc); 3224e77c048SCameron Grant return; 3238d934d50SPyun YongHyeon } 3244e77c048SCameron Grant 3253ac1ca33SNick Sayer if (sc->duplex) { 326fd1aaeccSCameron Grant pirq = (src & sc->pch.hwch)? 1 : 0; 327fd1aaeccSCameron Grant rirq = (src & sc->rch.hwch)? 1 : 0; 3283ac1ca33SNick Sayer } else { 3293ac1ca33SNick Sayer if (sc->simplex_dir == PCMDIR_PLAY) 3303ac1ca33SNick Sayer pirq = 1; 3313ac1ca33SNick Sayer if (sc->simplex_dir == PCMDIR_REC) 3323ac1ca33SNick Sayer rirq = 1; 3333ac1ca33SNick Sayer if (!pirq && !rirq) 3343ac1ca33SNick Sayer printf("solo: IRQ neither playback nor rec!\n"); 3353ac1ca33SNick Sayer } 336fd1aaeccSCameron Grant 337a7e11506SNick Sayer DEB(printf("ess_intr: pirq:%d rirq:%d\n",pirq,rirq)); 338a7e11506SNick Sayer 339fd1aaeccSCameron Grant if (pirq) { 340fd1aaeccSCameron Grant if (sc->pch.stopping) { 341fd1aaeccSCameron Grant ess_dmatrigger(sc, sc->pch.hwch, 0); 342fd1aaeccSCameron Grant sc->pch.stopping = 0; 343fd1aaeccSCameron Grant if (sc->pch.hwch == 1) 344fd1aaeccSCameron Grant ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01); 345fd1aaeccSCameron Grant else 346fd1aaeccSCameron Grant ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) & ~0x03); 347fd1aaeccSCameron Grant } 3488b6d3fe1SAriff Abdullah ess_unlock(sc); 349fd1aaeccSCameron Grant chn_intr(sc->pch.channel); 3508b6d3fe1SAriff Abdullah ess_lock(sc); 351fd1aaeccSCameron Grant } 352fd1aaeccSCameron Grant 353fd1aaeccSCameron Grant if (rirq) { 354fd1aaeccSCameron Grant if (sc->rch.stopping) { 355fd1aaeccSCameron Grant ess_dmatrigger(sc, sc->rch.hwch, 0); 356fd1aaeccSCameron Grant sc->rch.stopping = 0; 357fd1aaeccSCameron Grant /* XXX: will this stop audio2? */ 358fd1aaeccSCameron Grant ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01); 359fd1aaeccSCameron Grant } 3608b6d3fe1SAriff Abdullah ess_unlock(sc); 361fd1aaeccSCameron Grant chn_intr(sc->rch.channel); 3628b6d3fe1SAriff Abdullah ess_lock(sc); 363fd1aaeccSCameron Grant } 364fd1aaeccSCameron Grant 365fd1aaeccSCameron Grant if (src & 2) 366fd1aaeccSCameron Grant ess_setmixer(sc, 0x7a, ess_getmixer(sc, 0x7a) & ~0x80); 367fd1aaeccSCameron Grant if (src & 1) 368fd1aaeccSCameron Grant ess_rd(sc, DSP_DATA_AVAIL); 3698b6d3fe1SAriff Abdullah 3708b6d3fe1SAriff Abdullah ess_unlock(sc); 371fd1aaeccSCameron Grant } 372fd1aaeccSCameron Grant 373fd1aaeccSCameron Grant /* utility functions for ESS */ 374fd1aaeccSCameron Grant static u_int8_t 375fd1aaeccSCameron Grant ess_calcspeed8(int *spd) 376fd1aaeccSCameron Grant { 377fd1aaeccSCameron Grant int speed = *spd; 378fd1aaeccSCameron Grant u_int32_t t; 379fd1aaeccSCameron Grant 380fd1aaeccSCameron Grant if (speed > 22000) { 381fd1aaeccSCameron Grant t = (795500 + speed / 2) / speed; 382fd1aaeccSCameron Grant speed = (795500 + t / 2) / t; 383fd1aaeccSCameron Grant t = (256 - t) | 0x80; 384fd1aaeccSCameron Grant } else { 385fd1aaeccSCameron Grant t = (397700 + speed / 2) / speed; 386fd1aaeccSCameron Grant speed = (397700 + t / 2) / t; 387fd1aaeccSCameron Grant t = 128 - t; 388fd1aaeccSCameron Grant } 389fd1aaeccSCameron Grant *spd = speed; 390fd1aaeccSCameron Grant return t & 0x000000ff; 391fd1aaeccSCameron Grant } 392fd1aaeccSCameron Grant 393fd1aaeccSCameron Grant static u_int8_t 394fd1aaeccSCameron Grant ess_calcspeed9(int *spd) 395fd1aaeccSCameron Grant { 396fd1aaeccSCameron Grant int speed, s0, s1, use0; 397fd1aaeccSCameron Grant u_int8_t t0, t1; 398fd1aaeccSCameron Grant 399fd1aaeccSCameron Grant /* rate = source / (256 - divisor) */ 400fd1aaeccSCameron Grant /* divisor = 256 - (source / rate) */ 401fd1aaeccSCameron Grant speed = *spd; 402fd1aaeccSCameron Grant t0 = 128 - (793800 / speed); 403fd1aaeccSCameron Grant s0 = 793800 / (128 - t0); 404fd1aaeccSCameron Grant 405fd1aaeccSCameron Grant t1 = 128 - (768000 / speed); 406fd1aaeccSCameron Grant s1 = 768000 / (128 - t1); 407fd1aaeccSCameron Grant t1 |= 0x80; 408fd1aaeccSCameron Grant 409fd1aaeccSCameron Grant use0 = (ABS(speed - s0) < ABS(speed - s1))? 1 : 0; 410fd1aaeccSCameron Grant 411fd1aaeccSCameron Grant *spd = use0? s0 : s1; 412fd1aaeccSCameron Grant return use0? t0 : t1; 413fd1aaeccSCameron Grant } 414fd1aaeccSCameron Grant 415fd1aaeccSCameron Grant static u_int8_t 416fd1aaeccSCameron Grant ess_calcfilter(int spd) 417fd1aaeccSCameron Grant { 418fd1aaeccSCameron Grant int cutoff; 419fd1aaeccSCameron Grant 420fd1aaeccSCameron Grant /* cutoff = 7160000 / (256 - divisor) */ 421fd1aaeccSCameron Grant /* divisor = 256 - (7160000 / cutoff) */ 422fd1aaeccSCameron Grant cutoff = (spd * 9 * 82) / 20; 423fd1aaeccSCameron Grant return (256 - (7160000 / cutoff)); 424fd1aaeccSCameron Grant } 425fd1aaeccSCameron Grant 426fd1aaeccSCameron Grant static int 427fd1aaeccSCameron Grant ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int len) 428fd1aaeccSCameron Grant { 429fd1aaeccSCameron Grant int play = (dir == PCMDIR_PLAY)? 1 : 0; 430fd1aaeccSCameron Grant int b16 = (fmt & AFMT_16BIT)? 1 : 0; 43190da2b28SAriff Abdullah int stereo = (AFMT_CHANNEL(fmt) > 1)? 1 : 0; 43290da2b28SAriff Abdullah int unsign = (!(fmt & AFMT_SIGNED))? 1 : 0; 433fd1aaeccSCameron Grant u_int8_t spdval, fmtval; 434fd1aaeccSCameron Grant 435a7e11506SNick Sayer DEB(printf("ess_setupch\n")); 436fd1aaeccSCameron Grant spdval = (sc->newspeed)? ess_calcspeed9(&spd) : ess_calcspeed8(&spd); 437fd1aaeccSCameron Grant 4383ac1ca33SNick Sayer sc->simplex_dir = play ? PCMDIR_PLAY : PCMDIR_REC ; 4393ac1ca33SNick Sayer 440fd1aaeccSCameron Grant if (ch == 1) { 441fd1aaeccSCameron Grant KASSERT((dir == PCMDIR_PLAY) || (dir == PCMDIR_REC), ("ess_setupch: dir1 bad")); 442fd1aaeccSCameron Grant len = -len; 443fd1aaeccSCameron Grant /* transfer length low */ 444fd1aaeccSCameron Grant ess_write(sc, 0xa4, len & 0x00ff); 445fd1aaeccSCameron Grant /* transfer length high */ 446fd1aaeccSCameron Grant ess_write(sc, 0xa5, (len & 0xff00) >> 8); 447fd1aaeccSCameron Grant /* autoinit, dma dir */ 448a7e11506SNick Sayer ess_write(sc, 0xb8, 0x04 | (play? 0x00 : 0x0a)); 449fd1aaeccSCameron Grant /* mono/stereo */ 450fd1aaeccSCameron Grant ess_write(sc, 0xa8, (ess_read(sc, 0xa8) & ~0x03) | (stereo? 0x01 : 0x02)); 451fd1aaeccSCameron Grant /* demand mode, 4 bytes/xfer */ 452fd1aaeccSCameron Grant ess_write(sc, 0xb9, 0x02); 453fd1aaeccSCameron Grant /* sample rate */ 454fd1aaeccSCameron Grant ess_write(sc, 0xa1, spdval); 455fd1aaeccSCameron Grant /* filter cutoff */ 456fd1aaeccSCameron Grant ess_write(sc, 0xa2, ess_calcfilter(spd)); 457fd1aaeccSCameron Grant /* setup dac/adc */ 458fd1aaeccSCameron Grant /* 459fd1aaeccSCameron Grant if (play) 460fd1aaeccSCameron Grant ess_write(sc, 0xb6, unsign? 0x80 : 0x00); 461fd1aaeccSCameron Grant */ 462fd1aaeccSCameron Grant /* mono, b16: signed, load signal */ 463fd1aaeccSCameron Grant /* 464fd1aaeccSCameron Grant ess_write(sc, 0xb7, 0x51 | (unsign? 0x00 : 0x20)); 465fd1aaeccSCameron Grant */ 466fd1aaeccSCameron Grant /* setup fifo */ 467a7e11506SNick Sayer ess_write(sc, 0xb7, 0x91 | (unsign? 0x00 : 0x20) | 468fd1aaeccSCameron Grant (b16? 0x04 : 0x00) | 469fd1aaeccSCameron Grant (stereo? 0x08 : 0x40)); 470fd1aaeccSCameron Grant /* irq control */ 471fd1aaeccSCameron Grant ess_write(sc, 0xb1, (ess_read(sc, 0xb1) & 0x0f) | 0x50); 472fd1aaeccSCameron Grant /* drq control */ 473fd1aaeccSCameron Grant ess_write(sc, 0xb2, (ess_read(sc, 0xb2) & 0x0f) | 0x50); 474fd1aaeccSCameron Grant } else if (ch == 2) { 475fd1aaeccSCameron Grant KASSERT(dir == PCMDIR_PLAY, ("ess_setupch: dir2 bad")); 476fd1aaeccSCameron Grant len >>= 1; 477fd1aaeccSCameron Grant len = -len; 478fd1aaeccSCameron Grant /* transfer length low */ 479fd1aaeccSCameron Grant ess_setmixer(sc, 0x74, len & 0x00ff); 480fd1aaeccSCameron Grant /* transfer length high */ 481fd1aaeccSCameron Grant ess_setmixer(sc, 0x76, (len & 0xff00) >> 8); 482fd1aaeccSCameron Grant /* autoinit, 4 bytes/req */ 483fd1aaeccSCameron Grant ess_setmixer(sc, 0x78, 0x10); 48480a8e065SNick Sayer fmtval = b16 | (stereo << 1) | ((!unsign) << 2); 485fd1aaeccSCameron Grant /* enable irq, set format */ 486fd1aaeccSCameron Grant ess_setmixer(sc, 0x7a, 0x40 | fmtval); 487fd1aaeccSCameron Grant if (sc->newspeed) { 488fd1aaeccSCameron Grant /* sample rate */ 489fd1aaeccSCameron Grant ess_setmixer(sc, 0x70, spdval); 490fd1aaeccSCameron Grant /* filter cutoff */ 491fd1aaeccSCameron Grant ess_setmixer(sc, 0x72, ess_calcfilter(spd)); 492fd1aaeccSCameron Grant } 493fd1aaeccSCameron Grant 494fd1aaeccSCameron Grant } 495fd1aaeccSCameron Grant return 0; 496fd1aaeccSCameron Grant } 497fd1aaeccSCameron Grant static int 498fd1aaeccSCameron Grant ess_start(struct ess_chinfo *ch) 499fd1aaeccSCameron Grant { 500fd1aaeccSCameron Grant struct ess_info *sc = ch->parent; 501fd1aaeccSCameron Grant 502a7e11506SNick Sayer DEB(printf("ess_start\n");); 503350a5fafSCameron Grant ess_setupch(sc, ch->hwch, ch->dir, ch->spd, ch->fmt, ch->blksz); 504fd1aaeccSCameron Grant ch->stopping = 0; 50519a0702eSNick Sayer if (ch->hwch == 1) { 506fd1aaeccSCameron Grant ess_write(sc, 0xb8, ess_read(sc, 0xb8) | 0x01); 50719a0702eSNick Sayer if (ch->dir == PCMDIR_PLAY) { 5080edeb3dcSNick Sayer #if 0 50919a0702eSNick Sayer DELAY(100000); /* 100 ms */ 5100edeb3dcSNick Sayer #endif 51119a0702eSNick Sayer ess_cmd(sc, 0xd1); 51219a0702eSNick Sayer } 51319a0702eSNick Sayer } else 514fd1aaeccSCameron Grant ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) | 0x03); 515fd1aaeccSCameron Grant return 0; 516fd1aaeccSCameron Grant } 517fd1aaeccSCameron Grant 518fd1aaeccSCameron Grant static int 519fd1aaeccSCameron Grant ess_stop(struct ess_chinfo *ch) 520fd1aaeccSCameron Grant { 521fd1aaeccSCameron Grant struct ess_info *sc = ch->parent; 522fd1aaeccSCameron Grant 523a7e11506SNick Sayer DEB(printf("ess_stop\n")); 524fd1aaeccSCameron Grant ch->stopping = 1; 525fd1aaeccSCameron Grant if (ch->hwch == 1) 526fd1aaeccSCameron Grant ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x04); 527fd1aaeccSCameron Grant else 528fd1aaeccSCameron Grant ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) & ~0x10); 529a7e11506SNick Sayer DEB(printf("done with stop\n")); 530fd1aaeccSCameron Grant return 0; 531fd1aaeccSCameron Grant } 532fd1aaeccSCameron Grant 5330f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 534fd1aaeccSCameron Grant /* channel interface for ESS18xx */ 535fd1aaeccSCameron Grant static void * 53666ef8af5SCameron Grant esschan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 537fd1aaeccSCameron Grant { 538fd1aaeccSCameron Grant struct ess_info *sc = devinfo; 539fd1aaeccSCameron Grant struct ess_chinfo *ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch; 540fd1aaeccSCameron Grant 541a7e11506SNick Sayer DEB(printf("esschan_init\n")); 542fd1aaeccSCameron Grant ch->parent = sc; 543fd1aaeccSCameron Grant ch->channel = c; 544fd1aaeccSCameron Grant ch->buffer = b; 545306f91b6SCameron Grant ch->dir = dir; 5462e334adfSAriff Abdullah if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0) 547fd1aaeccSCameron Grant return NULL; 548fd1aaeccSCameron Grant ch->hwch = 1; 549fd1aaeccSCameron Grant if ((dir == PCMDIR_PLAY) && (sc->duplex)) 550fd1aaeccSCameron Grant ch->hwch = 2; 551fd1aaeccSCameron Grant return ch; 552fd1aaeccSCameron Grant } 553fd1aaeccSCameron Grant 554fd1aaeccSCameron Grant static int 5550f55ac6cSCameron Grant esschan_setformat(kobj_t obj, 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 56390da2b28SAriff Abdullah static u_int32_t 5640f55ac6cSCameron Grant esschan_setspeed(kobj_t obj, 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 57790da2b28SAriff Abdullah static u_int32_t 5780f55ac6cSCameron Grant esschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 579fd1aaeccSCameron Grant { 580350a5fafSCameron Grant struct ess_chinfo *ch = data; 581350a5fafSCameron Grant 582350a5fafSCameron Grant ch->blksz = blocksize; 583350a5fafSCameron Grant return ch->blksz; 584fd1aaeccSCameron Grant } 585fd1aaeccSCameron Grant 586fd1aaeccSCameron Grant static int 5870f55ac6cSCameron Grant esschan_trigger(kobj_t obj, void *data, int go) 588fd1aaeccSCameron Grant { 589fd1aaeccSCameron Grant struct ess_chinfo *ch = data; 590fd1aaeccSCameron Grant struct ess_info *sc = ch->parent; 591fd1aaeccSCameron Grant 592bdfbdcecSAriff Abdullah if (!PCMTRIG_COMMON(go)) 593fd1aaeccSCameron Grant return 0; 594fd1aaeccSCameron Grant 595bdfbdcecSAriff Abdullah DEB(printf("esschan_trigger: %d\n",go)); 596bdfbdcecSAriff Abdullah 5978b6d3fe1SAriff Abdullah ess_lock(sc); 598fd1aaeccSCameron Grant switch (go) { 599fd1aaeccSCameron Grant case PCMTRIG_START: 60038cc9942SOlivier Houchard ess_dmasetup(sc, ch->hwch, sndbuf_getbufaddr(ch->buffer), sndbuf_getsize(ch->buffer), ch->dir); 601fd1aaeccSCameron Grant ess_dmatrigger(sc, ch->hwch, 1); 602fd1aaeccSCameron Grant ess_start(ch); 603fd1aaeccSCameron Grant break; 604fd1aaeccSCameron Grant 605fd1aaeccSCameron Grant case PCMTRIG_STOP: 606fd1aaeccSCameron Grant case PCMTRIG_ABORT: 607fd1aaeccSCameron Grant default: 608fd1aaeccSCameron Grant ess_stop(ch); 609fd1aaeccSCameron Grant break; 610fd1aaeccSCameron Grant } 6118b6d3fe1SAriff Abdullah ess_unlock(sc); 612fd1aaeccSCameron Grant return 0; 613fd1aaeccSCameron Grant } 614fd1aaeccSCameron Grant 61590da2b28SAriff Abdullah static u_int32_t 6160f55ac6cSCameron Grant esschan_getptr(kobj_t obj, void *data) 617fd1aaeccSCameron Grant { 618fd1aaeccSCameron Grant struct ess_chinfo *ch = data; 619fd1aaeccSCameron Grant struct ess_info *sc = ch->parent; 62090da2b28SAriff Abdullah u_int32_t ret; 621fd1aaeccSCameron Grant 6228b6d3fe1SAriff Abdullah ess_lock(sc); 6238b6d3fe1SAriff Abdullah ret = ess_dmapos(sc, ch->hwch); 6248b6d3fe1SAriff Abdullah ess_unlock(sc); 6258b6d3fe1SAriff Abdullah return ret; 626fd1aaeccSCameron Grant } 627fd1aaeccSCameron Grant 62866ef8af5SCameron Grant static struct pcmchan_caps * 6290f55ac6cSCameron Grant esschan_getcaps(kobj_t obj, void *data) 630fd1aaeccSCameron Grant { 631fd1aaeccSCameron Grant struct ess_chinfo *ch = data; 632fd1aaeccSCameron Grant 633fd1aaeccSCameron Grant return (ch->dir == PCMDIR_PLAY)? &ess_playcaps : &ess_reccaps; 634fd1aaeccSCameron Grant } 635fd1aaeccSCameron Grant 6360f55ac6cSCameron Grant static kobj_method_t esschan_methods[] = { 6370f55ac6cSCameron Grant KOBJMETHOD(channel_init, esschan_init), 6380f55ac6cSCameron Grant KOBJMETHOD(channel_setformat, esschan_setformat), 6390f55ac6cSCameron Grant KOBJMETHOD(channel_setspeed, esschan_setspeed), 6400f55ac6cSCameron Grant KOBJMETHOD(channel_setblocksize, esschan_setblocksize), 6410f55ac6cSCameron Grant KOBJMETHOD(channel_trigger, esschan_trigger), 6420f55ac6cSCameron Grant KOBJMETHOD(channel_getptr, esschan_getptr), 6430f55ac6cSCameron Grant KOBJMETHOD(channel_getcaps, esschan_getcaps), 64490da2b28SAriff Abdullah KOBJMETHOD_END 6450f55ac6cSCameron Grant }; 6460f55ac6cSCameron Grant CHANNEL_DECLARE(esschan); 6470f55ac6cSCameron Grant 648fd1aaeccSCameron Grant /************************************************************/ 649fd1aaeccSCameron Grant 650fd1aaeccSCameron Grant static int 65166ef8af5SCameron Grant essmix_init(struct snd_mixer *m) 652fd1aaeccSCameron Grant { 653fd1aaeccSCameron Grant struct ess_info *sc = mix_getdevinfo(m); 654fd1aaeccSCameron Grant 655fd1aaeccSCameron Grant mix_setrecdevs(m, SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE | 656fd1aaeccSCameron Grant SOUND_MASK_IMIX); 657fd1aaeccSCameron Grant 658fd1aaeccSCameron Grant mix_setdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | 659fd1aaeccSCameron Grant SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME | 660fd1aaeccSCameron Grant SOUND_MASK_LINE1); 661fd1aaeccSCameron Grant 662fd1aaeccSCameron Grant ess_setmixer(sc, 0, 0); /* reset */ 663fd1aaeccSCameron Grant 664fd1aaeccSCameron Grant return 0; 665fd1aaeccSCameron Grant } 666fd1aaeccSCameron Grant 667fd1aaeccSCameron Grant static int 66866ef8af5SCameron Grant essmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 669fd1aaeccSCameron Grant { 670fd1aaeccSCameron Grant struct ess_info *sc = mix_getdevinfo(m); 671fd1aaeccSCameron Grant int preg = 0, rreg = 0, l, r; 672fd1aaeccSCameron Grant 673fd1aaeccSCameron Grant l = (left * 15) / 100; 674fd1aaeccSCameron Grant r = (right * 15) / 100; 675fd1aaeccSCameron Grant switch (dev) { 676fd1aaeccSCameron Grant case SOUND_MIXER_SYNTH: 677fd1aaeccSCameron Grant preg = 0x36; 678fd1aaeccSCameron Grant rreg = 0x6b; 679fd1aaeccSCameron Grant break; 680fd1aaeccSCameron Grant 681fd1aaeccSCameron Grant case SOUND_MIXER_PCM: 682fd1aaeccSCameron Grant preg = 0x14; 683fd1aaeccSCameron Grant rreg = 0x7c; 684fd1aaeccSCameron Grant break; 685fd1aaeccSCameron Grant 686fd1aaeccSCameron Grant case SOUND_MIXER_LINE: 687fd1aaeccSCameron Grant preg = 0x3e; 688fd1aaeccSCameron Grant rreg = 0x6e; 689fd1aaeccSCameron Grant break; 690fd1aaeccSCameron Grant 691fd1aaeccSCameron Grant case SOUND_MIXER_MIC: 692fd1aaeccSCameron Grant preg = 0x1a; 693fd1aaeccSCameron Grant rreg = 0x68; 694fd1aaeccSCameron Grant break; 695fd1aaeccSCameron Grant 696fd1aaeccSCameron Grant case SOUND_MIXER_LINE1: 697fd1aaeccSCameron Grant preg = 0x3a; 698fd1aaeccSCameron Grant rreg = 0x6c; 699fd1aaeccSCameron Grant break; 700fd1aaeccSCameron Grant 701fd1aaeccSCameron Grant case SOUND_MIXER_CD: 702fd1aaeccSCameron Grant preg = 0x38; 703fd1aaeccSCameron Grant rreg = 0x6a; 704fd1aaeccSCameron Grant break; 705fd1aaeccSCameron Grant 706fd1aaeccSCameron Grant case SOUND_MIXER_VOLUME: 707fd1aaeccSCameron Grant l = left? (left * 63) / 100 : 64; 708fd1aaeccSCameron Grant r = right? (right * 63) / 100 : 64; 709fd1aaeccSCameron Grant ess_setmixer(sc, 0x60, l); 710fd1aaeccSCameron Grant ess_setmixer(sc, 0x62, r); 711fd1aaeccSCameron Grant left = (l == 64)? 0 : (l * 100) / 63; 712fd1aaeccSCameron Grant right = (r == 64)? 0 : (r * 100) / 63; 713fd1aaeccSCameron Grant return left | (right << 8); 714fd1aaeccSCameron Grant } 715fd1aaeccSCameron Grant 716fd1aaeccSCameron Grant if (preg) 717fd1aaeccSCameron Grant ess_setmixer(sc, preg, (l << 4) | r); 718fd1aaeccSCameron Grant if (rreg) 719fd1aaeccSCameron Grant ess_setmixer(sc, rreg, (l << 4) | r); 720fd1aaeccSCameron Grant 721fd1aaeccSCameron Grant left = (l * 100) / 15; 722fd1aaeccSCameron Grant right = (r * 100) / 15; 723fd1aaeccSCameron Grant 724fd1aaeccSCameron Grant return left | (right << 8); 725fd1aaeccSCameron Grant } 726fd1aaeccSCameron Grant 72790da2b28SAriff Abdullah static u_int32_t 72866ef8af5SCameron Grant essmix_setrecsrc(struct snd_mixer *m, u_int32_t src) 729fd1aaeccSCameron Grant { 730fd1aaeccSCameron Grant struct ess_info *sc = mix_getdevinfo(m); 731fd1aaeccSCameron Grant u_char recdev; 732fd1aaeccSCameron Grant 733fd1aaeccSCameron Grant switch (src) { 734fd1aaeccSCameron Grant case SOUND_MASK_CD: 735fd1aaeccSCameron Grant recdev = 0x02; 736fd1aaeccSCameron Grant break; 737fd1aaeccSCameron Grant 738fd1aaeccSCameron Grant case SOUND_MASK_LINE: 739fd1aaeccSCameron Grant recdev = 0x06; 740fd1aaeccSCameron Grant break; 741fd1aaeccSCameron Grant 742fd1aaeccSCameron Grant case SOUND_MASK_IMIX: 743fd1aaeccSCameron Grant recdev = 0x05; 744fd1aaeccSCameron Grant break; 745fd1aaeccSCameron Grant 746fd1aaeccSCameron Grant case SOUND_MASK_MIC: 747fd1aaeccSCameron Grant default: 748fd1aaeccSCameron Grant recdev = 0x00; 749fd1aaeccSCameron Grant src = SOUND_MASK_MIC; 750fd1aaeccSCameron Grant break; 751fd1aaeccSCameron Grant } 752fd1aaeccSCameron Grant 753fd1aaeccSCameron Grant ess_setmixer(sc, 0x1c, recdev); 754fd1aaeccSCameron Grant 755fd1aaeccSCameron Grant return src; 756fd1aaeccSCameron Grant } 757fd1aaeccSCameron Grant 7580f55ac6cSCameron Grant static kobj_method_t solomixer_methods[] = { 7590f55ac6cSCameron Grant KOBJMETHOD(mixer_init, essmix_init), 7600f55ac6cSCameron Grant KOBJMETHOD(mixer_set, essmix_set), 7610f55ac6cSCameron Grant KOBJMETHOD(mixer_setrecsrc, essmix_setrecsrc), 76290da2b28SAriff Abdullah KOBJMETHOD_END 7630f55ac6cSCameron Grant }; 7640f55ac6cSCameron Grant MIXER_DECLARE(solomixer); 7650f55ac6cSCameron Grant 766fd1aaeccSCameron Grant /************************************************************/ 767fd1aaeccSCameron Grant 768fd1aaeccSCameron Grant static int 769fd1aaeccSCameron Grant ess_dmasetup(struct ess_info *sc, int ch, u_int32_t base, u_int16_t cnt, int dir) 770fd1aaeccSCameron Grant { 771fd1aaeccSCameron Grant KASSERT(ch == 1 || ch == 2, ("bad ch")); 772fd1aaeccSCameron Grant sc->dmasz[ch - 1] = cnt; 773fd1aaeccSCameron Grant if (ch == 1) { 77419a0702eSNick Sayer port_wr(sc->vc, 0x8, 0xc4, 1); /* command */ 775fd1aaeccSCameron Grant port_wr(sc->vc, 0xd, 0xff, 1); /* reset */ 776fd1aaeccSCameron Grant port_wr(sc->vc, 0xf, 0x01, 1); /* mask */ 77719a0702eSNick Sayer port_wr(sc->vc, 0xb, dir == PCMDIR_PLAY? 0x58 : 0x54, 1); /* mode */ 778fd1aaeccSCameron Grant port_wr(sc->vc, 0x0, base, 4); 779bb7f26c3SNick Sayer port_wr(sc->vc, 0x4, cnt - 1, 2); 780fd1aaeccSCameron Grant 781fd1aaeccSCameron Grant } else if (ch == 2) { 782fd1aaeccSCameron Grant port_wr(sc->io, 0x6, 0x08, 1); /* autoinit */ 783fd1aaeccSCameron Grant port_wr(sc->io, 0x0, base, 4); 784fd1aaeccSCameron Grant port_wr(sc->io, 0x4, cnt, 2); 785fd1aaeccSCameron Grant } 786fd1aaeccSCameron Grant return 0; 787fd1aaeccSCameron Grant } 788fd1aaeccSCameron Grant 789fd1aaeccSCameron Grant static int 790fd1aaeccSCameron Grant ess_dmapos(struct ess_info *sc, int ch) 791fd1aaeccSCameron Grant { 7926ba60b3cSNick Sayer int p = 0, i = 0, j = 0; 793fd1aaeccSCameron Grant 794fd1aaeccSCameron Grant KASSERT(ch == 1 || ch == 2, ("bad ch")); 7950edeb3dcSNick Sayer if (ch == 1) { 7960edeb3dcSNick Sayer 7970edeb3dcSNick Sayer /* 7980edeb3dcSNick Sayer * During recording, this register is known to give back 7990edeb3dcSNick Sayer * garbage if it's not quiescent while being read. That's 8006ba60b3cSNick Sayer * why we spl, stop the DMA, and try over and over until 8016ba60b3cSNick Sayer * adjacent reads are "close", in the right order and not 8026ba60b3cSNick Sayer * bigger than is otherwise possible. 8030edeb3dcSNick Sayer */ 8040edeb3dcSNick Sayer ess_dmatrigger(sc, ch, 0); 8050edeb3dcSNick Sayer DELAY(20); 8066ba60b3cSNick Sayer do { 8076ba60b3cSNick Sayer DELAY(10); 8086ba60b3cSNick Sayer if (j > 1) 8096ba60b3cSNick Sayer printf("DMA count reg bogus: %04x & %04x\n", 8106ba60b3cSNick Sayer i, p); 8116ba60b3cSNick Sayer i = port_rd(sc->vc, 0x4, 2) + 1; 812fd1aaeccSCameron Grant p = port_rd(sc->vc, 0x4, 2) + 1; 8136ba60b3cSNick Sayer } while ((p > sc->dmasz[ch - 1] || i < p || (p - i) > 0x8) && j++ < 1000); 8140edeb3dcSNick Sayer ess_dmatrigger(sc, ch, 1); 8150edeb3dcSNick Sayer } 816fd1aaeccSCameron Grant else if (ch == 2) 817fd1aaeccSCameron Grant p = port_rd(sc->io, 0x4, 2); 818fd1aaeccSCameron Grant return sc->dmasz[ch - 1] - p; 819fd1aaeccSCameron Grant } 820fd1aaeccSCameron Grant 821fd1aaeccSCameron Grant static int 822fd1aaeccSCameron Grant ess_dmatrigger(struct ess_info *sc, int ch, int go) 823fd1aaeccSCameron Grant { 824fd1aaeccSCameron Grant KASSERT(ch == 1 || ch == 2, ("bad ch")); 825fd1aaeccSCameron Grant if (ch == 1) 8268eb3acc9SNick Sayer port_wr(sc->vc, 0xf, go? 0x00 : 0x01, 1); /* mask */ 827fd1aaeccSCameron Grant else if (ch == 2) 828fd1aaeccSCameron Grant port_wr(sc->io, 0x6, 0x08 | (go? 0x02 : 0x00), 1); /* autoinit */ 829fd1aaeccSCameron Grant return 0; 830fd1aaeccSCameron Grant } 831fd1aaeccSCameron Grant 832fd1aaeccSCameron Grant static void 833fd1aaeccSCameron Grant ess_release_resources(struct ess_info *sc, device_t dev) 834fd1aaeccSCameron Grant { 835fd1aaeccSCameron Grant if (sc->irq) { 836306f91b6SCameron Grant if (sc->ih) 837306f91b6SCameron Grant bus_teardown_intr(dev, sc->irq, sc->ih); 838fd1aaeccSCameron Grant bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); 839fd1aaeccSCameron Grant sc->irq = 0; 840fd1aaeccSCameron Grant } 841fd1aaeccSCameron Grant if (sc->io) { 842e27951b2SJohn Baldwin bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), sc->io); 843fd1aaeccSCameron Grant sc->io = 0; 844fd1aaeccSCameron Grant } 845fd1aaeccSCameron Grant 846fd1aaeccSCameron Grant if (sc->sb) { 847e27951b2SJohn Baldwin bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(1), sc->sb); 848fd1aaeccSCameron Grant sc->sb = 0; 849fd1aaeccSCameron Grant } 850fd1aaeccSCameron Grant 851fd1aaeccSCameron Grant if (sc->vc) { 852e27951b2SJohn Baldwin bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(2), sc->vc); 853fd1aaeccSCameron Grant sc->vc = 0; 854fd1aaeccSCameron Grant } 855fd1aaeccSCameron Grant 856fd1aaeccSCameron Grant if (sc->mpu) { 857e27951b2SJohn Baldwin bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(3), sc->mpu); 858fd1aaeccSCameron Grant sc->mpu = 0; 859fd1aaeccSCameron Grant } 860fd1aaeccSCameron Grant 861fd1aaeccSCameron Grant if (sc->gp) { 862e27951b2SJohn Baldwin bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(4), sc->gp); 863fd1aaeccSCameron Grant sc->gp = 0; 864fd1aaeccSCameron Grant } 865fd1aaeccSCameron Grant 866306f91b6SCameron Grant if (sc->parent_dmat) { 867306f91b6SCameron Grant bus_dma_tag_destroy(sc->parent_dmat); 868306f91b6SCameron Grant sc->parent_dmat = 0; 869306f91b6SCameron Grant } 870306f91b6SCameron Grant 8718b6d3fe1SAriff Abdullah #if ESS18XX_MPSAFE == 1 8728b6d3fe1SAriff Abdullah if (sc->lock) { 8738b6d3fe1SAriff Abdullah snd_mtxfree(sc->lock); 8748b6d3fe1SAriff Abdullah sc->lock = NULL; 8758b6d3fe1SAriff Abdullah } 8768b6d3fe1SAriff Abdullah #endif 8778b6d3fe1SAriff Abdullah 878fd1aaeccSCameron Grant free(sc, M_DEVBUF); 879fd1aaeccSCameron Grant } 880fd1aaeccSCameron Grant 881fd1aaeccSCameron Grant static int 882fd1aaeccSCameron Grant ess_alloc_resources(struct ess_info *sc, device_t dev) 883fd1aaeccSCameron Grant { 884fd1aaeccSCameron Grant int rid; 885fd1aaeccSCameron Grant 886e27951b2SJohn Baldwin rid = PCIR_BAR(0); 8875f96beb9SNate Lawson sc->io = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); 888fd1aaeccSCameron Grant 889e27951b2SJohn Baldwin rid = PCIR_BAR(1); 8905f96beb9SNate Lawson sc->sb = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); 891fd1aaeccSCameron Grant 892e27951b2SJohn Baldwin rid = PCIR_BAR(2); 8935f96beb9SNate Lawson sc->vc = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); 894fd1aaeccSCameron Grant 895e27951b2SJohn Baldwin rid = PCIR_BAR(3); 8965f96beb9SNate Lawson sc->mpu = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); 897fd1aaeccSCameron Grant 898e27951b2SJohn Baldwin rid = PCIR_BAR(4); 8995f96beb9SNate Lawson sc->gp = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); 900fd1aaeccSCameron Grant 901fd1aaeccSCameron Grant rid = 0; 9025f96beb9SNate Lawson sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 9035f96beb9SNate Lawson RF_ACTIVE | RF_SHAREABLE); 904fd1aaeccSCameron Grant 9058b6d3fe1SAriff Abdullah #if ESS18XX_MPSAFE == 1 9064582b3a1SAriff Abdullah sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_solo softc"); 9078b6d3fe1SAriff Abdullah 9088b6d3fe1SAriff Abdullah return (sc->irq && sc->io && sc->sb && sc->vc && 9098b6d3fe1SAriff Abdullah sc->mpu && sc->gp && sc->lock)? 0 : ENXIO; 9108b6d3fe1SAriff Abdullah #else 911fd1aaeccSCameron Grant return (sc->irq && sc->io && sc->sb && sc->vc && sc->mpu && sc->gp)? 0 : ENXIO; 9128b6d3fe1SAriff Abdullah #endif 913fd1aaeccSCameron Grant } 914fd1aaeccSCameron Grant 915fd1aaeccSCameron Grant static int 916fd1aaeccSCameron Grant ess_probe(device_t dev) 917fd1aaeccSCameron Grant { 918fd1aaeccSCameron Grant char *s = NULL; 919fd1aaeccSCameron Grant u_int32_t subdev; 920fd1aaeccSCameron Grant 921fd1aaeccSCameron Grant subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev); 922fd1aaeccSCameron Grant switch (pci_get_devid(dev)) { 923fd1aaeccSCameron Grant case 0x1969125d: 924fd1aaeccSCameron Grant if (subdev == 0x8888125d) 925fd1aaeccSCameron Grant s = "ESS Solo-1E"; 926fd1aaeccSCameron Grant else if (subdev == 0x1818125d) 927fd1aaeccSCameron Grant s = "ESS Solo-1"; 928fd1aaeccSCameron Grant else 929fd1aaeccSCameron Grant s = "ESS Solo-1 (unknown vendor)"; 930fd1aaeccSCameron Grant break; 931fd1aaeccSCameron Grant } 932fd1aaeccSCameron Grant 933fd1aaeccSCameron Grant if (s) 934fd1aaeccSCameron Grant device_set_desc(dev, s); 935d2b677bbSWarner Losh return s ? BUS_PROBE_DEFAULT : ENXIO; 936fd1aaeccSCameron Grant } 937fd1aaeccSCameron Grant 938933ce6faSOrion Hodson #define ESS_PCI_LEGACYCONTROL 0x40 939933ce6faSOrion Hodson #define ESS_PCI_CONFIG 0x50 940933ce6faSOrion Hodson #define ESS_PCI_DDMACONTROL 0x60 941933ce6faSOrion Hodson 942933ce6faSOrion Hodson static int 943933ce6faSOrion Hodson ess_suspend(device_t dev) 944933ce6faSOrion Hodson { 945933ce6faSOrion Hodson return 0; 946933ce6faSOrion Hodson } 947933ce6faSOrion Hodson 948933ce6faSOrion Hodson static int 949933ce6faSOrion Hodson ess_resume(device_t dev) 950933ce6faSOrion Hodson { 951933ce6faSOrion Hodson uint16_t ddma; 952933ce6faSOrion Hodson struct ess_info *sc = pcm_getdevinfo(dev); 953933ce6faSOrion Hodson 9548b6d3fe1SAriff Abdullah ess_lock(sc); 955933ce6faSOrion Hodson ddma = rman_get_start(sc->vc) | 1; 956933ce6faSOrion Hodson pci_write_config(dev, ESS_PCI_LEGACYCONTROL, 0x805f, 2); 957933ce6faSOrion Hodson pci_write_config(dev, ESS_PCI_DDMACONTROL, ddma, 2); 958933ce6faSOrion Hodson pci_write_config(dev, ESS_PCI_CONFIG, 0, 2); 959933ce6faSOrion Hodson 9608b6d3fe1SAriff Abdullah if (ess_reset_dsp(sc)) { 9618b6d3fe1SAriff Abdullah ess_unlock(sc); 962933ce6faSOrion Hodson goto no; 9638b6d3fe1SAriff Abdullah } 9648b6d3fe1SAriff Abdullah ess_unlock(sc); 965933ce6faSOrion Hodson if (mixer_reinit(dev)) 966933ce6faSOrion Hodson goto no; 9678b6d3fe1SAriff Abdullah ess_lock(sc); 968933ce6faSOrion Hodson if (sc->newspeed) 969933ce6faSOrion Hodson ess_setmixer(sc, 0x71, 0x2a); 970933ce6faSOrion Hodson 971933ce6faSOrion Hodson port_wr(sc->io, 0x7, 0xb0, 1); /* enable irqs */ 9728b6d3fe1SAriff Abdullah ess_unlock(sc); 973933ce6faSOrion Hodson 974933ce6faSOrion Hodson return 0; 975933ce6faSOrion Hodson no: 976933ce6faSOrion Hodson return EIO; 977933ce6faSOrion Hodson } 978fd1aaeccSCameron Grant 979fd1aaeccSCameron Grant static int 980fd1aaeccSCameron Grant ess_attach(device_t dev) 981fd1aaeccSCameron Grant { 982fd1aaeccSCameron Grant struct ess_info *sc; 983fd1aaeccSCameron Grant char status[SND_STATUSLEN]; 984fd1aaeccSCameron Grant u_int16_t ddma; 985fd1aaeccSCameron Grant 986082f6383SAriff Abdullah sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); 987*c68534f1SScott Long pci_enable_busmaster(dev); 988fd1aaeccSCameron Grant 989fd1aaeccSCameron Grant if (ess_alloc_resources(sc, dev)) 990fd1aaeccSCameron Grant goto no; 991fd1aaeccSCameron Grant 99239dbd126SCameron Grant sc->bufsz = pcm_getbuffersize(dev, 4096, SOLO_DEFAULT_BUFSZ, 65536); 99339dbd126SCameron Grant 994fd1aaeccSCameron Grant ddma = rman_get_start(sc->vc) | 1; 995933ce6faSOrion Hodson pci_write_config(dev, ESS_PCI_LEGACYCONTROL, 0x805f, 2); 996933ce6faSOrion Hodson pci_write_config(dev, ESS_PCI_DDMACONTROL, ddma, 2); 997933ce6faSOrion Hodson pci_write_config(dev, ESS_PCI_CONFIG, 0, 2); 998fd1aaeccSCameron Grant 999fd1aaeccSCameron Grant port_wr(sc->io, 0x7, 0xb0, 1); /* enable irqs */ 10003ac1ca33SNick Sayer #ifdef ESS18XX_DUPLEX 1001fd1aaeccSCameron Grant sc->duplex = 1; 10023ac1ca33SNick Sayer #else 10033ac1ca33SNick Sayer sc->duplex = 0; 10043ac1ca33SNick Sayer #endif 1005fd1aaeccSCameron Grant 10063ac1ca33SNick Sayer #ifdef ESS18XX_NEWSPEED 10073ac1ca33SNick Sayer sc->newspeed = 1; 10083ac1ca33SNick Sayer #else 10093ac1ca33SNick Sayer sc->newspeed = 0; 10103ac1ca33SNick Sayer #endif 10118b6d3fe1SAriff Abdullah if (snd_setup_intr(dev, sc->irq, 10128b6d3fe1SAriff Abdullah #if ESS18XX_MPSAFE == 1 10138b6d3fe1SAriff Abdullah INTR_MPSAFE 10148b6d3fe1SAriff Abdullah #else 10158b6d3fe1SAriff Abdullah 0 10168b6d3fe1SAriff Abdullah #endif 10178b6d3fe1SAriff Abdullah , ess_intr, sc, &sc->ih)) { 10188b6d3fe1SAriff Abdullah device_printf(dev, "unable to map interrupt\n"); 10198b6d3fe1SAriff Abdullah goto no; 10208b6d3fe1SAriff Abdullah } 1021fd1aaeccSCameron Grant 1022fd1aaeccSCameron Grant if (!sc->duplex) 1023fd1aaeccSCameron Grant pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); 1024fd1aaeccSCameron Grant 10258b6d3fe1SAriff Abdullah #if 0 10260b989078SAlexander Leidinger if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/65536, /*boundary*/0, 10278b6d3fe1SAriff Abdullah #endif 10280b989078SAlexander Leidinger if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, /*boundary*/0, 1029fd1aaeccSCameron Grant /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, 1030fd1aaeccSCameron Grant /*highaddr*/BUS_SPACE_MAXADDR, 1031fd1aaeccSCameron Grant /*filter*/NULL, /*filterarg*/NULL, 103239dbd126SCameron Grant /*maxsize*/sc->bufsz, /*nsegments*/1, 1033fd1aaeccSCameron Grant /*maxsegz*/0x3ffff, 10348b6d3fe1SAriff Abdullah /*flags*/0, 10358b6d3fe1SAriff Abdullah #if ESS18XX_MPSAFE == 1 10368b6d3fe1SAriff Abdullah /*lockfunc*/NULL, /*lockarg*/NULL, 10378b6d3fe1SAriff Abdullah #else 10388b6d3fe1SAriff Abdullah /*lockfunc*/busdma_lock_mutex, /*lockarg*/&Giant, 10398b6d3fe1SAriff Abdullah #endif 10408b6d3fe1SAriff Abdullah &sc->parent_dmat) != 0) { 1041fd1aaeccSCameron Grant device_printf(dev, "unable to create dma tag\n"); 1042fd1aaeccSCameron Grant goto no; 1043fd1aaeccSCameron Grant } 1044fd1aaeccSCameron Grant 10458b6d3fe1SAriff Abdullah if (ess_reset_dsp(sc)) 10468b6d3fe1SAriff Abdullah goto no; 10478b6d3fe1SAriff Abdullah 10488b6d3fe1SAriff Abdullah if (sc->newspeed) 10498b6d3fe1SAriff Abdullah ess_setmixer(sc, 0x71, 0x2a); 10508b6d3fe1SAriff Abdullah 10518b6d3fe1SAriff Abdullah if (mixer_init(dev, &solomixer_class, sc)) 10528b6d3fe1SAriff Abdullah goto no; 10538b6d3fe1SAriff Abdullah 10540d8ed52eSMathew Kanner snprintf(status, SND_STATUSLEN, "at io 0x%lx,0x%lx,0x%lx irq %ld %s", 1055fd1aaeccSCameron Grant rman_get_start(sc->io), rman_get_start(sc->sb), rman_get_start(sc->vc), 10560d8ed52eSMathew Kanner rman_get_start(sc->irq),PCM_KLDSTRING(snd_solo)); 1057fd1aaeccSCameron Grant 1058fd1aaeccSCameron Grant if (pcm_register(dev, sc, 1, 1)) 1059fd1aaeccSCameron Grant goto no; 10600f55ac6cSCameron Grant pcm_addchan(dev, PCMDIR_REC, &esschan_class, sc); 10610f55ac6cSCameron Grant pcm_addchan(dev, PCMDIR_PLAY, &esschan_class, sc); 1062fd1aaeccSCameron Grant pcm_setstatus(dev, status); 1063fd1aaeccSCameron Grant 1064fd1aaeccSCameron Grant return 0; 1065fd1aaeccSCameron Grant 1066fd1aaeccSCameron Grant no: 1067fd1aaeccSCameron Grant ess_release_resources(sc, dev); 1068fd1aaeccSCameron Grant return ENXIO; 1069fd1aaeccSCameron Grant } 1070fd1aaeccSCameron Grant 1071306f91b6SCameron Grant static int 1072306f91b6SCameron Grant ess_detach(device_t dev) 1073306f91b6SCameron Grant { 1074306f91b6SCameron Grant int r; 10757dfc9325SCameron Grant struct ess_info *sc; 1076306f91b6SCameron Grant 1077306f91b6SCameron Grant r = pcm_unregister(dev); 1078306f91b6SCameron Grant if (r) 1079306f91b6SCameron Grant return r; 1080306f91b6SCameron Grant 1081306f91b6SCameron Grant sc = pcm_getdevinfo(dev); 1082306f91b6SCameron Grant ess_release_resources(sc, dev); 1083306f91b6SCameron Grant return 0; 1084306f91b6SCameron Grant } 1085306f91b6SCameron Grant 1086fd1aaeccSCameron Grant static device_method_t ess_methods[] = { 1087fd1aaeccSCameron Grant /* Device interface */ 1088fd1aaeccSCameron Grant DEVMETHOD(device_probe, ess_probe), 1089fd1aaeccSCameron Grant DEVMETHOD(device_attach, ess_attach), 1090306f91b6SCameron Grant DEVMETHOD(device_detach, ess_detach), 1091933ce6faSOrion Hodson DEVMETHOD(device_resume, ess_resume), 1092933ce6faSOrion Hodson DEVMETHOD(device_suspend, ess_suspend), 1093fd1aaeccSCameron Grant 1094fd1aaeccSCameron Grant { 0, 0 } 1095fd1aaeccSCameron Grant }; 1096fd1aaeccSCameron Grant 1097fd1aaeccSCameron Grant static driver_t ess_driver = { 1098fd1aaeccSCameron Grant "pcm", 1099fd1aaeccSCameron Grant ess_methods, 110067b1dce3SCameron Grant PCM_SOFTC_SIZE, 1101fd1aaeccSCameron Grant }; 1102fd1aaeccSCameron Grant 1103fd1aaeccSCameron Grant DRIVER_MODULE(snd_solo, pci, ess_driver, pcm_devclass, 0, 0); 11040739ea1dSSeigo Tanimura MODULE_DEPEND(snd_solo, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 1105fd1aaeccSCameron Grant MODULE_VERSION(snd_solo, 1); 1106fd1aaeccSCameron Grant 1107fd1aaeccSCameron Grant 1108fd1aaeccSCameron Grant 1109