15687c71dSFlorian Walpen /*- 25687c71dSFlorian Walpen * SPDX-License-Identifier: BSD-2-Clause 35687c71dSFlorian Walpen * 45687c71dSFlorian Walpen * Copyright (c) 2012-2016 Ruslan Bukin <br@bsdpad.com> 55687c71dSFlorian Walpen * Copyright (c) 2023-2024 Florian Walpen <dev@submerge.ch> 65687c71dSFlorian Walpen * All rights reserved. 75687c71dSFlorian Walpen * 85687c71dSFlorian Walpen * Redistribution and use in source and binary forms, with or without 95687c71dSFlorian Walpen * modification, are permitted provided that the following conditions 105687c71dSFlorian Walpen * are met: 115687c71dSFlorian Walpen * 1. Redistributions of source code must retain the above copyright 125687c71dSFlorian Walpen * notice, this list of conditions and the following disclaimer. 135687c71dSFlorian Walpen * 2. Redistributions in binary form must reproduce the above copyright 145687c71dSFlorian Walpen * notice, this list of conditions and the following disclaimer in the 155687c71dSFlorian Walpen * documentation and/or other materials provided with the distribution. 165687c71dSFlorian Walpen * 175687c71dSFlorian Walpen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 185687c71dSFlorian Walpen * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 195687c71dSFlorian Walpen * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 205687c71dSFlorian Walpen * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 215687c71dSFlorian Walpen * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 225687c71dSFlorian Walpen * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 235687c71dSFlorian Walpen * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 245687c71dSFlorian Walpen * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 255687c71dSFlorian Walpen * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 265687c71dSFlorian Walpen * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 275687c71dSFlorian Walpen * SUCH DAMAGE. 285687c71dSFlorian Walpen */ 295687c71dSFlorian Walpen 305687c71dSFlorian Walpen /* 315687c71dSFlorian Walpen * RME HDSP driver for FreeBSD. 325687c71dSFlorian Walpen * Supported cards: HDSP 9632, HDSP 9652. 335687c71dSFlorian Walpen */ 345687c71dSFlorian Walpen 355687c71dSFlorian Walpen #include <sys/types.h> 365687c71dSFlorian Walpen #include <sys/sysctl.h> 375687c71dSFlorian Walpen 385687c71dSFlorian Walpen #include <dev/sound/pcm/sound.h> 395687c71dSFlorian Walpen #include <dev/sound/pci/hdsp.h> 405687c71dSFlorian Walpen 415687c71dSFlorian Walpen #include <dev/pci/pcireg.h> 425687c71dSFlorian Walpen #include <dev/pci/pcivar.h> 435687c71dSFlorian Walpen 445687c71dSFlorian Walpen #include <mixer_if.h> 455687c71dSFlorian Walpen 465687c71dSFlorian Walpen static bool hdsp_unified_pcm = false; 475687c71dSFlorian Walpen 485687c71dSFlorian Walpen static SYSCTL_NODE(_hw, OID_AUTO, hdsp, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 495687c71dSFlorian Walpen "PCI HDSP"); 505687c71dSFlorian Walpen 515687c71dSFlorian Walpen SYSCTL_BOOL(_hw_hdsp, OID_AUTO, unified_pcm, CTLFLAG_RWTUN, 525687c71dSFlorian Walpen &hdsp_unified_pcm, 0, "Combine physical ports in one unified pcm device"); 535687c71dSFlorian Walpen 545687c71dSFlorian Walpen static struct hdsp_clock_source hdsp_clock_source_table_9632[] = { 555687c71dSFlorian Walpen { "internal", HDSP_CLOCK_INTERNAL }, 565687c71dSFlorian Walpen { "adat", HDSP_CLOCK_ADAT1 }, 575687c71dSFlorian Walpen { "spdif", HDSP_CLOCK_SPDIF }, 585687c71dSFlorian Walpen { "word", HDSP_CLOCK_WORD }, 595687c71dSFlorian Walpen { NULL, HDSP_CLOCK_INTERNAL } 605687c71dSFlorian Walpen }; 615687c71dSFlorian Walpen 625687c71dSFlorian Walpen static struct hdsp_clock_source hdsp_clock_source_table_9652[] = { 635687c71dSFlorian Walpen { "internal", HDSP_CLOCK_INTERNAL }, 645687c71dSFlorian Walpen { "adat1", HDSP_CLOCK_ADAT1 }, 655687c71dSFlorian Walpen { "adat2", HDSP_CLOCK_ADAT2 }, 665687c71dSFlorian Walpen { "adat3", HDSP_CLOCK_ADAT3 }, 675687c71dSFlorian Walpen { "spdif", HDSP_CLOCK_SPDIF }, 685687c71dSFlorian Walpen { "word", HDSP_CLOCK_WORD }, 695687c71dSFlorian Walpen { "adat_sync", HDSP_CLOCK_ADAT_SYNC }, 705687c71dSFlorian Walpen { NULL, HDSP_CLOCK_INTERNAL } 715687c71dSFlorian Walpen }; 725687c71dSFlorian Walpen 735687c71dSFlorian Walpen static struct hdsp_channel chan_map_9632[] = { 745687c71dSFlorian Walpen { HDSP_CHAN_9632_ADAT, "adat" }, 755687c71dSFlorian Walpen { HDSP_CHAN_9632_SPDIF, "s/pdif" }, 765687c71dSFlorian Walpen { HDSP_CHAN_9632_LINE, "line" }, 77e0c37c16SFlorian Walpen { HDSP_CHAN_9632_EXT, "ext" }, 785687c71dSFlorian Walpen { 0, NULL }, 795687c71dSFlorian Walpen }; 805687c71dSFlorian Walpen 815687c71dSFlorian Walpen static struct hdsp_channel chan_map_9632_uni[] = { 825687c71dSFlorian Walpen { HDSP_CHAN_9632_ALL, "all" }, 835687c71dSFlorian Walpen { 0, NULL }, 845687c71dSFlorian Walpen }; 855687c71dSFlorian Walpen 865687c71dSFlorian Walpen static struct hdsp_channel chan_map_9652[] = { 875687c71dSFlorian Walpen { HDSP_CHAN_9652_ADAT1, "adat1" }, 885687c71dSFlorian Walpen { HDSP_CHAN_9652_ADAT2, "adat2" }, 895687c71dSFlorian Walpen { HDSP_CHAN_9652_ADAT3, "adat3" }, 905687c71dSFlorian Walpen { HDSP_CHAN_9652_SPDIF, "s/pdif" }, 915687c71dSFlorian Walpen { 0, NULL }, 925687c71dSFlorian Walpen }; 935687c71dSFlorian Walpen 945687c71dSFlorian Walpen static struct hdsp_channel chan_map_9652_uni[] = { 955687c71dSFlorian Walpen { HDSP_CHAN_9652_ALL, "all" }, 965687c71dSFlorian Walpen { 0, NULL }, 975687c71dSFlorian Walpen }; 985687c71dSFlorian Walpen 995687c71dSFlorian Walpen static void 1005687c71dSFlorian Walpen hdsp_intr(void *p) 1015687c71dSFlorian Walpen { 1025687c71dSFlorian Walpen struct sc_pcminfo *scp; 1035687c71dSFlorian Walpen struct sc_info *sc; 1045687c71dSFlorian Walpen device_t *devlist; 1055687c71dSFlorian Walpen int devcount; 1065687c71dSFlorian Walpen int status; 1075687c71dSFlorian Walpen int err; 1085687c71dSFlorian Walpen int i; 1095687c71dSFlorian Walpen 1105687c71dSFlorian Walpen sc = (struct sc_info *)p; 1115687c71dSFlorian Walpen 1125687c71dSFlorian Walpen snd_mtxlock(sc->lock); 1135687c71dSFlorian Walpen 1145687c71dSFlorian Walpen status = hdsp_read_1(sc, HDSP_STATUS_REG); 1155687c71dSFlorian Walpen if (status & HDSP_AUDIO_IRQ_PENDING) { 1165687c71dSFlorian Walpen if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0) 1175687c71dSFlorian Walpen return; 1185687c71dSFlorian Walpen 1195687c71dSFlorian Walpen for (i = 0; i < devcount; i++) { 1205687c71dSFlorian Walpen scp = device_get_ivars(devlist[i]); 1215687c71dSFlorian Walpen if (scp->ih != NULL) 1225687c71dSFlorian Walpen scp->ih(scp); 1235687c71dSFlorian Walpen } 1245687c71dSFlorian Walpen 1255687c71dSFlorian Walpen hdsp_write_1(sc, HDSP_INTERRUPT_ACK, 0); 1265687c71dSFlorian Walpen free(devlist, M_TEMP); 1275687c71dSFlorian Walpen } 1285687c71dSFlorian Walpen 1295687c71dSFlorian Walpen snd_mtxunlock(sc->lock); 1305687c71dSFlorian Walpen } 1315687c71dSFlorian Walpen 1325687c71dSFlorian Walpen static void 1335687c71dSFlorian Walpen hdsp_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) 1345687c71dSFlorian Walpen { 1355687c71dSFlorian Walpen #if 0 1365687c71dSFlorian Walpen device_printf(sc->dev, "hdsp_dmapsetmap()\n"); 1375687c71dSFlorian Walpen #endif 1385687c71dSFlorian Walpen } 1395687c71dSFlorian Walpen 1405687c71dSFlorian Walpen static int 1415687c71dSFlorian Walpen hdsp_alloc_resources(struct sc_info *sc) 1425687c71dSFlorian Walpen { 1435687c71dSFlorian Walpen 1445687c71dSFlorian Walpen /* Allocate resource. */ 1455687c71dSFlorian Walpen sc->csid = PCIR_BAR(0); 1465687c71dSFlorian Walpen sc->cs = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, 1475687c71dSFlorian Walpen &sc->csid, RF_ACTIVE); 1485687c71dSFlorian Walpen 1495687c71dSFlorian Walpen if (!sc->cs) { 1505687c71dSFlorian Walpen device_printf(sc->dev, "Unable to map SYS_RES_MEMORY.\n"); 1515687c71dSFlorian Walpen return (ENXIO); 1525687c71dSFlorian Walpen } 1535687c71dSFlorian Walpen 1545687c71dSFlorian Walpen sc->cst = rman_get_bustag(sc->cs); 1555687c71dSFlorian Walpen sc->csh = rman_get_bushandle(sc->cs); 1565687c71dSFlorian Walpen 1575687c71dSFlorian Walpen /* Allocate interrupt resource. */ 1585687c71dSFlorian Walpen sc->irqid = 0; 1595687c71dSFlorian Walpen sc->irq = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &sc->irqid, 1605687c71dSFlorian Walpen RF_ACTIVE | RF_SHAREABLE); 1615687c71dSFlorian Walpen 1625687c71dSFlorian Walpen if (!sc->irq || 1635687c71dSFlorian Walpen bus_setup_intr(sc->dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV, 1645687c71dSFlorian Walpen NULL, hdsp_intr, sc, &sc->ih)) { 1655687c71dSFlorian Walpen device_printf(sc->dev, "Unable to alloc interrupt resource.\n"); 1665687c71dSFlorian Walpen return (ENXIO); 1675687c71dSFlorian Walpen } 1685687c71dSFlorian Walpen 1695687c71dSFlorian Walpen /* Allocate DMA resources. */ 1705687c71dSFlorian Walpen if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(sc->dev), 1715687c71dSFlorian Walpen /*alignment*/4, 1725687c71dSFlorian Walpen /*boundary*/0, 1735687c71dSFlorian Walpen /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, 1745687c71dSFlorian Walpen /*highaddr*/BUS_SPACE_MAXADDR, 1755687c71dSFlorian Walpen /*filter*/NULL, 1765687c71dSFlorian Walpen /*filterarg*/NULL, 1775687c71dSFlorian Walpen /*maxsize*/2 * HDSP_DMASEGSIZE, 1785687c71dSFlorian Walpen /*nsegments*/2, 1795687c71dSFlorian Walpen /*maxsegsz*/HDSP_DMASEGSIZE, 1805687c71dSFlorian Walpen /*flags*/0, 1815687c71dSFlorian Walpen /*lockfunc*/NULL, 1825687c71dSFlorian Walpen /*lockarg*/NULL, 1835687c71dSFlorian Walpen /*dmatag*/&sc->dmat) != 0) { 1845687c71dSFlorian Walpen device_printf(sc->dev, "Unable to create dma tag.\n"); 1855687c71dSFlorian Walpen return (ENXIO); 1865687c71dSFlorian Walpen } 1875687c71dSFlorian Walpen 1885687c71dSFlorian Walpen sc->bufsize = HDSP_DMASEGSIZE; 1895687c71dSFlorian Walpen 1905687c71dSFlorian Walpen /* pbuf (play buffer). */ 1915687c71dSFlorian Walpen if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf, BUS_DMA_WAITOK, 1925687c71dSFlorian Walpen &sc->pmap)) { 1935687c71dSFlorian Walpen device_printf(sc->dev, "Can't alloc pbuf.\n"); 1945687c71dSFlorian Walpen return (ENXIO); 1955687c71dSFlorian Walpen } 1965687c71dSFlorian Walpen 1975687c71dSFlorian Walpen if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->bufsize, 1985687c71dSFlorian Walpen hdsp_dmapsetmap, sc, BUS_DMA_NOWAIT)) { 1995687c71dSFlorian Walpen device_printf(sc->dev, "Can't load pbuf.\n"); 2005687c71dSFlorian Walpen return (ENXIO); 2015687c71dSFlorian Walpen } 2025687c71dSFlorian Walpen 2035687c71dSFlorian Walpen /* rbuf (rec buffer). */ 2045687c71dSFlorian Walpen if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf, BUS_DMA_WAITOK, 2055687c71dSFlorian Walpen &sc->rmap)) { 2065687c71dSFlorian Walpen device_printf(sc->dev, "Can't alloc rbuf.\n"); 2075687c71dSFlorian Walpen return (ENXIO); 2085687c71dSFlorian Walpen } 2095687c71dSFlorian Walpen 2105687c71dSFlorian Walpen if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->bufsize, 2115687c71dSFlorian Walpen hdsp_dmapsetmap, sc, BUS_DMA_NOWAIT)) { 2125687c71dSFlorian Walpen device_printf(sc->dev, "Can't load rbuf.\n"); 2135687c71dSFlorian Walpen return (ENXIO); 2145687c71dSFlorian Walpen } 2155687c71dSFlorian Walpen 2165687c71dSFlorian Walpen bzero(sc->pbuf, sc->bufsize); 2175687c71dSFlorian Walpen bzero(sc->rbuf, sc->bufsize); 2185687c71dSFlorian Walpen 2195687c71dSFlorian Walpen return (0); 2205687c71dSFlorian Walpen } 2215687c71dSFlorian Walpen 2225687c71dSFlorian Walpen static void 2235687c71dSFlorian Walpen hdsp_map_dmabuf(struct sc_info *sc) 2245687c71dSFlorian Walpen { 2255687c71dSFlorian Walpen uint32_t paddr, raddr; 2265687c71dSFlorian Walpen 2275687c71dSFlorian Walpen paddr = vtophys(sc->pbuf); 2285687c71dSFlorian Walpen raddr = vtophys(sc->rbuf); 2295687c71dSFlorian Walpen 2305687c71dSFlorian Walpen hdsp_write_4(sc, HDSP_PAGE_ADDR_BUF_OUT, paddr); 2315687c71dSFlorian Walpen hdsp_write_4(sc, HDSP_PAGE_ADDR_BUF_IN, raddr); 2325687c71dSFlorian Walpen } 2335687c71dSFlorian Walpen 23414457cf7SFlorian Walpen static const char * 23514457cf7SFlorian Walpen hdsp_control_input_level(uint32_t control) 23614457cf7SFlorian Walpen { 23714457cf7SFlorian Walpen switch (control & HDSP_INPUT_LEVEL_MASK) { 23814457cf7SFlorian Walpen case HDSP_INPUT_LEVEL_LOWGAIN: 23914457cf7SFlorian Walpen return ("LowGain"); 24014457cf7SFlorian Walpen case HDSP_INPUT_LEVEL_PLUS4DBU: 24114457cf7SFlorian Walpen return ("+4dBu"); 24214457cf7SFlorian Walpen case HDSP_INPUT_LEVEL_MINUS10DBV: 24314457cf7SFlorian Walpen return ("-10dBV"); 24414457cf7SFlorian Walpen default: 24514457cf7SFlorian Walpen return (NULL); 24614457cf7SFlorian Walpen } 24714457cf7SFlorian Walpen } 24814457cf7SFlorian Walpen 24914457cf7SFlorian Walpen static int 25014457cf7SFlorian Walpen hdsp_sysctl_input_level(SYSCTL_HANDLER_ARGS) 25114457cf7SFlorian Walpen { 25214457cf7SFlorian Walpen struct sc_info *sc; 25314457cf7SFlorian Walpen const char *label; 25414457cf7SFlorian Walpen char buf[16] = "invalid"; 25514457cf7SFlorian Walpen int error; 25614457cf7SFlorian Walpen uint32_t control; 25714457cf7SFlorian Walpen 25814457cf7SFlorian Walpen sc = oidp->oid_arg1; 25914457cf7SFlorian Walpen 26014457cf7SFlorian Walpen /* Only available on HDSP 9632. */ 26114457cf7SFlorian Walpen if (sc->type != HDSP_9632) 26214457cf7SFlorian Walpen return (ENXIO); 26314457cf7SFlorian Walpen 26414457cf7SFlorian Walpen /* Extract current input level from control register. */ 26514457cf7SFlorian Walpen control = sc->ctrl_register & HDSP_INPUT_LEVEL_MASK; 26614457cf7SFlorian Walpen label = hdsp_control_input_level(control); 26714457cf7SFlorian Walpen if (label != NULL) 26814457cf7SFlorian Walpen strlcpy(buf, label, sizeof(buf)); 26914457cf7SFlorian Walpen 27014457cf7SFlorian Walpen /* Process sysctl string request. */ 27114457cf7SFlorian Walpen error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 27214457cf7SFlorian Walpen if (error != 0 || req->newptr == NULL) 27314457cf7SFlorian Walpen return (error); 27414457cf7SFlorian Walpen 27514457cf7SFlorian Walpen /* Find input level matching the sysctl string. */ 27614457cf7SFlorian Walpen label = hdsp_control_input_level(HDSP_INPUT_LEVEL_LOWGAIN); 27714457cf7SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 27814457cf7SFlorian Walpen control = HDSP_INPUT_LEVEL_LOWGAIN; 27914457cf7SFlorian Walpen label = hdsp_control_input_level(HDSP_INPUT_LEVEL_PLUS4DBU); 28014457cf7SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 28114457cf7SFlorian Walpen control = HDSP_INPUT_LEVEL_PLUS4DBU; 28214457cf7SFlorian Walpen label = hdsp_control_input_level(HDSP_INPUT_LEVEL_MINUS10DBV); 28314457cf7SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 28414457cf7SFlorian Walpen control = HDSP_INPUT_LEVEL_MINUS10DBV; 28514457cf7SFlorian Walpen 28614457cf7SFlorian Walpen /* Set input level in control register. */ 28714457cf7SFlorian Walpen control &= HDSP_INPUT_LEVEL_MASK; 28814457cf7SFlorian Walpen if (control != (sc->ctrl_register & HDSP_INPUT_LEVEL_MASK)) { 28914457cf7SFlorian Walpen snd_mtxlock(sc->lock); 29014457cf7SFlorian Walpen sc->ctrl_register &= ~HDSP_INPUT_LEVEL_MASK; 29114457cf7SFlorian Walpen sc->ctrl_register |= control; 29214457cf7SFlorian Walpen hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register); 29314457cf7SFlorian Walpen snd_mtxunlock(sc->lock); 29414457cf7SFlorian Walpen } 29514457cf7SFlorian Walpen return (0); 29614457cf7SFlorian Walpen } 29714457cf7SFlorian Walpen 29814457cf7SFlorian Walpen static const char * 29914457cf7SFlorian Walpen hdsp_control_output_level(uint32_t control) 30014457cf7SFlorian Walpen { 30114457cf7SFlorian Walpen switch (control & HDSP_OUTPUT_LEVEL_MASK) { 30214457cf7SFlorian Walpen case HDSP_OUTPUT_LEVEL_MINUS10DBV: 30314457cf7SFlorian Walpen return ("-10dBV"); 30414457cf7SFlorian Walpen case HDSP_OUTPUT_LEVEL_PLUS4DBU: 30514457cf7SFlorian Walpen return ("+4dBu"); 30614457cf7SFlorian Walpen case HDSP_OUTPUT_LEVEL_HIGHGAIN: 30714457cf7SFlorian Walpen return ("HighGain"); 30814457cf7SFlorian Walpen default: 30914457cf7SFlorian Walpen return (NULL); 31014457cf7SFlorian Walpen } 31114457cf7SFlorian Walpen } 31214457cf7SFlorian Walpen 31314457cf7SFlorian Walpen static int 31414457cf7SFlorian Walpen hdsp_sysctl_output_level(SYSCTL_HANDLER_ARGS) 31514457cf7SFlorian Walpen { 31614457cf7SFlorian Walpen struct sc_info *sc; 31714457cf7SFlorian Walpen const char *label; 31814457cf7SFlorian Walpen char buf[16] = "invalid"; 31914457cf7SFlorian Walpen int error; 32014457cf7SFlorian Walpen uint32_t control; 32114457cf7SFlorian Walpen 32214457cf7SFlorian Walpen sc = oidp->oid_arg1; 32314457cf7SFlorian Walpen 32414457cf7SFlorian Walpen /* Only available on HDSP 9632. */ 32514457cf7SFlorian Walpen if (sc->type != HDSP_9632) 32614457cf7SFlorian Walpen return (ENXIO); 32714457cf7SFlorian Walpen 32814457cf7SFlorian Walpen /* Extract current output level from control register. */ 32914457cf7SFlorian Walpen control = sc->ctrl_register & HDSP_OUTPUT_LEVEL_MASK; 33014457cf7SFlorian Walpen label = hdsp_control_output_level(control); 33114457cf7SFlorian Walpen if (label != NULL) 33214457cf7SFlorian Walpen strlcpy(buf, label, sizeof(buf)); 33314457cf7SFlorian Walpen 33414457cf7SFlorian Walpen /* Process sysctl string request. */ 33514457cf7SFlorian Walpen error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 33614457cf7SFlorian Walpen if (error != 0 || req->newptr == NULL) 33714457cf7SFlorian Walpen return (error); 33814457cf7SFlorian Walpen 33914457cf7SFlorian Walpen /* Find output level matching the sysctl string. */ 34014457cf7SFlorian Walpen label = hdsp_control_output_level(HDSP_OUTPUT_LEVEL_MINUS10DBV); 34114457cf7SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 34214457cf7SFlorian Walpen control = HDSP_OUTPUT_LEVEL_MINUS10DBV; 34314457cf7SFlorian Walpen label = hdsp_control_output_level(HDSP_OUTPUT_LEVEL_PLUS4DBU); 34414457cf7SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 34514457cf7SFlorian Walpen control = HDSP_OUTPUT_LEVEL_PLUS4DBU; 34614457cf7SFlorian Walpen label = hdsp_control_output_level(HDSP_OUTPUT_LEVEL_HIGHGAIN); 34714457cf7SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 34814457cf7SFlorian Walpen control = HDSP_OUTPUT_LEVEL_HIGHGAIN; 34914457cf7SFlorian Walpen 35014457cf7SFlorian Walpen /* Set output level in control register. */ 35114457cf7SFlorian Walpen control &= HDSP_OUTPUT_LEVEL_MASK; 35214457cf7SFlorian Walpen if (control != (sc->ctrl_register & HDSP_OUTPUT_LEVEL_MASK)) { 35314457cf7SFlorian Walpen snd_mtxlock(sc->lock); 35414457cf7SFlorian Walpen sc->ctrl_register &= ~HDSP_OUTPUT_LEVEL_MASK; 35514457cf7SFlorian Walpen sc->ctrl_register |= control; 35614457cf7SFlorian Walpen hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register); 35714457cf7SFlorian Walpen snd_mtxunlock(sc->lock); 35814457cf7SFlorian Walpen } 35914457cf7SFlorian Walpen return (0); 36014457cf7SFlorian Walpen } 36114457cf7SFlorian Walpen 36214457cf7SFlorian Walpen static const char * 36314457cf7SFlorian Walpen hdsp_control_phones_level(uint32_t control) 36414457cf7SFlorian Walpen { 36514457cf7SFlorian Walpen switch (control & HDSP_PHONES_LEVEL_MASK) { 36614457cf7SFlorian Walpen case HDSP_PHONES_LEVEL_MINUS12DB: 36714457cf7SFlorian Walpen return ("-12dB"); 36814457cf7SFlorian Walpen case HDSP_PHONES_LEVEL_MINUS6DB: 36914457cf7SFlorian Walpen return ("-6dB"); 37014457cf7SFlorian Walpen case HDSP_PHONES_LEVEL_0DB: 37114457cf7SFlorian Walpen return ("0dB"); 37214457cf7SFlorian Walpen default: 37314457cf7SFlorian Walpen return (NULL); 37414457cf7SFlorian Walpen } 37514457cf7SFlorian Walpen } 37614457cf7SFlorian Walpen 37714457cf7SFlorian Walpen static int 37814457cf7SFlorian Walpen hdsp_sysctl_phones_level(SYSCTL_HANDLER_ARGS) 37914457cf7SFlorian Walpen { 38014457cf7SFlorian Walpen struct sc_info *sc; 38114457cf7SFlorian Walpen const char *label; 38214457cf7SFlorian Walpen char buf[16] = "invalid"; 38314457cf7SFlorian Walpen int error; 38414457cf7SFlorian Walpen uint32_t control; 38514457cf7SFlorian Walpen 38614457cf7SFlorian Walpen sc = oidp->oid_arg1; 38714457cf7SFlorian Walpen 38814457cf7SFlorian Walpen /* Only available on HDSP 9632. */ 38914457cf7SFlorian Walpen if (sc->type != HDSP_9632) 39014457cf7SFlorian Walpen return (ENXIO); 39114457cf7SFlorian Walpen 39214457cf7SFlorian Walpen /* Extract current phones level from control register. */ 39314457cf7SFlorian Walpen control = sc->ctrl_register & HDSP_PHONES_LEVEL_MASK; 39414457cf7SFlorian Walpen label = hdsp_control_phones_level(control); 39514457cf7SFlorian Walpen if (label != NULL) 39614457cf7SFlorian Walpen strlcpy(buf, label, sizeof(buf)); 39714457cf7SFlorian Walpen 39814457cf7SFlorian Walpen /* Process sysctl string request. */ 39914457cf7SFlorian Walpen error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 40014457cf7SFlorian Walpen if (error != 0 || req->newptr == NULL) 40114457cf7SFlorian Walpen return (error); 40214457cf7SFlorian Walpen 40314457cf7SFlorian Walpen /* Find phones level matching the sysctl string. */ 40414457cf7SFlorian Walpen label = hdsp_control_phones_level(HDSP_PHONES_LEVEL_MINUS12DB); 40514457cf7SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 40614457cf7SFlorian Walpen control = HDSP_PHONES_LEVEL_MINUS12DB; 40714457cf7SFlorian Walpen label = hdsp_control_phones_level(HDSP_PHONES_LEVEL_MINUS6DB); 40814457cf7SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 40914457cf7SFlorian Walpen control = HDSP_PHONES_LEVEL_MINUS6DB; 41014457cf7SFlorian Walpen label = hdsp_control_phones_level(HDSP_PHONES_LEVEL_0DB); 41114457cf7SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 41214457cf7SFlorian Walpen control = HDSP_PHONES_LEVEL_0DB; 41314457cf7SFlorian Walpen 41414457cf7SFlorian Walpen /* Set phones level in control register. */ 41514457cf7SFlorian Walpen control &= HDSP_PHONES_LEVEL_MASK; 41614457cf7SFlorian Walpen if (control != (sc->ctrl_register & HDSP_PHONES_LEVEL_MASK)) { 41714457cf7SFlorian Walpen snd_mtxlock(sc->lock); 41814457cf7SFlorian Walpen sc->ctrl_register &= ~HDSP_PHONES_LEVEL_MASK; 41914457cf7SFlorian Walpen sc->ctrl_register |= control; 42014457cf7SFlorian Walpen hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register); 42114457cf7SFlorian Walpen snd_mtxunlock(sc->lock); 42214457cf7SFlorian Walpen } 42314457cf7SFlorian Walpen return (0); 42414457cf7SFlorian Walpen } 42514457cf7SFlorian Walpen 4265687c71dSFlorian Walpen static int 4275687c71dSFlorian Walpen hdsp_sysctl_sample_rate(SYSCTL_HANDLER_ARGS) 4285687c71dSFlorian Walpen { 4295687c71dSFlorian Walpen struct sc_info *sc = oidp->oid_arg1; 4305687c71dSFlorian Walpen int error; 4315687c71dSFlorian Walpen unsigned int speed, multiplier; 4325687c71dSFlorian Walpen 4335687c71dSFlorian Walpen speed = sc->force_speed; 4345687c71dSFlorian Walpen 4355687c71dSFlorian Walpen /* Process sysctl (unsigned) integer request. */ 4365687c71dSFlorian Walpen error = sysctl_handle_int(oidp, &speed, 0, req); 4375687c71dSFlorian Walpen if (error != 0 || req->newptr == NULL) 4385687c71dSFlorian Walpen return (error); 4395687c71dSFlorian Walpen 4405687c71dSFlorian Walpen /* Speed from 32000 to 192000, 0 falls back to pcm speed setting. */ 4415687c71dSFlorian Walpen sc->force_speed = 0; 4425687c71dSFlorian Walpen if (speed > 0) { 4435687c71dSFlorian Walpen multiplier = 1; 4445687c71dSFlorian Walpen if ((speed > (96000 + 128000) / 2) && sc->type == HDSP_9632) 4455687c71dSFlorian Walpen multiplier = 4; 4465687c71dSFlorian Walpen else if (speed > (48000 + 64000) / 2) 4475687c71dSFlorian Walpen multiplier = 2; 4485687c71dSFlorian Walpen 4495687c71dSFlorian Walpen if (speed < ((32000 + 44100) / 2) * multiplier) 4505687c71dSFlorian Walpen sc->force_speed = 32000 * multiplier; 4515687c71dSFlorian Walpen else if (speed < ((44100 + 48000) / 2) * multiplier) 4525687c71dSFlorian Walpen sc->force_speed = 44100 * multiplier; 4535687c71dSFlorian Walpen else 4545687c71dSFlorian Walpen sc->force_speed = 48000 * multiplier; 4555687c71dSFlorian Walpen } 4565687c71dSFlorian Walpen 4575687c71dSFlorian Walpen return (0); 4585687c71dSFlorian Walpen } 4595687c71dSFlorian Walpen 4605687c71dSFlorian Walpen 4615687c71dSFlorian Walpen static int 4625687c71dSFlorian Walpen hdsp_sysctl_period(SYSCTL_HANDLER_ARGS) 4635687c71dSFlorian Walpen { 4645687c71dSFlorian Walpen struct sc_info *sc = oidp->oid_arg1; 4655687c71dSFlorian Walpen int error; 4665687c71dSFlorian Walpen unsigned int period; 4675687c71dSFlorian Walpen 4685687c71dSFlorian Walpen period = sc->force_period; 4695687c71dSFlorian Walpen 4705687c71dSFlorian Walpen /* Process sysctl (unsigned) integer request. */ 4715687c71dSFlorian Walpen error = sysctl_handle_int(oidp, &period, 0, req); 4725687c71dSFlorian Walpen if (error != 0 || req->newptr == NULL) 4735687c71dSFlorian Walpen return (error); 4745687c71dSFlorian Walpen 4755687c71dSFlorian Walpen /* Period is from 2^5 to 2^14, 0 falls back to pcm latency settings. */ 4765687c71dSFlorian Walpen sc->force_period = 0; 4775687c71dSFlorian Walpen if (period > 0) { 4785687c71dSFlorian Walpen sc->force_period = 32; 4795687c71dSFlorian Walpen while (sc->force_period < period && sc->force_period < 4096) 4805687c71dSFlorian Walpen sc->force_period <<= 1; 4815687c71dSFlorian Walpen } 4825687c71dSFlorian Walpen 4835687c71dSFlorian Walpen return (0); 4845687c71dSFlorian Walpen } 4855687c71dSFlorian Walpen 4865687c71dSFlorian Walpen static uint32_t 4875687c71dSFlorian Walpen hdsp_control_clock_preference(enum hdsp_clock_type type) 4885687c71dSFlorian Walpen { 4895687c71dSFlorian Walpen switch (type) { 4905687c71dSFlorian Walpen case HDSP_CLOCK_INTERNAL: 4915687c71dSFlorian Walpen return (HDSP_CONTROL_MASTER); 4925687c71dSFlorian Walpen case HDSP_CLOCK_ADAT1: 4935687c71dSFlorian Walpen return (HDSP_CONTROL_CLOCK(0)); 4945687c71dSFlorian Walpen case HDSP_CLOCK_ADAT2: 4955687c71dSFlorian Walpen return (HDSP_CONTROL_CLOCK(1)); 4965687c71dSFlorian Walpen case HDSP_CLOCK_ADAT3: 4975687c71dSFlorian Walpen return (HDSP_CONTROL_CLOCK(2)); 4985687c71dSFlorian Walpen case HDSP_CLOCK_SPDIF: 4995687c71dSFlorian Walpen return (HDSP_CONTROL_CLOCK(3)); 5005687c71dSFlorian Walpen case HDSP_CLOCK_WORD: 5015687c71dSFlorian Walpen return (HDSP_CONTROL_CLOCK(4)); 5025687c71dSFlorian Walpen case HDSP_CLOCK_ADAT_SYNC: 5035687c71dSFlorian Walpen return (HDSP_CONTROL_CLOCK(5)); 5045687c71dSFlorian Walpen default: 5055687c71dSFlorian Walpen return (HDSP_CONTROL_MASTER); 5065687c71dSFlorian Walpen } 5075687c71dSFlorian Walpen } 5085687c71dSFlorian Walpen 5095687c71dSFlorian Walpen static int 5105687c71dSFlorian Walpen hdsp_sysctl_clock_preference(SYSCTL_HANDLER_ARGS) 5115687c71dSFlorian Walpen { 5125687c71dSFlorian Walpen struct sc_info *sc; 5135687c71dSFlorian Walpen struct hdsp_clock_source *clock_table, *clock; 5145687c71dSFlorian Walpen char buf[16] = "invalid"; 5155687c71dSFlorian Walpen int error; 5165687c71dSFlorian Walpen uint32_t control; 5175687c71dSFlorian Walpen 5185687c71dSFlorian Walpen sc = oidp->oid_arg1; 5195687c71dSFlorian Walpen 5205687c71dSFlorian Walpen /* Select sync ports table for device type. */ 5215687c71dSFlorian Walpen if (sc->type == HDSP_9632) 5225687c71dSFlorian Walpen clock_table = hdsp_clock_source_table_9632; 5235687c71dSFlorian Walpen else if (sc->type == HDSP_9652) 5245687c71dSFlorian Walpen clock_table = hdsp_clock_source_table_9652; 5255687c71dSFlorian Walpen else 5265687c71dSFlorian Walpen return (ENXIO); 5275687c71dSFlorian Walpen 5285687c71dSFlorian Walpen /* Extract preferred clock source from control register. */ 5295687c71dSFlorian Walpen control = sc->ctrl_register & HDSP_CONTROL_CLOCK_MASK; 5305687c71dSFlorian Walpen for (clock = clock_table; clock->name != NULL; ++clock) { 5315687c71dSFlorian Walpen if (hdsp_control_clock_preference(clock->type) == control) 5325687c71dSFlorian Walpen break; 5335687c71dSFlorian Walpen } 5345687c71dSFlorian Walpen if (clock->name != NULL) 5355687c71dSFlorian Walpen strlcpy(buf, clock->name, sizeof(buf)); 5365687c71dSFlorian Walpen 5375687c71dSFlorian Walpen /* Process sysctl string request. */ 5385687c71dSFlorian Walpen error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 5395687c71dSFlorian Walpen if (error != 0 || req->newptr == NULL) 5405687c71dSFlorian Walpen return (error); 5415687c71dSFlorian Walpen 5425687c71dSFlorian Walpen /* Find clock source matching the sysctl string. */ 5435687c71dSFlorian Walpen for (clock = clock_table; clock->name != NULL; ++clock) { 5445687c71dSFlorian Walpen if (strncasecmp(buf, clock->name, sizeof(buf)) == 0) 5455687c71dSFlorian Walpen break; 5465687c71dSFlorian Walpen } 5475687c71dSFlorian Walpen 5485687c71dSFlorian Walpen /* Set preferred clock source in control register. */ 5495687c71dSFlorian Walpen if (clock->name != NULL) { 5505687c71dSFlorian Walpen control = hdsp_control_clock_preference(clock->type); 5515687c71dSFlorian Walpen control &= HDSP_CONTROL_CLOCK_MASK; 5525687c71dSFlorian Walpen snd_mtxlock(sc->lock); 5535687c71dSFlorian Walpen sc->ctrl_register &= ~HDSP_CONTROL_CLOCK_MASK; 5545687c71dSFlorian Walpen sc->ctrl_register |= control; 5555687c71dSFlorian Walpen hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register); 5565687c71dSFlorian Walpen snd_mtxunlock(sc->lock); 5575687c71dSFlorian Walpen } 5585687c71dSFlorian Walpen return (0); 5595687c71dSFlorian Walpen } 5605687c71dSFlorian Walpen 5615687c71dSFlorian Walpen static uint32_t 5625687c71dSFlorian Walpen hdsp_status2_clock_source(enum hdsp_clock_type type) 5635687c71dSFlorian Walpen { 5645687c71dSFlorian Walpen switch (type) { 5655687c71dSFlorian Walpen case HDSP_CLOCK_INTERNAL: 5665687c71dSFlorian Walpen return (0); 5675687c71dSFlorian Walpen case HDSP_CLOCK_ADAT1: 5685687c71dSFlorian Walpen return (HDSP_STATUS2_CLOCK(0)); 5695687c71dSFlorian Walpen case HDSP_CLOCK_ADAT2: 5705687c71dSFlorian Walpen return (HDSP_STATUS2_CLOCK(1)); 5715687c71dSFlorian Walpen case HDSP_CLOCK_ADAT3: 5725687c71dSFlorian Walpen return (HDSP_STATUS2_CLOCK(2)); 5735687c71dSFlorian Walpen case HDSP_CLOCK_SPDIF: 5745687c71dSFlorian Walpen return (HDSP_STATUS2_CLOCK(3)); 5755687c71dSFlorian Walpen case HDSP_CLOCK_WORD: 5765687c71dSFlorian Walpen return (HDSP_STATUS2_CLOCK(4)); 5775687c71dSFlorian Walpen case HDSP_CLOCK_ADAT_SYNC: 5785687c71dSFlorian Walpen return (HDSP_STATUS2_CLOCK(5)); 5795687c71dSFlorian Walpen default: 5805687c71dSFlorian Walpen return (0); 5815687c71dSFlorian Walpen } 5825687c71dSFlorian Walpen } 5835687c71dSFlorian Walpen 5845687c71dSFlorian Walpen static int 5855687c71dSFlorian Walpen hdsp_sysctl_clock_source(SYSCTL_HANDLER_ARGS) 5865687c71dSFlorian Walpen { 5875687c71dSFlorian Walpen struct sc_info *sc; 5885687c71dSFlorian Walpen struct hdsp_clock_source *clock_table, *clock; 5895687c71dSFlorian Walpen char buf[16] = "invalid"; 5905687c71dSFlorian Walpen uint32_t status2; 5915687c71dSFlorian Walpen 5925687c71dSFlorian Walpen sc = oidp->oid_arg1; 5935687c71dSFlorian Walpen 5945687c71dSFlorian Walpen /* Select sync ports table for device type. */ 5955687c71dSFlorian Walpen if (sc->type == HDSP_9632) 5965687c71dSFlorian Walpen clock_table = hdsp_clock_source_table_9632; 5975687c71dSFlorian Walpen else if (sc->type == HDSP_9652) 5985687c71dSFlorian Walpen clock_table = hdsp_clock_source_table_9652; 5995687c71dSFlorian Walpen else 6005687c71dSFlorian Walpen return (ENXIO); 6015687c71dSFlorian Walpen 6025687c71dSFlorian Walpen /* Read current (autosync) clock source from status2 register. */ 6035687c71dSFlorian Walpen snd_mtxlock(sc->lock); 6045687c71dSFlorian Walpen status2 = hdsp_read_4(sc, HDSP_STATUS2_REG); 6055687c71dSFlorian Walpen status2 &= HDSP_STATUS2_CLOCK_MASK; 6065687c71dSFlorian Walpen snd_mtxunlock(sc->lock); 6075687c71dSFlorian Walpen 6085687c71dSFlorian Walpen /* Translate status2 register value to clock source. */ 6095687c71dSFlorian Walpen for (clock = clock_table; clock->name != NULL; ++clock) { 6105687c71dSFlorian Walpen /* In clock master mode, override with internal clock source. */ 6115687c71dSFlorian Walpen if (sc->ctrl_register & HDSP_CONTROL_MASTER) { 6125687c71dSFlorian Walpen if (clock->type == HDSP_CLOCK_INTERNAL) 6135687c71dSFlorian Walpen break; 6145687c71dSFlorian Walpen } else if (hdsp_status2_clock_source(clock->type) == status2) 6155687c71dSFlorian Walpen break; 6165687c71dSFlorian Walpen } 6175687c71dSFlorian Walpen 6185687c71dSFlorian Walpen /* Process sysctl string request. */ 6195687c71dSFlorian Walpen if (clock->name != NULL) 6205687c71dSFlorian Walpen strlcpy(buf, clock->name, sizeof(buf)); 6215687c71dSFlorian Walpen return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); 6225687c71dSFlorian Walpen } 6235687c71dSFlorian Walpen 6245687c71dSFlorian Walpen static int 6255687c71dSFlorian Walpen hdsp_sysctl_clock_list(SYSCTL_HANDLER_ARGS) 6265687c71dSFlorian Walpen { 6275687c71dSFlorian Walpen struct sc_info *sc; 6285687c71dSFlorian Walpen struct hdsp_clock_source *clock_table, *clock; 6295687c71dSFlorian Walpen char buf[256]; 6305687c71dSFlorian Walpen int n; 6315687c71dSFlorian Walpen 6325687c71dSFlorian Walpen sc = oidp->oid_arg1; 6335687c71dSFlorian Walpen n = 0; 6345687c71dSFlorian Walpen 6355687c71dSFlorian Walpen /* Select clock source table for device type. */ 6365687c71dSFlorian Walpen if (sc->type == HDSP_9632) 6375687c71dSFlorian Walpen clock_table = hdsp_clock_source_table_9632; 6385687c71dSFlorian Walpen else if (sc->type == HDSP_9652) 6395687c71dSFlorian Walpen clock_table = hdsp_clock_source_table_9652; 6405687c71dSFlorian Walpen else 6415687c71dSFlorian Walpen return (ENXIO); 6425687c71dSFlorian Walpen 6435687c71dSFlorian Walpen /* List available clock sources. */ 6445687c71dSFlorian Walpen buf[0] = 0; 6455687c71dSFlorian Walpen for (clock = clock_table; clock->name != NULL; ++clock) { 6465687c71dSFlorian Walpen if (n > 0) 6475687c71dSFlorian Walpen n += strlcpy(buf + n, ",", sizeof(buf) - n); 6485687c71dSFlorian Walpen n += strlcpy(buf + n, clock->name, sizeof(buf) - n); 6495687c71dSFlorian Walpen } 6505687c71dSFlorian Walpen return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); 6515687c71dSFlorian Walpen } 6525687c71dSFlorian Walpen 6535687c71dSFlorian Walpen static bool 6545687c71dSFlorian Walpen hdsp_clock_source_locked(enum hdsp_clock_type type, uint32_t status, 6555687c71dSFlorian Walpen uint32_t status2) 6565687c71dSFlorian Walpen { 6575687c71dSFlorian Walpen switch (type) { 6585687c71dSFlorian Walpen case HDSP_CLOCK_INTERNAL: 6595687c71dSFlorian Walpen return (true); 6605687c71dSFlorian Walpen case HDSP_CLOCK_ADAT1: 6615687c71dSFlorian Walpen return ((status >> 3) & 0x01); 6625687c71dSFlorian Walpen case HDSP_CLOCK_ADAT2: 6635687c71dSFlorian Walpen return ((status >> 2) & 0x01); 6645687c71dSFlorian Walpen case HDSP_CLOCK_ADAT3: 6655687c71dSFlorian Walpen return ((status >> 1) & 0x01); 6665687c71dSFlorian Walpen case HDSP_CLOCK_SPDIF: 6675687c71dSFlorian Walpen return (!((status >> 25) & 0x01)); 6685687c71dSFlorian Walpen case HDSP_CLOCK_WORD: 6695687c71dSFlorian Walpen return ((status2 >> 3) & 0x01); 6705687c71dSFlorian Walpen case HDSP_CLOCK_ADAT_SYNC: 6715687c71dSFlorian Walpen return ((status >> 5) & 0x01); 6725687c71dSFlorian Walpen default: 6735687c71dSFlorian Walpen return (false); 6745687c71dSFlorian Walpen } 6755687c71dSFlorian Walpen } 6765687c71dSFlorian Walpen 6775687c71dSFlorian Walpen static bool 6785687c71dSFlorian Walpen hdsp_clock_source_synced(enum hdsp_clock_type type, uint32_t status, 6795687c71dSFlorian Walpen uint32_t status2) 6805687c71dSFlorian Walpen { 6815687c71dSFlorian Walpen switch (type) { 6825687c71dSFlorian Walpen case HDSP_CLOCK_INTERNAL: 6835687c71dSFlorian Walpen return (true); 6845687c71dSFlorian Walpen case HDSP_CLOCK_ADAT1: 6855687c71dSFlorian Walpen return ((status >> 18) & 0x01); 6865687c71dSFlorian Walpen case HDSP_CLOCK_ADAT2: 6875687c71dSFlorian Walpen return ((status >> 17) & 0x01); 6885687c71dSFlorian Walpen case HDSP_CLOCK_ADAT3: 6895687c71dSFlorian Walpen return ((status >> 16) & 0x01); 6905687c71dSFlorian Walpen case HDSP_CLOCK_SPDIF: 6915687c71dSFlorian Walpen return (((status >> 4) & 0x01) && !((status >> 25) & 0x01)); 6925687c71dSFlorian Walpen case HDSP_CLOCK_WORD: 6935687c71dSFlorian Walpen return ((status2 >> 4) & 0x01); 6945687c71dSFlorian Walpen case HDSP_CLOCK_ADAT_SYNC: 6955687c71dSFlorian Walpen return ((status >> 27) & 0x01); 6965687c71dSFlorian Walpen default: 6975687c71dSFlorian Walpen return (false); 6985687c71dSFlorian Walpen } 6995687c71dSFlorian Walpen } 7005687c71dSFlorian Walpen 7015687c71dSFlorian Walpen static int 7025687c71dSFlorian Walpen hdsp_sysctl_sync_status(SYSCTL_HANDLER_ARGS) 7035687c71dSFlorian Walpen { 7045687c71dSFlorian Walpen struct sc_info *sc; 7055687c71dSFlorian Walpen struct hdsp_clock_source *clock_table, *clock; 7065687c71dSFlorian Walpen char buf[256]; 7075687c71dSFlorian Walpen char *state; 7085687c71dSFlorian Walpen int n; 7095687c71dSFlorian Walpen uint32_t status, status2; 7105687c71dSFlorian Walpen 7115687c71dSFlorian Walpen sc = oidp->oid_arg1; 7125687c71dSFlorian Walpen n = 0; 7135687c71dSFlorian Walpen 7145687c71dSFlorian Walpen /* Select sync ports table for device type. */ 7155687c71dSFlorian Walpen if (sc->type == HDSP_9632) 7165687c71dSFlorian Walpen clock_table = hdsp_clock_source_table_9632; 7175687c71dSFlorian Walpen else if (sc->type == HDSP_9652) 7185687c71dSFlorian Walpen clock_table = hdsp_clock_source_table_9652; 7195687c71dSFlorian Walpen else 7205687c71dSFlorian Walpen return (ENXIO); 7215687c71dSFlorian Walpen 7225687c71dSFlorian Walpen /* Read current lock and sync bits from status registers. */ 7235687c71dSFlorian Walpen snd_mtxlock(sc->lock); 7245687c71dSFlorian Walpen status = hdsp_read_4(sc, HDSP_STATUS_REG); 7255687c71dSFlorian Walpen status2 = hdsp_read_4(sc, HDSP_STATUS2_REG); 7265687c71dSFlorian Walpen snd_mtxunlock(sc->lock); 7275687c71dSFlorian Walpen 7285687c71dSFlorian Walpen /* List clock sources with lock and sync state. */ 7295687c71dSFlorian Walpen for (clock = clock_table; clock->name != NULL; ++clock) { 7305687c71dSFlorian Walpen if (clock->type == HDSP_CLOCK_INTERNAL) 7315687c71dSFlorian Walpen continue; 7325687c71dSFlorian Walpen if (n > 0) 7335687c71dSFlorian Walpen n += strlcpy(buf + n, ",", sizeof(buf) - n); 7345687c71dSFlorian Walpen state = "none"; 7355687c71dSFlorian Walpen if (hdsp_clock_source_locked(clock->type, status, status2)) { 7365687c71dSFlorian Walpen if (hdsp_clock_source_synced(clock->type, status, 7375687c71dSFlorian Walpen status2)) 7385687c71dSFlorian Walpen state = "sync"; 7395687c71dSFlorian Walpen else 7405687c71dSFlorian Walpen state = "lock"; 7415687c71dSFlorian Walpen } 7425687c71dSFlorian Walpen n += snprintf(buf + n, sizeof(buf) - n, "%s(%s)", 7435687c71dSFlorian Walpen clock->name, state); 7445687c71dSFlorian Walpen } 7455687c71dSFlorian Walpen return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); 7465687c71dSFlorian Walpen } 7475687c71dSFlorian Walpen 7485687c71dSFlorian Walpen static int 7495687c71dSFlorian Walpen hdsp_probe(device_t dev) 7505687c71dSFlorian Walpen { 7515687c71dSFlorian Walpen uint32_t rev; 7525687c71dSFlorian Walpen 7535687c71dSFlorian Walpen if (pci_get_vendor(dev) == PCI_VENDOR_XILINX && 7545687c71dSFlorian Walpen pci_get_device(dev) == PCI_DEVICE_XILINX_HDSP) { 7555687c71dSFlorian Walpen rev = pci_get_revid(dev); 7565687c71dSFlorian Walpen switch (rev) { 7575687c71dSFlorian Walpen case PCI_REVISION_9632: 7585687c71dSFlorian Walpen device_set_desc(dev, "RME HDSP 9632"); 7595687c71dSFlorian Walpen return (0); 7605687c71dSFlorian Walpen case PCI_REVISION_9652: 7615687c71dSFlorian Walpen device_set_desc(dev, "RME HDSP 9652"); 7625687c71dSFlorian Walpen return (0); 7635687c71dSFlorian Walpen } 7645687c71dSFlorian Walpen } 7655687c71dSFlorian Walpen 7665687c71dSFlorian Walpen return (ENXIO); 7675687c71dSFlorian Walpen } 7685687c71dSFlorian Walpen 7695687c71dSFlorian Walpen static int 7705687c71dSFlorian Walpen hdsp_init(struct sc_info *sc) 7715687c71dSFlorian Walpen { 7725687c71dSFlorian Walpen unsigned mixer_controls; 7735687c71dSFlorian Walpen 7745687c71dSFlorian Walpen /* Set latency. */ 7755687c71dSFlorian Walpen sc->period = 256; 7765687c71dSFlorian Walpen /* 7775687c71dSFlorian Walpen * The pcm channel latency settings propagate unreliable blocksizes, 7785687c71dSFlorian Walpen * different for recording and playback, and skewed due to rounding 7795687c71dSFlorian Walpen * and total buffer size limits. 7805687c71dSFlorian Walpen * Force period to a consistent default until these issues are fixed. 7815687c71dSFlorian Walpen */ 7825687c71dSFlorian Walpen sc->force_period = 256; 7835687c71dSFlorian Walpen sc->ctrl_register = hdsp_encode_latency(2); 7845687c71dSFlorian Walpen 7855687c71dSFlorian Walpen /* Set rate. */ 7865687c71dSFlorian Walpen sc->speed = HDSP_SPEED_DEFAULT; 7875687c71dSFlorian Walpen sc->force_speed = 0; 7885687c71dSFlorian Walpen sc->ctrl_register &= ~HDSP_FREQ_MASK; 7895687c71dSFlorian Walpen sc->ctrl_register |= HDSP_FREQ_MASK_DEFAULT; 7905687c71dSFlorian Walpen 7915687c71dSFlorian Walpen /* Set internal clock source (master). */ 7925687c71dSFlorian Walpen sc->ctrl_register &= ~HDSP_CONTROL_CLOCK_MASK; 7935687c71dSFlorian Walpen sc->ctrl_register |= HDSP_CONTROL_MASTER; 7945687c71dSFlorian Walpen 7955687c71dSFlorian Walpen /* SPDIF from coax in, line out. */ 7965687c71dSFlorian Walpen sc->ctrl_register &= ~HDSP_CONTROL_SPDIF_COAX; 7975687c71dSFlorian Walpen sc->ctrl_register |= HDSP_CONTROL_SPDIF_COAX; 7985687c71dSFlorian Walpen sc->ctrl_register &= ~HDSP_CONTROL_LINE_OUT; 7995687c71dSFlorian Walpen sc->ctrl_register |= HDSP_CONTROL_LINE_OUT; 8005687c71dSFlorian Walpen 80114457cf7SFlorian Walpen /* Default gain levels. */ 80214457cf7SFlorian Walpen sc->ctrl_register &= ~HDSP_INPUT_LEVEL_MASK; 80314457cf7SFlorian Walpen sc->ctrl_register |= HDSP_INPUT_LEVEL_LOWGAIN; 80414457cf7SFlorian Walpen sc->ctrl_register &= ~HDSP_OUTPUT_LEVEL_MASK; 80514457cf7SFlorian Walpen sc->ctrl_register |= HDSP_OUTPUT_LEVEL_MINUS10DBV; 80614457cf7SFlorian Walpen sc->ctrl_register &= ~HDSP_PHONES_LEVEL_MASK; 80714457cf7SFlorian Walpen sc->ctrl_register |= HDSP_PHONES_LEVEL_MINUS12DB; 80814457cf7SFlorian Walpen 8095687c71dSFlorian Walpen hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register); 8105687c71dSFlorian Walpen 8115687c71dSFlorian Walpen if (sc->type == HDSP_9652) 8125687c71dSFlorian Walpen hdsp_write_4(sc, HDSP_CONTROL2_REG, HDSP_CONTROL2_9652_MIXER); 8135687c71dSFlorian Walpen else 8145687c71dSFlorian Walpen hdsp_write_4(sc, HDSP_CONTROL2_REG, 0); 8155687c71dSFlorian Walpen 8165687c71dSFlorian Walpen switch (sc->type) { 8175687c71dSFlorian Walpen case HDSP_9632: 8185687c71dSFlorian Walpen /* Mixer matrix is 2 source rows (input, playback) per output. */ 8195687c71dSFlorian Walpen mixer_controls = 2 * HDSP_MIX_SLOTS_9632 * HDSP_MIX_SLOTS_9632; 8205687c71dSFlorian Walpen break; 8215687c71dSFlorian Walpen case HDSP_9652: 8225687c71dSFlorian Walpen /* Mixer matrix is 2 source rows (input, playback) per output. */ 8235687c71dSFlorian Walpen mixer_controls = 2 * HDSP_MIX_SLOTS_9652 * HDSP_MIX_SLOTS_9652; 8245687c71dSFlorian Walpen break; 8255687c71dSFlorian Walpen default: 8265687c71dSFlorian Walpen return (ENXIO); 8275687c71dSFlorian Walpen } 8285687c71dSFlorian Walpen 8295687c71dSFlorian Walpen /* Initialize mixer matrix by silencing all controls. */ 8305687c71dSFlorian Walpen for (unsigned offset = 0; offset < mixer_controls * 2; offset += 4) { 8315687c71dSFlorian Walpen /* Only accepts 4 byte values, pairs of 16 bit volume controls. */ 8325687c71dSFlorian Walpen hdsp_write_4(sc, HDSP_MIXER_BASE + offset, 8335687c71dSFlorian Walpen (HDSP_MIN_GAIN << 16) | HDSP_MIN_GAIN); 8345687c71dSFlorian Walpen } 8355687c71dSFlorian Walpen 8365687c71dSFlorian Walpen /* Reset pointer, rewrite frequency (same register) for 9632. */ 8375687c71dSFlorian Walpen hdsp_write_4(sc, HDSP_RESET_POINTER, 0); 8385687c71dSFlorian Walpen if (sc->type == HDSP_9632) { 8395687c71dSFlorian Walpen /* Set DDS value. */ 8405687c71dSFlorian Walpen hdsp_write_4(sc, HDSP_FREQ_REG, hdsp_freq_reg_value(sc->speed)); 8415687c71dSFlorian Walpen } 8425687c71dSFlorian Walpen 8435687c71dSFlorian Walpen return (0); 8445687c71dSFlorian Walpen } 8455687c71dSFlorian Walpen 8465687c71dSFlorian Walpen static int 8475687c71dSFlorian Walpen hdsp_attach(device_t dev) 8485687c71dSFlorian Walpen { 8495687c71dSFlorian Walpen struct hdsp_channel *chan_map; 8505687c71dSFlorian Walpen struct sc_pcminfo *scp; 8515687c71dSFlorian Walpen struct sc_info *sc; 8525687c71dSFlorian Walpen uint32_t rev; 8535687c71dSFlorian Walpen int i, err; 8545687c71dSFlorian Walpen 8555687c71dSFlorian Walpen #if 0 8565687c71dSFlorian Walpen device_printf(dev, "hdsp_attach()\n"); 8575687c71dSFlorian Walpen #endif 8585687c71dSFlorian Walpen 8595687c71dSFlorian Walpen sc = device_get_softc(dev); 8605687c71dSFlorian Walpen sc->lock = snd_mtxcreate(device_get_nameunit(dev), 8615687c71dSFlorian Walpen "snd_hdsp softc"); 8625687c71dSFlorian Walpen sc->dev = dev; 8635687c71dSFlorian Walpen 8645687c71dSFlorian Walpen pci_enable_busmaster(dev); 8655687c71dSFlorian Walpen rev = pci_get_revid(dev); 8665687c71dSFlorian Walpen switch (rev) { 8675687c71dSFlorian Walpen case PCI_REVISION_9632: 8685687c71dSFlorian Walpen sc->type = HDSP_9632; 8695687c71dSFlorian Walpen chan_map = hdsp_unified_pcm ? chan_map_9632_uni : chan_map_9632; 8705687c71dSFlorian Walpen break; 8715687c71dSFlorian Walpen case PCI_REVISION_9652: 8725687c71dSFlorian Walpen sc->type = HDSP_9652; 8735687c71dSFlorian Walpen chan_map = hdsp_unified_pcm ? chan_map_9652_uni : chan_map_9652; 8745687c71dSFlorian Walpen break; 8755687c71dSFlorian Walpen default: 8765687c71dSFlorian Walpen return (ENXIO); 8775687c71dSFlorian Walpen } 8785687c71dSFlorian Walpen 8795687c71dSFlorian Walpen /* Allocate resources. */ 8805687c71dSFlorian Walpen err = hdsp_alloc_resources(sc); 8815687c71dSFlorian Walpen if (err) { 8825687c71dSFlorian Walpen device_printf(dev, "Unable to allocate system resources.\n"); 8835687c71dSFlorian Walpen return (ENXIO); 8845687c71dSFlorian Walpen } 8855687c71dSFlorian Walpen 8865687c71dSFlorian Walpen if (hdsp_init(sc) != 0) 8875687c71dSFlorian Walpen return (ENXIO); 8885687c71dSFlorian Walpen 8895687c71dSFlorian Walpen for (i = 0; i < HDSP_MAX_CHANS && chan_map[i].descr != NULL; i++) { 890a17a41ffSJohn Baldwin scp = malloc(sizeof(struct sc_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO); 8915687c71dSFlorian Walpen scp->hc = &chan_map[i]; 8925687c71dSFlorian Walpen scp->sc = sc; 8935687c71dSFlorian Walpen scp->dev = device_add_child(dev, "pcm", -1); 8945687c71dSFlorian Walpen device_set_ivars(scp->dev, scp); 8955687c71dSFlorian Walpen } 8965687c71dSFlorian Walpen 8975687c71dSFlorian Walpen hdsp_map_dmabuf(sc); 8985687c71dSFlorian Walpen 8995687c71dSFlorian Walpen SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 9005687c71dSFlorian Walpen SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 9015687c71dSFlorian Walpen "sync_status", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 9025687c71dSFlorian Walpen sc, 0, hdsp_sysctl_sync_status, "A", 9035687c71dSFlorian Walpen "List clock source signal lock and sync status"); 9045687c71dSFlorian Walpen 9055687c71dSFlorian Walpen SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 9065687c71dSFlorian Walpen SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 9075687c71dSFlorian Walpen "clock_source", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 9085687c71dSFlorian Walpen sc, 0, hdsp_sysctl_clock_source, "A", 9095687c71dSFlorian Walpen "Currently effective clock source"); 9105687c71dSFlorian Walpen 9115687c71dSFlorian Walpen SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 9125687c71dSFlorian Walpen SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 9135687c71dSFlorian Walpen "clock_preference", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 9145687c71dSFlorian Walpen sc, 0, hdsp_sysctl_clock_preference, "A", 9155687c71dSFlorian Walpen "Set 'internal' (master) or preferred autosync clock source"); 9165687c71dSFlorian Walpen 9175687c71dSFlorian Walpen SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 9185687c71dSFlorian Walpen SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 9195687c71dSFlorian Walpen "clock_list", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 9205687c71dSFlorian Walpen sc, 0, hdsp_sysctl_clock_list, "A", 9215687c71dSFlorian Walpen "List of supported clock sources"); 9225687c71dSFlorian Walpen 9235687c71dSFlorian Walpen SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 9245687c71dSFlorian Walpen SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 9255687c71dSFlorian Walpen "period", CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, 9265687c71dSFlorian Walpen sc, 0, hdsp_sysctl_period, "A", 9275687c71dSFlorian Walpen "Force period of samples per interrupt (32, 64, ... 4096)"); 9285687c71dSFlorian Walpen 9295687c71dSFlorian Walpen SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 9305687c71dSFlorian Walpen SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 9315687c71dSFlorian Walpen "sample_rate", CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, 9325687c71dSFlorian Walpen sc, 0, hdsp_sysctl_sample_rate, "A", 9335687c71dSFlorian Walpen "Force sample rate (32000, 44100, 48000, ... 192000)"); 9345687c71dSFlorian Walpen 93514457cf7SFlorian Walpen if (sc->type == HDSP_9632) { 93614457cf7SFlorian Walpen SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 93714457cf7SFlorian Walpen SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 93814457cf7SFlorian Walpen "phones_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 93914457cf7SFlorian Walpen sc, 0, hdsp_sysctl_phones_level, "A", 94014457cf7SFlorian Walpen "Phones output level ('0dB', '-6dB', '-12dB')"); 94114457cf7SFlorian Walpen 94214457cf7SFlorian Walpen SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 94314457cf7SFlorian Walpen SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 94414457cf7SFlorian Walpen "output_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 94514457cf7SFlorian Walpen sc, 0, hdsp_sysctl_output_level, "A", 94614457cf7SFlorian Walpen "Analog output level ('HighGain', '+4dBU', '-10dBV')"); 94714457cf7SFlorian Walpen 94814457cf7SFlorian Walpen SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 94914457cf7SFlorian Walpen SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 95014457cf7SFlorian Walpen "input_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 95114457cf7SFlorian Walpen sc, 0, hdsp_sysctl_input_level, "A", 95214457cf7SFlorian Walpen "Analog input level ('LowGain', '+4dBU', '-10dBV')"); 95314457cf7SFlorian Walpen } 95414457cf7SFlorian Walpen 95518250ec6SJohn Baldwin bus_attach_children(dev); 95618250ec6SJohn Baldwin return (0); 9575687c71dSFlorian Walpen } 9585687c71dSFlorian Walpen 9595687c71dSFlorian Walpen static void 960a17a41ffSJohn Baldwin hdsp_child_deleted(device_t dev, device_t child) 961a17a41ffSJohn Baldwin { 962a17a41ffSJohn Baldwin free(device_get_ivars(child), M_DEVBUF); 963a17a41ffSJohn Baldwin } 964a17a41ffSJohn Baldwin 965a17a41ffSJohn Baldwin static void 9665687c71dSFlorian Walpen hdsp_dmafree(struct sc_info *sc) 9675687c71dSFlorian Walpen { 9685687c71dSFlorian Walpen 9695687c71dSFlorian Walpen bus_dmamap_unload(sc->dmat, sc->rmap); 9705687c71dSFlorian Walpen bus_dmamap_unload(sc->dmat, sc->pmap); 9715687c71dSFlorian Walpen bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap); 9725687c71dSFlorian Walpen bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap); 9735687c71dSFlorian Walpen sc->rbuf = sc->pbuf = NULL; 9745687c71dSFlorian Walpen } 9755687c71dSFlorian Walpen 9765687c71dSFlorian Walpen static int 9775687c71dSFlorian Walpen hdsp_detach(device_t dev) 9785687c71dSFlorian Walpen { 9795687c71dSFlorian Walpen struct sc_info *sc; 9805687c71dSFlorian Walpen int err; 9815687c71dSFlorian Walpen 9825687c71dSFlorian Walpen sc = device_get_softc(dev); 9835687c71dSFlorian Walpen if (sc == NULL) { 9845687c71dSFlorian Walpen device_printf(dev,"Can't detach: softc is null.\n"); 9855687c71dSFlorian Walpen return (0); 9865687c71dSFlorian Walpen } 9875687c71dSFlorian Walpen 988*3ddaf820SJohn Baldwin err = bus_generic_detach(dev); 9895687c71dSFlorian Walpen if (err) 9905687c71dSFlorian Walpen return (err); 9915687c71dSFlorian Walpen 9925687c71dSFlorian Walpen hdsp_dmafree(sc); 9935687c71dSFlorian Walpen 9945687c71dSFlorian Walpen if (sc->ih) 9955687c71dSFlorian Walpen bus_teardown_intr(dev, sc->irq, sc->ih); 9965687c71dSFlorian Walpen if (sc->dmat) 9975687c71dSFlorian Walpen bus_dma_tag_destroy(sc->dmat); 9985687c71dSFlorian Walpen if (sc->irq) 9995687c71dSFlorian Walpen bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); 10005687c71dSFlorian Walpen if (sc->cs) 10015687c71dSFlorian Walpen bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(0), sc->cs); 10025687c71dSFlorian Walpen if (sc->lock) 10035687c71dSFlorian Walpen snd_mtxfree(sc->lock); 10045687c71dSFlorian Walpen 10055687c71dSFlorian Walpen return (0); 10065687c71dSFlorian Walpen } 10075687c71dSFlorian Walpen 10085687c71dSFlorian Walpen static device_method_t hdsp_methods[] = { 10095687c71dSFlorian Walpen DEVMETHOD(device_probe, hdsp_probe), 10105687c71dSFlorian Walpen DEVMETHOD(device_attach, hdsp_attach), 10115687c71dSFlorian Walpen DEVMETHOD(device_detach, hdsp_detach), 1012a17a41ffSJohn Baldwin DEVMETHOD(bus_child_deleted, hdsp_child_deleted), 10135687c71dSFlorian Walpen { 0, 0 } 10145687c71dSFlorian Walpen }; 10155687c71dSFlorian Walpen 10165687c71dSFlorian Walpen static driver_t hdsp_driver = { 10175687c71dSFlorian Walpen "hdsp", 10185687c71dSFlorian Walpen hdsp_methods, 10195687c71dSFlorian Walpen PCM_SOFTC_SIZE, 10205687c71dSFlorian Walpen }; 10215687c71dSFlorian Walpen 10225687c71dSFlorian Walpen DRIVER_MODULE(snd_hdsp, pci, hdsp_driver, 0, 0); 1023