xref: /freebsd/sys/dev/sound/pci/hdspe.c (revision 3ddaf8200bc90b1410755ebac7b5c979ea90a2f6)
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
hdspe_intr(void * p)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
hdspe_dmapsetmap(void * arg,bus_dma_segment_t * segs,int nseg,int error)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
hdspe_alloc_resources(struct sc_info * sc)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
hdspe_map_dmabuf(struct sc_info * sc)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 
24949a7f2b3SFlorian Walpen static const char *
hdspe_settings_input_level(uint32_t settings)25049a7f2b3SFlorian Walpen hdspe_settings_input_level(uint32_t settings)
25149a7f2b3SFlorian Walpen {
25249a7f2b3SFlorian Walpen 	switch (settings & HDSPE_INPUT_LEVEL_MASK) {
25349a7f2b3SFlorian Walpen 	case HDSPE_INPUT_LEVEL_LOWGAIN:
25449a7f2b3SFlorian Walpen 		return ("LowGain");
25549a7f2b3SFlorian Walpen 	case HDSPE_INPUT_LEVEL_PLUS4DBU:
25649a7f2b3SFlorian Walpen 		return ("+4dBu");
25749a7f2b3SFlorian Walpen 	case HDSPE_INPUT_LEVEL_MINUS10DBV:
25849a7f2b3SFlorian Walpen 		return ("-10dBV");
25949a7f2b3SFlorian Walpen 	default:
26049a7f2b3SFlorian Walpen 		return (NULL);
26149a7f2b3SFlorian Walpen 	}
26249a7f2b3SFlorian Walpen }
26349a7f2b3SFlorian Walpen 
26449a7f2b3SFlorian Walpen static int
hdspe_sysctl_input_level(SYSCTL_HANDLER_ARGS)26549a7f2b3SFlorian Walpen hdspe_sysctl_input_level(SYSCTL_HANDLER_ARGS)
26649a7f2b3SFlorian Walpen {
26749a7f2b3SFlorian Walpen 	struct sc_info *sc;
26849a7f2b3SFlorian Walpen 	const char *label;
26949a7f2b3SFlorian Walpen 	char buf[16] = "invalid";
27049a7f2b3SFlorian Walpen 	int error;
27149a7f2b3SFlorian Walpen 	uint32_t settings;
27249a7f2b3SFlorian Walpen 
27349a7f2b3SFlorian Walpen 	sc = oidp->oid_arg1;
27449a7f2b3SFlorian Walpen 
27549a7f2b3SFlorian Walpen 	/* Only available on HDSPE AIO. */
27649a7f2b3SFlorian Walpen 	if (sc->type != HDSPE_AIO)
27749a7f2b3SFlorian Walpen 		return (ENXIO);
27849a7f2b3SFlorian Walpen 
27949a7f2b3SFlorian Walpen 	/* Extract current input level from settings register. */
28049a7f2b3SFlorian Walpen 	settings = sc->settings_register & HDSPE_INPUT_LEVEL_MASK;
28149a7f2b3SFlorian Walpen 	label = hdspe_settings_input_level(settings);
28249a7f2b3SFlorian Walpen 	if (label != NULL)
28349a7f2b3SFlorian Walpen 		strlcpy(buf, label, sizeof(buf));
28449a7f2b3SFlorian Walpen 
28549a7f2b3SFlorian Walpen 	/* Process sysctl string request. */
28649a7f2b3SFlorian Walpen 	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
28749a7f2b3SFlorian Walpen 	if (error != 0 || req->newptr == NULL)
28849a7f2b3SFlorian Walpen 		return (error);
28949a7f2b3SFlorian Walpen 
29049a7f2b3SFlorian Walpen 	/* Find input level matching the sysctl string. */
29149a7f2b3SFlorian Walpen 	label = hdspe_settings_input_level(HDSPE_INPUT_LEVEL_LOWGAIN);
29249a7f2b3SFlorian Walpen 	if (strncasecmp(buf, label, sizeof(buf)) == 0)
29349a7f2b3SFlorian Walpen 		settings = HDSPE_INPUT_LEVEL_LOWGAIN;
29449a7f2b3SFlorian Walpen 	label = hdspe_settings_input_level(HDSPE_INPUT_LEVEL_PLUS4DBU);
29549a7f2b3SFlorian Walpen 	if (strncasecmp(buf, label, sizeof(buf)) == 0)
29649a7f2b3SFlorian Walpen 		settings = HDSPE_INPUT_LEVEL_PLUS4DBU;
29749a7f2b3SFlorian Walpen 	label = hdspe_settings_input_level(HDSPE_INPUT_LEVEL_MINUS10DBV);
29849a7f2b3SFlorian Walpen 	if (strncasecmp(buf, label, sizeof(buf)) == 0)
29949a7f2b3SFlorian Walpen 		settings = HDSPE_INPUT_LEVEL_MINUS10DBV;
30049a7f2b3SFlorian Walpen 
30149a7f2b3SFlorian Walpen 	/* Set input level in settings register. */
30249a7f2b3SFlorian Walpen 	settings &= HDSPE_INPUT_LEVEL_MASK;
30349a7f2b3SFlorian Walpen 	if (settings != (sc->settings_register & HDSPE_INPUT_LEVEL_MASK)) {
30449a7f2b3SFlorian Walpen 		snd_mtxlock(sc->lock);
30549a7f2b3SFlorian Walpen 		sc->settings_register &= ~HDSPE_INPUT_LEVEL_MASK;
30649a7f2b3SFlorian Walpen 		sc->settings_register |= settings;
30749a7f2b3SFlorian Walpen 		hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register);
30849a7f2b3SFlorian Walpen 		snd_mtxunlock(sc->lock);
30949a7f2b3SFlorian Walpen 	}
31049a7f2b3SFlorian Walpen 	return (0);
31149a7f2b3SFlorian Walpen }
31249a7f2b3SFlorian Walpen 
31349a7f2b3SFlorian Walpen static const char *
hdspe_settings_output_level(uint32_t settings)31449a7f2b3SFlorian Walpen hdspe_settings_output_level(uint32_t settings)
31549a7f2b3SFlorian Walpen {
31649a7f2b3SFlorian Walpen 	switch (settings & HDSPE_OUTPUT_LEVEL_MASK) {
31749a7f2b3SFlorian Walpen 	case HDSPE_OUTPUT_LEVEL_HIGHGAIN:
31849a7f2b3SFlorian Walpen 		return ("HighGain");
31949a7f2b3SFlorian Walpen 	case HDSPE_OUTPUT_LEVEL_PLUS4DBU:
32049a7f2b3SFlorian Walpen 		return ("+4dBu");
32149a7f2b3SFlorian Walpen 	case HDSPE_OUTPUT_LEVEL_MINUS10DBV:
32249a7f2b3SFlorian Walpen 		return ("-10dBV");
32349a7f2b3SFlorian Walpen 	default:
32449a7f2b3SFlorian Walpen 		return (NULL);
32549a7f2b3SFlorian Walpen 	}
32649a7f2b3SFlorian Walpen }
32749a7f2b3SFlorian Walpen 
32849a7f2b3SFlorian Walpen static int
hdspe_sysctl_output_level(SYSCTL_HANDLER_ARGS)32949a7f2b3SFlorian Walpen hdspe_sysctl_output_level(SYSCTL_HANDLER_ARGS)
33049a7f2b3SFlorian Walpen {
33149a7f2b3SFlorian Walpen 	struct sc_info *sc;
33249a7f2b3SFlorian Walpen 	const char *label;
33349a7f2b3SFlorian Walpen 	char buf[16] = "invalid";
33449a7f2b3SFlorian Walpen 	int error;
33549a7f2b3SFlorian Walpen 	uint32_t settings;
33649a7f2b3SFlorian Walpen 
33749a7f2b3SFlorian Walpen 	sc = oidp->oid_arg1;
33849a7f2b3SFlorian Walpen 
33949a7f2b3SFlorian Walpen 	/* Only available on HDSPE AIO. */
34049a7f2b3SFlorian Walpen 	if (sc->type != HDSPE_AIO)
34149a7f2b3SFlorian Walpen 		return (ENXIO);
34249a7f2b3SFlorian Walpen 
34349a7f2b3SFlorian Walpen 	/* Extract current output level from settings register. */
34449a7f2b3SFlorian Walpen 	settings = sc->settings_register & HDSPE_OUTPUT_LEVEL_MASK;
34549a7f2b3SFlorian Walpen 	label = hdspe_settings_output_level(settings);
34649a7f2b3SFlorian Walpen 	if (label != NULL)
34749a7f2b3SFlorian Walpen 		strlcpy(buf, label, sizeof(buf));
34849a7f2b3SFlorian Walpen 
34949a7f2b3SFlorian Walpen 	/* Process sysctl string request. */
35049a7f2b3SFlorian Walpen 	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
35149a7f2b3SFlorian Walpen 	if (error != 0 || req->newptr == NULL)
35249a7f2b3SFlorian Walpen 		return (error);
35349a7f2b3SFlorian Walpen 
35449a7f2b3SFlorian Walpen 	/* Find output level matching the sysctl string. */
35549a7f2b3SFlorian Walpen 	label = hdspe_settings_output_level(HDSPE_OUTPUT_LEVEL_HIGHGAIN);
35649a7f2b3SFlorian Walpen 	if (strncasecmp(buf, label, sizeof(buf)) == 0)
35749a7f2b3SFlorian Walpen 		settings = HDSPE_OUTPUT_LEVEL_HIGHGAIN;
35849a7f2b3SFlorian Walpen 	label = hdspe_settings_output_level(HDSPE_OUTPUT_LEVEL_PLUS4DBU);
35949a7f2b3SFlorian Walpen 	if (strncasecmp(buf, label, sizeof(buf)) == 0)
36049a7f2b3SFlorian Walpen 		settings = HDSPE_OUTPUT_LEVEL_PLUS4DBU;
36149a7f2b3SFlorian Walpen 	label = hdspe_settings_output_level(HDSPE_OUTPUT_LEVEL_MINUS10DBV);
36249a7f2b3SFlorian Walpen 	if (strncasecmp(buf, label, sizeof(buf)) == 0)
36349a7f2b3SFlorian Walpen 		settings = HDSPE_OUTPUT_LEVEL_MINUS10DBV;
36449a7f2b3SFlorian Walpen 
36549a7f2b3SFlorian Walpen 	/* Set output level in settings register. */
36649a7f2b3SFlorian Walpen 	settings &= HDSPE_OUTPUT_LEVEL_MASK;
36749a7f2b3SFlorian Walpen 	if (settings != (sc->settings_register & HDSPE_OUTPUT_LEVEL_MASK)) {
36849a7f2b3SFlorian Walpen 		snd_mtxlock(sc->lock);
36949a7f2b3SFlorian Walpen 		sc->settings_register &= ~HDSPE_OUTPUT_LEVEL_MASK;
37049a7f2b3SFlorian Walpen 		sc->settings_register |= settings;
37149a7f2b3SFlorian Walpen 		hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register);
37249a7f2b3SFlorian Walpen 		snd_mtxunlock(sc->lock);
37349a7f2b3SFlorian Walpen 	}
37449a7f2b3SFlorian Walpen 	return (0);
37549a7f2b3SFlorian Walpen }
37649a7f2b3SFlorian Walpen 
37749a7f2b3SFlorian Walpen static const char *
hdspe_settings_phones_level(uint32_t settings)37849a7f2b3SFlorian Walpen hdspe_settings_phones_level(uint32_t settings)
37949a7f2b3SFlorian Walpen {
38049a7f2b3SFlorian Walpen 	switch (settings & HDSPE_PHONES_LEVEL_MASK) {
38149a7f2b3SFlorian Walpen 	case HDSPE_PHONES_LEVEL_HIGHGAIN:
38249a7f2b3SFlorian Walpen 		return ("HighGain");
38349a7f2b3SFlorian Walpen 	case HDSPE_PHONES_LEVEL_PLUS4DBU:
38449a7f2b3SFlorian Walpen 		return ("+4dBu");
38549a7f2b3SFlorian Walpen 	case HDSPE_PHONES_LEVEL_MINUS10DBV:
38649a7f2b3SFlorian Walpen 		return ("-10dBV");
38749a7f2b3SFlorian Walpen 	default:
38849a7f2b3SFlorian Walpen 		return (NULL);
38949a7f2b3SFlorian Walpen 	}
39049a7f2b3SFlorian Walpen }
39149a7f2b3SFlorian Walpen 
39249a7f2b3SFlorian Walpen static int
hdspe_sysctl_phones_level(SYSCTL_HANDLER_ARGS)39349a7f2b3SFlorian Walpen hdspe_sysctl_phones_level(SYSCTL_HANDLER_ARGS)
39449a7f2b3SFlorian Walpen {
39549a7f2b3SFlorian Walpen 	struct sc_info *sc;
39649a7f2b3SFlorian Walpen 	const char *label;
39749a7f2b3SFlorian Walpen 	char buf[16] = "invalid";
39849a7f2b3SFlorian Walpen 	int error;
39949a7f2b3SFlorian Walpen 	uint32_t settings;
40049a7f2b3SFlorian Walpen 
40149a7f2b3SFlorian Walpen 	sc = oidp->oid_arg1;
40249a7f2b3SFlorian Walpen 
40349a7f2b3SFlorian Walpen 	/* Only available on HDSPE AIO. */
40449a7f2b3SFlorian Walpen 	if (sc->type != HDSPE_AIO)
40549a7f2b3SFlorian Walpen 		return (ENXIO);
40649a7f2b3SFlorian Walpen 
40749a7f2b3SFlorian Walpen 	/* Extract current phones level from settings register. */
40849a7f2b3SFlorian Walpen 	settings = sc->settings_register & HDSPE_PHONES_LEVEL_MASK;
40949a7f2b3SFlorian Walpen 	label = hdspe_settings_phones_level(settings);
41049a7f2b3SFlorian Walpen 	if (label != NULL)
41149a7f2b3SFlorian Walpen 		strlcpy(buf, label, sizeof(buf));
41249a7f2b3SFlorian Walpen 
41349a7f2b3SFlorian Walpen 	/* Process sysctl string request. */
41449a7f2b3SFlorian Walpen 	error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
41549a7f2b3SFlorian Walpen 	if (error != 0 || req->newptr == NULL)
41649a7f2b3SFlorian Walpen 		return (error);
41749a7f2b3SFlorian Walpen 
41849a7f2b3SFlorian Walpen 	/* Find phones level matching the sysctl string. */
41949a7f2b3SFlorian Walpen 	label = hdspe_settings_phones_level(HDSPE_PHONES_LEVEL_HIGHGAIN);
42049a7f2b3SFlorian Walpen 	if (strncasecmp(buf, label, sizeof(buf)) == 0)
42149a7f2b3SFlorian Walpen 		settings = HDSPE_PHONES_LEVEL_HIGHGAIN;
42249a7f2b3SFlorian Walpen 	label = hdspe_settings_phones_level(HDSPE_PHONES_LEVEL_PLUS4DBU);
42349a7f2b3SFlorian Walpen 	if (strncasecmp(buf, label, sizeof(buf)) == 0)
42449a7f2b3SFlorian Walpen 		settings = HDSPE_PHONES_LEVEL_PLUS4DBU;
42549a7f2b3SFlorian Walpen 	label = hdspe_settings_phones_level(HDSPE_PHONES_LEVEL_MINUS10DBV);
42649a7f2b3SFlorian Walpen 	if (strncasecmp(buf, label, sizeof(buf)) == 0)
42749a7f2b3SFlorian Walpen 		settings = HDSPE_PHONES_LEVEL_MINUS10DBV;
42849a7f2b3SFlorian Walpen 
42949a7f2b3SFlorian Walpen 	/* Set phones level in settings register. */
43049a7f2b3SFlorian Walpen 	settings &= HDSPE_PHONES_LEVEL_MASK;
43149a7f2b3SFlorian Walpen 	if (settings != (sc->settings_register & HDSPE_PHONES_LEVEL_MASK)) {
43249a7f2b3SFlorian Walpen 		snd_mtxlock(sc->lock);
43349a7f2b3SFlorian Walpen 		sc->settings_register &= ~HDSPE_PHONES_LEVEL_MASK;
43449a7f2b3SFlorian Walpen 		sc->settings_register |= settings;
43549a7f2b3SFlorian Walpen 		hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register);
43649a7f2b3SFlorian Walpen 		snd_mtxunlock(sc->lock);
43749a7f2b3SFlorian Walpen 	}
43849a7f2b3SFlorian Walpen 	return (0);
43949a7f2b3SFlorian Walpen }
44049a7f2b3SFlorian Walpen 
441e4afd792SAlexander Motin static int
hdspe_sysctl_sample_rate(SYSCTL_HANDLER_ARGS)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
hdspe_sysctl_period(SYSCTL_HANDLER_ARGS)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
hdspe_sysctl_clock_preference(SYSCTL_HANDLER_ARGS)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
hdspe_sysctl_clock_source(SYSCTL_HANDLER_ARGS)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
hdspe_sysctl_clock_list(SYSCTL_HANDLER_ARGS)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
hdspe_sysctl_sync_status(SYSCTL_HANDLER_ARGS)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
hdspe_probe(device_t dev)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
hdspe_init(struct sc_info * sc)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;
72449a7f2b3SFlorian Walpen 
72549a7f2b3SFlorian Walpen 	/* Default gain levels. */
72649a7f2b3SFlorian Walpen 	sc->settings_register &= ~HDSPE_INPUT_LEVEL_MASK;
72749a7f2b3SFlorian Walpen 	sc->settings_register |= HDSPE_INPUT_LEVEL_LOWGAIN;
72849a7f2b3SFlorian Walpen 	sc->settings_register &= ~HDSPE_OUTPUT_LEVEL_MASK;
72949a7f2b3SFlorian Walpen 	sc->settings_register |= HDSPE_OUTPUT_LEVEL_MINUS10DBV;
73049a7f2b3SFlorian Walpen 	sc->settings_register &= ~HDSPE_PHONES_LEVEL_MASK;
73149a7f2b3SFlorian Walpen 	sc->settings_register |= HDSPE_PHONES_LEVEL_MINUS10DBV;
73249a7f2b3SFlorian 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
hdspe_attach(device_t dev)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 
82749a7f2b3SFlorian Walpen 	if (sc->type == HDSPE_AIO) {
82849a7f2b3SFlorian Walpen 		SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
82949a7f2b3SFlorian Walpen 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
83049a7f2b3SFlorian Walpen 		    "phones_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
83149a7f2b3SFlorian Walpen 		    sc, 0, hdspe_sysctl_phones_level, "A",
83249a7f2b3SFlorian Walpen 		    "Phones output level ('HighGain', '+4dBU', '-10dBV')");
83349a7f2b3SFlorian Walpen 
83449a7f2b3SFlorian Walpen 		SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
83549a7f2b3SFlorian Walpen 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
83649a7f2b3SFlorian Walpen 		    "output_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
83749a7f2b3SFlorian Walpen 		    sc, 0, hdspe_sysctl_output_level, "A",
83849a7f2b3SFlorian Walpen 		    "Analog output level ('HighGain', '+4dBU', '-10dBV')");
83949a7f2b3SFlorian Walpen 
84049a7f2b3SFlorian Walpen 		SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
84149a7f2b3SFlorian Walpen 		    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
84249a7f2b3SFlorian Walpen 		    "input_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
84349a7f2b3SFlorian Walpen 		    sc, 0, hdspe_sysctl_input_level, "A",
84449a7f2b3SFlorian Walpen 		    "Analog input level ('LowGain', '+4dBU', '-10dBV')");
84549a7f2b3SFlorian Walpen 	}
84649a7f2b3SFlorian Walpen 
84718250ec6SJohn Baldwin 	bus_attach_children(dev);
84818250ec6SJohn Baldwin 	return (0);
849e4afd792SAlexander Motin }
850e4afd792SAlexander Motin 
851e4afd792SAlexander Motin static void
hdspe_child_deleted(device_t dev,device_t child)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
hdspe_dmafree(struct sc_info * sc)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
hdspe_detach(device_t dev)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 
880*3ddaf820SJohn Baldwin 	err = bus_generic_detach(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