1e4afd792SAlexander Motin /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 420a9f771SRuslan Bukin * Copyright (c) 2012-2016 Ruslan Bukin <br@bsdpad.com> 5d7fde2c9SFlorian Walpen * Copyright (c) 2023-2024 Florian Walpen <dev@submerge.ch> 6e4afd792SAlexander Motin * All rights reserved. 7e4afd792SAlexander Motin * 8e4afd792SAlexander Motin * Redistribution and use in source and binary forms, with or without 9e4afd792SAlexander Motin * modification, are permitted provided that the following conditions 10e4afd792SAlexander Motin * are met: 11e4afd792SAlexander Motin * 1. Redistributions of source code must retain the above copyright 12e4afd792SAlexander Motin * notice, this list of conditions and the following disclaimer. 13e4afd792SAlexander Motin * 2. Redistributions in binary form must reproduce the above copyright 14e4afd792SAlexander Motin * notice, this list of conditions and the following disclaimer in the 15e4afd792SAlexander Motin * documentation and/or other materials provided with the distribution. 16e4afd792SAlexander Motin * 17e4afd792SAlexander Motin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18e4afd792SAlexander Motin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19e4afd792SAlexander Motin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20e4afd792SAlexander Motin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21e4afd792SAlexander Motin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22e4afd792SAlexander Motin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23e4afd792SAlexander Motin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24e4afd792SAlexander Motin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25e4afd792SAlexander Motin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26e4afd792SAlexander Motin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27e4afd792SAlexander Motin * SUCH DAMAGE. 28e4afd792SAlexander Motin */ 29e4afd792SAlexander Motin 30e4afd792SAlexander Motin /* 31e4afd792SAlexander Motin * RME HDSPe driver for FreeBSD. 32e4afd792SAlexander Motin * Supported cards: AIO, RayDAT. 33e4afd792SAlexander Motin */ 34e4afd792SAlexander Motin 35b6052c10SRuslan Bukin #include <sys/types.h> 36b6052c10SRuslan Bukin #include <sys/sysctl.h> 37b6052c10SRuslan Bukin 38e4afd792SAlexander Motin #include <dev/sound/pcm/sound.h> 39e4afd792SAlexander Motin #include <dev/sound/pci/hdspe.h> 40e4afd792SAlexander Motin 41e4afd792SAlexander Motin #include <dev/pci/pcireg.h> 42e4afd792SAlexander Motin #include <dev/pci/pcivar.h> 43e4afd792SAlexander Motin 44e4afd792SAlexander Motin #include <mixer_if.h> 45e4afd792SAlexander Motin 46dc15f025SFlorian Walpen static bool hdspe_unified_pcm = false; 47dc15f025SFlorian Walpen 48dc15f025SFlorian Walpen static SYSCTL_NODE(_hw, OID_AUTO, hdspe, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 49dc15f025SFlorian Walpen "PCI HDSPe"); 50dc15f025SFlorian Walpen 51dc15f025SFlorian Walpen SYSCTL_BOOL(_hw_hdspe, OID_AUTO, unified_pcm, CTLFLAG_RWTUN, 52dc15f025SFlorian Walpen &hdspe_unified_pcm, 0, "Combine physical ports in one unified pcm device"); 53dc15f025SFlorian Walpen 54b6052c10SRuslan Bukin static struct hdspe_clock_source hdspe_clock_source_table_rd[] = { 55b6052c10SRuslan Bukin { "internal", 0 << 1 | 1, HDSPE_STATUS1_CLOCK(15), 0, 0 }, 56b6052c10SRuslan Bukin { "word", 0 << 1 | 0, HDSPE_STATUS1_CLOCK( 0), 1 << 24, 1 << 25 }, 57b6052c10SRuslan Bukin { "aes", 1 << 1 | 0, HDSPE_STATUS1_CLOCK( 1), 1 << 0, 1 << 8 }, 58b6052c10SRuslan Bukin { "spdif", 2 << 1 | 0, HDSPE_STATUS1_CLOCK( 2), 1 << 1, 1 << 9 }, 59b6052c10SRuslan Bukin { "adat1", 3 << 1 | 0, HDSPE_STATUS1_CLOCK( 3), 1 << 2, 1 << 10 }, 60b6052c10SRuslan Bukin { "adat2", 4 << 1 | 0, HDSPE_STATUS1_CLOCK( 4), 1 << 3, 1 << 11 }, 61b6052c10SRuslan Bukin { "adat3", 5 << 1 | 0, HDSPE_STATUS1_CLOCK( 5), 1 << 4, 1 << 12 }, 62b6052c10SRuslan Bukin { "adat4", 6 << 1 | 0, HDSPE_STATUS1_CLOCK( 6), 1 << 5, 1 << 13 }, 63b6052c10SRuslan Bukin { "tco", 9 << 1 | 0, HDSPE_STATUS1_CLOCK( 9), 1 << 26, 1 << 27 }, 64b6052c10SRuslan Bukin { "sync_in", 10 << 1 | 0, HDSPE_STATUS1_CLOCK(10), 0, 0 }, 65b6052c10SRuslan Bukin { NULL, 0 << 1 | 0, HDSPE_STATUS1_CLOCK( 0), 0, 0 }, 66b6052c10SRuslan Bukin }; 67b6052c10SRuslan Bukin 68b6052c10SRuslan Bukin static struct hdspe_clock_source hdspe_clock_source_table_aio[] = { 69b6052c10SRuslan Bukin { "internal", 0 << 1 | 1, HDSPE_STATUS1_CLOCK(15), 0, 0 }, 70b6052c10SRuslan Bukin { "word", 0 << 1 | 0, HDSPE_STATUS1_CLOCK( 0), 1 << 24, 1 << 25 }, 71b6052c10SRuslan Bukin { "aes", 1 << 1 | 0, HDSPE_STATUS1_CLOCK( 1), 1 << 0, 1 << 8 }, 72b6052c10SRuslan Bukin { "spdif", 2 << 1 | 0, HDSPE_STATUS1_CLOCK( 2), 1 << 1, 1 << 9 }, 73b6052c10SRuslan Bukin { "adat", 3 << 1 | 0, HDSPE_STATUS1_CLOCK( 3), 1 << 2, 1 << 10 }, 74b6052c10SRuslan Bukin { "tco", 9 << 1 | 0, HDSPE_STATUS1_CLOCK( 9), 1 << 26, 1 << 27 }, 75b6052c10SRuslan Bukin { "sync_in", 10 << 1 | 0, HDSPE_STATUS1_CLOCK(10), 0, 0 }, 76b6052c10SRuslan Bukin { NULL, 0 << 1 | 0, HDSPE_STATUS1_CLOCK( 0), 0, 0 }, 77b6052c10SRuslan Bukin }; 78b6052c10SRuslan Bukin 79e4afd792SAlexander Motin static struct hdspe_channel chan_map_aio[] = { 80d7fde2c9SFlorian Walpen { HDSPE_CHAN_AIO_LINE, "line" }, 81d7fde2c9SFlorian Walpen { HDSPE_CHAN_AIO_PHONE, "phone" }, 82d7fde2c9SFlorian Walpen { HDSPE_CHAN_AIO_AES, "aes" }, 83d7fde2c9SFlorian Walpen { HDSPE_CHAN_AIO_SPDIF, "s/pdif" }, 84d7fde2c9SFlorian Walpen { HDSPE_CHAN_AIO_ADAT, "adat" }, 85d7fde2c9SFlorian Walpen { 0, NULL }, 86e4afd792SAlexander Motin }; 87e4afd792SAlexander Motin 88dc15f025SFlorian Walpen static struct hdspe_channel chan_map_aio_uni[] = { 89dc15f025SFlorian Walpen { HDSPE_CHAN_AIO_ALL, "all" }, 90dc15f025SFlorian Walpen { 0, NULL }, 91dc15f025SFlorian Walpen }; 92dc15f025SFlorian Walpen 93e4afd792SAlexander Motin static struct hdspe_channel chan_map_rd[] = { 94d7fde2c9SFlorian Walpen { HDSPE_CHAN_RAY_AES, "aes" }, 95d7fde2c9SFlorian Walpen { HDSPE_CHAN_RAY_SPDIF, "s/pdif" }, 96d7fde2c9SFlorian Walpen { HDSPE_CHAN_RAY_ADAT1, "adat1" }, 97d7fde2c9SFlorian Walpen { HDSPE_CHAN_RAY_ADAT2, "adat2" }, 98d7fde2c9SFlorian Walpen { HDSPE_CHAN_RAY_ADAT3, "adat3" }, 99d7fde2c9SFlorian Walpen { HDSPE_CHAN_RAY_ADAT4, "adat4" }, 100d7fde2c9SFlorian Walpen { 0, NULL }, 101e4afd792SAlexander Motin }; 102e4afd792SAlexander Motin 103dc15f025SFlorian Walpen static struct hdspe_channel chan_map_rd_uni[] = { 104dc15f025SFlorian Walpen { HDSPE_CHAN_RAY_ALL, "all" }, 105dc15f025SFlorian Walpen { 0, NULL }, 106dc15f025SFlorian Walpen }; 107dc15f025SFlorian Walpen 108e4afd792SAlexander Motin static void 109e4afd792SAlexander Motin hdspe_intr(void *p) 110e4afd792SAlexander Motin { 111e4afd792SAlexander Motin struct sc_pcminfo *scp; 11220a9f771SRuslan Bukin struct sc_info *sc; 113e4afd792SAlexander Motin device_t *devlist; 11420a9f771SRuslan Bukin int devcount; 11520a9f771SRuslan Bukin int status; 11620a9f771SRuslan Bukin int err; 11720a9f771SRuslan Bukin int i; 11820a9f771SRuslan Bukin 11920a9f771SRuslan Bukin sc = (struct sc_info *)p; 120e4afd792SAlexander Motin 121e4afd792SAlexander Motin snd_mtxlock(sc->lock); 122e4afd792SAlexander Motin 123e4afd792SAlexander Motin status = hdspe_read_1(sc, HDSPE_STATUS_REG); 124e4afd792SAlexander Motin if (status & HDSPE_AUDIO_IRQ_PENDING) { 125e4afd792SAlexander Motin if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0) 126e4afd792SAlexander Motin return; 127e4afd792SAlexander Motin 128e4afd792SAlexander Motin for (i = 0; i < devcount; i++) { 129e4afd792SAlexander Motin scp = device_get_ivars(devlist[i]); 130e4afd792SAlexander Motin if (scp->ih != NULL) 131e4afd792SAlexander Motin scp->ih(scp); 132e4afd792SAlexander Motin } 133e4afd792SAlexander Motin 134e4afd792SAlexander Motin hdspe_write_1(sc, HDSPE_INTERRUPT_ACK, 0); 135b5db12bfSKevin Lo free(devlist, M_TEMP); 136e4afd792SAlexander Motin } 137e4afd792SAlexander Motin 138e4afd792SAlexander Motin snd_mtxunlock(sc->lock); 139e4afd792SAlexander Motin } 140e4afd792SAlexander Motin 141e4afd792SAlexander Motin static void 142e4afd792SAlexander Motin hdspe_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) 143e4afd792SAlexander Motin { 144e4afd792SAlexander Motin #if 0 145e4afd792SAlexander Motin device_printf(sc->dev, "hdspe_dmapsetmap()\n"); 146e4afd792SAlexander Motin #endif 147e4afd792SAlexander Motin } 148e4afd792SAlexander Motin 149e4afd792SAlexander Motin static int 150e4afd792SAlexander Motin hdspe_alloc_resources(struct sc_info *sc) 151e4afd792SAlexander Motin { 152e4afd792SAlexander Motin 153e4afd792SAlexander Motin /* Allocate resource. */ 154e4afd792SAlexander Motin sc->csid = PCIR_BAR(0); 15543cd6160SJustin Hibbits sc->cs = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, 15643cd6160SJustin Hibbits &sc->csid, RF_ACTIVE); 157e4afd792SAlexander Motin 158e4afd792SAlexander Motin if (!sc->cs) { 159e4afd792SAlexander Motin device_printf(sc->dev, "Unable to map SYS_RES_MEMORY.\n"); 160e4afd792SAlexander Motin return (ENXIO); 161e4afd792SAlexander Motin } 16220a9f771SRuslan Bukin 163e4afd792SAlexander Motin sc->cst = rman_get_bustag(sc->cs); 164e4afd792SAlexander Motin sc->csh = rman_get_bushandle(sc->cs); 165e4afd792SAlexander Motin 166e4afd792SAlexander Motin /* Allocate interrupt resource. */ 167e4afd792SAlexander Motin sc->irqid = 0; 16843cd6160SJustin Hibbits sc->irq = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &sc->irqid, 16943cd6160SJustin Hibbits RF_ACTIVE | RF_SHAREABLE); 170e4afd792SAlexander Motin 171e4afd792SAlexander Motin if (!sc->irq || 172e4afd792SAlexander Motin bus_setup_intr(sc->dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV, 173e4afd792SAlexander Motin NULL, hdspe_intr, sc, &sc->ih)) { 174e4afd792SAlexander Motin device_printf(sc->dev, "Unable to alloc interrupt resource.\n"); 175e4afd792SAlexander Motin return (ENXIO); 176e4afd792SAlexander Motin } 177e4afd792SAlexander Motin 178e4afd792SAlexander Motin /* Allocate DMA resources. */ 179e4afd792SAlexander Motin if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(sc->dev), 180e4afd792SAlexander Motin /*alignment*/4, 181e4afd792SAlexander Motin /*boundary*/0, 182e4afd792SAlexander Motin /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, 183e4afd792SAlexander Motin /*highaddr*/BUS_SPACE_MAXADDR, 184e4afd792SAlexander Motin /*filter*/NULL, 185e4afd792SAlexander Motin /*filterarg*/NULL, 186e4afd792SAlexander Motin /*maxsize*/2 * HDSPE_DMASEGSIZE, 187e4afd792SAlexander Motin /*nsegments*/2, 188e4afd792SAlexander Motin /*maxsegsz*/HDSPE_DMASEGSIZE, 189e4afd792SAlexander Motin /*flags*/0, 1901f7a6325SAlexander Motin /*lockfunc*/NULL, 1911f7a6325SAlexander Motin /*lockarg*/NULL, 192e4afd792SAlexander Motin /*dmatag*/&sc->dmat) != 0) { 193e4afd792SAlexander Motin device_printf(sc->dev, "Unable to create dma tag.\n"); 194e4afd792SAlexander Motin return (ENXIO); 195e4afd792SAlexander Motin } 196e4afd792SAlexander Motin 197e4afd792SAlexander Motin sc->bufsize = HDSPE_DMASEGSIZE; 198e4afd792SAlexander Motin 199e4afd792SAlexander Motin /* pbuf (play buffer). */ 2001f7a6325SAlexander Motin if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf, BUS_DMA_WAITOK, 2011f7a6325SAlexander Motin &sc->pmap)) { 202e4afd792SAlexander Motin device_printf(sc->dev, "Can't alloc pbuf.\n"); 203e4afd792SAlexander Motin return (ENXIO); 204e4afd792SAlexander Motin } 205e4afd792SAlexander Motin 206e4afd792SAlexander Motin if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->bufsize, 2071f7a6325SAlexander Motin hdspe_dmapsetmap, sc, BUS_DMA_NOWAIT)) { 208e4afd792SAlexander Motin device_printf(sc->dev, "Can't load pbuf.\n"); 209e4afd792SAlexander Motin return (ENXIO); 210e4afd792SAlexander Motin } 211e4afd792SAlexander Motin 212e4afd792SAlexander Motin /* rbuf (rec buffer). */ 2131f7a6325SAlexander Motin if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf, BUS_DMA_WAITOK, 2141f7a6325SAlexander Motin &sc->rmap)) { 215e4afd792SAlexander Motin device_printf(sc->dev, "Can't alloc rbuf.\n"); 216e4afd792SAlexander Motin return (ENXIO); 217e4afd792SAlexander Motin } 218e4afd792SAlexander Motin 219e4afd792SAlexander Motin if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->bufsize, 2201f7a6325SAlexander Motin hdspe_dmapsetmap, sc, BUS_DMA_NOWAIT)) { 221e4afd792SAlexander Motin device_printf(sc->dev, "Can't load rbuf.\n"); 222e4afd792SAlexander Motin return (ENXIO); 223e4afd792SAlexander Motin } 224e4afd792SAlexander Motin 225e4afd792SAlexander Motin bzero(sc->pbuf, sc->bufsize); 226e4afd792SAlexander Motin bzero(sc->rbuf, sc->bufsize); 227e4afd792SAlexander Motin 228e4afd792SAlexander Motin return (0); 229e4afd792SAlexander Motin } 230e4afd792SAlexander Motin 231e4afd792SAlexander Motin static void 232e4afd792SAlexander Motin hdspe_map_dmabuf(struct sc_info *sc) 233e4afd792SAlexander Motin { 234e4afd792SAlexander Motin uint32_t paddr, raddr; 235e4afd792SAlexander Motin int i; 236e4afd792SAlexander Motin 237e4afd792SAlexander Motin paddr = vtophys(sc->pbuf); 238e4afd792SAlexander Motin raddr = vtophys(sc->rbuf); 239e4afd792SAlexander Motin 240e4afd792SAlexander Motin for (i = 0; i < HDSPE_MAX_SLOTS * 16; i++) { 241e4afd792SAlexander Motin hdspe_write_4(sc, HDSPE_PAGE_ADDR_BUF_OUT + 4 * i, 242e4afd792SAlexander Motin paddr + i * 4096); 243e4afd792SAlexander Motin hdspe_write_4(sc, HDSPE_PAGE_ADDR_BUF_IN + 4 * i, 244e4afd792SAlexander Motin raddr + i * 4096); 245e4afd792SAlexander Motin } 246e4afd792SAlexander Motin } 247e4afd792SAlexander Motin 248e4afd792SAlexander Motin static int 2496c892b79SFlorian Walpen hdspe_sysctl_sample_rate(SYSCTL_HANDLER_ARGS) 2506c892b79SFlorian Walpen { 2516c892b79SFlorian Walpen struct sc_info *sc = oidp->oid_arg1; 2526c892b79SFlorian Walpen int error; 2536c892b79SFlorian Walpen unsigned int speed, multiplier; 2546c892b79SFlorian Walpen 2556c892b79SFlorian Walpen speed = sc->force_speed; 2566c892b79SFlorian Walpen 2576c892b79SFlorian Walpen /* Process sysctl (unsigned) integer request. */ 2586c892b79SFlorian Walpen error = sysctl_handle_int(oidp, &speed, 0, req); 2596c892b79SFlorian Walpen if (error != 0 || req->newptr == NULL) 2606c892b79SFlorian Walpen return (error); 2616c892b79SFlorian Walpen 2626c892b79SFlorian Walpen /* Speed from 32000 to 192000, 0 falls back to pcm speed setting. */ 2636c892b79SFlorian Walpen sc->force_speed = 0; 2646c892b79SFlorian Walpen if (speed > 0) { 2656c892b79SFlorian Walpen multiplier = 1; 2666c892b79SFlorian Walpen if (speed > (96000 + 128000) / 2) 2676c892b79SFlorian Walpen multiplier = 4; 2686c892b79SFlorian Walpen else if (speed > (48000 + 64000) / 2) 2696c892b79SFlorian Walpen multiplier = 2; 2706c892b79SFlorian Walpen 2716c892b79SFlorian Walpen if (speed < ((32000 + 44100) / 2) * multiplier) 2726c892b79SFlorian Walpen sc->force_speed = 32000 * multiplier; 2736c892b79SFlorian Walpen else if (speed < ((44100 + 48000) / 2) * multiplier) 2746c892b79SFlorian Walpen sc->force_speed = 44100 * multiplier; 2756c892b79SFlorian Walpen else 2766c892b79SFlorian Walpen sc->force_speed = 48000 * multiplier; 2776c892b79SFlorian Walpen } 2786c892b79SFlorian Walpen 2796c892b79SFlorian Walpen return (0); 2806c892b79SFlorian Walpen } 2816c892b79SFlorian Walpen 2826c892b79SFlorian Walpen 2836c892b79SFlorian Walpen static int 284fb877263SFlorian Walpen hdspe_sysctl_period(SYSCTL_HANDLER_ARGS) 285fb877263SFlorian Walpen { 286fb877263SFlorian Walpen struct sc_info *sc = oidp->oid_arg1; 287fb877263SFlorian Walpen int error; 288fb877263SFlorian Walpen unsigned int period; 289fb877263SFlorian Walpen 290fb877263SFlorian Walpen period = sc->force_period; 291fb877263SFlorian Walpen 292fb877263SFlorian Walpen /* Process sysctl (unsigned) integer request. */ 293fb877263SFlorian Walpen error = sysctl_handle_int(oidp, &period, 0, req); 294fb877263SFlorian Walpen if (error != 0 || req->newptr == NULL) 295fb877263SFlorian Walpen return (error); 296fb877263SFlorian Walpen 297fb877263SFlorian Walpen /* Period is from 2^5 to 2^14, 0 falls back to pcm latency settings. */ 298fb877263SFlorian Walpen sc->force_period = 0; 299fb877263SFlorian Walpen if (period > 0) { 300fb877263SFlorian Walpen sc->force_period = 32; 301fb877263SFlorian Walpen while (sc->force_period < period && sc->force_period < 4096) 302fb877263SFlorian Walpen sc->force_period <<= 1; 303fb877263SFlorian Walpen } 304fb877263SFlorian Walpen 305fb877263SFlorian Walpen return (0); 306fb877263SFlorian Walpen } 307fb877263SFlorian Walpen 308fb877263SFlorian Walpen static int 309b6052c10SRuslan Bukin hdspe_sysctl_clock_preference(SYSCTL_HANDLER_ARGS) 310b6052c10SRuslan Bukin { 311b6052c10SRuslan Bukin struct sc_info *sc; 312b6052c10SRuslan Bukin struct hdspe_clock_source *clock_table, *clock; 313b6052c10SRuslan Bukin char buf[16] = "invalid"; 314b6052c10SRuslan Bukin int error; 315b6052c10SRuslan Bukin uint32_t setting; 316b6052c10SRuslan Bukin 317b6052c10SRuslan Bukin sc = oidp->oid_arg1; 318b6052c10SRuslan Bukin 319b6052c10SRuslan Bukin /* Select sync ports table for device type. */ 320b6052c10SRuslan Bukin if (sc->type == HDSPE_AIO) 321b6052c10SRuslan Bukin clock_table = hdspe_clock_source_table_aio; 322b6052c10SRuslan Bukin else if (sc->type == HDSPE_RAYDAT) 323b6052c10SRuslan Bukin clock_table = hdspe_clock_source_table_rd; 324b6052c10SRuslan Bukin else 325b6052c10SRuslan Bukin return (ENXIO); 326b6052c10SRuslan Bukin 327b6052c10SRuslan Bukin /* Extract preferred clock source from settings register. */ 328b6052c10SRuslan Bukin setting = sc->settings_register & HDSPE_SETTING_CLOCK_MASK; 329b6052c10SRuslan Bukin for (clock = clock_table; clock->name != NULL; ++clock) { 330b6052c10SRuslan Bukin if (clock->setting == setting) 331b6052c10SRuslan Bukin break; 332b6052c10SRuslan Bukin } 333b6052c10SRuslan Bukin if (clock->name != NULL) 334b6052c10SRuslan Bukin strlcpy(buf, clock->name, sizeof(buf)); 335b6052c10SRuslan Bukin 336b6052c10SRuslan Bukin /* Process sysctl string request. */ 337b6052c10SRuslan Bukin error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 338b6052c10SRuslan Bukin if (error != 0 || req->newptr == NULL) 339b6052c10SRuslan Bukin return (error); 340b6052c10SRuslan Bukin 341b6052c10SRuslan Bukin /* Find clock source matching the sysctl string. */ 342b6052c10SRuslan Bukin for (clock = clock_table; clock->name != NULL; ++clock) { 343b6052c10SRuslan Bukin if (strncasecmp(buf, clock->name, sizeof(buf)) == 0) 344b6052c10SRuslan Bukin break; 345b6052c10SRuslan Bukin } 346b6052c10SRuslan Bukin 347b6052c10SRuslan Bukin /* Set preferred clock source in settings register. */ 348b6052c10SRuslan Bukin if (clock->name != NULL) { 349b6052c10SRuslan Bukin setting = clock->setting & HDSPE_SETTING_CLOCK_MASK; 350b6052c10SRuslan Bukin snd_mtxlock(sc->lock); 351b6052c10SRuslan Bukin sc->settings_register &= ~HDSPE_SETTING_CLOCK_MASK; 352b6052c10SRuslan Bukin sc->settings_register |= setting; 353b6052c10SRuslan Bukin hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register); 354b6052c10SRuslan Bukin snd_mtxunlock(sc->lock); 355b6052c10SRuslan Bukin } 356b6052c10SRuslan Bukin return (0); 357b6052c10SRuslan Bukin } 358b6052c10SRuslan Bukin 359b6052c10SRuslan Bukin static int 360b6052c10SRuslan Bukin hdspe_sysctl_clock_source(SYSCTL_HANDLER_ARGS) 361b6052c10SRuslan Bukin { 362b6052c10SRuslan Bukin struct sc_info *sc; 363b6052c10SRuslan Bukin struct hdspe_clock_source *clock_table, *clock; 364b6052c10SRuslan Bukin char buf[16] = "invalid"; 365b6052c10SRuslan Bukin uint32_t status; 366b6052c10SRuslan Bukin 367b6052c10SRuslan Bukin sc = oidp->oid_arg1; 368b6052c10SRuslan Bukin 369b6052c10SRuslan Bukin /* Select sync ports table for device type. */ 370b6052c10SRuslan Bukin if (sc->type == HDSPE_AIO) 371b6052c10SRuslan Bukin clock_table = hdspe_clock_source_table_aio; 372b6052c10SRuslan Bukin else if (sc->type == HDSPE_RAYDAT) 373b6052c10SRuslan Bukin clock_table = hdspe_clock_source_table_rd; 374b6052c10SRuslan Bukin else 375b6052c10SRuslan Bukin return (ENXIO); 376b6052c10SRuslan Bukin 377b6052c10SRuslan Bukin /* Read current (autosync) clock source from status register. */ 378b6052c10SRuslan Bukin snd_mtxlock(sc->lock); 379b6052c10SRuslan Bukin status = hdspe_read_4(sc, HDSPE_STATUS1_REG); 380b6052c10SRuslan Bukin status &= HDSPE_STATUS1_CLOCK_MASK; 381b6052c10SRuslan Bukin snd_mtxunlock(sc->lock); 382b6052c10SRuslan Bukin 383b6052c10SRuslan Bukin /* Translate status register value to clock source. */ 384b6052c10SRuslan Bukin for (clock = clock_table; clock->name != NULL; ++clock) { 385b6052c10SRuslan Bukin /* In clock master mode, override with internal clock source. */ 386b6052c10SRuslan Bukin if (sc->settings_register & HDSPE_SETTING_MASTER) { 387b6052c10SRuslan Bukin if (clock->setting & HDSPE_SETTING_MASTER) 388b6052c10SRuslan Bukin break; 389b6052c10SRuslan Bukin } else if (clock->status == status) 390b6052c10SRuslan Bukin break; 391b6052c10SRuslan Bukin } 392b6052c10SRuslan Bukin 393b6052c10SRuslan Bukin /* Process sysctl string request. */ 394b6052c10SRuslan Bukin if (clock->name != NULL) 395b6052c10SRuslan Bukin strlcpy(buf, clock->name, sizeof(buf)); 396b6052c10SRuslan Bukin return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); 397b6052c10SRuslan Bukin } 398b6052c10SRuslan Bukin 399b6052c10SRuslan Bukin static int 400b6052c10SRuslan Bukin hdspe_sysctl_clock_list(SYSCTL_HANDLER_ARGS) 401b6052c10SRuslan Bukin { 402b6052c10SRuslan Bukin struct sc_info *sc; 403b6052c10SRuslan Bukin struct hdspe_clock_source *clock_table, *clock; 404b6052c10SRuslan Bukin char buf[256]; 405b6052c10SRuslan Bukin int n; 406b6052c10SRuslan Bukin 407b6052c10SRuslan Bukin sc = oidp->oid_arg1; 408b6052c10SRuslan Bukin n = 0; 409b6052c10SRuslan Bukin 410b6052c10SRuslan Bukin /* Select clock source table for device type. */ 411b6052c10SRuslan Bukin if (sc->type == HDSPE_AIO) 412b6052c10SRuslan Bukin clock_table = hdspe_clock_source_table_aio; 413b6052c10SRuslan Bukin else if (sc->type == HDSPE_RAYDAT) 414b6052c10SRuslan Bukin clock_table = hdspe_clock_source_table_rd; 415b6052c10SRuslan Bukin else 416b6052c10SRuslan Bukin return (ENXIO); 417b6052c10SRuslan Bukin 418b6052c10SRuslan Bukin /* List available clock sources. */ 419b6052c10SRuslan Bukin buf[0] = 0; 420b6052c10SRuslan Bukin for (clock = clock_table; clock->name != NULL; ++clock) { 421b6052c10SRuslan Bukin if (n > 0) 422b6052c10SRuslan Bukin n += strlcpy(buf + n, ",", sizeof(buf) - n); 423b6052c10SRuslan Bukin n += strlcpy(buf + n, clock->name, sizeof(buf) - n); 424b6052c10SRuslan Bukin } 425b6052c10SRuslan Bukin return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); 426b6052c10SRuslan Bukin } 427b6052c10SRuslan Bukin 428b6052c10SRuslan Bukin static int 429b6052c10SRuslan Bukin hdspe_sysctl_sync_status(SYSCTL_HANDLER_ARGS) 430b6052c10SRuslan Bukin { 431b6052c10SRuslan Bukin struct sc_info *sc; 432b6052c10SRuslan Bukin struct hdspe_clock_source *clock_table, *clock; 433b6052c10SRuslan Bukin char buf[256]; 434b6052c10SRuslan Bukin char *state; 435b6052c10SRuslan Bukin int n; 436b6052c10SRuslan Bukin uint32_t status; 437b6052c10SRuslan Bukin 438b6052c10SRuslan Bukin sc = oidp->oid_arg1; 439b6052c10SRuslan Bukin n = 0; 440b6052c10SRuslan Bukin 441b6052c10SRuslan Bukin /* Select sync ports table for device type. */ 442b6052c10SRuslan Bukin if (sc->type == HDSPE_AIO) 443b6052c10SRuslan Bukin clock_table = hdspe_clock_source_table_aio; 444b6052c10SRuslan Bukin else if (sc->type == HDSPE_RAYDAT) 445b6052c10SRuslan Bukin clock_table = hdspe_clock_source_table_rd; 446b6052c10SRuslan Bukin else 447b6052c10SRuslan Bukin return (ENXIO); 448b6052c10SRuslan Bukin 449b6052c10SRuslan Bukin /* Read current lock and sync bits from status register. */ 450b6052c10SRuslan Bukin snd_mtxlock(sc->lock); 451b6052c10SRuslan Bukin status = hdspe_read_4(sc, HDSPE_STATUS1_REG); 452b6052c10SRuslan Bukin snd_mtxunlock(sc->lock); 453b6052c10SRuslan Bukin 454b6052c10SRuslan Bukin /* List clock sources with lock and sync state. */ 455b6052c10SRuslan Bukin for (clock = clock_table; clock->name != NULL; ++clock) { 456b6052c10SRuslan Bukin if (clock->sync_bit != 0) { 457b6052c10SRuslan Bukin if (n > 0) 458b6052c10SRuslan Bukin n += strlcpy(buf + n, ",", sizeof(buf) - n); 459b6052c10SRuslan Bukin state = "none"; 460b6052c10SRuslan Bukin if ((clock->sync_bit & status) != 0) 461b6052c10SRuslan Bukin state = "sync"; 462b6052c10SRuslan Bukin else if ((clock->lock_bit & status) != 0) 463b6052c10SRuslan Bukin state = "lock"; 464b6052c10SRuslan Bukin n += snprintf(buf + n, sizeof(buf) - n, "%s(%s)", 465b6052c10SRuslan Bukin clock->name, state); 466b6052c10SRuslan Bukin } 467b6052c10SRuslan Bukin } 468b6052c10SRuslan Bukin return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); 469b6052c10SRuslan Bukin } 470b6052c10SRuslan Bukin 471b6052c10SRuslan Bukin static int 472e4afd792SAlexander Motin hdspe_probe(device_t dev) 473e4afd792SAlexander Motin { 474e4afd792SAlexander Motin uint32_t rev; 475e4afd792SAlexander Motin 4769718d4abSFlorian Walpen if ((pci_get_vendor(dev) == PCI_VENDOR_XILINX || 4779718d4abSFlorian Walpen pci_get_vendor(dev) == PCI_VENDOR_RME) && 478e4afd792SAlexander Motin pci_get_device(dev) == PCI_DEVICE_XILINX_HDSPE) { 479e4afd792SAlexander Motin rev = pci_get_revid(dev); 480e4afd792SAlexander Motin switch (rev) { 481e4afd792SAlexander Motin case PCI_REVISION_AIO: 482e4afd792SAlexander Motin device_set_desc(dev, "RME HDSPe AIO"); 48320a9f771SRuslan Bukin return (0); 484e4afd792SAlexander Motin case PCI_REVISION_RAYDAT: 485e4afd792SAlexander Motin device_set_desc(dev, "RME HDSPe RayDAT"); 48620a9f771SRuslan Bukin return (0); 487e4afd792SAlexander Motin } 488e4afd792SAlexander Motin } 489e4afd792SAlexander Motin 490e4afd792SAlexander Motin return (ENXIO); 491e4afd792SAlexander Motin } 492e4afd792SAlexander Motin 493e4afd792SAlexander Motin static int 494e4afd792SAlexander Motin hdspe_init(struct sc_info *sc) 495e4afd792SAlexander Motin { 496e4afd792SAlexander Motin long long period; 497e4afd792SAlexander Motin 498e4afd792SAlexander Motin /* Set latency. */ 499e4afd792SAlexander Motin sc->period = 32; 500fb877263SFlorian Walpen /* 501fb877263SFlorian Walpen * The pcm channel latency settings propagate unreliable blocksizes, 502fb877263SFlorian Walpen * different for recording and playback, and skewed due to rounding 503fb877263SFlorian Walpen * and total buffer size limits. 504fb877263SFlorian Walpen * Force period to a consistent default until these issues are fixed. 505fb877263SFlorian Walpen */ 506fb877263SFlorian Walpen sc->force_period = 256; 507e4afd792SAlexander Motin sc->ctrl_register = hdspe_encode_latency(7); 508e4afd792SAlexander Motin 509e4afd792SAlexander Motin /* Set rate. */ 510e4afd792SAlexander Motin sc->speed = HDSPE_SPEED_DEFAULT; 5116c892b79SFlorian Walpen sc->force_speed = 0; 512e4afd792SAlexander Motin sc->ctrl_register &= ~HDSPE_FREQ_MASK; 513e4afd792SAlexander Motin sc->ctrl_register |= HDSPE_FREQ_MASK_DEFAULT; 514e4afd792SAlexander Motin hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register); 515e4afd792SAlexander Motin 516e4afd792SAlexander Motin switch (sc->type) { 517b6052c10SRuslan Bukin case HDSPE_RAYDAT: 518b6052c10SRuslan Bukin case HDSPE_AIO: 519e4afd792SAlexander Motin period = HDSPE_FREQ_AIO; 520e4afd792SAlexander Motin break; 521e4afd792SAlexander Motin default: 522e4afd792SAlexander Motin return (ENXIO); 523e4afd792SAlexander Motin } 524e4afd792SAlexander Motin 525e4afd792SAlexander Motin /* Set DDS value. */ 526e4afd792SAlexander Motin period /= sc->speed; 527e4afd792SAlexander Motin hdspe_write_4(sc, HDSPE_FREQ_REG, period); 528e4afd792SAlexander Motin 529e4afd792SAlexander Motin /* Other settings. */ 530e4afd792SAlexander Motin sc->settings_register = 0; 531e4afd792SAlexander Motin hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register); 532e4afd792SAlexander Motin 53320a9f771SRuslan Bukin return (0); 534e4afd792SAlexander Motin } 535e4afd792SAlexander Motin 536e4afd792SAlexander Motin static int 537e4afd792SAlexander Motin hdspe_attach(device_t dev) 538e4afd792SAlexander Motin { 539e4afd792SAlexander Motin struct hdspe_channel *chan_map; 54020a9f771SRuslan Bukin struct sc_pcminfo *scp; 54120a9f771SRuslan Bukin struct sc_info *sc; 542e4afd792SAlexander Motin uint32_t rev; 543e4afd792SAlexander Motin int i, err; 544e4afd792SAlexander Motin 545e4afd792SAlexander Motin #if 0 546e4afd792SAlexander Motin device_printf(dev, "hdspe_attach()\n"); 547e4afd792SAlexander Motin #endif 548e4afd792SAlexander Motin 549e4afd792SAlexander Motin sc = device_get_softc(dev); 550e4afd792SAlexander Motin sc->lock = snd_mtxcreate(device_get_nameunit(dev), 551e4afd792SAlexander Motin "snd_hdspe softc"); 552e4afd792SAlexander Motin sc->dev = dev; 553e4afd792SAlexander Motin 554c68534f1SScott Long pci_enable_busmaster(dev); 555e4afd792SAlexander Motin rev = pci_get_revid(dev); 556e4afd792SAlexander Motin switch (rev) { 557e4afd792SAlexander Motin case PCI_REVISION_AIO: 558b6052c10SRuslan Bukin sc->type = HDSPE_AIO; 559dc15f025SFlorian Walpen chan_map = hdspe_unified_pcm ? chan_map_aio_uni : chan_map_aio; 560e4afd792SAlexander Motin break; 561e4afd792SAlexander Motin case PCI_REVISION_RAYDAT: 562b6052c10SRuslan Bukin sc->type = HDSPE_RAYDAT; 563dc15f025SFlorian Walpen chan_map = hdspe_unified_pcm ? chan_map_rd_uni : chan_map_rd; 564e4afd792SAlexander Motin break; 565e4afd792SAlexander Motin default: 56620a9f771SRuslan Bukin return (ENXIO); 567e4afd792SAlexander Motin } 568e4afd792SAlexander Motin 569e4afd792SAlexander Motin /* Allocate resources. */ 570e4afd792SAlexander Motin err = hdspe_alloc_resources(sc); 571e4afd792SAlexander Motin if (err) { 572e4afd792SAlexander Motin device_printf(dev, "Unable to allocate system resources.\n"); 57320a9f771SRuslan Bukin return (ENXIO); 574e4afd792SAlexander Motin } 575e4afd792SAlexander Motin 576e4afd792SAlexander Motin if (hdspe_init(sc) != 0) 57720a9f771SRuslan Bukin return (ENXIO); 578e4afd792SAlexander Motin 579e4afd792SAlexander Motin for (i = 0; i < HDSPE_MAX_CHANS && chan_map[i].descr != NULL; i++) { 580e4afd792SAlexander Motin scp = malloc(sizeof(struct sc_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO); 581e4afd792SAlexander Motin scp->hc = &chan_map[i]; 582e4afd792SAlexander Motin scp->sc = sc; 583*5b56413dSWarner Losh scp->dev = device_add_child(dev, "pcm", DEVICE_UNIT_ANY); 584e4afd792SAlexander Motin device_set_ivars(scp->dev, scp); 585e4afd792SAlexander Motin } 586e4afd792SAlexander Motin 587e4afd792SAlexander Motin hdspe_map_dmabuf(sc); 588e4afd792SAlexander Motin 589b6052c10SRuslan Bukin SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 590b6052c10SRuslan Bukin SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 591b6052c10SRuslan Bukin "sync_status", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 592b6052c10SRuslan Bukin sc, 0, hdspe_sysctl_sync_status, "A", 593b6052c10SRuslan Bukin "List clock source signal lock and sync status"); 594b6052c10SRuslan Bukin 595b6052c10SRuslan Bukin SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 596b6052c10SRuslan Bukin SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 597b6052c10SRuslan Bukin "clock_source", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 598b6052c10SRuslan Bukin sc, 0, hdspe_sysctl_clock_source, "A", 599b6052c10SRuslan Bukin "Currently effective clock source"); 600b6052c10SRuslan Bukin 601b6052c10SRuslan Bukin SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 602b6052c10SRuslan Bukin SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 603b6052c10SRuslan Bukin "clock_preference", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 604b6052c10SRuslan Bukin sc, 0, hdspe_sysctl_clock_preference, "A", 605b6052c10SRuslan Bukin "Set 'internal' (master) or preferred autosync clock source"); 606b6052c10SRuslan Bukin 607b6052c10SRuslan Bukin SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 608b6052c10SRuslan Bukin SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 609b6052c10SRuslan Bukin "clock_list", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 610b6052c10SRuslan Bukin sc, 0, hdspe_sysctl_clock_list, "A", 611b6052c10SRuslan Bukin "List of supported clock sources"); 612b6052c10SRuslan Bukin 613fb877263SFlorian Walpen SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 614fb877263SFlorian Walpen SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 615fb877263SFlorian Walpen "period", CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, 616fb877263SFlorian Walpen sc, 0, hdspe_sysctl_period, "A", 617fb877263SFlorian Walpen "Force period of samples per interrupt (32, 64, ... 4096)"); 618fb877263SFlorian Walpen 6196c892b79SFlorian Walpen SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 6206c892b79SFlorian Walpen SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 6216c892b79SFlorian Walpen "sample_rate", CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, 6226c892b79SFlorian Walpen sc, 0, hdspe_sysctl_sample_rate, "A", 6236c892b79SFlorian Walpen "Force sample rate (32000, 44100, 48000, ... 192000)"); 6246c892b79SFlorian Walpen 62535d393bfSGleb Smirnoff return (bus_generic_attach(dev)); 626e4afd792SAlexander Motin } 627e4afd792SAlexander Motin 628e4afd792SAlexander Motin static void 629e4afd792SAlexander Motin hdspe_dmafree(struct sc_info *sc) 630e4afd792SAlexander Motin { 631e4afd792SAlexander Motin 632e4afd792SAlexander Motin bus_dmamap_unload(sc->dmat, sc->rmap); 633e4afd792SAlexander Motin bus_dmamap_unload(sc->dmat, sc->pmap); 634e4afd792SAlexander Motin bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap); 635e4afd792SAlexander Motin bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap); 636e4afd792SAlexander Motin sc->rbuf = sc->pbuf = NULL; 637e4afd792SAlexander Motin } 638e4afd792SAlexander Motin 639e4afd792SAlexander Motin static int 640e4afd792SAlexander Motin hdspe_detach(device_t dev) 641e4afd792SAlexander Motin { 642e4afd792SAlexander Motin struct sc_info *sc; 643e4afd792SAlexander Motin int err; 644e4afd792SAlexander Motin 645e4afd792SAlexander Motin sc = device_get_softc(dev); 646e4afd792SAlexander Motin if (sc == NULL) { 647e4afd792SAlexander Motin device_printf(dev,"Can't detach: softc is null.\n"); 64820a9f771SRuslan Bukin return (0); 649e4afd792SAlexander Motin } 650e4afd792SAlexander Motin 651e4afd792SAlexander Motin err = device_delete_children(dev); 652e4afd792SAlexander Motin if (err) 653e4afd792SAlexander Motin return (err); 654e4afd792SAlexander Motin 655e4afd792SAlexander Motin hdspe_dmafree(sc); 656e4afd792SAlexander Motin 657e4afd792SAlexander Motin if (sc->ih) 658e4afd792SAlexander Motin bus_teardown_intr(dev, sc->irq, sc->ih); 659e4afd792SAlexander Motin if (sc->dmat) 660e4afd792SAlexander Motin bus_dma_tag_destroy(sc->dmat); 661e4afd792SAlexander Motin if (sc->irq) 662e4afd792SAlexander Motin bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); 663e4afd792SAlexander Motin if (sc->cs) 664e4afd792SAlexander Motin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(0), sc->cs); 665e4afd792SAlexander Motin if (sc->lock) 666e4afd792SAlexander Motin snd_mtxfree(sc->lock); 667e4afd792SAlexander Motin 66820a9f771SRuslan Bukin return (0); 669e4afd792SAlexander Motin } 670e4afd792SAlexander Motin 671e4afd792SAlexander Motin static device_method_t hdspe_methods[] = { 672e4afd792SAlexander Motin DEVMETHOD(device_probe, hdspe_probe), 673e4afd792SAlexander Motin DEVMETHOD(device_attach, hdspe_attach), 674e4afd792SAlexander Motin DEVMETHOD(device_detach, hdspe_detach), 675e4afd792SAlexander Motin { 0, 0 } 676e4afd792SAlexander Motin }; 677e4afd792SAlexander Motin 678e4afd792SAlexander Motin static driver_t hdspe_driver = { 679e4afd792SAlexander Motin "hdspe", 680e4afd792SAlexander Motin hdspe_methods, 681e4afd792SAlexander Motin PCM_SOFTC_SIZE, 682e4afd792SAlexander Motin }; 683e4afd792SAlexander Motin 6843390adfeSJohn Baldwin DRIVER_MODULE(snd_hdspe, pci, hdspe_driver, 0, 0); 685