xref: /freebsd-src/sys/dev/sound/pci/hdsp.c (revision 3ddaf8200bc90b1410755ebac7b5c979ea90a2f6)
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