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" }, 819e7e15b5SRuslan Bukin { HDSPE_CHAN_AIO_EXT, "ext" }, 82d7fde2c9SFlorian Walpen { HDSPE_CHAN_AIO_PHONE, "phone" }, 83d7fde2c9SFlorian Walpen { HDSPE_CHAN_AIO_AES, "aes" }, 84d7fde2c9SFlorian Walpen { HDSPE_CHAN_AIO_SPDIF, "s/pdif" }, 85d7fde2c9SFlorian Walpen { HDSPE_CHAN_AIO_ADAT, "adat" }, 86d7fde2c9SFlorian Walpen { 0, NULL }, 87e4afd792SAlexander Motin }; 88e4afd792SAlexander Motin 89dc15f025SFlorian Walpen static struct hdspe_channel chan_map_aio_uni[] = { 90dc15f025SFlorian Walpen { HDSPE_CHAN_AIO_ALL, "all" }, 91dc15f025SFlorian Walpen { 0, NULL }, 92dc15f025SFlorian Walpen }; 93dc15f025SFlorian Walpen 94e4afd792SAlexander Motin static struct hdspe_channel chan_map_rd[] = { 95d7fde2c9SFlorian Walpen { HDSPE_CHAN_RAY_AES, "aes" }, 96d7fde2c9SFlorian Walpen { HDSPE_CHAN_RAY_SPDIF, "s/pdif" }, 97d7fde2c9SFlorian Walpen { HDSPE_CHAN_RAY_ADAT1, "adat1" }, 98d7fde2c9SFlorian Walpen { HDSPE_CHAN_RAY_ADAT2, "adat2" }, 99d7fde2c9SFlorian Walpen { HDSPE_CHAN_RAY_ADAT3, "adat3" }, 100d7fde2c9SFlorian Walpen { HDSPE_CHAN_RAY_ADAT4, "adat4" }, 101d7fde2c9SFlorian Walpen { 0, NULL }, 102e4afd792SAlexander Motin }; 103e4afd792SAlexander Motin 104dc15f025SFlorian Walpen static struct hdspe_channel chan_map_rd_uni[] = { 105dc15f025SFlorian Walpen { HDSPE_CHAN_RAY_ALL, "all" }, 106dc15f025SFlorian Walpen { 0, NULL }, 107dc15f025SFlorian Walpen }; 108dc15f025SFlorian Walpen 109e4afd792SAlexander Motin static void 110e4afd792SAlexander Motin hdspe_intr(void *p) 111e4afd792SAlexander Motin { 112e4afd792SAlexander Motin struct sc_pcminfo *scp; 11320a9f771SRuslan Bukin struct sc_info *sc; 114e4afd792SAlexander Motin device_t *devlist; 11520a9f771SRuslan Bukin int devcount; 11620a9f771SRuslan Bukin int status; 11720a9f771SRuslan Bukin int err; 11820a9f771SRuslan Bukin int i; 11920a9f771SRuslan Bukin 12020a9f771SRuslan Bukin sc = (struct sc_info *)p; 121e4afd792SAlexander Motin 122e4afd792SAlexander Motin snd_mtxlock(sc->lock); 123e4afd792SAlexander Motin 124e4afd792SAlexander Motin status = hdspe_read_1(sc, HDSPE_STATUS_REG); 125e4afd792SAlexander Motin if (status & HDSPE_AUDIO_IRQ_PENDING) { 126e4afd792SAlexander Motin if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0) 127e4afd792SAlexander Motin return; 128e4afd792SAlexander Motin 129e4afd792SAlexander Motin for (i = 0; i < devcount; i++) { 130e4afd792SAlexander Motin scp = device_get_ivars(devlist[i]); 131e4afd792SAlexander Motin if (scp->ih != NULL) 132e4afd792SAlexander Motin scp->ih(scp); 133e4afd792SAlexander Motin } 134e4afd792SAlexander Motin 135e4afd792SAlexander Motin hdspe_write_1(sc, HDSPE_INTERRUPT_ACK, 0); 136b5db12bfSKevin Lo free(devlist, M_TEMP); 137e4afd792SAlexander Motin } 138e4afd792SAlexander Motin 139e4afd792SAlexander Motin snd_mtxunlock(sc->lock); 140e4afd792SAlexander Motin } 141e4afd792SAlexander Motin 142e4afd792SAlexander Motin static void 143e4afd792SAlexander Motin hdspe_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) 144e4afd792SAlexander Motin { 145e4afd792SAlexander Motin #if 0 146e4afd792SAlexander Motin device_printf(sc->dev, "hdspe_dmapsetmap()\n"); 147e4afd792SAlexander Motin #endif 148e4afd792SAlexander Motin } 149e4afd792SAlexander Motin 150e4afd792SAlexander Motin static int 151e4afd792SAlexander Motin hdspe_alloc_resources(struct sc_info *sc) 152e4afd792SAlexander Motin { 153e4afd792SAlexander Motin 154e4afd792SAlexander Motin /* Allocate resource. */ 155e4afd792SAlexander Motin sc->csid = PCIR_BAR(0); 15643cd6160SJustin Hibbits sc->cs = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, 15743cd6160SJustin Hibbits &sc->csid, RF_ACTIVE); 158e4afd792SAlexander Motin 159e4afd792SAlexander Motin if (!sc->cs) { 160e4afd792SAlexander Motin device_printf(sc->dev, "Unable to map SYS_RES_MEMORY.\n"); 161e4afd792SAlexander Motin return (ENXIO); 162e4afd792SAlexander Motin } 16320a9f771SRuslan Bukin 164e4afd792SAlexander Motin sc->cst = rman_get_bustag(sc->cs); 165e4afd792SAlexander Motin sc->csh = rman_get_bushandle(sc->cs); 166e4afd792SAlexander Motin 167e4afd792SAlexander Motin /* Allocate interrupt resource. */ 168e4afd792SAlexander Motin sc->irqid = 0; 16943cd6160SJustin Hibbits sc->irq = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &sc->irqid, 17043cd6160SJustin Hibbits RF_ACTIVE | RF_SHAREABLE); 171e4afd792SAlexander Motin 172e4afd792SAlexander Motin if (!sc->irq || 173e4afd792SAlexander Motin bus_setup_intr(sc->dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV, 174e4afd792SAlexander Motin NULL, hdspe_intr, sc, &sc->ih)) { 175e4afd792SAlexander Motin device_printf(sc->dev, "Unable to alloc interrupt resource.\n"); 176e4afd792SAlexander Motin return (ENXIO); 177e4afd792SAlexander Motin } 178e4afd792SAlexander Motin 179e4afd792SAlexander Motin /* Allocate DMA resources. */ 180e4afd792SAlexander Motin if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(sc->dev), 181e4afd792SAlexander Motin /*alignment*/4, 182e4afd792SAlexander Motin /*boundary*/0, 183e4afd792SAlexander Motin /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, 184e4afd792SAlexander Motin /*highaddr*/BUS_SPACE_MAXADDR, 185e4afd792SAlexander Motin /*filter*/NULL, 186e4afd792SAlexander Motin /*filterarg*/NULL, 187e4afd792SAlexander Motin /*maxsize*/2 * HDSPE_DMASEGSIZE, 188e4afd792SAlexander Motin /*nsegments*/2, 189e4afd792SAlexander Motin /*maxsegsz*/HDSPE_DMASEGSIZE, 190e4afd792SAlexander Motin /*flags*/0, 1911f7a6325SAlexander Motin /*lockfunc*/NULL, 1921f7a6325SAlexander Motin /*lockarg*/NULL, 193e4afd792SAlexander Motin /*dmatag*/&sc->dmat) != 0) { 194e4afd792SAlexander Motin device_printf(sc->dev, "Unable to create dma tag.\n"); 195e4afd792SAlexander Motin return (ENXIO); 196e4afd792SAlexander Motin } 197e4afd792SAlexander Motin 198e4afd792SAlexander Motin sc->bufsize = HDSPE_DMASEGSIZE; 199e4afd792SAlexander Motin 200e4afd792SAlexander Motin /* pbuf (play buffer). */ 2011f7a6325SAlexander Motin if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf, BUS_DMA_WAITOK, 2021f7a6325SAlexander Motin &sc->pmap)) { 203e4afd792SAlexander Motin device_printf(sc->dev, "Can't alloc pbuf.\n"); 204e4afd792SAlexander Motin return (ENXIO); 205e4afd792SAlexander Motin } 206e4afd792SAlexander Motin 207e4afd792SAlexander Motin if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->bufsize, 2081f7a6325SAlexander Motin hdspe_dmapsetmap, sc, BUS_DMA_NOWAIT)) { 209e4afd792SAlexander Motin device_printf(sc->dev, "Can't load pbuf.\n"); 210e4afd792SAlexander Motin return (ENXIO); 211e4afd792SAlexander Motin } 212e4afd792SAlexander Motin 213e4afd792SAlexander Motin /* rbuf (rec buffer). */ 2141f7a6325SAlexander Motin if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf, BUS_DMA_WAITOK, 2151f7a6325SAlexander Motin &sc->rmap)) { 216e4afd792SAlexander Motin device_printf(sc->dev, "Can't alloc rbuf.\n"); 217e4afd792SAlexander Motin return (ENXIO); 218e4afd792SAlexander Motin } 219e4afd792SAlexander Motin 220e4afd792SAlexander Motin if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->bufsize, 2211f7a6325SAlexander Motin hdspe_dmapsetmap, sc, BUS_DMA_NOWAIT)) { 222e4afd792SAlexander Motin device_printf(sc->dev, "Can't load rbuf.\n"); 223e4afd792SAlexander Motin return (ENXIO); 224e4afd792SAlexander Motin } 225e4afd792SAlexander Motin 226e4afd792SAlexander Motin bzero(sc->pbuf, sc->bufsize); 227e4afd792SAlexander Motin bzero(sc->rbuf, sc->bufsize); 228e4afd792SAlexander Motin 229e4afd792SAlexander Motin return (0); 230e4afd792SAlexander Motin } 231e4afd792SAlexander Motin 232e4afd792SAlexander Motin static void 233e4afd792SAlexander Motin hdspe_map_dmabuf(struct sc_info *sc) 234e4afd792SAlexander Motin { 235e4afd792SAlexander Motin uint32_t paddr, raddr; 236e4afd792SAlexander Motin int i; 237e4afd792SAlexander Motin 238e4afd792SAlexander Motin paddr = vtophys(sc->pbuf); 239e4afd792SAlexander Motin raddr = vtophys(sc->rbuf); 240e4afd792SAlexander Motin 241e4afd792SAlexander Motin for (i = 0; i < HDSPE_MAX_SLOTS * 16; i++) { 242e4afd792SAlexander Motin hdspe_write_4(sc, HDSPE_PAGE_ADDR_BUF_OUT + 4 * i, 243e4afd792SAlexander Motin paddr + i * 4096); 244e4afd792SAlexander Motin hdspe_write_4(sc, HDSPE_PAGE_ADDR_BUF_IN + 4 * i, 245e4afd792SAlexander Motin raddr + i * 4096); 246e4afd792SAlexander Motin } 247e4afd792SAlexander Motin } 248e4afd792SAlexander Motin 249*49a7f2b3SFlorian Walpen static const char * 250*49a7f2b3SFlorian Walpen hdspe_settings_input_level(uint32_t settings) 251*49a7f2b3SFlorian Walpen { 252*49a7f2b3SFlorian Walpen switch (settings & HDSPE_INPUT_LEVEL_MASK) { 253*49a7f2b3SFlorian Walpen case HDSPE_INPUT_LEVEL_LOWGAIN: 254*49a7f2b3SFlorian Walpen return ("LowGain"); 255*49a7f2b3SFlorian Walpen case HDSPE_INPUT_LEVEL_PLUS4DBU: 256*49a7f2b3SFlorian Walpen return ("+4dBu"); 257*49a7f2b3SFlorian Walpen case HDSPE_INPUT_LEVEL_MINUS10DBV: 258*49a7f2b3SFlorian Walpen return ("-10dBV"); 259*49a7f2b3SFlorian Walpen default: 260*49a7f2b3SFlorian Walpen return (NULL); 261*49a7f2b3SFlorian Walpen } 262*49a7f2b3SFlorian Walpen } 263*49a7f2b3SFlorian Walpen 264*49a7f2b3SFlorian Walpen static int 265*49a7f2b3SFlorian Walpen hdspe_sysctl_input_level(SYSCTL_HANDLER_ARGS) 266*49a7f2b3SFlorian Walpen { 267*49a7f2b3SFlorian Walpen struct sc_info *sc; 268*49a7f2b3SFlorian Walpen const char *label; 269*49a7f2b3SFlorian Walpen char buf[16] = "invalid"; 270*49a7f2b3SFlorian Walpen int error; 271*49a7f2b3SFlorian Walpen uint32_t settings; 272*49a7f2b3SFlorian Walpen 273*49a7f2b3SFlorian Walpen sc = oidp->oid_arg1; 274*49a7f2b3SFlorian Walpen 275*49a7f2b3SFlorian Walpen /* Only available on HDSPE AIO. */ 276*49a7f2b3SFlorian Walpen if (sc->type != HDSPE_AIO) 277*49a7f2b3SFlorian Walpen return (ENXIO); 278*49a7f2b3SFlorian Walpen 279*49a7f2b3SFlorian Walpen /* Extract current input level from settings register. */ 280*49a7f2b3SFlorian Walpen settings = sc->settings_register & HDSPE_INPUT_LEVEL_MASK; 281*49a7f2b3SFlorian Walpen label = hdspe_settings_input_level(settings); 282*49a7f2b3SFlorian Walpen if (label != NULL) 283*49a7f2b3SFlorian Walpen strlcpy(buf, label, sizeof(buf)); 284*49a7f2b3SFlorian Walpen 285*49a7f2b3SFlorian Walpen /* Process sysctl string request. */ 286*49a7f2b3SFlorian Walpen error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 287*49a7f2b3SFlorian Walpen if (error != 0 || req->newptr == NULL) 288*49a7f2b3SFlorian Walpen return (error); 289*49a7f2b3SFlorian Walpen 290*49a7f2b3SFlorian Walpen /* Find input level matching the sysctl string. */ 291*49a7f2b3SFlorian Walpen label = hdspe_settings_input_level(HDSPE_INPUT_LEVEL_LOWGAIN); 292*49a7f2b3SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 293*49a7f2b3SFlorian Walpen settings = HDSPE_INPUT_LEVEL_LOWGAIN; 294*49a7f2b3SFlorian Walpen label = hdspe_settings_input_level(HDSPE_INPUT_LEVEL_PLUS4DBU); 295*49a7f2b3SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 296*49a7f2b3SFlorian Walpen settings = HDSPE_INPUT_LEVEL_PLUS4DBU; 297*49a7f2b3SFlorian Walpen label = hdspe_settings_input_level(HDSPE_INPUT_LEVEL_MINUS10DBV); 298*49a7f2b3SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 299*49a7f2b3SFlorian Walpen settings = HDSPE_INPUT_LEVEL_MINUS10DBV; 300*49a7f2b3SFlorian Walpen 301*49a7f2b3SFlorian Walpen /* Set input level in settings register. */ 302*49a7f2b3SFlorian Walpen settings &= HDSPE_INPUT_LEVEL_MASK; 303*49a7f2b3SFlorian Walpen if (settings != (sc->settings_register & HDSPE_INPUT_LEVEL_MASK)) { 304*49a7f2b3SFlorian Walpen snd_mtxlock(sc->lock); 305*49a7f2b3SFlorian Walpen sc->settings_register &= ~HDSPE_INPUT_LEVEL_MASK; 306*49a7f2b3SFlorian Walpen sc->settings_register |= settings; 307*49a7f2b3SFlorian Walpen hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register); 308*49a7f2b3SFlorian Walpen snd_mtxunlock(sc->lock); 309*49a7f2b3SFlorian Walpen } 310*49a7f2b3SFlorian Walpen return (0); 311*49a7f2b3SFlorian Walpen } 312*49a7f2b3SFlorian Walpen 313*49a7f2b3SFlorian Walpen static const char * 314*49a7f2b3SFlorian Walpen hdspe_settings_output_level(uint32_t settings) 315*49a7f2b3SFlorian Walpen { 316*49a7f2b3SFlorian Walpen switch (settings & HDSPE_OUTPUT_LEVEL_MASK) { 317*49a7f2b3SFlorian Walpen case HDSPE_OUTPUT_LEVEL_HIGHGAIN: 318*49a7f2b3SFlorian Walpen return ("HighGain"); 319*49a7f2b3SFlorian Walpen case HDSPE_OUTPUT_LEVEL_PLUS4DBU: 320*49a7f2b3SFlorian Walpen return ("+4dBu"); 321*49a7f2b3SFlorian Walpen case HDSPE_OUTPUT_LEVEL_MINUS10DBV: 322*49a7f2b3SFlorian Walpen return ("-10dBV"); 323*49a7f2b3SFlorian Walpen default: 324*49a7f2b3SFlorian Walpen return (NULL); 325*49a7f2b3SFlorian Walpen } 326*49a7f2b3SFlorian Walpen } 327*49a7f2b3SFlorian Walpen 328*49a7f2b3SFlorian Walpen static int 329*49a7f2b3SFlorian Walpen hdspe_sysctl_output_level(SYSCTL_HANDLER_ARGS) 330*49a7f2b3SFlorian Walpen { 331*49a7f2b3SFlorian Walpen struct sc_info *sc; 332*49a7f2b3SFlorian Walpen const char *label; 333*49a7f2b3SFlorian Walpen char buf[16] = "invalid"; 334*49a7f2b3SFlorian Walpen int error; 335*49a7f2b3SFlorian Walpen uint32_t settings; 336*49a7f2b3SFlorian Walpen 337*49a7f2b3SFlorian Walpen sc = oidp->oid_arg1; 338*49a7f2b3SFlorian Walpen 339*49a7f2b3SFlorian Walpen /* Only available on HDSPE AIO. */ 340*49a7f2b3SFlorian Walpen if (sc->type != HDSPE_AIO) 341*49a7f2b3SFlorian Walpen return (ENXIO); 342*49a7f2b3SFlorian Walpen 343*49a7f2b3SFlorian Walpen /* Extract current output level from settings register. */ 344*49a7f2b3SFlorian Walpen settings = sc->settings_register & HDSPE_OUTPUT_LEVEL_MASK; 345*49a7f2b3SFlorian Walpen label = hdspe_settings_output_level(settings); 346*49a7f2b3SFlorian Walpen if (label != NULL) 347*49a7f2b3SFlorian Walpen strlcpy(buf, label, sizeof(buf)); 348*49a7f2b3SFlorian Walpen 349*49a7f2b3SFlorian Walpen /* Process sysctl string request. */ 350*49a7f2b3SFlorian Walpen error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 351*49a7f2b3SFlorian Walpen if (error != 0 || req->newptr == NULL) 352*49a7f2b3SFlorian Walpen return (error); 353*49a7f2b3SFlorian Walpen 354*49a7f2b3SFlorian Walpen /* Find output level matching the sysctl string. */ 355*49a7f2b3SFlorian Walpen label = hdspe_settings_output_level(HDSPE_OUTPUT_LEVEL_HIGHGAIN); 356*49a7f2b3SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 357*49a7f2b3SFlorian Walpen settings = HDSPE_OUTPUT_LEVEL_HIGHGAIN; 358*49a7f2b3SFlorian Walpen label = hdspe_settings_output_level(HDSPE_OUTPUT_LEVEL_PLUS4DBU); 359*49a7f2b3SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 360*49a7f2b3SFlorian Walpen settings = HDSPE_OUTPUT_LEVEL_PLUS4DBU; 361*49a7f2b3SFlorian Walpen label = hdspe_settings_output_level(HDSPE_OUTPUT_LEVEL_MINUS10DBV); 362*49a7f2b3SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 363*49a7f2b3SFlorian Walpen settings = HDSPE_OUTPUT_LEVEL_MINUS10DBV; 364*49a7f2b3SFlorian Walpen 365*49a7f2b3SFlorian Walpen /* Set output level in settings register. */ 366*49a7f2b3SFlorian Walpen settings &= HDSPE_OUTPUT_LEVEL_MASK; 367*49a7f2b3SFlorian Walpen if (settings != (sc->settings_register & HDSPE_OUTPUT_LEVEL_MASK)) { 368*49a7f2b3SFlorian Walpen snd_mtxlock(sc->lock); 369*49a7f2b3SFlorian Walpen sc->settings_register &= ~HDSPE_OUTPUT_LEVEL_MASK; 370*49a7f2b3SFlorian Walpen sc->settings_register |= settings; 371*49a7f2b3SFlorian Walpen hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register); 372*49a7f2b3SFlorian Walpen snd_mtxunlock(sc->lock); 373*49a7f2b3SFlorian Walpen } 374*49a7f2b3SFlorian Walpen return (0); 375*49a7f2b3SFlorian Walpen } 376*49a7f2b3SFlorian Walpen 377*49a7f2b3SFlorian Walpen static const char * 378*49a7f2b3SFlorian Walpen hdspe_settings_phones_level(uint32_t settings) 379*49a7f2b3SFlorian Walpen { 380*49a7f2b3SFlorian Walpen switch (settings & HDSPE_PHONES_LEVEL_MASK) { 381*49a7f2b3SFlorian Walpen case HDSPE_PHONES_LEVEL_HIGHGAIN: 382*49a7f2b3SFlorian Walpen return ("HighGain"); 383*49a7f2b3SFlorian Walpen case HDSPE_PHONES_LEVEL_PLUS4DBU: 384*49a7f2b3SFlorian Walpen return ("+4dBu"); 385*49a7f2b3SFlorian Walpen case HDSPE_PHONES_LEVEL_MINUS10DBV: 386*49a7f2b3SFlorian Walpen return ("-10dBV"); 387*49a7f2b3SFlorian Walpen default: 388*49a7f2b3SFlorian Walpen return (NULL); 389*49a7f2b3SFlorian Walpen } 390*49a7f2b3SFlorian Walpen } 391*49a7f2b3SFlorian Walpen 392*49a7f2b3SFlorian Walpen static int 393*49a7f2b3SFlorian Walpen hdspe_sysctl_phones_level(SYSCTL_HANDLER_ARGS) 394*49a7f2b3SFlorian Walpen { 395*49a7f2b3SFlorian Walpen struct sc_info *sc; 396*49a7f2b3SFlorian Walpen const char *label; 397*49a7f2b3SFlorian Walpen char buf[16] = "invalid"; 398*49a7f2b3SFlorian Walpen int error; 399*49a7f2b3SFlorian Walpen uint32_t settings; 400*49a7f2b3SFlorian Walpen 401*49a7f2b3SFlorian Walpen sc = oidp->oid_arg1; 402*49a7f2b3SFlorian Walpen 403*49a7f2b3SFlorian Walpen /* Only available on HDSPE AIO. */ 404*49a7f2b3SFlorian Walpen if (sc->type != HDSPE_AIO) 405*49a7f2b3SFlorian Walpen return (ENXIO); 406*49a7f2b3SFlorian Walpen 407*49a7f2b3SFlorian Walpen /* Extract current phones level from settings register. */ 408*49a7f2b3SFlorian Walpen settings = sc->settings_register & HDSPE_PHONES_LEVEL_MASK; 409*49a7f2b3SFlorian Walpen label = hdspe_settings_phones_level(settings); 410*49a7f2b3SFlorian Walpen if (label != NULL) 411*49a7f2b3SFlorian Walpen strlcpy(buf, label, sizeof(buf)); 412*49a7f2b3SFlorian Walpen 413*49a7f2b3SFlorian Walpen /* Process sysctl string request. */ 414*49a7f2b3SFlorian Walpen error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 415*49a7f2b3SFlorian Walpen if (error != 0 || req->newptr == NULL) 416*49a7f2b3SFlorian Walpen return (error); 417*49a7f2b3SFlorian Walpen 418*49a7f2b3SFlorian Walpen /* Find phones level matching the sysctl string. */ 419*49a7f2b3SFlorian Walpen label = hdspe_settings_phones_level(HDSPE_PHONES_LEVEL_HIGHGAIN); 420*49a7f2b3SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 421*49a7f2b3SFlorian Walpen settings = HDSPE_PHONES_LEVEL_HIGHGAIN; 422*49a7f2b3SFlorian Walpen label = hdspe_settings_phones_level(HDSPE_PHONES_LEVEL_PLUS4DBU); 423*49a7f2b3SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 424*49a7f2b3SFlorian Walpen settings = HDSPE_PHONES_LEVEL_PLUS4DBU; 425*49a7f2b3SFlorian Walpen label = hdspe_settings_phones_level(HDSPE_PHONES_LEVEL_MINUS10DBV); 426*49a7f2b3SFlorian Walpen if (strncasecmp(buf, label, sizeof(buf)) == 0) 427*49a7f2b3SFlorian Walpen settings = HDSPE_PHONES_LEVEL_MINUS10DBV; 428*49a7f2b3SFlorian Walpen 429*49a7f2b3SFlorian Walpen /* Set phones level in settings register. */ 430*49a7f2b3SFlorian Walpen settings &= HDSPE_PHONES_LEVEL_MASK; 431*49a7f2b3SFlorian Walpen if (settings != (sc->settings_register & HDSPE_PHONES_LEVEL_MASK)) { 432*49a7f2b3SFlorian Walpen snd_mtxlock(sc->lock); 433*49a7f2b3SFlorian Walpen sc->settings_register &= ~HDSPE_PHONES_LEVEL_MASK; 434*49a7f2b3SFlorian Walpen sc->settings_register |= settings; 435*49a7f2b3SFlorian Walpen hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register); 436*49a7f2b3SFlorian Walpen snd_mtxunlock(sc->lock); 437*49a7f2b3SFlorian Walpen } 438*49a7f2b3SFlorian Walpen return (0); 439*49a7f2b3SFlorian Walpen } 440*49a7f2b3SFlorian Walpen 441e4afd792SAlexander Motin static int 4426c892b79SFlorian Walpen hdspe_sysctl_sample_rate(SYSCTL_HANDLER_ARGS) 4436c892b79SFlorian Walpen { 4446c892b79SFlorian Walpen struct sc_info *sc = oidp->oid_arg1; 4456c892b79SFlorian Walpen int error; 4466c892b79SFlorian Walpen unsigned int speed, multiplier; 4476c892b79SFlorian Walpen 4486c892b79SFlorian Walpen speed = sc->force_speed; 4496c892b79SFlorian Walpen 4506c892b79SFlorian Walpen /* Process sysctl (unsigned) integer request. */ 4516c892b79SFlorian Walpen error = sysctl_handle_int(oidp, &speed, 0, req); 4526c892b79SFlorian Walpen if (error != 0 || req->newptr == NULL) 4536c892b79SFlorian Walpen return (error); 4546c892b79SFlorian Walpen 4556c892b79SFlorian Walpen /* Speed from 32000 to 192000, 0 falls back to pcm speed setting. */ 4566c892b79SFlorian Walpen sc->force_speed = 0; 4576c892b79SFlorian Walpen if (speed > 0) { 4586c892b79SFlorian Walpen multiplier = 1; 4596c892b79SFlorian Walpen if (speed > (96000 + 128000) / 2) 4606c892b79SFlorian Walpen multiplier = 4; 4616c892b79SFlorian Walpen else if (speed > (48000 + 64000) / 2) 4626c892b79SFlorian Walpen multiplier = 2; 4636c892b79SFlorian Walpen 4646c892b79SFlorian Walpen if (speed < ((32000 + 44100) / 2) * multiplier) 4656c892b79SFlorian Walpen sc->force_speed = 32000 * multiplier; 4666c892b79SFlorian Walpen else if (speed < ((44100 + 48000) / 2) * multiplier) 4676c892b79SFlorian Walpen sc->force_speed = 44100 * multiplier; 4686c892b79SFlorian Walpen else 4696c892b79SFlorian Walpen sc->force_speed = 48000 * multiplier; 4706c892b79SFlorian Walpen } 4716c892b79SFlorian Walpen 4726c892b79SFlorian Walpen return (0); 4736c892b79SFlorian Walpen } 4746c892b79SFlorian Walpen 4756c892b79SFlorian Walpen 4766c892b79SFlorian Walpen static int 477fb877263SFlorian Walpen hdspe_sysctl_period(SYSCTL_HANDLER_ARGS) 478fb877263SFlorian Walpen { 479fb877263SFlorian Walpen struct sc_info *sc = oidp->oid_arg1; 480fb877263SFlorian Walpen int error; 481fb877263SFlorian Walpen unsigned int period; 482fb877263SFlorian Walpen 483fb877263SFlorian Walpen period = sc->force_period; 484fb877263SFlorian Walpen 485fb877263SFlorian Walpen /* Process sysctl (unsigned) integer request. */ 486fb877263SFlorian Walpen error = sysctl_handle_int(oidp, &period, 0, req); 487fb877263SFlorian Walpen if (error != 0 || req->newptr == NULL) 488fb877263SFlorian Walpen return (error); 489fb877263SFlorian Walpen 490fb877263SFlorian Walpen /* Period is from 2^5 to 2^14, 0 falls back to pcm latency settings. */ 491fb877263SFlorian Walpen sc->force_period = 0; 492fb877263SFlorian Walpen if (period > 0) { 493fb877263SFlorian Walpen sc->force_period = 32; 494fb877263SFlorian Walpen while (sc->force_period < period && sc->force_period < 4096) 495fb877263SFlorian Walpen sc->force_period <<= 1; 496fb877263SFlorian Walpen } 497fb877263SFlorian Walpen 498fb877263SFlorian Walpen return (0); 499fb877263SFlorian Walpen } 500fb877263SFlorian Walpen 501fb877263SFlorian Walpen static int 502b6052c10SRuslan Bukin hdspe_sysctl_clock_preference(SYSCTL_HANDLER_ARGS) 503b6052c10SRuslan Bukin { 504b6052c10SRuslan Bukin struct sc_info *sc; 505b6052c10SRuslan Bukin struct hdspe_clock_source *clock_table, *clock; 506b6052c10SRuslan Bukin char buf[16] = "invalid"; 507b6052c10SRuslan Bukin int error; 508b6052c10SRuslan Bukin uint32_t setting; 509b6052c10SRuslan Bukin 510b6052c10SRuslan Bukin sc = oidp->oid_arg1; 511b6052c10SRuslan Bukin 512b6052c10SRuslan Bukin /* Select sync ports table for device type. */ 513b6052c10SRuslan Bukin if (sc->type == HDSPE_AIO) 514b6052c10SRuslan Bukin clock_table = hdspe_clock_source_table_aio; 515b6052c10SRuslan Bukin else if (sc->type == HDSPE_RAYDAT) 516b6052c10SRuslan Bukin clock_table = hdspe_clock_source_table_rd; 517b6052c10SRuslan Bukin else 518b6052c10SRuslan Bukin return (ENXIO); 519b6052c10SRuslan Bukin 520b6052c10SRuslan Bukin /* Extract preferred clock source from settings register. */ 521b6052c10SRuslan Bukin setting = sc->settings_register & HDSPE_SETTING_CLOCK_MASK; 522b6052c10SRuslan Bukin for (clock = clock_table; clock->name != NULL; ++clock) { 523b6052c10SRuslan Bukin if (clock->setting == setting) 524b6052c10SRuslan Bukin break; 525b6052c10SRuslan Bukin } 526b6052c10SRuslan Bukin if (clock->name != NULL) 527b6052c10SRuslan Bukin strlcpy(buf, clock->name, sizeof(buf)); 528b6052c10SRuslan Bukin 529b6052c10SRuslan Bukin /* Process sysctl string request. */ 530b6052c10SRuslan Bukin error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 531b6052c10SRuslan Bukin if (error != 0 || req->newptr == NULL) 532b6052c10SRuslan Bukin return (error); 533b6052c10SRuslan Bukin 534b6052c10SRuslan Bukin /* Find clock source matching the sysctl string. */ 535b6052c10SRuslan Bukin for (clock = clock_table; clock->name != NULL; ++clock) { 536b6052c10SRuslan Bukin if (strncasecmp(buf, clock->name, sizeof(buf)) == 0) 537b6052c10SRuslan Bukin break; 538b6052c10SRuslan Bukin } 539b6052c10SRuslan Bukin 540b6052c10SRuslan Bukin /* Set preferred clock source in settings register. */ 541b6052c10SRuslan Bukin if (clock->name != NULL) { 542b6052c10SRuslan Bukin setting = clock->setting & HDSPE_SETTING_CLOCK_MASK; 543b6052c10SRuslan Bukin snd_mtxlock(sc->lock); 544b6052c10SRuslan Bukin sc->settings_register &= ~HDSPE_SETTING_CLOCK_MASK; 545b6052c10SRuslan Bukin sc->settings_register |= setting; 546b6052c10SRuslan Bukin hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register); 547b6052c10SRuslan Bukin snd_mtxunlock(sc->lock); 548b6052c10SRuslan Bukin } 549b6052c10SRuslan Bukin return (0); 550b6052c10SRuslan Bukin } 551b6052c10SRuslan Bukin 552b6052c10SRuslan Bukin static int 553b6052c10SRuslan Bukin hdspe_sysctl_clock_source(SYSCTL_HANDLER_ARGS) 554b6052c10SRuslan Bukin { 555b6052c10SRuslan Bukin struct sc_info *sc; 556b6052c10SRuslan Bukin struct hdspe_clock_source *clock_table, *clock; 557b6052c10SRuslan Bukin char buf[16] = "invalid"; 558b6052c10SRuslan Bukin uint32_t status; 559b6052c10SRuslan Bukin 560b6052c10SRuslan Bukin sc = oidp->oid_arg1; 561b6052c10SRuslan Bukin 562b6052c10SRuslan Bukin /* Select sync ports table for device type. */ 563b6052c10SRuslan Bukin if (sc->type == HDSPE_AIO) 564b6052c10SRuslan Bukin clock_table = hdspe_clock_source_table_aio; 565b6052c10SRuslan Bukin else if (sc->type == HDSPE_RAYDAT) 566b6052c10SRuslan Bukin clock_table = hdspe_clock_source_table_rd; 567b6052c10SRuslan Bukin else 568b6052c10SRuslan Bukin return (ENXIO); 569b6052c10SRuslan Bukin 570b6052c10SRuslan Bukin /* Read current (autosync) clock source from status register. */ 571b6052c10SRuslan Bukin snd_mtxlock(sc->lock); 572b6052c10SRuslan Bukin status = hdspe_read_4(sc, HDSPE_STATUS1_REG); 573b6052c10SRuslan Bukin status &= HDSPE_STATUS1_CLOCK_MASK; 574b6052c10SRuslan Bukin snd_mtxunlock(sc->lock); 575b6052c10SRuslan Bukin 576b6052c10SRuslan Bukin /* Translate status register value to clock source. */ 577b6052c10SRuslan Bukin for (clock = clock_table; clock->name != NULL; ++clock) { 578b6052c10SRuslan Bukin /* In clock master mode, override with internal clock source. */ 579b6052c10SRuslan Bukin if (sc->settings_register & HDSPE_SETTING_MASTER) { 580b6052c10SRuslan Bukin if (clock->setting & HDSPE_SETTING_MASTER) 581b6052c10SRuslan Bukin break; 582b6052c10SRuslan Bukin } else if (clock->status == status) 583b6052c10SRuslan Bukin break; 584b6052c10SRuslan Bukin } 585b6052c10SRuslan Bukin 586b6052c10SRuslan Bukin /* Process sysctl string request. */ 587b6052c10SRuslan Bukin if (clock->name != NULL) 588b6052c10SRuslan Bukin strlcpy(buf, clock->name, sizeof(buf)); 589b6052c10SRuslan Bukin return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); 590b6052c10SRuslan Bukin } 591b6052c10SRuslan Bukin 592b6052c10SRuslan Bukin static int 593b6052c10SRuslan Bukin hdspe_sysctl_clock_list(SYSCTL_HANDLER_ARGS) 594b6052c10SRuslan Bukin { 595b6052c10SRuslan Bukin struct sc_info *sc; 596b6052c10SRuslan Bukin struct hdspe_clock_source *clock_table, *clock; 597b6052c10SRuslan Bukin char buf[256]; 598b6052c10SRuslan Bukin int n; 599b6052c10SRuslan Bukin 600b6052c10SRuslan Bukin sc = oidp->oid_arg1; 601b6052c10SRuslan Bukin n = 0; 602b6052c10SRuslan Bukin 603b6052c10SRuslan Bukin /* Select clock source table for device type. */ 604b6052c10SRuslan Bukin if (sc->type == HDSPE_AIO) 605b6052c10SRuslan Bukin clock_table = hdspe_clock_source_table_aio; 606b6052c10SRuslan Bukin else if (sc->type == HDSPE_RAYDAT) 607b6052c10SRuslan Bukin clock_table = hdspe_clock_source_table_rd; 608b6052c10SRuslan Bukin else 609b6052c10SRuslan Bukin return (ENXIO); 610b6052c10SRuslan Bukin 611b6052c10SRuslan Bukin /* List available clock sources. */ 612b6052c10SRuslan Bukin buf[0] = 0; 613b6052c10SRuslan Bukin for (clock = clock_table; clock->name != NULL; ++clock) { 614b6052c10SRuslan Bukin if (n > 0) 615b6052c10SRuslan Bukin n += strlcpy(buf + n, ",", sizeof(buf) - n); 616b6052c10SRuslan Bukin n += strlcpy(buf + n, clock->name, sizeof(buf) - n); 617b6052c10SRuslan Bukin } 618b6052c10SRuslan Bukin return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); 619b6052c10SRuslan Bukin } 620b6052c10SRuslan Bukin 621b6052c10SRuslan Bukin static int 622b6052c10SRuslan Bukin hdspe_sysctl_sync_status(SYSCTL_HANDLER_ARGS) 623b6052c10SRuslan Bukin { 624b6052c10SRuslan Bukin struct sc_info *sc; 625b6052c10SRuslan Bukin struct hdspe_clock_source *clock_table, *clock; 626b6052c10SRuslan Bukin char buf[256]; 627b6052c10SRuslan Bukin char *state; 628b6052c10SRuslan Bukin int n; 629b6052c10SRuslan Bukin uint32_t status; 630b6052c10SRuslan Bukin 631b6052c10SRuslan Bukin sc = oidp->oid_arg1; 632b6052c10SRuslan Bukin n = 0; 633b6052c10SRuslan Bukin 634b6052c10SRuslan Bukin /* Select sync ports table for device type. */ 635b6052c10SRuslan Bukin if (sc->type == HDSPE_AIO) 636b6052c10SRuslan Bukin clock_table = hdspe_clock_source_table_aio; 637b6052c10SRuslan Bukin else if (sc->type == HDSPE_RAYDAT) 638b6052c10SRuslan Bukin clock_table = hdspe_clock_source_table_rd; 639b6052c10SRuslan Bukin else 640b6052c10SRuslan Bukin return (ENXIO); 641b6052c10SRuslan Bukin 642b6052c10SRuslan Bukin /* Read current lock and sync bits from status register. */ 643b6052c10SRuslan Bukin snd_mtxlock(sc->lock); 644b6052c10SRuslan Bukin status = hdspe_read_4(sc, HDSPE_STATUS1_REG); 645b6052c10SRuslan Bukin snd_mtxunlock(sc->lock); 646b6052c10SRuslan Bukin 647b6052c10SRuslan Bukin /* List clock sources with lock and sync state. */ 648b6052c10SRuslan Bukin for (clock = clock_table; clock->name != NULL; ++clock) { 649b6052c10SRuslan Bukin if (clock->sync_bit != 0) { 650b6052c10SRuslan Bukin if (n > 0) 651b6052c10SRuslan Bukin n += strlcpy(buf + n, ",", sizeof(buf) - n); 652b6052c10SRuslan Bukin state = "none"; 653b6052c10SRuslan Bukin if ((clock->sync_bit & status) != 0) 654b6052c10SRuslan Bukin state = "sync"; 655b6052c10SRuslan Bukin else if ((clock->lock_bit & status) != 0) 656b6052c10SRuslan Bukin state = "lock"; 657b6052c10SRuslan Bukin n += snprintf(buf + n, sizeof(buf) - n, "%s(%s)", 658b6052c10SRuslan Bukin clock->name, state); 659b6052c10SRuslan Bukin } 660b6052c10SRuslan Bukin } 661b6052c10SRuslan Bukin return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); 662b6052c10SRuslan Bukin } 663b6052c10SRuslan Bukin 664b6052c10SRuslan Bukin static int 665e4afd792SAlexander Motin hdspe_probe(device_t dev) 666e4afd792SAlexander Motin { 667e4afd792SAlexander Motin uint32_t rev; 668e4afd792SAlexander Motin 6699718d4abSFlorian Walpen if ((pci_get_vendor(dev) == PCI_VENDOR_XILINX || 6709718d4abSFlorian Walpen pci_get_vendor(dev) == PCI_VENDOR_RME) && 671e4afd792SAlexander Motin pci_get_device(dev) == PCI_DEVICE_XILINX_HDSPE) { 672e4afd792SAlexander Motin rev = pci_get_revid(dev); 673e4afd792SAlexander Motin switch (rev) { 674e4afd792SAlexander Motin case PCI_REVISION_AIO: 675e4afd792SAlexander Motin device_set_desc(dev, "RME HDSPe AIO"); 67620a9f771SRuslan Bukin return (0); 677e4afd792SAlexander Motin case PCI_REVISION_RAYDAT: 678e4afd792SAlexander Motin device_set_desc(dev, "RME HDSPe RayDAT"); 67920a9f771SRuslan Bukin return (0); 680e4afd792SAlexander Motin } 681e4afd792SAlexander Motin } 682e4afd792SAlexander Motin 683e4afd792SAlexander Motin return (ENXIO); 684e4afd792SAlexander Motin } 685e4afd792SAlexander Motin 686e4afd792SAlexander Motin static int 687e4afd792SAlexander Motin hdspe_init(struct sc_info *sc) 688e4afd792SAlexander Motin { 689e4afd792SAlexander Motin long long period; 690e4afd792SAlexander Motin 691e4afd792SAlexander Motin /* Set latency. */ 692e4afd792SAlexander Motin sc->period = 32; 693fb877263SFlorian Walpen /* 694fb877263SFlorian Walpen * The pcm channel latency settings propagate unreliable blocksizes, 695fb877263SFlorian Walpen * different for recording and playback, and skewed due to rounding 696fb877263SFlorian Walpen * and total buffer size limits. 697fb877263SFlorian Walpen * Force period to a consistent default until these issues are fixed. 698fb877263SFlorian Walpen */ 699fb877263SFlorian Walpen sc->force_period = 256; 700e4afd792SAlexander Motin sc->ctrl_register = hdspe_encode_latency(7); 701e4afd792SAlexander Motin 702e4afd792SAlexander Motin /* Set rate. */ 703e4afd792SAlexander Motin sc->speed = HDSPE_SPEED_DEFAULT; 7046c892b79SFlorian Walpen sc->force_speed = 0; 705e4afd792SAlexander Motin sc->ctrl_register &= ~HDSPE_FREQ_MASK; 706e4afd792SAlexander Motin sc->ctrl_register |= HDSPE_FREQ_MASK_DEFAULT; 707e4afd792SAlexander Motin hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register); 708e4afd792SAlexander Motin 709e4afd792SAlexander Motin switch (sc->type) { 710b6052c10SRuslan Bukin case HDSPE_RAYDAT: 711b6052c10SRuslan Bukin case HDSPE_AIO: 712e4afd792SAlexander Motin period = HDSPE_FREQ_AIO; 713e4afd792SAlexander Motin break; 714e4afd792SAlexander Motin default: 715e4afd792SAlexander Motin return (ENXIO); 716e4afd792SAlexander Motin } 717e4afd792SAlexander Motin 718e4afd792SAlexander Motin /* Set DDS value. */ 719e4afd792SAlexander Motin period /= sc->speed; 720e4afd792SAlexander Motin hdspe_write_4(sc, HDSPE_FREQ_REG, period); 721e4afd792SAlexander Motin 722e4afd792SAlexander Motin /* Other settings. */ 723e4afd792SAlexander Motin sc->settings_register = 0; 724*49a7f2b3SFlorian Walpen 725*49a7f2b3SFlorian Walpen /* Default gain levels. */ 726*49a7f2b3SFlorian Walpen sc->settings_register &= ~HDSPE_INPUT_LEVEL_MASK; 727*49a7f2b3SFlorian Walpen sc->settings_register |= HDSPE_INPUT_LEVEL_LOWGAIN; 728*49a7f2b3SFlorian Walpen sc->settings_register &= ~HDSPE_OUTPUT_LEVEL_MASK; 729*49a7f2b3SFlorian Walpen sc->settings_register |= HDSPE_OUTPUT_LEVEL_MINUS10DBV; 730*49a7f2b3SFlorian Walpen sc->settings_register &= ~HDSPE_PHONES_LEVEL_MASK; 731*49a7f2b3SFlorian Walpen sc->settings_register |= HDSPE_PHONES_LEVEL_MINUS10DBV; 732*49a7f2b3SFlorian Walpen 733e4afd792SAlexander Motin hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register); 734e4afd792SAlexander Motin 73520a9f771SRuslan Bukin return (0); 736e4afd792SAlexander Motin } 737e4afd792SAlexander Motin 738e4afd792SAlexander Motin static int 739e4afd792SAlexander Motin hdspe_attach(device_t dev) 740e4afd792SAlexander Motin { 741e4afd792SAlexander Motin struct hdspe_channel *chan_map; 74220a9f771SRuslan Bukin struct sc_pcminfo *scp; 74320a9f771SRuslan Bukin struct sc_info *sc; 744e4afd792SAlexander Motin uint32_t rev; 745e4afd792SAlexander Motin int i, err; 746e4afd792SAlexander Motin 747e4afd792SAlexander Motin #if 0 748e4afd792SAlexander Motin device_printf(dev, "hdspe_attach()\n"); 749e4afd792SAlexander Motin #endif 750e4afd792SAlexander Motin 751e4afd792SAlexander Motin sc = device_get_softc(dev); 752e4afd792SAlexander Motin sc->lock = snd_mtxcreate(device_get_nameunit(dev), 753e4afd792SAlexander Motin "snd_hdspe softc"); 754e4afd792SAlexander Motin sc->dev = dev; 755e4afd792SAlexander Motin 756c68534f1SScott Long pci_enable_busmaster(dev); 757e4afd792SAlexander Motin rev = pci_get_revid(dev); 758e4afd792SAlexander Motin switch (rev) { 759e4afd792SAlexander Motin case PCI_REVISION_AIO: 760b6052c10SRuslan Bukin sc->type = HDSPE_AIO; 761dc15f025SFlorian Walpen chan_map = hdspe_unified_pcm ? chan_map_aio_uni : chan_map_aio; 762e4afd792SAlexander Motin break; 763e4afd792SAlexander Motin case PCI_REVISION_RAYDAT: 764b6052c10SRuslan Bukin sc->type = HDSPE_RAYDAT; 765dc15f025SFlorian Walpen chan_map = hdspe_unified_pcm ? chan_map_rd_uni : chan_map_rd; 766e4afd792SAlexander Motin break; 767e4afd792SAlexander Motin default: 76820a9f771SRuslan Bukin return (ENXIO); 769e4afd792SAlexander Motin } 770e4afd792SAlexander Motin 771e4afd792SAlexander Motin /* Allocate resources. */ 772e4afd792SAlexander Motin err = hdspe_alloc_resources(sc); 773e4afd792SAlexander Motin if (err) { 774e4afd792SAlexander Motin device_printf(dev, "Unable to allocate system resources.\n"); 77520a9f771SRuslan Bukin return (ENXIO); 776e4afd792SAlexander Motin } 777e4afd792SAlexander Motin 778e4afd792SAlexander Motin if (hdspe_init(sc) != 0) 77920a9f771SRuslan Bukin return (ENXIO); 780e4afd792SAlexander Motin 781e4afd792SAlexander Motin for (i = 0; i < HDSPE_MAX_CHANS && chan_map[i].descr != NULL; i++) { 782a17a41ffSJohn Baldwin scp = malloc(sizeof(struct sc_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO); 783e4afd792SAlexander Motin scp->hc = &chan_map[i]; 784e4afd792SAlexander Motin scp->sc = sc; 7855b56413dSWarner Losh scp->dev = device_add_child(dev, "pcm", DEVICE_UNIT_ANY); 786e4afd792SAlexander Motin device_set_ivars(scp->dev, scp); 787e4afd792SAlexander Motin } 788e4afd792SAlexander Motin 789e4afd792SAlexander Motin hdspe_map_dmabuf(sc); 790e4afd792SAlexander Motin 791b6052c10SRuslan Bukin SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 792b6052c10SRuslan Bukin SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 793b6052c10SRuslan Bukin "sync_status", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 794b6052c10SRuslan Bukin sc, 0, hdspe_sysctl_sync_status, "A", 795b6052c10SRuslan Bukin "List clock source signal lock and sync status"); 796b6052c10SRuslan Bukin 797b6052c10SRuslan Bukin SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 798b6052c10SRuslan Bukin SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 799b6052c10SRuslan Bukin "clock_source", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 800b6052c10SRuslan Bukin sc, 0, hdspe_sysctl_clock_source, "A", 801b6052c10SRuslan Bukin "Currently effective clock source"); 802b6052c10SRuslan Bukin 803b6052c10SRuslan Bukin SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 804b6052c10SRuslan Bukin SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 805b6052c10SRuslan Bukin "clock_preference", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 806b6052c10SRuslan Bukin sc, 0, hdspe_sysctl_clock_preference, "A", 807b6052c10SRuslan Bukin "Set 'internal' (master) or preferred autosync clock source"); 808b6052c10SRuslan Bukin 809b6052c10SRuslan Bukin SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 810b6052c10SRuslan Bukin SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 811b6052c10SRuslan Bukin "clock_list", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, 812b6052c10SRuslan Bukin sc, 0, hdspe_sysctl_clock_list, "A", 813b6052c10SRuslan Bukin "List of supported clock sources"); 814b6052c10SRuslan Bukin 815fb877263SFlorian Walpen SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 816fb877263SFlorian Walpen SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 817fb877263SFlorian Walpen "period", CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, 818fb877263SFlorian Walpen sc, 0, hdspe_sysctl_period, "A", 819fb877263SFlorian Walpen "Force period of samples per interrupt (32, 64, ... 4096)"); 820fb877263SFlorian Walpen 8216c892b79SFlorian Walpen SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 8226c892b79SFlorian Walpen SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 8236c892b79SFlorian Walpen "sample_rate", CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE, 8246c892b79SFlorian Walpen sc, 0, hdspe_sysctl_sample_rate, "A", 8256c892b79SFlorian Walpen "Force sample rate (32000, 44100, 48000, ... 192000)"); 8266c892b79SFlorian Walpen 827*49a7f2b3SFlorian Walpen if (sc->type == HDSPE_AIO) { 828*49a7f2b3SFlorian Walpen SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 829*49a7f2b3SFlorian Walpen SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 830*49a7f2b3SFlorian Walpen "phones_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 831*49a7f2b3SFlorian Walpen sc, 0, hdspe_sysctl_phones_level, "A", 832*49a7f2b3SFlorian Walpen "Phones output level ('HighGain', '+4dBU', '-10dBV')"); 833*49a7f2b3SFlorian Walpen 834*49a7f2b3SFlorian Walpen SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 835*49a7f2b3SFlorian Walpen SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 836*49a7f2b3SFlorian Walpen "output_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 837*49a7f2b3SFlorian Walpen sc, 0, hdspe_sysctl_output_level, "A", 838*49a7f2b3SFlorian Walpen "Analog output level ('HighGain', '+4dBU', '-10dBV')"); 839*49a7f2b3SFlorian Walpen 840*49a7f2b3SFlorian Walpen SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 841*49a7f2b3SFlorian Walpen SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 842*49a7f2b3SFlorian Walpen "input_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, 843*49a7f2b3SFlorian Walpen sc, 0, hdspe_sysctl_input_level, "A", 844*49a7f2b3SFlorian Walpen "Analog input level ('LowGain', '+4dBU', '-10dBV')"); 845*49a7f2b3SFlorian Walpen } 846*49a7f2b3SFlorian Walpen 847*49a7f2b3SFlorian Walpen 84835d393bfSGleb Smirnoff return (bus_generic_attach(dev)); 849e4afd792SAlexander Motin } 850e4afd792SAlexander Motin 851e4afd792SAlexander Motin static void 852a17a41ffSJohn Baldwin hdspe_child_deleted(device_t dev, device_t child) 853a17a41ffSJohn Baldwin { 854a17a41ffSJohn Baldwin free(device_get_ivars(child), M_DEVBUF); 855a17a41ffSJohn Baldwin } 856a17a41ffSJohn Baldwin 857a17a41ffSJohn Baldwin static void 858e4afd792SAlexander Motin hdspe_dmafree(struct sc_info *sc) 859e4afd792SAlexander Motin { 860e4afd792SAlexander Motin 861e4afd792SAlexander Motin bus_dmamap_unload(sc->dmat, sc->rmap); 862e4afd792SAlexander Motin bus_dmamap_unload(sc->dmat, sc->pmap); 863e4afd792SAlexander Motin bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap); 864e4afd792SAlexander Motin bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap); 865e4afd792SAlexander Motin sc->rbuf = sc->pbuf = NULL; 866e4afd792SAlexander Motin } 867e4afd792SAlexander Motin 868e4afd792SAlexander Motin static int 869e4afd792SAlexander Motin hdspe_detach(device_t dev) 870e4afd792SAlexander Motin { 871e4afd792SAlexander Motin struct sc_info *sc; 872e4afd792SAlexander Motin int err; 873e4afd792SAlexander Motin 874e4afd792SAlexander Motin sc = device_get_softc(dev); 875e4afd792SAlexander Motin if (sc == NULL) { 876e4afd792SAlexander Motin device_printf(dev,"Can't detach: softc is null.\n"); 87720a9f771SRuslan Bukin return (0); 878e4afd792SAlexander Motin } 879e4afd792SAlexander Motin 880e4afd792SAlexander Motin err = device_delete_children(dev); 881e4afd792SAlexander Motin if (err) 882e4afd792SAlexander Motin return (err); 883e4afd792SAlexander Motin 884e4afd792SAlexander Motin hdspe_dmafree(sc); 885e4afd792SAlexander Motin 886e4afd792SAlexander Motin if (sc->ih) 887e4afd792SAlexander Motin bus_teardown_intr(dev, sc->irq, sc->ih); 888e4afd792SAlexander Motin if (sc->dmat) 889e4afd792SAlexander Motin bus_dma_tag_destroy(sc->dmat); 890e4afd792SAlexander Motin if (sc->irq) 891e4afd792SAlexander Motin bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); 892e4afd792SAlexander Motin if (sc->cs) 893e4afd792SAlexander Motin bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(0), sc->cs); 894e4afd792SAlexander Motin if (sc->lock) 895e4afd792SAlexander Motin snd_mtxfree(sc->lock); 896e4afd792SAlexander Motin 89720a9f771SRuslan Bukin return (0); 898e4afd792SAlexander Motin } 899e4afd792SAlexander Motin 900e4afd792SAlexander Motin static device_method_t hdspe_methods[] = { 901e4afd792SAlexander Motin DEVMETHOD(device_probe, hdspe_probe), 902e4afd792SAlexander Motin DEVMETHOD(device_attach, hdspe_attach), 903e4afd792SAlexander Motin DEVMETHOD(device_detach, hdspe_detach), 904a17a41ffSJohn Baldwin DEVMETHOD(bus_child_deleted, hdspe_child_deleted), 905e4afd792SAlexander Motin { 0, 0 } 906e4afd792SAlexander Motin }; 907e4afd792SAlexander Motin 908e4afd792SAlexander Motin static driver_t hdspe_driver = { 909e4afd792SAlexander Motin "hdspe", 910e4afd792SAlexander Motin hdspe_methods, 911e4afd792SAlexander Motin PCM_SOFTC_SIZE, 912e4afd792SAlexander Motin }; 913e4afd792SAlexander Motin 9143390adfeSJohn Baldwin DRIVER_MODULE(snd_hdspe, pci, hdspe_driver, 0, 0); 915