15687c71dSFlorian Walpen /*- 25687c71dSFlorian Walpen * SPDX-License-Identifier: BSD-2-Clause 35687c71dSFlorian Walpen * 45687c71dSFlorian Walpen * Copyright (c) 2012-2021 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 (pcm-part). 325687c71dSFlorian Walpen * Supported cards: HDSP 9632, HDSP 9652. 335687c71dSFlorian Walpen */ 345687c71dSFlorian Walpen 355687c71dSFlorian Walpen #include <sys/libkern.h> 365687c71dSFlorian Walpen 375687c71dSFlorian Walpen #include <dev/sound/pcm/sound.h> 385687c71dSFlorian Walpen #include <dev/sound/pci/hdsp.h> 395687c71dSFlorian Walpen 405687c71dSFlorian Walpen #include <dev/pci/pcireg.h> 415687c71dSFlorian Walpen #include <dev/pci/pcivar.h> 425687c71dSFlorian Walpen 435687c71dSFlorian Walpen #include <mixer_if.h> 445687c71dSFlorian Walpen 455687c71dSFlorian Walpen #define HDSP_MATRIX_MAX 8 465687c71dSFlorian Walpen 475687c71dSFlorian Walpen struct hdsp_latency { 485687c71dSFlorian Walpen uint32_t n; 495687c71dSFlorian Walpen uint32_t period; 505687c71dSFlorian Walpen float ms; 515687c71dSFlorian Walpen }; 525687c71dSFlorian Walpen 535687c71dSFlorian Walpen static struct hdsp_latency latency_map[] = { 545687c71dSFlorian Walpen { 7, 32, 0.7 }, 555687c71dSFlorian Walpen { 0, 64, 1.5 }, 565687c71dSFlorian Walpen { 1, 128, 3 }, 575687c71dSFlorian Walpen { 2, 256, 6 }, 585687c71dSFlorian Walpen { 3, 512, 12 }, 595687c71dSFlorian Walpen { 4, 1024, 23 }, 605687c71dSFlorian Walpen { 5, 2048, 46 }, 615687c71dSFlorian Walpen { 6, 4096, 93 }, 625687c71dSFlorian Walpen 635687c71dSFlorian Walpen { 0, 0, 0 }, 645687c71dSFlorian Walpen }; 655687c71dSFlorian Walpen 665687c71dSFlorian Walpen struct hdsp_rate { 675687c71dSFlorian Walpen uint32_t speed; 685687c71dSFlorian Walpen uint32_t reg; 695687c71dSFlorian Walpen }; 705687c71dSFlorian Walpen 715687c71dSFlorian Walpen static struct hdsp_rate rate_map[] = { 725687c71dSFlorian Walpen { 32000, (HDSP_FREQ_32000) }, 735687c71dSFlorian Walpen { 44100, (HDSP_FREQ_44100) }, 745687c71dSFlorian Walpen { 48000, (HDSP_FREQ_48000) }, 755687c71dSFlorian Walpen { 64000, (HDSP_FREQ_32000 | HDSP_FREQ_DOUBLE) }, 765687c71dSFlorian Walpen { 88200, (HDSP_FREQ_44100 | HDSP_FREQ_DOUBLE) }, 775687c71dSFlorian Walpen { 96000, (HDSP_FREQ_48000 | HDSP_FREQ_DOUBLE) }, 785687c71dSFlorian Walpen { 128000, (HDSP_FREQ_32000 | HDSP_FREQ_QUAD) }, 795687c71dSFlorian Walpen { 176400, (HDSP_FREQ_44100 | HDSP_FREQ_QUAD) }, 805687c71dSFlorian Walpen { 192000, (HDSP_FREQ_48000 | HDSP_FREQ_QUAD) }, 815687c71dSFlorian Walpen 825687c71dSFlorian Walpen { 0, 0 }, 835687c71dSFlorian Walpen }; 845687c71dSFlorian Walpen 855687c71dSFlorian Walpen static uint32_t 865687c71dSFlorian Walpen hdsp_adat_slot_map(uint32_t speed) 875687c71dSFlorian Walpen { 885687c71dSFlorian Walpen /* ADAT slot bitmap depends on sample rate. */ 895687c71dSFlorian Walpen if (speed <= 48000) 905687c71dSFlorian Walpen return (0x000000ff); /* 8 channels single speed. */ 915687c71dSFlorian Walpen else if (speed <= 96000) 925687c71dSFlorian Walpen return (0x000000aa); /* 4 channels (1,3,5,7) double speed. */ 935687c71dSFlorian Walpen else 945687c71dSFlorian Walpen return (0x00000000); /* ADAT disabled at quad speed. */ 955687c71dSFlorian Walpen } 965687c71dSFlorian Walpen 975687c71dSFlorian Walpen static uint32_t 985687c71dSFlorian Walpen hdsp_port_slot_map(uint32_t ports, uint32_t speed) 995687c71dSFlorian Walpen { 1005687c71dSFlorian Walpen uint32_t slot_map = 0; 1015687c71dSFlorian Walpen 1025687c71dSFlorian Walpen if (ports & HDSP_CHAN_9632_ALL) { 1035687c71dSFlorian Walpen /* Map HDSP 9632 ports to slot bitmap. */ 1045687c71dSFlorian Walpen if (ports & HDSP_CHAN_9632_ADAT) 1055687c71dSFlorian Walpen slot_map |= (hdsp_adat_slot_map(speed) << 0); 1065687c71dSFlorian Walpen if (ports & HDSP_CHAN_9632_SPDIF) 1075687c71dSFlorian Walpen slot_map |= (0x03 << 8); /* 2 channels SPDIF. */ 1085687c71dSFlorian Walpen if (ports & HDSP_CHAN_9632_LINE) 1095687c71dSFlorian Walpen slot_map |= (0x03 << 10); /* 2 channels line. */ 110e0c37c16SFlorian Walpen if (ports & HDSP_CHAN_9632_EXT) 1115687c71dSFlorian Walpen slot_map |= (0x0f << 12); /* 4 channels extension. */ 1125687c71dSFlorian Walpen } else if ((ports & HDSP_CHAN_9652_ALL) && (speed <= 96000)) { 1135687c71dSFlorian Walpen /* Map HDSP 9652 ports to slot bitmap, no quad speed. */ 1145687c71dSFlorian Walpen if (ports & HDSP_CHAN_9652_ADAT1) 1155687c71dSFlorian Walpen slot_map |= (hdsp_adat_slot_map(speed) << 0); 1165687c71dSFlorian Walpen if (ports & HDSP_CHAN_9652_ADAT2) 1175687c71dSFlorian Walpen slot_map |= (hdsp_adat_slot_map(speed) << 8); 1185687c71dSFlorian Walpen if (ports & HDSP_CHAN_9652_ADAT3) 1195687c71dSFlorian Walpen slot_map |= (hdsp_adat_slot_map(speed) << 16); 1205687c71dSFlorian Walpen if (ports & HDSP_CHAN_9652_SPDIF) 1215687c71dSFlorian Walpen slot_map |= (0x03 << 24); /* 2 channels SPDIF. */ 1225687c71dSFlorian Walpen } 1235687c71dSFlorian Walpen 1245687c71dSFlorian Walpen return (slot_map); 1255687c71dSFlorian Walpen } 1265687c71dSFlorian Walpen 1275687c71dSFlorian Walpen static uint32_t 1285687c71dSFlorian Walpen hdsp_slot_first(uint32_t slots) 1295687c71dSFlorian Walpen { 1305687c71dSFlorian Walpen return (slots & (~(slots - 1))); /* Extract first bit set. */ 1315687c71dSFlorian Walpen } 1325687c71dSFlorian Walpen 1335687c71dSFlorian Walpen static uint32_t 1345687c71dSFlorian Walpen hdsp_slot_first_row(uint32_t slots) 1355687c71dSFlorian Walpen { 1365687c71dSFlorian Walpen uint32_t ends; 1375687c71dSFlorian Walpen 1385687c71dSFlorian Walpen /* Ends of slot rows are followed by a slot which is not in the set. */ 1395687c71dSFlorian Walpen ends = slots & (~(slots >> 1)); 1405687c71dSFlorian Walpen /* First row of contiguous slots ends in the first row end. */ 1415687c71dSFlorian Walpen return (slots & (ends ^ (ends - 1))); 1425687c71dSFlorian Walpen } 1435687c71dSFlorian Walpen 1445687c71dSFlorian Walpen static uint32_t 1455687c71dSFlorian Walpen hdsp_slot_first_n(uint32_t slots, unsigned int n) 1465687c71dSFlorian Walpen { 1475687c71dSFlorian Walpen /* Clear all but the first n slots. */ 1485687c71dSFlorian Walpen for (uint32_t slot = 1; slot != 0; slot <<= 1) { 1495687c71dSFlorian Walpen if ((slots & slot) && n > 0) 1505687c71dSFlorian Walpen --n; 1515687c71dSFlorian Walpen else 1525687c71dSFlorian Walpen slots &= ~slot; 1535687c71dSFlorian Walpen } 1545687c71dSFlorian Walpen return (slots); 1555687c71dSFlorian Walpen } 1565687c71dSFlorian Walpen 1575687c71dSFlorian Walpen static unsigned int 1585687c71dSFlorian Walpen hdsp_slot_count(uint32_t slots) 1595687c71dSFlorian Walpen { 1605687c71dSFlorian Walpen return (bitcount32(slots)); 1615687c71dSFlorian Walpen } 1625687c71dSFlorian Walpen 1635687c71dSFlorian Walpen static unsigned int 1645687c71dSFlorian Walpen hdsp_slot_offset(uint32_t slots) 1655687c71dSFlorian Walpen { 1665687c71dSFlorian Walpen return (hdsp_slot_count(hdsp_slot_first(slots) - 1)); 1675687c71dSFlorian Walpen } 1685687c71dSFlorian Walpen 1695687c71dSFlorian Walpen static unsigned int 1705687c71dSFlorian Walpen hdsp_slot_channel_offset(uint32_t subset, uint32_t slots) 1715687c71dSFlorian Walpen { 1725687c71dSFlorian Walpen uint32_t preceding; 1735687c71dSFlorian Walpen 1745687c71dSFlorian Walpen /* Make sure we have a subset of slots. */ 1755687c71dSFlorian Walpen subset &= slots; 1765687c71dSFlorian Walpen /* Include all slots preceding the first one of the subset. */ 1775687c71dSFlorian Walpen preceding = slots & (hdsp_slot_first(subset) - 1); 1785687c71dSFlorian Walpen 1795687c71dSFlorian Walpen return (hdsp_slot_count(preceding)); 1805687c71dSFlorian Walpen } 1815687c71dSFlorian Walpen 1825687c71dSFlorian Walpen static uint32_t 1835687c71dSFlorian Walpen hdsp_port_first(uint32_t ports) 1845687c71dSFlorian Walpen { 1855687c71dSFlorian Walpen return (ports & (~(ports - 1))); /* Extract first bit set. */ 1865687c71dSFlorian Walpen } 1875687c71dSFlorian Walpen 1885687c71dSFlorian Walpen static unsigned int 1895687c71dSFlorian Walpen hdsp_port_slot_count(uint32_t ports, uint32_t speed) 1905687c71dSFlorian Walpen { 1915687c71dSFlorian Walpen return (hdsp_slot_count(hdsp_port_slot_map(ports, speed))); 1925687c71dSFlorian Walpen } 1935687c71dSFlorian Walpen 1945687c71dSFlorian Walpen static unsigned int 1955687c71dSFlorian Walpen hdsp_port_slot_count_max(uint32_t ports) 1965687c71dSFlorian Walpen { 1975687c71dSFlorian Walpen return (hdsp_slot_count(hdsp_port_slot_map(ports, 48000))); 1985687c71dSFlorian Walpen } 1995687c71dSFlorian Walpen 2005687c71dSFlorian Walpen static uint32_t 2015687c71dSFlorian Walpen hdsp_channel_play_ports(struct hdsp_channel *hc) 2025687c71dSFlorian Walpen { 2035687c71dSFlorian Walpen return (hc->ports & (HDSP_CHAN_9632_ALL | HDSP_CHAN_9652_ALL)); 2045687c71dSFlorian Walpen } 2055687c71dSFlorian Walpen 2065687c71dSFlorian Walpen static uint32_t 2075687c71dSFlorian Walpen hdsp_channel_rec_ports(struct hdsp_channel *hc) 2085687c71dSFlorian Walpen { 2095687c71dSFlorian Walpen return (hc->ports & (HDSP_CHAN_9632_ALL | HDSP_CHAN_9652_ALL)); 2105687c71dSFlorian Walpen } 2115687c71dSFlorian Walpen 2125687c71dSFlorian Walpen static int 2135687c71dSFlorian Walpen hdsp_hw_mixer(struct sc_chinfo *ch, unsigned int dst, 2145687c71dSFlorian Walpen unsigned int src, unsigned short data) 2155687c71dSFlorian Walpen { 2165687c71dSFlorian Walpen struct sc_pcminfo *scp; 2175687c71dSFlorian Walpen struct sc_info *sc; 2185687c71dSFlorian Walpen uint32_t value; 2195687c71dSFlorian Walpen int offset; 2205687c71dSFlorian Walpen 2215687c71dSFlorian Walpen scp = ch->parent; 2225687c71dSFlorian Walpen sc = scp->sc; 2235687c71dSFlorian Walpen 2245687c71dSFlorian Walpen offset = 0; 2255687c71dSFlorian Walpen value = (HDSP_MIN_GAIN << 16) | (uint16_t) data; 2265687c71dSFlorian Walpen 2275687c71dSFlorian Walpen if (ch->dir != PCMDIR_PLAY) 2285687c71dSFlorian Walpen return (0); 2295687c71dSFlorian Walpen 2305687c71dSFlorian Walpen switch (sc->type) { 2315687c71dSFlorian Walpen case HDSP_9632: 2325687c71dSFlorian Walpen /* Mixer is 2 rows of sources (inputs, playback) per output. */ 2335687c71dSFlorian Walpen offset = dst * (2 * HDSP_MIX_SLOTS_9632); 2345687c71dSFlorian Walpen /* Source index in the second row (playback). */ 2355687c71dSFlorian Walpen offset += HDSP_MIX_SLOTS_9632 + src; 2365687c71dSFlorian Walpen break; 2375687c71dSFlorian Walpen case HDSP_9652: 2385687c71dSFlorian Walpen /* Mixer is 2 rows of sources (inputs, playback) per output. */ 2395687c71dSFlorian Walpen offset = dst * (2 * HDSP_MIX_SLOTS_9652); 2405687c71dSFlorian Walpen /* Source index in the second row (playback). */ 2415687c71dSFlorian Walpen offset += HDSP_MIX_SLOTS_9652 + src; 2425687c71dSFlorian Walpen break; 2435687c71dSFlorian Walpen default: 2445687c71dSFlorian Walpen return (0); 2455687c71dSFlorian Walpen } 2465687c71dSFlorian Walpen 2475687c71dSFlorian Walpen /* 2485687c71dSFlorian Walpen * We have to write mixer matrix values in pairs, with the second 2495687c71dSFlorian Walpen * (odd) value in the upper 16 bits of the 32 bit value. 2505687c71dSFlorian Walpen * Make value offset even and shift value accordingly. 2515687c71dSFlorian Walpen * Assume the paired value to be silenced, since we only set gain 2525687c71dSFlorian Walpen * on the diagonal where src and dst are the same. 2535687c71dSFlorian Walpen */ 2545687c71dSFlorian Walpen if (offset % 2) { 2555687c71dSFlorian Walpen offset -= 1; 2565687c71dSFlorian Walpen value = (value << 16) | HDSP_MIN_GAIN; 2575687c71dSFlorian Walpen } 2585687c71dSFlorian Walpen 2595687c71dSFlorian Walpen hdsp_write_4(sc, HDSP_MIXER_BASE + offset * sizeof(uint16_t), value); 2605687c71dSFlorian Walpen 2615687c71dSFlorian Walpen return (0); 2625687c71dSFlorian Walpen }; 2635687c71dSFlorian Walpen 2645687c71dSFlorian Walpen static int 2655687c71dSFlorian Walpen hdspchan_setgain(struct sc_chinfo *ch) 2665687c71dSFlorian Walpen { 2675687c71dSFlorian Walpen uint32_t port, ports; 2685687c71dSFlorian Walpen uint32_t slot, slots; 2695687c71dSFlorian Walpen unsigned int offset; 2705687c71dSFlorian Walpen unsigned short volume; 2715687c71dSFlorian Walpen 2725687c71dSFlorian Walpen /* Iterate through all physical ports of the channel. */ 2735687c71dSFlorian Walpen ports = ch->ports; 2745687c71dSFlorian Walpen port = hdsp_port_first(ports); 2755687c71dSFlorian Walpen while (port != 0) { 2765687c71dSFlorian Walpen /* 2775687c71dSFlorian Walpen * Get slot map from physical port. 2785687c71dSFlorian Walpen * Unlike DMA buffers, the hardware mixer's channel mapping 2795687c71dSFlorian Walpen * does not change with double or quad speed sample rates. 2805687c71dSFlorian Walpen */ 2815687c71dSFlorian Walpen slots = hdsp_port_slot_map(port, 48000); 2825687c71dSFlorian Walpen slot = hdsp_slot_first(slots); 2835687c71dSFlorian Walpen 2845687c71dSFlorian Walpen /* Treat first slot as left channel. */ 2855687c71dSFlorian Walpen volume = ch->lvol * HDSP_MAX_GAIN / 100; 2865687c71dSFlorian Walpen while (slot != 0) { 2875687c71dSFlorian Walpen offset = hdsp_slot_offset(slot); 2885687c71dSFlorian Walpen hdsp_hw_mixer(ch, offset, offset, volume); 2895687c71dSFlorian Walpen 2905687c71dSFlorian Walpen slots &= ~slot; 2915687c71dSFlorian Walpen slot = hdsp_slot_first(slots); 2925687c71dSFlorian Walpen 2935687c71dSFlorian Walpen /* Subsequent slots all get the right channel volume. */ 2945687c71dSFlorian Walpen volume = ch->rvol * HDSP_MAX_GAIN / 100; 2955687c71dSFlorian Walpen } 2965687c71dSFlorian Walpen 2975687c71dSFlorian Walpen ports &= ~port; 2985687c71dSFlorian Walpen port = hdsp_port_first(ports); 2995687c71dSFlorian Walpen } 3005687c71dSFlorian Walpen 3015687c71dSFlorian Walpen return (0); 3025687c71dSFlorian Walpen } 3035687c71dSFlorian Walpen 3045687c71dSFlorian Walpen static int 3055687c71dSFlorian Walpen hdspmixer_init(struct snd_mixer *m) 3065687c71dSFlorian Walpen { 3075687c71dSFlorian Walpen struct sc_pcminfo *scp; 3085687c71dSFlorian Walpen struct sc_info *sc; 3095687c71dSFlorian Walpen int mask; 3105687c71dSFlorian Walpen 3115687c71dSFlorian Walpen scp = mix_getdevinfo(m); 3125687c71dSFlorian Walpen sc = scp->sc; 3135687c71dSFlorian Walpen if (sc == NULL) 3145687c71dSFlorian Walpen return (-1); 3155687c71dSFlorian Walpen 3165687c71dSFlorian Walpen mask = SOUND_MASK_PCM; 3175687c71dSFlorian Walpen 3185687c71dSFlorian Walpen if (hdsp_channel_play_ports(scp->hc)) 3195687c71dSFlorian Walpen mask |= SOUND_MASK_VOLUME; 3205687c71dSFlorian Walpen 3215687c71dSFlorian Walpen if (hdsp_channel_rec_ports(scp->hc)) 3225687c71dSFlorian Walpen mask |= SOUND_MASK_RECLEV; 3235687c71dSFlorian Walpen 3245687c71dSFlorian Walpen snd_mtxlock(sc->lock); 3255687c71dSFlorian Walpen pcm_setflags(scp->dev, pcm_getflags(scp->dev) | SD_F_SOFTPCMVOL); 3265687c71dSFlorian Walpen mix_setdevs(m, mask); 3275687c71dSFlorian Walpen snd_mtxunlock(sc->lock); 3285687c71dSFlorian Walpen 3295687c71dSFlorian Walpen return (0); 3305687c71dSFlorian Walpen } 3315687c71dSFlorian Walpen 3325687c71dSFlorian Walpen static int 3335687c71dSFlorian Walpen hdspmixer_set(struct snd_mixer *m, unsigned dev, 3345687c71dSFlorian Walpen unsigned left, unsigned right) 3355687c71dSFlorian Walpen { 3365687c71dSFlorian Walpen struct sc_pcminfo *scp; 3375687c71dSFlorian Walpen struct sc_chinfo *ch; 3385687c71dSFlorian Walpen int i; 3395687c71dSFlorian Walpen 3405687c71dSFlorian Walpen scp = mix_getdevinfo(m); 3415687c71dSFlorian Walpen 3425687c71dSFlorian Walpen #if 0 3435687c71dSFlorian Walpen device_printf(scp->dev, "hdspmixer_set() %d %d\n", 3445687c71dSFlorian Walpen left, right); 3455687c71dSFlorian Walpen #endif 3465687c71dSFlorian Walpen 3475687c71dSFlorian Walpen for (i = 0; i < scp->chnum; i++) { 3485687c71dSFlorian Walpen ch = &scp->chan[i]; 3495687c71dSFlorian Walpen if ((dev == SOUND_MIXER_VOLUME && ch->dir == PCMDIR_PLAY) || 3505687c71dSFlorian Walpen (dev == SOUND_MIXER_RECLEV && ch->dir == PCMDIR_REC)) { 3515687c71dSFlorian Walpen ch->lvol = left; 3525687c71dSFlorian Walpen ch->rvol = right; 3535687c71dSFlorian Walpen if (ch->run) 3545687c71dSFlorian Walpen hdspchan_setgain(ch); 3555687c71dSFlorian Walpen } 3565687c71dSFlorian Walpen } 3575687c71dSFlorian Walpen 3585687c71dSFlorian Walpen return (0); 3595687c71dSFlorian Walpen } 3605687c71dSFlorian Walpen 3615687c71dSFlorian Walpen static kobj_method_t hdspmixer_methods[] = { 3625687c71dSFlorian Walpen KOBJMETHOD(mixer_init, hdspmixer_init), 3635687c71dSFlorian Walpen KOBJMETHOD(mixer_set, hdspmixer_set), 3645687c71dSFlorian Walpen KOBJMETHOD_END 3655687c71dSFlorian Walpen }; 3665687c71dSFlorian Walpen MIXER_DECLARE(hdspmixer); 3675687c71dSFlorian Walpen 3685687c71dSFlorian Walpen static void 3695687c71dSFlorian Walpen hdspchan_enable(struct sc_chinfo *ch, int value) 3705687c71dSFlorian Walpen { 3715687c71dSFlorian Walpen struct sc_pcminfo *scp; 3725687c71dSFlorian Walpen struct sc_info *sc; 3735687c71dSFlorian Walpen uint32_t slot, slots; 3745687c71dSFlorian Walpen unsigned int offset; 3755687c71dSFlorian Walpen int reg; 3765687c71dSFlorian Walpen 3775687c71dSFlorian Walpen scp = ch->parent; 3785687c71dSFlorian Walpen sc = scp->sc; 3795687c71dSFlorian Walpen 3805687c71dSFlorian Walpen if (ch->dir == PCMDIR_PLAY) 3815687c71dSFlorian Walpen reg = HDSP_OUT_ENABLE_BASE; 3825687c71dSFlorian Walpen else 3835687c71dSFlorian Walpen reg = HDSP_IN_ENABLE_BASE; 3845687c71dSFlorian Walpen 3855687c71dSFlorian Walpen ch->run = value; 3865687c71dSFlorian Walpen 3875687c71dSFlorian Walpen /* Iterate through all slots of the channel's physical ports. */ 3885687c71dSFlorian Walpen slots = hdsp_port_slot_map(ch->ports, sc->speed); 3895687c71dSFlorian Walpen slot = hdsp_slot_first(slots); 3905687c71dSFlorian Walpen while (slot != 0) { 3915687c71dSFlorian Walpen /* Set register to enable or disable slot. */ 3925687c71dSFlorian Walpen offset = hdsp_slot_offset(slot); 3935687c71dSFlorian Walpen hdsp_write_1(sc, reg + (4 * offset), value); 3945687c71dSFlorian Walpen 3955687c71dSFlorian Walpen slots &= ~slot; 3965687c71dSFlorian Walpen slot = hdsp_slot_first(slots); 3975687c71dSFlorian Walpen } 3985687c71dSFlorian Walpen } 3995687c71dSFlorian Walpen 4005687c71dSFlorian Walpen static int 4015687c71dSFlorian Walpen hdsp_running(struct sc_info *sc) 4025687c71dSFlorian Walpen { 4035687c71dSFlorian Walpen struct sc_pcminfo *scp; 4045687c71dSFlorian Walpen struct sc_chinfo *ch; 4055687c71dSFlorian Walpen device_t *devlist; 4065687c71dSFlorian Walpen int devcount; 4075687c71dSFlorian Walpen int i, j; 4085687c71dSFlorian Walpen int running; 4095687c71dSFlorian Walpen 4105687c71dSFlorian Walpen running = 0; 4115687c71dSFlorian Walpen 4125687c71dSFlorian Walpen devlist = NULL; 4135687c71dSFlorian Walpen devcount = 0; 4145687c71dSFlorian Walpen 4155687c71dSFlorian Walpen if (device_get_children(sc->dev, &devlist, &devcount) != 0) 4165687c71dSFlorian Walpen running = 1; /* On error, avoid channel config changes. */ 4175687c71dSFlorian Walpen 4185687c71dSFlorian Walpen for (i = 0; running == 0 && i < devcount; i++) { 4195687c71dSFlorian Walpen scp = device_get_ivars(devlist[i]); 4205687c71dSFlorian Walpen for (j = 0; j < scp->chnum; j++) { 4215687c71dSFlorian Walpen ch = &scp->chan[j]; 4225687c71dSFlorian Walpen if (ch->run) { 4235687c71dSFlorian Walpen running = 1; 4245687c71dSFlorian Walpen break; 4255687c71dSFlorian Walpen } 4265687c71dSFlorian Walpen } 4275687c71dSFlorian Walpen } 4285687c71dSFlorian Walpen 4295687c71dSFlorian Walpen #if 0 4305687c71dSFlorian Walpen if (running == 1) 4315687c71dSFlorian Walpen device_printf(sc->dev, "hdsp is running\n"); 4325687c71dSFlorian Walpen #endif 4335687c71dSFlorian Walpen 4345687c71dSFlorian Walpen free(devlist, M_TEMP); 4355687c71dSFlorian Walpen 4365687c71dSFlorian Walpen return (running); 4375687c71dSFlorian Walpen } 4385687c71dSFlorian Walpen 4395687c71dSFlorian Walpen static void 4405687c71dSFlorian Walpen hdsp_start_audio(struct sc_info *sc) 4415687c71dSFlorian Walpen { 4425687c71dSFlorian Walpen 4435687c71dSFlorian Walpen sc->ctrl_register |= (HDSP_AUDIO_INT_ENABLE | HDSP_ENABLE); 4445687c71dSFlorian Walpen hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register); 4455687c71dSFlorian Walpen } 4465687c71dSFlorian Walpen 4475687c71dSFlorian Walpen static void 4485687c71dSFlorian Walpen hdsp_stop_audio(struct sc_info *sc) 4495687c71dSFlorian Walpen { 4505687c71dSFlorian Walpen 4515687c71dSFlorian Walpen if (hdsp_running(sc) == 1) 4525687c71dSFlorian Walpen return; 4535687c71dSFlorian Walpen 4545687c71dSFlorian Walpen sc->ctrl_register &= ~(HDSP_AUDIO_INT_ENABLE | HDSP_ENABLE); 4555687c71dSFlorian Walpen hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register); 4565687c71dSFlorian Walpen } 4575687c71dSFlorian Walpen 4585687c71dSFlorian Walpen static void 4595687c71dSFlorian Walpen buffer_mux_write(uint32_t *dma, uint32_t *pcm, unsigned int pos, 4605687c71dSFlorian Walpen unsigned int pos_end, unsigned int width, unsigned int channels) 4615687c71dSFlorian Walpen { 4625687c71dSFlorian Walpen unsigned int slot; 4635687c71dSFlorian Walpen 4645687c71dSFlorian Walpen for (; pos < pos_end; ++pos) { 4655687c71dSFlorian Walpen for (slot = 0; slot < width; slot++) { 4665687c71dSFlorian Walpen dma[slot * HDSP_CHANBUF_SAMPLES + pos] = 4675687c71dSFlorian Walpen pcm[pos * channels + slot]; 4685687c71dSFlorian Walpen } 4695687c71dSFlorian Walpen } 4705687c71dSFlorian Walpen } 4715687c71dSFlorian Walpen 4725687c71dSFlorian Walpen static void 4735687c71dSFlorian Walpen buffer_mux_port(uint32_t *dma, uint32_t *pcm, uint32_t subset, uint32_t slots, 4745687c71dSFlorian Walpen unsigned int pos, unsigned int samples, unsigned int channels) 4755687c71dSFlorian Walpen { 4765687c71dSFlorian Walpen unsigned int slot_offset, width; 4775687c71dSFlorian Walpen unsigned int chan_pos; 4785687c71dSFlorian Walpen 4795687c71dSFlorian Walpen /* Translate DMA slot offset to DMA buffer offset. */ 4805687c71dSFlorian Walpen slot_offset = hdsp_slot_offset(subset); 4815687c71dSFlorian Walpen dma += slot_offset * HDSP_CHANBUF_SAMPLES; 4825687c71dSFlorian Walpen 4835687c71dSFlorian Walpen /* Channel position of the slot subset. */ 4845687c71dSFlorian Walpen chan_pos = hdsp_slot_channel_offset(subset, slots); 4855687c71dSFlorian Walpen pcm += chan_pos; 4865687c71dSFlorian Walpen 4875687c71dSFlorian Walpen /* Only copy channels supported by both hardware and pcm format. */ 4885687c71dSFlorian Walpen width = hdsp_slot_count(subset); 4895687c71dSFlorian Walpen 4905687c71dSFlorian Walpen /* Let the compiler inline and loop unroll common cases. */ 4915687c71dSFlorian Walpen if (width == 1) 4925687c71dSFlorian Walpen buffer_mux_write(dma, pcm, pos, pos + samples, 1, channels); 4935687c71dSFlorian Walpen else if (width == 2) 4945687c71dSFlorian Walpen buffer_mux_write(dma, pcm, pos, pos + samples, 2, channels); 4955687c71dSFlorian Walpen else if (width == 4) 4965687c71dSFlorian Walpen buffer_mux_write(dma, pcm, pos, pos + samples, 4, channels); 4975687c71dSFlorian Walpen else if (width == 8) 4985687c71dSFlorian Walpen buffer_mux_write(dma, pcm, pos, pos + samples, 8, channels); 4995687c71dSFlorian Walpen else 5005687c71dSFlorian Walpen buffer_mux_write(dma, pcm, pos, pos + samples, width, channels); 5015687c71dSFlorian Walpen } 5025687c71dSFlorian Walpen 5035687c71dSFlorian Walpen static void 5045687c71dSFlorian Walpen buffer_demux_read(uint32_t *dma, uint32_t *pcm, unsigned int pos, 5055687c71dSFlorian Walpen unsigned int pos_end, unsigned int width, unsigned int channels) 5065687c71dSFlorian Walpen { 5075687c71dSFlorian Walpen unsigned int slot; 5085687c71dSFlorian Walpen 5095687c71dSFlorian Walpen for (; pos < pos_end; ++pos) { 5105687c71dSFlorian Walpen for (slot = 0; slot < width; slot++) { 5115687c71dSFlorian Walpen pcm[pos * channels + slot] = 5125687c71dSFlorian Walpen dma[slot * HDSP_CHANBUF_SAMPLES + pos]; 5135687c71dSFlorian Walpen } 5145687c71dSFlorian Walpen } 5155687c71dSFlorian Walpen } 5165687c71dSFlorian Walpen 5175687c71dSFlorian Walpen static void 5185687c71dSFlorian Walpen buffer_demux_port(uint32_t *dma, uint32_t *pcm, uint32_t subset, uint32_t slots, 5195687c71dSFlorian Walpen unsigned int pos, unsigned int samples, unsigned int channels) 5205687c71dSFlorian Walpen { 5215687c71dSFlorian Walpen unsigned int slot_offset, width; 5225687c71dSFlorian Walpen unsigned int chan_pos; 5235687c71dSFlorian Walpen 5245687c71dSFlorian Walpen /* Translate DMA slot offset to DMA buffer offset. */ 5255687c71dSFlorian Walpen slot_offset = hdsp_slot_offset(subset); 5265687c71dSFlorian Walpen dma += slot_offset * HDSP_CHANBUF_SAMPLES; 5275687c71dSFlorian Walpen 5285687c71dSFlorian Walpen /* Channel position of the slot subset. */ 5295687c71dSFlorian Walpen chan_pos = hdsp_slot_channel_offset(subset, slots); 5305687c71dSFlorian Walpen pcm += chan_pos; 5315687c71dSFlorian Walpen 5325687c71dSFlorian Walpen /* Only copy channels supported by both hardware and pcm format. */ 5335687c71dSFlorian Walpen width = hdsp_slot_count(subset); 5345687c71dSFlorian Walpen 5355687c71dSFlorian Walpen /* Let the compiler inline and loop unroll common cases. */ 5365687c71dSFlorian Walpen if (width == 1) 5375687c71dSFlorian Walpen buffer_demux_read(dma, pcm, pos, pos + samples, 1, channels); 5385687c71dSFlorian Walpen else if (width == 2) 5395687c71dSFlorian Walpen buffer_demux_read(dma, pcm, pos, pos + samples, 2, channels); 5405687c71dSFlorian Walpen else if (width == 4) 5415687c71dSFlorian Walpen buffer_demux_read(dma, pcm, pos, pos + samples, 4, channels); 5425687c71dSFlorian Walpen else if (width == 8) 5435687c71dSFlorian Walpen buffer_demux_read(dma, pcm, pos, pos + samples, 8, channels); 5445687c71dSFlorian Walpen else 5455687c71dSFlorian Walpen buffer_demux_read(dma, pcm, pos, pos + samples, width, channels); 5465687c71dSFlorian Walpen } 5475687c71dSFlorian Walpen 5485687c71dSFlorian Walpen 5495687c71dSFlorian Walpen /* Copy data between DMA and PCM buffers. */ 5505687c71dSFlorian Walpen static void 5515687c71dSFlorian Walpen buffer_copy(struct sc_chinfo *ch) 5525687c71dSFlorian Walpen { 5535687c71dSFlorian Walpen struct sc_pcminfo *scp; 5545687c71dSFlorian Walpen struct sc_info *sc; 5555687c71dSFlorian Walpen uint32_t row, slots; 5565687c71dSFlorian Walpen uint32_t dma_pos; 5575687c71dSFlorian Walpen unsigned int pos, length, remainder, offset, buffer_size; 5585687c71dSFlorian Walpen unsigned int channels; 5595687c71dSFlorian Walpen 5605687c71dSFlorian Walpen scp = ch->parent; 5615687c71dSFlorian Walpen sc = scp->sc; 5625687c71dSFlorian Walpen 5635687c71dSFlorian Walpen channels = AFMT_CHANNEL(ch->format); /* Number of PCM channels. */ 5645687c71dSFlorian Walpen 5655687c71dSFlorian Walpen /* HDSP cards read / write a double buffer, twice the latency period. */ 5665687c71dSFlorian Walpen buffer_size = 2 * sc->period * sizeof(uint32_t); 5675687c71dSFlorian Walpen 5685687c71dSFlorian Walpen /* Derive buffer position and length to be copied. */ 5695687c71dSFlorian Walpen if (ch->dir == PCMDIR_PLAY) { 5705687c71dSFlorian Walpen /* Buffer position scaled down to a single channel. */ 5715687c71dSFlorian Walpen pos = sndbuf_getreadyptr(ch->buffer) / channels; 5725687c71dSFlorian Walpen length = sndbuf_getready(ch->buffer) / channels; 5735687c71dSFlorian Walpen /* Copy no more than 2 periods in advance. */ 5745687c71dSFlorian Walpen if (length > buffer_size) 5755687c71dSFlorian Walpen length = buffer_size; 5765687c71dSFlorian Walpen /* Skip what was already copied last time. */ 5775687c71dSFlorian Walpen offset = (ch->position + buffer_size) - pos; 5785687c71dSFlorian Walpen offset %= buffer_size; 5795687c71dSFlorian Walpen if (offset <= length) { 5805687c71dSFlorian Walpen pos = (pos + offset) % buffer_size; 5815687c71dSFlorian Walpen length -= offset; 5825687c71dSFlorian Walpen } 5835687c71dSFlorian Walpen } else { 5845687c71dSFlorian Walpen /* Buffer position scaled down to a single channel. */ 5855687c71dSFlorian Walpen pos = sndbuf_getfreeptr(ch->buffer) / channels; 5865687c71dSFlorian Walpen /* Get DMA buffer write position. */ 5875687c71dSFlorian Walpen dma_pos = hdsp_read_2(sc, HDSP_STATUS_REG); 5885687c71dSFlorian Walpen dma_pos &= HDSP_BUF_POSITION_MASK; 5895687c71dSFlorian Walpen dma_pos %= buffer_size; 5905687c71dSFlorian Walpen /* Copy what is newly available. */ 5915687c71dSFlorian Walpen length = (dma_pos + buffer_size) - pos; 5925687c71dSFlorian Walpen length %= buffer_size; 5935687c71dSFlorian Walpen } 5945687c71dSFlorian Walpen 5955687c71dSFlorian Walpen /* Position and length in samples (4 bytes). */ 5965687c71dSFlorian Walpen pos /= 4; 5975687c71dSFlorian Walpen length /= 4; 5985687c71dSFlorian Walpen buffer_size /= sizeof(uint32_t); 5995687c71dSFlorian Walpen 6005687c71dSFlorian Walpen /* Split copy length to wrap around at buffer end. */ 6015687c71dSFlorian Walpen remainder = 0; 6025687c71dSFlorian Walpen if (pos + length > buffer_size) 6035687c71dSFlorian Walpen remainder = (pos + length) - buffer_size; 6045687c71dSFlorian Walpen 6055687c71dSFlorian Walpen /* Iterate through rows of contiguous slots. */ 6065687c71dSFlorian Walpen slots = hdsp_port_slot_map(ch->ports, sc->speed); 6075687c71dSFlorian Walpen slots = hdsp_slot_first_n(slots, channels); 6085687c71dSFlorian Walpen row = hdsp_slot_first_row(slots); 6095687c71dSFlorian Walpen 6105687c71dSFlorian Walpen while (row != 0) { 6115687c71dSFlorian Walpen if (ch->dir == PCMDIR_PLAY) { 6125687c71dSFlorian Walpen buffer_mux_port(sc->pbuf, ch->data, row, slots, pos, 6135687c71dSFlorian Walpen length - remainder, channels); 6145687c71dSFlorian Walpen buffer_mux_port(sc->pbuf, ch->data, row, slots, 0, 6155687c71dSFlorian Walpen remainder, channels); 6165687c71dSFlorian Walpen } else { 6175687c71dSFlorian Walpen buffer_demux_port(sc->rbuf, ch->data, row, slots, pos, 6185687c71dSFlorian Walpen length - remainder, channels); 6195687c71dSFlorian Walpen buffer_demux_port(sc->rbuf, ch->data, row, slots, 0, 6205687c71dSFlorian Walpen remainder, channels); 6215687c71dSFlorian Walpen } 6225687c71dSFlorian Walpen 6235687c71dSFlorian Walpen slots &= ~row; 6245687c71dSFlorian Walpen row = hdsp_slot_first_row(slots); 6255687c71dSFlorian Walpen } 6265687c71dSFlorian Walpen 6275687c71dSFlorian Walpen ch->position = ((pos + length) * 4) % buffer_size; 6285687c71dSFlorian Walpen } 6295687c71dSFlorian Walpen 6305687c71dSFlorian Walpen static int 6315687c71dSFlorian Walpen clean(struct sc_chinfo *ch) 6325687c71dSFlorian Walpen { 6335687c71dSFlorian Walpen struct sc_pcminfo *scp; 6345687c71dSFlorian Walpen struct sc_info *sc; 6355687c71dSFlorian Walpen uint32_t *buf; 6365687c71dSFlorian Walpen uint32_t slot, slots; 6375687c71dSFlorian Walpen unsigned int offset; 6385687c71dSFlorian Walpen 6395687c71dSFlorian Walpen scp = ch->parent; 6405687c71dSFlorian Walpen sc = scp->sc; 6415687c71dSFlorian Walpen buf = sc->rbuf; 6425687c71dSFlorian Walpen 6435687c71dSFlorian Walpen if (ch->dir == PCMDIR_PLAY) 6445687c71dSFlorian Walpen buf = sc->pbuf; 6455687c71dSFlorian Walpen 6465687c71dSFlorian Walpen /* Iterate through all of the channel's slots. */ 6475687c71dSFlorian Walpen slots = hdsp_port_slot_map(ch->ports, sc->speed); 6485687c71dSFlorian Walpen slot = hdsp_slot_first(slots); 6495687c71dSFlorian Walpen while (slot != 0) { 6505687c71dSFlorian Walpen /* Clear the slot's buffer. */ 6515687c71dSFlorian Walpen offset = hdsp_slot_offset(slot); 6525687c71dSFlorian Walpen bzero(buf + offset * HDSP_CHANBUF_SAMPLES, HDSP_CHANBUF_SIZE); 6535687c71dSFlorian Walpen 6545687c71dSFlorian Walpen slots &= ~slot; 6555687c71dSFlorian Walpen slot = hdsp_slot_first(slots); 6565687c71dSFlorian Walpen } 6575687c71dSFlorian Walpen 6585687c71dSFlorian Walpen ch->position = 0; 6595687c71dSFlorian Walpen 6605687c71dSFlorian Walpen return (0); 6615687c71dSFlorian Walpen } 6625687c71dSFlorian Walpen 6635687c71dSFlorian Walpen /* Channel interface. */ 66485d1c84cSChristos Margiolis static int 66585d1c84cSChristos Margiolis hdspchan_free(kobj_t obj, void *data) 66685d1c84cSChristos Margiolis { 66785d1c84cSChristos Margiolis struct sc_pcminfo *scp; 66885d1c84cSChristos Margiolis struct sc_chinfo *ch; 66985d1c84cSChristos Margiolis struct sc_info *sc; 67085d1c84cSChristos Margiolis 67185d1c84cSChristos Margiolis ch = data; 67285d1c84cSChristos Margiolis scp = ch->parent; 67385d1c84cSChristos Margiolis sc = scp->sc; 67485d1c84cSChristos Margiolis 67585d1c84cSChristos Margiolis #if 0 67685d1c84cSChristos Margiolis device_printf(scp->dev, "hdspchan_free()\n"); 67785d1c84cSChristos Margiolis #endif 67885d1c84cSChristos Margiolis 67985d1c84cSChristos Margiolis snd_mtxlock(sc->lock); 68085d1c84cSChristos Margiolis if (ch->data != NULL) { 68185d1c84cSChristos Margiolis free(ch->data, M_HDSP); 68285d1c84cSChristos Margiolis ch->data = NULL; 68385d1c84cSChristos Margiolis } 68485d1c84cSChristos Margiolis if (ch->caps != NULL) { 68585d1c84cSChristos Margiolis free(ch->caps, M_HDSP); 68685d1c84cSChristos Margiolis ch->caps = NULL; 68785d1c84cSChristos Margiolis } 68885d1c84cSChristos Margiolis snd_mtxunlock(sc->lock); 68985d1c84cSChristos Margiolis 69085d1c84cSChristos Margiolis return (0); 69185d1c84cSChristos Margiolis } 69285d1c84cSChristos Margiolis 6935687c71dSFlorian Walpen static void * 6945687c71dSFlorian Walpen hdspchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 6955687c71dSFlorian Walpen struct pcm_channel *c, int dir) 6965687c71dSFlorian Walpen { 6975687c71dSFlorian Walpen struct sc_pcminfo *scp; 6985687c71dSFlorian Walpen struct sc_chinfo *ch; 6995687c71dSFlorian Walpen struct sc_info *sc; 7005687c71dSFlorian Walpen int num; 7015687c71dSFlorian Walpen 7025687c71dSFlorian Walpen scp = devinfo; 7035687c71dSFlorian Walpen sc = scp->sc; 7045687c71dSFlorian Walpen 7055687c71dSFlorian Walpen snd_mtxlock(sc->lock); 7065687c71dSFlorian Walpen num = scp->chnum; 7075687c71dSFlorian Walpen 7085687c71dSFlorian Walpen ch = &scp->chan[num]; 7095687c71dSFlorian Walpen 7105687c71dSFlorian Walpen if (dir == PCMDIR_PLAY) 7115687c71dSFlorian Walpen ch->ports = hdsp_channel_play_ports(scp->hc); 7125687c71dSFlorian Walpen else 7135687c71dSFlorian Walpen ch->ports = hdsp_channel_rec_ports(scp->hc); 7145687c71dSFlorian Walpen 7155687c71dSFlorian Walpen ch->run = 0; 7165687c71dSFlorian Walpen ch->lvol = 0; 7175687c71dSFlorian Walpen ch->rvol = 0; 7185687c71dSFlorian Walpen 7195687c71dSFlorian Walpen /* Support all possible ADAT widths as channel formats. */ 7205687c71dSFlorian Walpen ch->cap_fmts[0] = 7215687c71dSFlorian Walpen SND_FORMAT(AFMT_S32_LE, hdsp_port_slot_count(ch->ports, 48000), 0); 7225687c71dSFlorian Walpen ch->cap_fmts[1] = 7235687c71dSFlorian Walpen SND_FORMAT(AFMT_S32_LE, hdsp_port_slot_count(ch->ports, 96000), 0); 7245687c71dSFlorian Walpen ch->cap_fmts[2] = 7255687c71dSFlorian Walpen SND_FORMAT(AFMT_S32_LE, hdsp_port_slot_count(ch->ports, 192000), 0); 7265687c71dSFlorian Walpen ch->cap_fmts[3] = 0; 7275687c71dSFlorian Walpen 7285687c71dSFlorian Walpen ch->caps = malloc(sizeof(struct pcmchan_caps), M_HDSP, M_NOWAIT); 7295687c71dSFlorian Walpen *(ch->caps) = (struct pcmchan_caps) {32000, 192000, ch->cap_fmts, 0}; 7305687c71dSFlorian Walpen 7315687c71dSFlorian Walpen /* HDSP 9652 does not support quad speed sample rates. */ 7325687c71dSFlorian Walpen if (sc->type == HDSP_9652) { 7335687c71dSFlorian Walpen ch->cap_fmts[2] = SND_FORMAT(AFMT_S32_LE, 2, 0); 7345687c71dSFlorian Walpen ch->caps->maxspeed = 96000; 7355687c71dSFlorian Walpen } 7365687c71dSFlorian Walpen 7375687c71dSFlorian Walpen /* Allocate maximum buffer size. */ 7385687c71dSFlorian Walpen ch->size = HDSP_CHANBUF_SIZE * hdsp_port_slot_count_max(ch->ports); 7395687c71dSFlorian Walpen ch->data = malloc(ch->size, M_HDSP, M_NOWAIT); 7405687c71dSFlorian Walpen ch->position = 0; 7415687c71dSFlorian Walpen 7425687c71dSFlorian Walpen ch->buffer = b; 7435687c71dSFlorian Walpen ch->channel = c; 7445687c71dSFlorian Walpen ch->parent = scp; 7455687c71dSFlorian Walpen 7465687c71dSFlorian Walpen ch->dir = dir; 7475687c71dSFlorian Walpen 7485687c71dSFlorian Walpen snd_mtxunlock(sc->lock); 7495687c71dSFlorian Walpen 7505687c71dSFlorian Walpen if (sndbuf_setup(ch->buffer, ch->data, ch->size) != 0) { 7515687c71dSFlorian Walpen device_printf(scp->dev, "Can't setup sndbuf.\n"); 75285d1c84cSChristos Margiolis hdspchan_free(obj, ch); 7535687c71dSFlorian Walpen return (NULL); 7545687c71dSFlorian Walpen } 7555687c71dSFlorian Walpen 7565687c71dSFlorian Walpen return (ch); 7575687c71dSFlorian Walpen } 7585687c71dSFlorian Walpen 7595687c71dSFlorian Walpen static int 7605687c71dSFlorian Walpen hdspchan_trigger(kobj_t obj, void *data, int go) 7615687c71dSFlorian Walpen { 7625687c71dSFlorian Walpen struct sc_pcminfo *scp; 7635687c71dSFlorian Walpen struct sc_chinfo *ch; 7645687c71dSFlorian Walpen struct sc_info *sc; 7655687c71dSFlorian Walpen 7665687c71dSFlorian Walpen ch = data; 7675687c71dSFlorian Walpen scp = ch->parent; 7685687c71dSFlorian Walpen sc = scp->sc; 7695687c71dSFlorian Walpen 7705687c71dSFlorian Walpen snd_mtxlock(sc->lock); 7715687c71dSFlorian Walpen switch (go) { 7725687c71dSFlorian Walpen case PCMTRIG_START: 7735687c71dSFlorian Walpen #if 0 7745687c71dSFlorian Walpen device_printf(scp->dev, "hdspchan_trigger(): start\n"); 7755687c71dSFlorian Walpen #endif 7765687c71dSFlorian Walpen hdspchan_enable(ch, 1); 7775687c71dSFlorian Walpen hdspchan_setgain(ch); 7785687c71dSFlorian Walpen hdsp_start_audio(sc); 7795687c71dSFlorian Walpen break; 7805687c71dSFlorian Walpen 7815687c71dSFlorian Walpen case PCMTRIG_STOP: 7825687c71dSFlorian Walpen case PCMTRIG_ABORT: 7835687c71dSFlorian Walpen #if 0 7845687c71dSFlorian Walpen device_printf(scp->dev, "hdspchan_trigger(): stop or abort\n"); 7855687c71dSFlorian Walpen #endif 7865687c71dSFlorian Walpen clean(ch); 7875687c71dSFlorian Walpen hdspchan_enable(ch, 0); 7885687c71dSFlorian Walpen hdsp_stop_audio(sc); 7895687c71dSFlorian Walpen break; 7905687c71dSFlorian Walpen 7915687c71dSFlorian Walpen case PCMTRIG_EMLDMAWR: 7925687c71dSFlorian Walpen case PCMTRIG_EMLDMARD: 7935687c71dSFlorian Walpen if(ch->run) 7945687c71dSFlorian Walpen buffer_copy(ch); 7955687c71dSFlorian Walpen break; 7965687c71dSFlorian Walpen } 7975687c71dSFlorian Walpen 7985687c71dSFlorian Walpen snd_mtxunlock(sc->lock); 7995687c71dSFlorian Walpen 8005687c71dSFlorian Walpen return (0); 8015687c71dSFlorian Walpen } 8025687c71dSFlorian Walpen 8035687c71dSFlorian Walpen static uint32_t 8045687c71dSFlorian Walpen hdspchan_getptr(kobj_t obj, void *data) 8055687c71dSFlorian Walpen { 8065687c71dSFlorian Walpen struct sc_pcminfo *scp; 8075687c71dSFlorian Walpen struct sc_chinfo *ch; 8085687c71dSFlorian Walpen struct sc_info *sc; 8095687c71dSFlorian Walpen uint32_t ret, pos; 8105687c71dSFlorian Walpen 8115687c71dSFlorian Walpen ch = data; 8125687c71dSFlorian Walpen scp = ch->parent; 8135687c71dSFlorian Walpen sc = scp->sc; 8145687c71dSFlorian Walpen 8155687c71dSFlorian Walpen snd_mtxlock(sc->lock); 8165687c71dSFlorian Walpen ret = hdsp_read_2(sc, HDSP_STATUS_REG); 8175687c71dSFlorian Walpen snd_mtxunlock(sc->lock); 8185687c71dSFlorian Walpen 8195687c71dSFlorian Walpen pos = ret & HDSP_BUF_POSITION_MASK; 8205687c71dSFlorian Walpen pos %= (2 * sc->period * sizeof(uint32_t)); /* Double buffer. */ 8215687c71dSFlorian Walpen pos *= AFMT_CHANNEL(ch->format); /* Hardbuf with multiple channels. */ 8225687c71dSFlorian Walpen 8235687c71dSFlorian Walpen return (pos); 8245687c71dSFlorian Walpen } 8255687c71dSFlorian Walpen 8265687c71dSFlorian Walpen static int 8275687c71dSFlorian Walpen hdspchan_setformat(kobj_t obj, void *data, uint32_t format) 8285687c71dSFlorian Walpen { 8295687c71dSFlorian Walpen struct sc_chinfo *ch; 8305687c71dSFlorian Walpen 8315687c71dSFlorian Walpen ch = data; 8325687c71dSFlorian Walpen 8335687c71dSFlorian Walpen #if 0 8345687c71dSFlorian Walpen struct sc_pcminfo *scp = ch->parent; 8355687c71dSFlorian Walpen device_printf(scp->dev, "hdspchan_setformat(%d)\n", format); 8365687c71dSFlorian Walpen #endif 8375687c71dSFlorian Walpen 8385687c71dSFlorian Walpen ch->format = format; 8395687c71dSFlorian Walpen 8405687c71dSFlorian Walpen return (0); 8415687c71dSFlorian Walpen } 8425687c71dSFlorian Walpen 8435687c71dSFlorian Walpen static uint32_t 8445687c71dSFlorian Walpen hdspchan_setspeed(kobj_t obj, void *data, uint32_t speed) 8455687c71dSFlorian Walpen { 8465687c71dSFlorian Walpen struct sc_pcminfo *scp; 8475687c71dSFlorian Walpen struct hdsp_rate *hr; 8485687c71dSFlorian Walpen struct sc_chinfo *ch; 8495687c71dSFlorian Walpen struct sc_info *sc; 8505687c71dSFlorian Walpen int threshold; 8515687c71dSFlorian Walpen int i; 8525687c71dSFlorian Walpen 8535687c71dSFlorian Walpen ch = data; 8545687c71dSFlorian Walpen scp = ch->parent; 8555687c71dSFlorian Walpen sc = scp->sc; 8565687c71dSFlorian Walpen hr = NULL; 8575687c71dSFlorian Walpen 8585687c71dSFlorian Walpen #if 0 8595687c71dSFlorian Walpen device_printf(scp->dev, "hdspchan_setspeed(%d)\n", speed); 8605687c71dSFlorian Walpen #endif 8615687c71dSFlorian Walpen 8625687c71dSFlorian Walpen if (hdsp_running(sc) == 1) 8635687c71dSFlorian Walpen goto end; 8645687c71dSFlorian Walpen 8655687c71dSFlorian Walpen /* HDSP 9652 only supports sample rates up to 96kHz. */ 8665687c71dSFlorian Walpen if (sc->type == HDSP_9652 && speed > 96000) 8675687c71dSFlorian Walpen speed = 96000; 8685687c71dSFlorian Walpen 8695687c71dSFlorian Walpen if (sc->force_speed > 0) 8705687c71dSFlorian Walpen speed = sc->force_speed; 8715687c71dSFlorian Walpen 8725687c71dSFlorian Walpen /* First look for equal frequency. */ 8735687c71dSFlorian Walpen for (i = 0; rate_map[i].speed != 0; i++) { 8745687c71dSFlorian Walpen if (rate_map[i].speed == speed) 8755687c71dSFlorian Walpen hr = &rate_map[i]; 8765687c71dSFlorian Walpen } 8775687c71dSFlorian Walpen 8785687c71dSFlorian Walpen /* If no match, just find nearest. */ 8795687c71dSFlorian Walpen if (hr == NULL) { 8805687c71dSFlorian Walpen for (i = 0; rate_map[i].speed != 0; i++) { 8815687c71dSFlorian Walpen hr = &rate_map[i]; 8825687c71dSFlorian Walpen threshold = hr->speed + ((rate_map[i + 1].speed != 0) ? 8835687c71dSFlorian Walpen ((rate_map[i + 1].speed - hr->speed) >> 1) : 0); 8845687c71dSFlorian Walpen if (speed < threshold) 8855687c71dSFlorian Walpen break; 8865687c71dSFlorian Walpen } 8875687c71dSFlorian Walpen } 8885687c71dSFlorian Walpen 8895687c71dSFlorian Walpen /* Write frequency on the device. */ 8905687c71dSFlorian Walpen sc->ctrl_register &= ~HDSP_FREQ_MASK; 8915687c71dSFlorian Walpen sc->ctrl_register |= hr->reg; 8925687c71dSFlorian Walpen hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register); 8935687c71dSFlorian Walpen 8945687c71dSFlorian Walpen if (sc->type == HDSP_9632) { 8955687c71dSFlorian Walpen /* Set DDS value. */ 8965687c71dSFlorian Walpen hdsp_write_4(sc, HDSP_FREQ_REG, hdsp_freq_reg_value(hr->speed)); 8975687c71dSFlorian Walpen } 8985687c71dSFlorian Walpen 8995687c71dSFlorian Walpen sc->speed = hr->speed; 9005687c71dSFlorian Walpen end: 9015687c71dSFlorian Walpen 9025687c71dSFlorian Walpen return (sc->speed); 9035687c71dSFlorian Walpen } 9045687c71dSFlorian Walpen 9055687c71dSFlorian Walpen static uint32_t 9065687c71dSFlorian Walpen hdspchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize) 9075687c71dSFlorian Walpen { 9085687c71dSFlorian Walpen struct hdsp_latency *hl; 9095687c71dSFlorian Walpen struct sc_pcminfo *scp; 9105687c71dSFlorian Walpen struct sc_chinfo *ch; 9115687c71dSFlorian Walpen struct sc_info *sc; 9125687c71dSFlorian Walpen int threshold; 9135687c71dSFlorian Walpen int i; 9145687c71dSFlorian Walpen 9155687c71dSFlorian Walpen ch = data; 9165687c71dSFlorian Walpen scp = ch->parent; 9175687c71dSFlorian Walpen sc = scp->sc; 9185687c71dSFlorian Walpen hl = NULL; 9195687c71dSFlorian Walpen 9205687c71dSFlorian Walpen #if 0 9215687c71dSFlorian Walpen device_printf(scp->dev, "hdspchan_setblocksize(%d)\n", blocksize); 9225687c71dSFlorian Walpen #endif 9235687c71dSFlorian Walpen 9245687c71dSFlorian Walpen if (hdsp_running(sc) == 1) 9255687c71dSFlorian Walpen goto end; 9265687c71dSFlorian Walpen 9275687c71dSFlorian Walpen if (blocksize > HDSP_LAT_BYTES_MAX) 9285687c71dSFlorian Walpen blocksize = HDSP_LAT_BYTES_MAX; 9295687c71dSFlorian Walpen else if (blocksize < HDSP_LAT_BYTES_MIN) 9305687c71dSFlorian Walpen blocksize = HDSP_LAT_BYTES_MIN; 9315687c71dSFlorian Walpen 9325687c71dSFlorian Walpen blocksize /= 4 /* samples */; 9335687c71dSFlorian Walpen 9345687c71dSFlorian Walpen if (sc->force_period > 0) 9355687c71dSFlorian Walpen blocksize = sc->force_period; 9365687c71dSFlorian Walpen 9375687c71dSFlorian Walpen /* First look for equal latency. */ 9385687c71dSFlorian Walpen for (i = 0; latency_map[i].period != 0; i++) { 9395687c71dSFlorian Walpen if (latency_map[i].period == blocksize) 9405687c71dSFlorian Walpen hl = &latency_map[i]; 9415687c71dSFlorian Walpen } 9425687c71dSFlorian Walpen 9435687c71dSFlorian Walpen /* If no match, just find nearest. */ 9445687c71dSFlorian Walpen if (hl == NULL) { 9455687c71dSFlorian Walpen for (i = 0; latency_map[i].period != 0; i++) { 9465687c71dSFlorian Walpen hl = &latency_map[i]; 9475687c71dSFlorian Walpen threshold = hl->period + ((latency_map[i + 1].period != 0) ? 9485687c71dSFlorian Walpen ((latency_map[i + 1].period - hl->period) >> 1) : 0); 9495687c71dSFlorian Walpen if (blocksize < threshold) 9505687c71dSFlorian Walpen break; 9515687c71dSFlorian Walpen } 9525687c71dSFlorian Walpen } 9535687c71dSFlorian Walpen 9545687c71dSFlorian Walpen snd_mtxlock(sc->lock); 9555687c71dSFlorian Walpen sc->ctrl_register &= ~HDSP_LAT_MASK; 9565687c71dSFlorian Walpen sc->ctrl_register |= hdsp_encode_latency(hl->n); 9575687c71dSFlorian Walpen hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register); 9585687c71dSFlorian Walpen sc->period = hl->period; 9595687c71dSFlorian Walpen snd_mtxunlock(sc->lock); 9605687c71dSFlorian Walpen 9615687c71dSFlorian Walpen #if 0 9625687c71dSFlorian Walpen device_printf(scp->dev, "New period=%d\n", sc->period); 9635687c71dSFlorian Walpen #endif 9645687c71dSFlorian Walpen 9655687c71dSFlorian Walpen sndbuf_resize(ch->buffer, 2, 9665687c71dSFlorian Walpen (sc->period * AFMT_CHANNEL(ch->format) * sizeof(uint32_t))); 9675687c71dSFlorian Walpen 9685687c71dSFlorian Walpen /* Reset pointer, rewrite frequency (same register) for 9632. */ 9695687c71dSFlorian Walpen hdsp_write_4(sc, HDSP_RESET_POINTER, 0); 9705687c71dSFlorian Walpen if (sc->type == HDSP_9632) 9715687c71dSFlorian Walpen hdsp_write_4(sc, HDSP_FREQ_REG, hdsp_freq_reg_value(sc->speed)); 9725687c71dSFlorian Walpen end: 9735687c71dSFlorian Walpen 9745687c71dSFlorian Walpen return (sndbuf_getblksz(ch->buffer)); 9755687c71dSFlorian Walpen } 9765687c71dSFlorian Walpen 9775687c71dSFlorian Walpen static uint32_t hdsp_bkp_fmt[] = { 9785687c71dSFlorian Walpen SND_FORMAT(AFMT_S32_LE, 2, 0), 9795687c71dSFlorian Walpen 0 9805687c71dSFlorian Walpen }; 9815687c71dSFlorian Walpen 9825687c71dSFlorian Walpen /* Capabilities fallback, no quad speed for HDSP 9652 compatibility. */ 9835687c71dSFlorian Walpen static struct pcmchan_caps hdsp_bkp_caps = {32000, 96000, hdsp_bkp_fmt, 0}; 9845687c71dSFlorian Walpen 9855687c71dSFlorian Walpen static struct pcmchan_caps * 9865687c71dSFlorian Walpen hdspchan_getcaps(kobj_t obj, void *data) 9875687c71dSFlorian Walpen { 9885687c71dSFlorian Walpen struct sc_chinfo *ch; 9895687c71dSFlorian Walpen 9905687c71dSFlorian Walpen ch = data; 9915687c71dSFlorian Walpen 9925687c71dSFlorian Walpen #if 0 9935687c71dSFlorian Walpen device_printf(ch->parent->dev, "hdspchan_getcaps()\n"); 9945687c71dSFlorian Walpen #endif 9955687c71dSFlorian Walpen 9965687c71dSFlorian Walpen if (ch->caps != NULL) 9975687c71dSFlorian Walpen return (ch->caps); 9985687c71dSFlorian Walpen 9995687c71dSFlorian Walpen return (&hdsp_bkp_caps); 10005687c71dSFlorian Walpen } 10015687c71dSFlorian Walpen 10025687c71dSFlorian Walpen static kobj_method_t hdspchan_methods[] = { 10035687c71dSFlorian Walpen KOBJMETHOD(channel_init, hdspchan_init), 10045687c71dSFlorian Walpen KOBJMETHOD(channel_free, hdspchan_free), 10055687c71dSFlorian Walpen KOBJMETHOD(channel_setformat, hdspchan_setformat), 10065687c71dSFlorian Walpen KOBJMETHOD(channel_setspeed, hdspchan_setspeed), 10075687c71dSFlorian Walpen KOBJMETHOD(channel_setblocksize, hdspchan_setblocksize), 10085687c71dSFlorian Walpen KOBJMETHOD(channel_trigger, hdspchan_trigger), 10095687c71dSFlorian Walpen KOBJMETHOD(channel_getptr, hdspchan_getptr), 10105687c71dSFlorian Walpen KOBJMETHOD(channel_getcaps, hdspchan_getcaps), 10115687c71dSFlorian Walpen KOBJMETHOD_END 10125687c71dSFlorian Walpen }; 10135687c71dSFlorian Walpen CHANNEL_DECLARE(hdspchan); 10145687c71dSFlorian Walpen 10155687c71dSFlorian Walpen static int 10165687c71dSFlorian Walpen hdsp_pcm_probe(device_t dev) 10175687c71dSFlorian Walpen { 10185687c71dSFlorian Walpen 10195687c71dSFlorian Walpen #if 0 10205687c71dSFlorian Walpen device_printf(dev,"hdsp_pcm_probe()\n"); 10215687c71dSFlorian Walpen #endif 10225687c71dSFlorian Walpen 10235687c71dSFlorian Walpen return (0); 10245687c71dSFlorian Walpen } 10255687c71dSFlorian Walpen 10265687c71dSFlorian Walpen static uint32_t 10275687c71dSFlorian Walpen hdsp_pcm_intr(struct sc_pcminfo *scp) 10285687c71dSFlorian Walpen { 10295687c71dSFlorian Walpen struct sc_chinfo *ch; 10305687c71dSFlorian Walpen struct sc_info *sc; 10315687c71dSFlorian Walpen int i; 10325687c71dSFlorian Walpen 10335687c71dSFlorian Walpen sc = scp->sc; 10345687c71dSFlorian Walpen 10355687c71dSFlorian Walpen for (i = 0; i < scp->chnum; i++) { 10365687c71dSFlorian Walpen ch = &scp->chan[i]; 10375687c71dSFlorian Walpen snd_mtxunlock(sc->lock); 10385687c71dSFlorian Walpen chn_intr(ch->channel); 10395687c71dSFlorian Walpen snd_mtxlock(sc->lock); 10405687c71dSFlorian Walpen } 10415687c71dSFlorian Walpen 10425687c71dSFlorian Walpen return (0); 10435687c71dSFlorian Walpen } 10445687c71dSFlorian Walpen 10455687c71dSFlorian Walpen static int 10465687c71dSFlorian Walpen hdsp_pcm_attach(device_t dev) 10475687c71dSFlorian Walpen { 10485687c71dSFlorian Walpen char status[SND_STATUSLEN]; 10495687c71dSFlorian Walpen struct sc_pcminfo *scp; 10505687c71dSFlorian Walpen const char *buf; 10515687c71dSFlorian Walpen uint32_t pcm_flags; 10525687c71dSFlorian Walpen int err; 10535687c71dSFlorian Walpen int play, rec; 10545687c71dSFlorian Walpen 10555687c71dSFlorian Walpen scp = device_get_ivars(dev); 10565687c71dSFlorian Walpen scp->ih = &hdsp_pcm_intr; 10575687c71dSFlorian Walpen 10585687c71dSFlorian Walpen if (scp->hc->ports & HDSP_CHAN_9632_ALL) 10595687c71dSFlorian Walpen buf = "9632"; 10605687c71dSFlorian Walpen else if (scp->hc->ports & HDSP_CHAN_9652_ALL) 10615687c71dSFlorian Walpen buf = "9652"; 10625687c71dSFlorian Walpen else 10635687c71dSFlorian Walpen buf = "?"; 10645687c71dSFlorian Walpen device_set_descf(dev, "HDSP %s [%s]", buf, scp->hc->descr); 10655687c71dSFlorian Walpen 10665687c71dSFlorian Walpen /* 10675687c71dSFlorian Walpen * We don't register interrupt handler with snd_setup_intr 10685687c71dSFlorian Walpen * in pcm device. Mark pcm device as MPSAFE manually. 10695687c71dSFlorian Walpen */ 10705687c71dSFlorian Walpen pcm_flags = pcm_getflags(dev) | SD_F_MPSAFE; 10715687c71dSFlorian Walpen if (hdsp_port_slot_count_max(scp->hc->ports) > HDSP_MATRIX_MAX) 10725687c71dSFlorian Walpen /* Disable vchan conversion, too many channels. */ 10735687c71dSFlorian Walpen pcm_flags |= SD_F_BITPERFECT; 10745687c71dSFlorian Walpen pcm_setflags(dev, pcm_flags); 10755687c71dSFlorian Walpen 1076*516a9c02SChristos Margiolis pcm_init(dev, scp); 1077*516a9c02SChristos Margiolis 10785687c71dSFlorian Walpen play = (hdsp_channel_play_ports(scp->hc)) ? 1 : 0; 10795687c71dSFlorian Walpen rec = (hdsp_channel_rec_ports(scp->hc)) ? 1 : 0; 10805687c71dSFlorian Walpen 10815687c71dSFlorian Walpen scp->chnum = 0; 10825687c71dSFlorian Walpen if (play) { 10835687c71dSFlorian Walpen pcm_addchan(dev, PCMDIR_PLAY, &hdspchan_class, scp); 10845687c71dSFlorian Walpen scp->chnum++; 10855687c71dSFlorian Walpen } 10865687c71dSFlorian Walpen 10875687c71dSFlorian Walpen if (rec) { 10885687c71dSFlorian Walpen pcm_addchan(dev, PCMDIR_REC, &hdspchan_class, scp); 10895687c71dSFlorian Walpen scp->chnum++; 10905687c71dSFlorian Walpen } 10915687c71dSFlorian Walpen 10925687c71dSFlorian Walpen snprintf(status, SND_STATUSLEN, "port 0x%jx irq %jd on %s", 10935687c71dSFlorian Walpen rman_get_start(scp->sc->cs), 10945687c71dSFlorian Walpen rman_get_start(scp->sc->irq), 10955687c71dSFlorian Walpen device_get_nameunit(device_get_parent(dev))); 1096*516a9c02SChristos Margiolis err = pcm_register(dev, status); 1097*516a9c02SChristos Margiolis if (err) { 1098*516a9c02SChristos Margiolis device_printf(dev, "Can't register pcm.\n"); 1099*516a9c02SChristos Margiolis return (ENXIO); 1100*516a9c02SChristos Margiolis } 11015687c71dSFlorian Walpen 11025687c71dSFlorian Walpen mixer_init(dev, &hdspmixer_class, scp); 11035687c71dSFlorian Walpen 11045687c71dSFlorian Walpen return (0); 11055687c71dSFlorian Walpen } 11065687c71dSFlorian Walpen 11075687c71dSFlorian Walpen static int 11085687c71dSFlorian Walpen hdsp_pcm_detach(device_t dev) 11095687c71dSFlorian Walpen { 11105687c71dSFlorian Walpen int err; 11115687c71dSFlorian Walpen 11125687c71dSFlorian Walpen err = pcm_unregister(dev); 11135687c71dSFlorian Walpen if (err) { 11145687c71dSFlorian Walpen device_printf(dev, "Can't unregister device.\n"); 11155687c71dSFlorian Walpen return (err); 11165687c71dSFlorian Walpen } 11175687c71dSFlorian Walpen 11185687c71dSFlorian Walpen return (0); 11195687c71dSFlorian Walpen } 11205687c71dSFlorian Walpen 11215687c71dSFlorian Walpen static device_method_t hdsp_pcm_methods[] = { 11225687c71dSFlorian Walpen DEVMETHOD(device_probe, hdsp_pcm_probe), 11235687c71dSFlorian Walpen DEVMETHOD(device_attach, hdsp_pcm_attach), 11245687c71dSFlorian Walpen DEVMETHOD(device_detach, hdsp_pcm_detach), 11255687c71dSFlorian Walpen { 0, 0 } 11265687c71dSFlorian Walpen }; 11275687c71dSFlorian Walpen 11285687c71dSFlorian Walpen static driver_t hdsp_pcm_driver = { 11295687c71dSFlorian Walpen "pcm", 11305687c71dSFlorian Walpen hdsp_pcm_methods, 11315687c71dSFlorian Walpen PCM_SOFTC_SIZE, 11325687c71dSFlorian Walpen }; 11335687c71dSFlorian Walpen 11345687c71dSFlorian Walpen DRIVER_MODULE(snd_hdsp_pcm, hdsp, hdsp_pcm_driver, 0, 0); 11355687c71dSFlorian Walpen MODULE_DEPEND(snd_hdsp, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 11365687c71dSFlorian Walpen MODULE_VERSION(snd_hdsp, 1); 1137