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