1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2012-2016 Ruslan Bukin <[email protected]>
5 * Copyright (c) 2023-2024 Florian Walpen <[email protected]>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 /*
31 * RME HDSPe driver for FreeBSD.
32 * Supported cards: AIO, RayDAT.
33 */
34
35 #include <sys/types.h>
36 #include <sys/sysctl.h>
37
38 #include <dev/sound/pcm/sound.h>
39 #include <dev/sound/pci/hdspe.h>
40
41 #include <dev/pci/pcireg.h>
42 #include <dev/pci/pcivar.h>
43
44 #include <mixer_if.h>
45
46 static struct hdspe_clock_source hdspe_clock_source_table_rd[] = {
47 { "internal", 0 << 1 | 1, HDSPE_STATUS1_CLOCK(15), 0, 0 },
48 { "word", 0 << 1 | 0, HDSPE_STATUS1_CLOCK( 0), 1 << 24, 1 << 25 },
49 { "aes", 1 << 1 | 0, HDSPE_STATUS1_CLOCK( 1), 1 << 0, 1 << 8 },
50 { "spdif", 2 << 1 | 0, HDSPE_STATUS1_CLOCK( 2), 1 << 1, 1 << 9 },
51 { "adat1", 3 << 1 | 0, HDSPE_STATUS1_CLOCK( 3), 1 << 2, 1 << 10 },
52 { "adat2", 4 << 1 | 0, HDSPE_STATUS1_CLOCK( 4), 1 << 3, 1 << 11 },
53 { "adat3", 5 << 1 | 0, HDSPE_STATUS1_CLOCK( 5), 1 << 4, 1 << 12 },
54 { "adat4", 6 << 1 | 0, HDSPE_STATUS1_CLOCK( 6), 1 << 5, 1 << 13 },
55 { "tco", 9 << 1 | 0, HDSPE_STATUS1_CLOCK( 9), 1 << 26, 1 << 27 },
56 { "sync_in", 10 << 1 | 0, HDSPE_STATUS1_CLOCK(10), 0, 0 },
57 { NULL, 0 << 1 | 0, HDSPE_STATUS1_CLOCK( 0), 0, 0 },
58 };
59
60 static struct hdspe_clock_source hdspe_clock_source_table_aio[] = {
61 { "internal", 0 << 1 | 1, HDSPE_STATUS1_CLOCK(15), 0, 0 },
62 { "word", 0 << 1 | 0, HDSPE_STATUS1_CLOCK( 0), 1 << 24, 1 << 25 },
63 { "aes", 1 << 1 | 0, HDSPE_STATUS1_CLOCK( 1), 1 << 0, 1 << 8 },
64 { "spdif", 2 << 1 | 0, HDSPE_STATUS1_CLOCK( 2), 1 << 1, 1 << 9 },
65 { "adat", 3 << 1 | 0, HDSPE_STATUS1_CLOCK( 3), 1 << 2, 1 << 10 },
66 { "tco", 9 << 1 | 0, HDSPE_STATUS1_CLOCK( 9), 1 << 26, 1 << 27 },
67 { "sync_in", 10 << 1 | 0, HDSPE_STATUS1_CLOCK(10), 0, 0 },
68 { NULL, 0 << 1 | 0, HDSPE_STATUS1_CLOCK( 0), 0, 0 },
69 };
70
71 static struct hdspe_channel chan_map_aio[] = {
72 { HDSPE_CHAN_AIO_LINE, "line" },
73 { HDSPE_CHAN_AIO_PHONE, "phone" },
74 { HDSPE_CHAN_AIO_AES, "aes" },
75 { HDSPE_CHAN_AIO_SPDIF, "s/pdif" },
76 { HDSPE_CHAN_AIO_ADAT, "adat" },
77 { 0, NULL },
78 };
79
80 static struct hdspe_channel chan_map_rd[] = {
81 { HDSPE_CHAN_RAY_AES, "aes" },
82 { HDSPE_CHAN_RAY_SPDIF, "s/pdif" },
83 { HDSPE_CHAN_RAY_ADAT1, "adat1" },
84 { HDSPE_CHAN_RAY_ADAT2, "adat2" },
85 { HDSPE_CHAN_RAY_ADAT3, "adat3" },
86 { HDSPE_CHAN_RAY_ADAT4, "adat4" },
87 { 0, NULL },
88 };
89
90 static void
hdspe_intr(void * p)91 hdspe_intr(void *p)
92 {
93 struct sc_pcminfo *scp;
94 struct sc_info *sc;
95 device_t *devlist;
96 int devcount;
97 int status;
98 int err;
99 int i;
100
101 sc = (struct sc_info *)p;
102
103 snd_mtxlock(sc->lock);
104
105 status = hdspe_read_1(sc, HDSPE_STATUS_REG);
106 if (status & HDSPE_AUDIO_IRQ_PENDING) {
107 if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0)
108 return;
109
110 for (i = 0; i < devcount; i++) {
111 scp = device_get_ivars(devlist[i]);
112 if (scp->ih != NULL)
113 scp->ih(scp);
114 }
115
116 hdspe_write_1(sc, HDSPE_INTERRUPT_ACK, 0);
117 free(devlist, M_TEMP);
118 }
119
120 snd_mtxunlock(sc->lock);
121 }
122
123 static void
hdspe_dmapsetmap(void * arg,bus_dma_segment_t * segs,int nseg,int error)124 hdspe_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
125 {
126 #if 0
127 device_printf(sc->dev, "hdspe_dmapsetmap()\n");
128 #endif
129 }
130
131 static int
hdspe_alloc_resources(struct sc_info * sc)132 hdspe_alloc_resources(struct sc_info *sc)
133 {
134
135 /* Allocate resource. */
136 sc->csid = PCIR_BAR(0);
137 sc->cs = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
138 &sc->csid, RF_ACTIVE);
139
140 if (!sc->cs) {
141 device_printf(sc->dev, "Unable to map SYS_RES_MEMORY.\n");
142 return (ENXIO);
143 }
144
145 sc->cst = rman_get_bustag(sc->cs);
146 sc->csh = rman_get_bushandle(sc->cs);
147
148 /* Allocate interrupt resource. */
149 sc->irqid = 0;
150 sc->irq = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &sc->irqid,
151 RF_ACTIVE | RF_SHAREABLE);
152
153 if (!sc->irq ||
154 bus_setup_intr(sc->dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV,
155 NULL, hdspe_intr, sc, &sc->ih)) {
156 device_printf(sc->dev, "Unable to alloc interrupt resource.\n");
157 return (ENXIO);
158 }
159
160 /* Allocate DMA resources. */
161 if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(sc->dev),
162 /*alignment*/4,
163 /*boundary*/0,
164 /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
165 /*highaddr*/BUS_SPACE_MAXADDR,
166 /*filter*/NULL,
167 /*filterarg*/NULL,
168 /*maxsize*/2 * HDSPE_DMASEGSIZE,
169 /*nsegments*/2,
170 /*maxsegsz*/HDSPE_DMASEGSIZE,
171 /*flags*/0,
172 /*lockfunc*/NULL,
173 /*lockarg*/NULL,
174 /*dmatag*/&sc->dmat) != 0) {
175 device_printf(sc->dev, "Unable to create dma tag.\n");
176 return (ENXIO);
177 }
178
179 sc->bufsize = HDSPE_DMASEGSIZE;
180
181 /* pbuf (play buffer). */
182 if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf, BUS_DMA_WAITOK,
183 &sc->pmap)) {
184 device_printf(sc->dev, "Can't alloc pbuf.\n");
185 return (ENXIO);
186 }
187
188 if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->bufsize,
189 hdspe_dmapsetmap, sc, BUS_DMA_NOWAIT)) {
190 device_printf(sc->dev, "Can't load pbuf.\n");
191 return (ENXIO);
192 }
193
194 /* rbuf (rec buffer). */
195 if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf, BUS_DMA_WAITOK,
196 &sc->rmap)) {
197 device_printf(sc->dev, "Can't alloc rbuf.\n");
198 return (ENXIO);
199 }
200
201 if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->bufsize,
202 hdspe_dmapsetmap, sc, BUS_DMA_NOWAIT)) {
203 device_printf(sc->dev, "Can't load rbuf.\n");
204 return (ENXIO);
205 }
206
207 bzero(sc->pbuf, sc->bufsize);
208 bzero(sc->rbuf, sc->bufsize);
209
210 return (0);
211 }
212
213 static void
hdspe_map_dmabuf(struct sc_info * sc)214 hdspe_map_dmabuf(struct sc_info *sc)
215 {
216 uint32_t paddr, raddr;
217 int i;
218
219 paddr = vtophys(sc->pbuf);
220 raddr = vtophys(sc->rbuf);
221
222 for (i = 0; i < HDSPE_MAX_SLOTS * 16; i++) {
223 hdspe_write_4(sc, HDSPE_PAGE_ADDR_BUF_OUT + 4 * i,
224 paddr + i * 4096);
225 hdspe_write_4(sc, HDSPE_PAGE_ADDR_BUF_IN + 4 * i,
226 raddr + i * 4096);
227 }
228 }
229
230 static int
hdspe_sysctl_clock_preference(SYSCTL_HANDLER_ARGS)231 hdspe_sysctl_clock_preference(SYSCTL_HANDLER_ARGS)
232 {
233 struct sc_info *sc;
234 struct hdspe_clock_source *clock_table, *clock;
235 char buf[16] = "invalid";
236 int error;
237 uint32_t setting;
238
239 sc = oidp->oid_arg1;
240
241 /* Select sync ports table for device type. */
242 if (sc->type == HDSPE_AIO)
243 clock_table = hdspe_clock_source_table_aio;
244 else if (sc->type == HDSPE_RAYDAT)
245 clock_table = hdspe_clock_source_table_rd;
246 else
247 return (ENXIO);
248
249 /* Extract preferred clock source from settings register. */
250 setting = sc->settings_register & HDSPE_SETTING_CLOCK_MASK;
251 for (clock = clock_table; clock->name != NULL; ++clock) {
252 if (clock->setting == setting)
253 break;
254 }
255 if (clock->name != NULL)
256 strlcpy(buf, clock->name, sizeof(buf));
257
258 /* Process sysctl string request. */
259 error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
260 if (error != 0 || req->newptr == NULL)
261 return (error);
262
263 /* Find clock source matching the sysctl string. */
264 for (clock = clock_table; clock->name != NULL; ++clock) {
265 if (strncasecmp(buf, clock->name, sizeof(buf)) == 0)
266 break;
267 }
268
269 /* Set preferred clock source in settings register. */
270 if (clock->name != NULL) {
271 setting = clock->setting & HDSPE_SETTING_CLOCK_MASK;
272 snd_mtxlock(sc->lock);
273 sc->settings_register &= ~HDSPE_SETTING_CLOCK_MASK;
274 sc->settings_register |= setting;
275 hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register);
276 snd_mtxunlock(sc->lock);
277 }
278 return (0);
279 }
280
281 static int
hdspe_sysctl_clock_source(SYSCTL_HANDLER_ARGS)282 hdspe_sysctl_clock_source(SYSCTL_HANDLER_ARGS)
283 {
284 struct sc_info *sc;
285 struct hdspe_clock_source *clock_table, *clock;
286 char buf[16] = "invalid";
287 uint32_t status;
288
289 sc = oidp->oid_arg1;
290
291 /* Select sync ports table for device type. */
292 if (sc->type == HDSPE_AIO)
293 clock_table = hdspe_clock_source_table_aio;
294 else if (sc->type == HDSPE_RAYDAT)
295 clock_table = hdspe_clock_source_table_rd;
296 else
297 return (ENXIO);
298
299 /* Read current (autosync) clock source from status register. */
300 snd_mtxlock(sc->lock);
301 status = hdspe_read_4(sc, HDSPE_STATUS1_REG);
302 status &= HDSPE_STATUS1_CLOCK_MASK;
303 snd_mtxunlock(sc->lock);
304
305 /* Translate status register value to clock source. */
306 for (clock = clock_table; clock->name != NULL; ++clock) {
307 /* In clock master mode, override with internal clock source. */
308 if (sc->settings_register & HDSPE_SETTING_MASTER) {
309 if (clock->setting & HDSPE_SETTING_MASTER)
310 break;
311 } else if (clock->status == status)
312 break;
313 }
314
315 /* Process sysctl string request. */
316 if (clock->name != NULL)
317 strlcpy(buf, clock->name, sizeof(buf));
318 return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
319 }
320
321 static int
hdspe_sysctl_clock_list(SYSCTL_HANDLER_ARGS)322 hdspe_sysctl_clock_list(SYSCTL_HANDLER_ARGS)
323 {
324 struct sc_info *sc;
325 struct hdspe_clock_source *clock_table, *clock;
326 char buf[256];
327 int n;
328
329 sc = oidp->oid_arg1;
330 n = 0;
331
332 /* Select clock source table for device type. */
333 if (sc->type == HDSPE_AIO)
334 clock_table = hdspe_clock_source_table_aio;
335 else if (sc->type == HDSPE_RAYDAT)
336 clock_table = hdspe_clock_source_table_rd;
337 else
338 return (ENXIO);
339
340 /* List available clock sources. */
341 buf[0] = 0;
342 for (clock = clock_table; clock->name != NULL; ++clock) {
343 if (n > 0)
344 n += strlcpy(buf + n, ",", sizeof(buf) - n);
345 n += strlcpy(buf + n, clock->name, sizeof(buf) - n);
346 }
347 return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
348 }
349
350 static int
hdspe_sysctl_sync_status(SYSCTL_HANDLER_ARGS)351 hdspe_sysctl_sync_status(SYSCTL_HANDLER_ARGS)
352 {
353 struct sc_info *sc;
354 struct hdspe_clock_source *clock_table, *clock;
355 char buf[256];
356 char *state;
357 int n;
358 uint32_t status;
359
360 sc = oidp->oid_arg1;
361 n = 0;
362
363 /* Select sync ports table for device type. */
364 if (sc->type == HDSPE_AIO)
365 clock_table = hdspe_clock_source_table_aio;
366 else if (sc->type == HDSPE_RAYDAT)
367 clock_table = hdspe_clock_source_table_rd;
368 else
369 return (ENXIO);
370
371 /* Read current lock and sync bits from status register. */
372 snd_mtxlock(sc->lock);
373 status = hdspe_read_4(sc, HDSPE_STATUS1_REG);
374 snd_mtxunlock(sc->lock);
375
376 /* List clock sources with lock and sync state. */
377 for (clock = clock_table; clock->name != NULL; ++clock) {
378 if (clock->sync_bit != 0) {
379 if (n > 0)
380 n += strlcpy(buf + n, ",", sizeof(buf) - n);
381 state = "none";
382 if ((clock->sync_bit & status) != 0)
383 state = "sync";
384 else if ((clock->lock_bit & status) != 0)
385 state = "lock";
386 n += snprintf(buf + n, sizeof(buf) - n, "%s(%s)",
387 clock->name, state);
388 }
389 }
390 return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
391 }
392
393 static int
hdspe_probe(device_t dev)394 hdspe_probe(device_t dev)
395 {
396 uint32_t rev;
397
398 if ((pci_get_vendor(dev) == PCI_VENDOR_XILINX ||
399 pci_get_vendor(dev) == PCI_VENDOR_RME) &&
400 pci_get_device(dev) == PCI_DEVICE_XILINX_HDSPE) {
401 rev = pci_get_revid(dev);
402 switch (rev) {
403 case PCI_REVISION_AIO:
404 device_set_desc(dev, "RME HDSPe AIO");
405 return (0);
406 case PCI_REVISION_RAYDAT:
407 device_set_desc(dev, "RME HDSPe RayDAT");
408 return (0);
409 }
410 }
411
412 return (ENXIO);
413 }
414
415 static int
hdspe_init(struct sc_info * sc)416 hdspe_init(struct sc_info *sc)
417 {
418 long long period;
419
420 /* Set latency. */
421 sc->period = 32;
422 sc->ctrl_register = hdspe_encode_latency(7);
423
424 /* Set rate. */
425 sc->speed = HDSPE_SPEED_DEFAULT;
426 sc->ctrl_register &= ~HDSPE_FREQ_MASK;
427 sc->ctrl_register |= HDSPE_FREQ_MASK_DEFAULT;
428 hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register);
429
430 switch (sc->type) {
431 case HDSPE_RAYDAT:
432 case HDSPE_AIO:
433 period = HDSPE_FREQ_AIO;
434 break;
435 default:
436 return (ENXIO);
437 }
438
439 /* Set DDS value. */
440 period /= sc->speed;
441 hdspe_write_4(sc, HDSPE_FREQ_REG, period);
442
443 /* Other settings. */
444 sc->settings_register = 0;
445 hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register);
446
447 return (0);
448 }
449
450 static int
hdspe_attach(device_t dev)451 hdspe_attach(device_t dev)
452 {
453 struct hdspe_channel *chan_map;
454 struct sc_pcminfo *scp;
455 struct sc_info *sc;
456 uint32_t rev;
457 int i, err;
458
459 #if 0
460 device_printf(dev, "hdspe_attach()\n");
461 #endif
462
463 sc = device_get_softc(dev);
464 sc->lock = snd_mtxcreate(device_get_nameunit(dev),
465 "snd_hdspe softc");
466 sc->dev = dev;
467
468 pci_enable_busmaster(dev);
469 rev = pci_get_revid(dev);
470 switch (rev) {
471 case PCI_REVISION_AIO:
472 sc->type = HDSPE_AIO;
473 chan_map = chan_map_aio;
474 break;
475 case PCI_REVISION_RAYDAT:
476 sc->type = HDSPE_RAYDAT;
477 chan_map = chan_map_rd;
478 break;
479 default:
480 return (ENXIO);
481 }
482
483 /* Allocate resources. */
484 err = hdspe_alloc_resources(sc);
485 if (err) {
486 device_printf(dev, "Unable to allocate system resources.\n");
487 return (ENXIO);
488 }
489
490 if (hdspe_init(sc) != 0)
491 return (ENXIO);
492
493 for (i = 0; i < HDSPE_MAX_CHANS && chan_map[i].descr != NULL; i++) {
494 scp = malloc(sizeof(struct sc_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO);
495 scp->hc = &chan_map[i];
496 scp->sc = sc;
497 scp->dev = device_add_child(dev, "pcm", -1);
498 device_set_ivars(scp->dev, scp);
499 }
500
501 hdspe_map_dmabuf(sc);
502
503 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
504 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
505 "sync_status", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
506 sc, 0, hdspe_sysctl_sync_status, "A",
507 "List clock source signal lock and sync status");
508
509 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
510 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
511 "clock_source", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
512 sc, 0, hdspe_sysctl_clock_source, "A",
513 "Currently effective clock source");
514
515 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
516 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
517 "clock_preference", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
518 sc, 0, hdspe_sysctl_clock_preference, "A",
519 "Set 'internal' (master) or preferred autosync clock source");
520
521 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
522 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
523 "clock_list", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
524 sc, 0, hdspe_sysctl_clock_list, "A",
525 "List of supported clock sources");
526
527 return (bus_generic_attach(dev));
528 }
529
530 static void
hdspe_dmafree(struct sc_info * sc)531 hdspe_dmafree(struct sc_info *sc)
532 {
533
534 bus_dmamap_unload(sc->dmat, sc->rmap);
535 bus_dmamap_unload(sc->dmat, sc->pmap);
536 bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap);
537 bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap);
538 sc->rbuf = sc->pbuf = NULL;
539 }
540
541 static int
hdspe_detach(device_t dev)542 hdspe_detach(device_t dev)
543 {
544 struct sc_info *sc;
545 int err;
546
547 sc = device_get_softc(dev);
548 if (sc == NULL) {
549 device_printf(dev,"Can't detach: softc is null.\n");
550 return (0);
551 }
552
553 err = device_delete_children(dev);
554 if (err)
555 return (err);
556
557 hdspe_dmafree(sc);
558
559 if (sc->ih)
560 bus_teardown_intr(dev, sc->irq, sc->ih);
561 if (sc->dmat)
562 bus_dma_tag_destroy(sc->dmat);
563 if (sc->irq)
564 bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq);
565 if (sc->cs)
566 bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(0), sc->cs);
567 if (sc->lock)
568 snd_mtxfree(sc->lock);
569
570 return (0);
571 }
572
573 static device_method_t hdspe_methods[] = {
574 DEVMETHOD(device_probe, hdspe_probe),
575 DEVMETHOD(device_attach, hdspe_attach),
576 DEVMETHOD(device_detach, hdspe_detach),
577 { 0, 0 }
578 };
579
580 static driver_t hdspe_driver = {
581 "hdspe",
582 hdspe_methods,
583 PCM_SOFTC_SIZE,
584 };
585
586 DRIVER_MODULE(snd_hdspe, pci, hdspe_driver, 0, 0);
587