svn commit: r233165 - in stable/9: share/man/man4 sys/conf
sys/dev/sound/pci sys/modules/sound/driver
sys/modules/sound/driver/hdspe
Alexander Motin
mav at FreeBSD.org
Mon Mar 19 07:34:10 UTC 2012
Author: mav
Date: Mon Mar 19 07:34:09 2012
New Revision: 233165
URL: http://svn.freebsd.org/changeset/base/233165
Log:
MFC r232337:
Add driver for the RME HDSPe AIO/RayDAT sound cards -- snd_hdspe(4).
Cards are expensive and so rare, so leave the driver as module.
Submitted by: Ruslan Bukin <br at bsdpad.com>
Added:
stable/9/share/man/man4/snd_hdspe.4
- copied unchanged from r232337, head/share/man/man4/snd_hdspe.4
stable/9/sys/dev/sound/pci/hdspe-pcm.c
- copied unchanged from r232337, head/sys/dev/sound/pci/hdspe-pcm.c
stable/9/sys/dev/sound/pci/hdspe.c
- copied unchanged from r232337, head/sys/dev/sound/pci/hdspe.c
stable/9/sys/dev/sound/pci/hdspe.h
- copied unchanged from r232337, head/sys/dev/sound/pci/hdspe.h
stable/9/sys/modules/sound/driver/hdspe/
- copied from r232337, head/sys/modules/sound/driver/hdspe/
Modified:
stable/9/share/man/man4/Makefile
stable/9/share/man/man4/pcm.4
stable/9/sys/conf/NOTES
stable/9/sys/conf/files
stable/9/sys/modules/sound/driver/Makefile
Directory Properties:
stable/9/share/man/man4/ (props changed)
stable/9/sys/ (props changed)
stable/9/sys/conf/ (props changed)
Modified: stable/9/share/man/man4/Makefile
==============================================================================
--- stable/9/share/man/man4/Makefile Mon Mar 19 07:24:26 2012 (r233164)
+++ stable/9/share/man/man4/Makefile Mon Mar 19 07:34:09 2012 (r233165)
@@ -414,6 +414,7 @@ MAN= aac.4 \
snd_fm801.4 \
snd_gusc.4 \
snd_hda.4 \
+ snd_hdspe.4 \
snd_ich.4 \
snd_maestro3.4 \
snd_maestro.4 \
Modified: stable/9/share/man/man4/pcm.4
==============================================================================
--- stable/9/share/man/man4/pcm.4 Mon Mar 19 07:24:26 2012 (r233164)
+++ stable/9/share/man/man4/pcm.4 Mon Mar 19 07:34:09 2012 (r233165)
@@ -113,6 +113,8 @@ The following bridge device drivers are
.It
.Xr snd_hda 4 (enabled by default on amd64, i386)
.It
+.Xr snd_hdspe 4
+.It
.Xr snd_ich 4 (enabled by default on amd64, i386)
.It
.Xr snd_maestro 4
@@ -714,6 +716,7 @@ A device node is not created properly.
.Xr snd_fm801 4 ,
.Xr snd_gusc 4 ,
.Xr snd_hda 4 ,
+.Xr snd_hdspe 4 ,
.Xr snd_ich 4 ,
.Xr snd_maestro 4 ,
.Xr snd_maestro3 4 ,
Copied: stable/9/share/man/man4/snd_hdspe.4 (from r232337, head/share/man/man4/snd_hdspe.4)
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ stable/9/share/man/man4/snd_hdspe.4 Mon Mar 19 07:34:09 2012 (r233165, copy of r232337, head/share/man/man4/snd_hdspe.4)
@@ -0,0 +1,76 @@
+.\" Copyright (c) 2012 Ruslan Bukin <br at bsdpad.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 13, 2012
+.Dt SND_HDSPE 4
+.Os
+.Sh NAME
+.Nm snd_hdspe
+.Nd "RME HDSPe brigde device driver"
+.Sh SYNOPSIS
+To compile this driver into the kernel, place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device sound"
+.Cd "device snd_hdspe"
+.Ed
+.Pp
+Alternatively, to load the driver as a module at boot time, place the
+following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+snd_hdspe_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+bridge driver allows the generic audio driver
+.Xr sound 4
+to attach to RME HDSPe audio devices.
+.Sh HARDWARE
+The
+.Nm
+driver supports the following audio devices:
+.Pp
+.Bl -bullet -compact
+.It
+RME HDSPe AIO
+.It
+RME HDSPe RayDAT
+.El
+.Sh SEE ALSO
+.Xr sound 4
+.Sh HISTORY
+The
+.Nm
+device driver first appeared in
+.Fx 10.0 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Ruslan Bukin <br at bsdpad.com> .
Modified: stable/9/sys/conf/NOTES
==============================================================================
--- stable/9/sys/conf/NOTES Mon Mar 19 07:24:26 2012 (r233164)
+++ stable/9/sys/conf/NOTES Mon Mar 19 07:34:09 2012 (r233165)
@@ -2246,6 +2246,7 @@ device sound
# snd_gusc: Gravis UltraSound ISA PnP/non-PnP.
# snd_hda: Intel High Definition Audio (Controller) and
# compatible.
+# snd_hdspe: RME HDSPe AIO and RayDAT.
# snd_ich: Intel ICH AC'97 and some more audio controllers
# embedded in a chipset, for example nVidia
# nForce controllers.
@@ -2285,6 +2286,7 @@ device snd_ess
device snd_fm801
device snd_gusc
device snd_hda
+device snd_hdspe
device snd_ich
device snd_maestro
device snd_maestro3
Modified: stable/9/sys/conf/files
==============================================================================
--- stable/9/sys/conf/files Mon Mar 19 07:24:26 2012 (r233164)
+++ stable/9/sys/conf/files Mon Mar 19 07:34:09 2012 (r233165)
@@ -1754,6 +1754,8 @@ dev/sound/pci/hda/hdaa_patches.c optiona
dev/sound/pci/hda/hdac.c optional snd_hda pci
dev/sound/pci/hda/hdac_if.m optional snd_hda pci
dev/sound/pci/hda/hdacc.c optional snd_hda pci
+dev/sound/pci/hdspe.c optional snd_hdspe pci
+dev/sound/pci/hdspe-pcm.c optional snd_hdspe pci
dev/sound/pcm/ac97.c optional sound
dev/sound/pcm/ac97_if.m optional sound
dev/sound/pcm/ac97_patch.c optional sound
Copied: stable/9/sys/dev/sound/pci/hdspe-pcm.c (from r232337, head/sys/dev/sound/pci/hdspe-pcm.c)
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ stable/9/sys/dev/sound/pci/hdspe-pcm.c Mon Mar 19 07:34:09 2012 (r233165, copy of r232337, head/sys/dev/sound/pci/hdspe-pcm.c)
@@ -0,0 +1,709 @@
+/*-
+ * Copyright (c) 2012 Ruslan Bukin <br at bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RME HDSPe driver for FreeBSD (pcm-part).
+ * Supported cards: AIO, RayDAT.
+ */
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pci/hdspe.h>
+#include <dev/sound/chip.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <mixer_if.h>
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+struct hdspe_latency {
+ uint32_t n;
+ uint32_t period;
+ float ms;
+};
+
+static struct hdspe_latency latency_map[] = {
+ { 7, 32, 0.7 },
+ { 0, 64, 1.5 },
+ { 1, 128, 3 },
+ { 2, 256, 6 },
+ { 3, 512, 12 },
+ { 4, 1024, 23 },
+ { 5, 2048, 46 },
+ { 6, 4096, 93 },
+
+ { 0, 0, 0 },
+};
+
+struct hdspe_rate {
+ uint32_t speed;
+ uint32_t reg;
+};
+
+static struct hdspe_rate rate_map[] = {
+ { 32000, (HDSPE_FREQ_32000) },
+ { 44100, (HDSPE_FREQ_44100) },
+ { 48000, (HDSPE_FREQ_48000) },
+ { 64000, (HDSPE_FREQ_32000 | HDSPE_FREQ_DOUBLE) },
+ { 88200, (HDSPE_FREQ_44100 | HDSPE_FREQ_DOUBLE) },
+ { 96000, (HDSPE_FREQ_48000 | HDSPE_FREQ_DOUBLE) },
+ { 128000, (HDSPE_FREQ_32000 | HDSPE_FREQ_QUAD) },
+ { 176400, (HDSPE_FREQ_44100 | HDSPE_FREQ_QUAD) },
+ { 192000, (HDSPE_FREQ_48000 | HDSPE_FREQ_QUAD) },
+
+ { 0, 0 },
+};
+
+
+static int
+hdspe_hw_mixer(struct sc_chinfo *ch, unsigned int dst,
+ unsigned int src, unsigned short data)
+{
+ struct sc_pcminfo *scp = ch->parent;
+ struct sc_info *sc = scp->sc;
+ int offs = 0;
+
+ if (ch->dir == PCMDIR_PLAY)
+ offs = 64;
+
+ hdspe_write_4(sc, HDSPE_MIXER_BASE +
+ ((offs + src + 128 * dst) * sizeof(uint32_t)),
+ data & 0xFFFF);
+
+ return 0;
+};
+
+static int
+hdspechan_setgain(struct sc_chinfo *ch)
+{
+
+ hdspe_hw_mixer(ch, ch->lslot, ch->lslot,
+ ch->lvol * HDSPE_MAX_GAIN / 100);
+ hdspe_hw_mixer(ch, ch->rslot, ch->rslot,
+ ch->rvol * HDSPE_MAX_GAIN / 100);
+
+ return 0;
+}
+
+static int
+hdspemixer_init(struct snd_mixer *m)
+{
+ struct sc_pcminfo *scp = mix_getdevinfo(m);
+ struct sc_info *sc = scp->sc;
+ int mask;
+
+ if (sc == NULL)
+ return -1;
+
+ mask = SOUND_MASK_PCM;
+
+ if (scp->hc->play)
+ mask |= SOUND_MASK_VOLUME;
+
+ if (scp->hc->rec)
+ mask |= SOUND_MASK_RECLEV;
+
+ snd_mtxlock(sc->lock);
+ pcm_setflags(scp->dev, pcm_getflags(scp->dev) | SD_F_SOFTPCMVOL);
+ mix_setdevs(m, mask);
+ snd_mtxunlock(sc->lock);
+
+ return 0;
+}
+
+static int
+hdspemixer_set(struct snd_mixer *m, unsigned dev,
+ unsigned left, unsigned right)
+{
+ struct sc_pcminfo *scp = mix_getdevinfo(m);
+ struct sc_chinfo *ch;
+ int i;
+
+#if 0
+ device_printf(scp->dev, "hdspemixer_set() %d %d\n",
+ left,right);
+#endif
+
+ for (i = 0; i < scp->chnum; i++) {
+ ch = &scp->chan[i];
+ if ((dev == SOUND_MIXER_VOLUME && ch->dir == PCMDIR_PLAY) ||
+ (dev == SOUND_MIXER_RECLEV && ch->dir == PCMDIR_REC)) {
+ ch->lvol = left;
+ ch->rvol = right;
+ if (ch->run)
+ hdspechan_setgain(ch);
+ }
+ }
+
+ return 0;
+}
+
+static kobj_method_t hdspemixer_methods[] = {
+ KOBJMETHOD(mixer_init, hdspemixer_init),
+ KOBJMETHOD(mixer_set, hdspemixer_set),
+ KOBJMETHOD_END
+};
+MIXER_DECLARE(hdspemixer);
+
+static void
+hdspechan_enable(struct sc_chinfo *ch, int value)
+{
+ struct sc_pcminfo *scp = ch->parent;
+ struct sc_info *sc = scp->sc;
+ int reg;
+
+ if (ch->dir == PCMDIR_PLAY)
+ reg = HDSPE_OUT_ENABLE_BASE;
+ else
+ reg = HDSPE_IN_ENABLE_BASE;
+
+ ch->run = value;
+
+ hdspe_write_1(sc, reg + (4 * ch->lslot), value);
+ hdspe_write_1(sc, reg + (4 * ch->rslot), value);
+}
+
+static int
+hdspe_running(struct sc_info *sc)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ int i, j, devcount, err;
+ device_t *devlist;
+
+ if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0)
+ goto bad;
+
+ for (i = 0; i < devcount; i++) {
+ scp = device_get_ivars(devlist[i]);
+ for (j = 0; j < scp->chnum; j++) {
+ ch = &scp->chan[j];
+ if (ch->run)
+ goto bad;
+ }
+ }
+
+ return 0;
+bad:
+
+#if 0
+ device_printf(sc->dev,"hdspe is running\n");
+#endif
+
+ return 1;
+}
+
+static void
+hdspe_start_audio(struct sc_info *sc)
+{
+
+ sc->ctrl_register |= (HDSPE_AUDIO_INT_ENABLE | HDSPE_ENABLE);
+ hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register);
+}
+
+static void
+hdspe_stop_audio(struct sc_info *sc)
+{
+
+ if (hdspe_running(sc) == 1)
+ return;
+
+ sc->ctrl_register &= ~(HDSPE_AUDIO_INT_ENABLE | HDSPE_ENABLE);
+ hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register);
+}
+
+/* Multiplex / demultiplex: 2.0 <-> 2 x 1.0. */
+static void
+buffer_copy(struct sc_chinfo *ch)
+{
+ struct sc_pcminfo *scp = ch->parent;
+ struct sc_info *sc = scp->sc;
+ int length,src,dst;
+ int ssize, dsize;
+ int i;
+
+ length = sndbuf_getready(ch->buffer) /
+ (4 /* Bytes per sample. */ * 2 /* channels */);
+
+ if (ch->dir == PCMDIR_PLAY) {
+ src = sndbuf_getreadyptr(ch->buffer);
+ } else {
+ src = sndbuf_getfreeptr(ch->buffer);
+ }
+
+ src /= 4; /* Bytes per sample. */
+ dst = src / 2; /* Destination buffer twice smaller. */
+
+ ssize = ch->size / 4;
+ dsize = ch->size / 8;
+
+ /*
+ * Use two fragment buffer to avoid sound clipping.
+ */
+
+ for (i = 0; i < sc->period * 2 /* fragments */; i++) {
+ if (ch->dir == PCMDIR_PLAY) {
+ sc->pbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->lslot] =
+ ch->data[src];
+ sc->pbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->rslot] =
+ ch->data[src + 1];
+
+ } else {
+ ch->data[src] =
+ sc->rbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->lslot];
+ ch->data[src+1] =
+ sc->rbuf[dst + HDSPE_CHANBUF_SAMPLES * ch->rslot];
+ }
+
+ dst+=1;
+ dst %= dsize;
+ src+=2;
+ src %= ssize;
+ }
+}
+
+static int
+clean(struct sc_chinfo *ch){
+ struct sc_pcminfo *scp = ch->parent;
+ struct sc_info *sc = scp->sc;
+ uint32_t *buf = sc->rbuf;
+
+ if (ch->dir == PCMDIR_PLAY) {
+ buf = sc->pbuf;
+ }
+
+ bzero(buf + HDSPE_CHANBUF_SAMPLES * ch->lslot, HDSPE_CHANBUF_SIZE);
+ bzero(buf + HDSPE_CHANBUF_SAMPLES * ch->rslot, HDSPE_CHANBUF_SIZE);
+
+ return 0;
+}
+
+
+/* Channel interface. */
+static void *
+hdspechan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir)
+{
+ struct sc_pcminfo *scp = devinfo;
+ struct sc_info *sc = scp->sc;
+ struct sc_chinfo *ch;
+ int num;
+
+ snd_mtxlock(sc->lock);
+ num = scp->chnum;
+
+ ch = &scp->chan[num];
+ ch->lslot = scp->hc->left;
+ ch->rslot = scp->hc->right;
+ ch->run = 0;
+ ch->lvol = 0;
+ ch->rvol = 0;
+
+ ch->size = HDSPE_CHANBUF_SIZE * 2 /* slots */;
+ ch->data = malloc(ch->size, M_HDSPE, M_NOWAIT);
+
+ ch->buffer = b;
+ ch->channel = c;
+ ch->parent = scp;
+
+ ch->dir = dir;
+
+ snd_mtxunlock(sc->lock);
+
+ if (sndbuf_setup(ch->buffer, ch->data, ch->size) != 0) {
+ device_printf(scp->dev, "Can't setup sndbuf.\n");
+ return NULL;
+ }
+
+ return ch;
+}
+
+static int
+hdspechan_trigger(kobj_t obj, void *data, int go)
+{
+ struct sc_chinfo *ch = data;
+ struct sc_pcminfo *scp = ch->parent;
+ struct sc_info *sc = scp->sc;
+
+ snd_mtxlock(sc->lock);
+ switch (go) {
+ case PCMTRIG_START:
+#if 0
+ device_printf(scp->dev, "hdspechan_trigger(): start\n");
+#endif
+ hdspechan_enable(ch, 1);
+ hdspechan_setgain(ch);
+ hdspe_start_audio(sc);
+ break;
+
+ case PCMTRIG_STOP:
+ case PCMTRIG_ABORT:
+#if 0
+ device_printf(scp->dev, "hdspechan_trigger(): stop or abort\n");
+#endif
+ clean(ch);
+ hdspechan_enable(ch, 0);
+ hdspe_stop_audio(sc);
+ break;
+
+ case PCMTRIG_EMLDMAWR:
+ case PCMTRIG_EMLDMARD:
+ if(ch->run)
+ buffer_copy(ch);
+ break;
+ }
+
+ snd_mtxunlock(sc->lock);
+
+ return 0;
+}
+
+static uint32_t
+hdspechan_getptr(kobj_t obj, void *data)
+{
+ struct sc_chinfo *ch = data;
+ struct sc_pcminfo *scp = ch->parent;
+ struct sc_info *sc = scp->sc;
+ uint32_t ret, pos;
+
+ snd_mtxlock(sc->lock);
+ ret = hdspe_read_2(sc, HDSPE_STATUS_REG);
+ snd_mtxunlock(sc->lock);
+
+ pos = ret & HDSPE_BUF_POSITION_MASK;
+ pos *= 2; /* Hardbuf twice bigger. */
+
+ return pos;
+}
+
+static int
+hdspechan_free(kobj_t obj, void *data)
+{
+ struct sc_chinfo *ch = data;
+ struct sc_pcminfo *scp = ch->parent;
+ struct sc_info *sc = scp->sc;
+
+#if 0
+ device_printf(scp->dev, "hdspechan_free()\n");
+#endif
+ snd_mtxlock(sc->lock);
+ if (ch->data != NULL) {
+ free(ch->data, M_HDSPE);
+ ch->data = NULL;
+ }
+ snd_mtxunlock(sc->lock);
+
+ return 0;
+}
+
+static int
+hdspechan_setformat(kobj_t obj, void *data, uint32_t format)
+{
+ struct sc_chinfo *ch = data;
+
+#if 0
+ struct sc_pcminfo *scp = ch->parent;
+ device_printf(scp->dev, "hdspechan_setformat(%d)\n", format);
+#endif
+
+ ch->format = format;
+
+ return 0;
+}
+
+static uint32_t
+hdspechan_setspeed(kobj_t obj, void *data, uint32_t speed)
+{
+ struct sc_chinfo *ch = data;
+ struct sc_pcminfo *scp = ch->parent;
+ struct sc_info *sc = scp->sc;
+ struct hdspe_rate *hr = NULL;
+ long long period;
+ int threshold;
+ int i;
+
+#if 0
+ device_printf(scp->dev, "hdspechan_setspeed(%d)\n", speed);
+#endif
+
+ if (hdspe_running(sc) == 1)
+ goto end;
+
+ /* First look for equal frequency. */
+ for (i = 0; rate_map[i].speed != 0; i++) {
+ if (rate_map[i].speed == speed)
+ hr = &rate_map[i];
+ }
+
+ /* If no match, just find nearest. */
+ if (hr == NULL) {
+ for (i = 0; rate_map[i].speed != 0; i++) {
+ hr = &rate_map[i];
+ threshold = hr->speed + ((rate_map[i + 1].speed != 0) ?
+ ((rate_map[i + 1].speed - hr->speed) >> 1) : 0);
+ if (speed < threshold)
+ break;
+ }
+ }
+
+ switch (sc->type) {
+ case RAYDAT:
+ case AIO:
+ period = HDSPE_FREQ_AIO;
+ break;
+ default:
+ /* Unsupported card. */
+ goto end;
+ }
+
+ /* Write frequency on the device. */
+ sc->ctrl_register &= ~HDSPE_FREQ_MASK;
+ sc->ctrl_register |= hr->reg;
+ hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register);
+
+ speed = hr->speed;
+ if (speed > 96000)
+ speed /= 4;
+ else if (speed > 48000)
+ speed /= 2;
+
+ /* Set DDS value. */
+ period /= speed;
+ hdspe_write_4(sc, HDSPE_FREQ_REG, period);
+
+ sc->speed = hr->speed;
+end:
+ return sc->speed;
+}
+
+static uint32_t
+hdspechan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
+{
+ struct sc_chinfo *ch = data;
+ struct sc_pcminfo *scp = ch->parent;
+ struct sc_info *sc = scp->sc;
+ struct hdspe_latency *hl = NULL;
+ int threshold;
+ int i;
+
+#if 0
+ device_printf(scp->dev, "hdspechan_setblocksize(%d)\n", blocksize);
+#endif
+
+ if (hdspe_running(sc) == 1)
+ goto end;
+
+ if (blocksize > HDSPE_LAT_BYTES_MAX)
+ blocksize = HDSPE_LAT_BYTES_MAX;
+ else if (blocksize < HDSPE_LAT_BYTES_MIN)
+ blocksize = HDSPE_LAT_BYTES_MIN;
+
+ blocksize /= 4 /* samples */;
+
+ /* First look for equal latency. */
+ for (i = 0; latency_map[i].period != 0; i++) {
+ if (latency_map[i].period == blocksize) {
+ hl = &latency_map[i];
+ }
+ }
+
+ /* If no match, just find nearest. */
+ if (hl == NULL) {
+ for (i = 0; latency_map[i].period != 0; i++) {
+ hl = &latency_map[i];
+ threshold = hl->period + ((latency_map[i + 1].period != 0) ?
+ ((latency_map[i + 1].period - hl->period) >> 1) : 0);
+ if (blocksize < threshold)
+ break;
+ }
+ }
+
+ snd_mtxlock(sc->lock);
+ sc->ctrl_register &= ~HDSPE_LAT_MASK;
+ sc->ctrl_register |= hdspe_encode_latency(hl->n);
+ hdspe_write_4(sc, HDSPE_CONTROL_REG, sc->ctrl_register);
+ sc->period = hl->period;
+ snd_mtxunlock(sc->lock);
+
+#if 0
+ device_printf(scp->dev, "New period=%d\n", sc->period);
+#endif
+
+ sndbuf_resize(ch->buffer, (HDSPE_CHANBUF_SIZE * 2) / (sc->period * 4),
+ (sc->period * 4));
+end:
+ return sndbuf_getblksz(ch->buffer);
+}
+
+static uint32_t hdspe_rfmt[] = {
+ SND_FORMAT(AFMT_S32_LE, 2, 0),
+ 0
+};
+
+static struct pcmchan_caps hdspe_rcaps = {32000, 192000, hdspe_rfmt, 0};
+
+static uint32_t hdspe_pfmt[] = {
+ SND_FORMAT(AFMT_S32_LE, 2, 0),
+ 0
+};
+
+static struct pcmchan_caps hdspe_pcaps = {32000, 192000, hdspe_pfmt, 0};
+
+static struct pcmchan_caps *
+hdspechan_getcaps(kobj_t obj, void *data)
+{
+ struct sc_chinfo *ch = data;
+
+#if 0
+ struct sc_pcminfo *scl = ch->parent;
+ device_printf(scp->dev, "hdspechan_getcaps()\n");
+#endif
+
+ return (ch->dir == PCMDIR_PLAY) ?
+ &hdspe_pcaps : &hdspe_rcaps;
+}
+
+static kobj_method_t hdspechan_methods[] = {
+ KOBJMETHOD(channel_init, hdspechan_init),
+ KOBJMETHOD(channel_free, hdspechan_free),
+ KOBJMETHOD(channel_setformat, hdspechan_setformat),
+ KOBJMETHOD(channel_setspeed, hdspechan_setspeed),
+ KOBJMETHOD(channel_setblocksize, hdspechan_setblocksize),
+ KOBJMETHOD(channel_trigger, hdspechan_trigger),
+ KOBJMETHOD(channel_getptr, hdspechan_getptr),
+ KOBJMETHOD(channel_getcaps, hdspechan_getcaps),
+ KOBJMETHOD_END
+};
+CHANNEL_DECLARE(hdspechan);
+
+
+static int
+hdspe_pcm_probe(device_t dev)
+{
+
+#if 0
+ device_printf(dev,"hdspe_pcm_probe()\n");
+#endif
+
+ return 0;
+}
+
+static uint32_t
+hdspe_pcm_intr(struct sc_pcminfo *scp) {
+ struct sc_chinfo *ch;
+ struct sc_info *sc = scp->sc;
+ int i;
+
+ for (i = 0; i < scp->chnum; i++) {
+ ch = &scp->chan[i];
+ snd_mtxunlock(sc->lock);
+ chn_intr(ch->channel);
+ snd_mtxlock(sc->lock);
+ }
+
+ return 0;
+}
+
+static int
+hdspe_pcm_attach(device_t dev)
+{
+ struct sc_pcminfo *scp;
+ char status[SND_STATUSLEN];
+ char desc[64];
+ int i, err;
+
+ scp = device_get_ivars(dev);
+ scp->ih = &hdspe_pcm_intr;
+
+ bzero(desc, sizeof(desc));
+ snprintf(desc, sizeof(desc), "HDSPe AIO [%s]", scp->hc->descr);
+ device_set_desc_copy(dev, desc);
+
+ /*
+ * We don't register interrupt handler with snd_setup_intr
+ * in pcm device. Mark pcm device as MPSAFE manually.
+ */
+ pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
+
+ err = pcm_register(dev, scp, scp->hc->play, scp->hc->rec);
+ if (err) {
+ device_printf(dev, "Can't register pcm.\n");
+ return ENXIO;
+ }
+
+ scp->chnum = 0;
+ for (i = 0; i < scp->hc->play; i++) {
+ pcm_addchan(dev, PCMDIR_PLAY, &hdspechan_class, scp);
+ scp->chnum++;
+ }
+
+ for (i = 0; i < scp->hc->rec; i++) {
+ pcm_addchan(dev, PCMDIR_REC, &hdspechan_class, scp);
+ scp->chnum++;
+ }
+
+ snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s",
+ rman_get_start(scp->sc->cs),
+ rman_get_start(scp->sc->irq),
+ PCM_KLDSTRING(snd_hdspe));
+ pcm_setstatus(dev, status);
+
+ mixer_init(dev, &hdspemixer_class, scp);
+
+ return 0;
+}
+
+static int
+hdspe_pcm_detach(device_t dev)
+{
+ int err;
+
+ err = pcm_unregister(dev);
+ if (err) {
+ device_printf(dev, "Can't unregister device.\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static device_method_t hdspe_pcm_methods[] = {
+ DEVMETHOD(device_probe, hdspe_pcm_probe),
+ DEVMETHOD(device_attach, hdspe_pcm_attach),
+ DEVMETHOD(device_detach, hdspe_pcm_detach),
+ { 0, 0 }
+};
+
+static driver_t hdspe_pcm_driver = {
+ "pcm",
+ hdspe_pcm_methods,
+ PCM_SOFTC_SIZE,
+};
+
+DRIVER_MODULE(snd_hdspe_pcm, hdspe, hdspe_pcm_driver, pcm_devclass, 0, 0);
+MODULE_DEPEND(snd_hdspe, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_VERSION(snd_hdspe, 1);
Copied: stable/9/sys/dev/sound/pci/hdspe.c (from r232337, head/sys/dev/sound/pci/hdspe.c)
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ stable/9/sys/dev/sound/pci/hdspe.c Mon Mar 19 07:34:09 2012 (r233165, copy of r232337, head/sys/dev/sound/pci/hdspe.c)
@@ -0,0 +1,410 @@
+/*-
+ * Copyright (c) 2012 Ruslan Bukin <br at bsdpad.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RME HDSPe driver for FreeBSD.
+ * Supported cards: AIO, RayDAT.
+ */
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pci/hdspe.h>
+#include <dev/sound/chip.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <mixer_if.h>
+
+SND_DECLARE_FILE("$FreeBSD$");
+
+static struct hdspe_channel chan_map_aio[] = {
+ { 0, 1, "line", 1, 1 },
+ { 6, 7, "phone", 1, 0 },
+ { 8, 9, "aes", 1, 1 },
+ { 10, 11, "s/pdif", 1, 1 },
+ { 12, 16, "adat", 1, 1 },
+
+ /* Single or double speed. */
+ { 14, 18, "adat", 1, 1 },
+
+ /* Single speed only. */
+ { 13, 15, "adat", 1, 1 },
+ { 17, 19, "adat", 1, 1 },
+
+ { 0, 0, NULL, 0, 0 },
+};
+
+static struct hdspe_channel chan_map_rd[] = {
+ { 0, 1, "aes", 1, 1 },
+ { 2, 3, "s/pdif", 1, 1 },
+ { 4, 5, "adat", 1, 1 },
+ { 6, 7, "adat", 1, 1 },
+ { 8, 9, "adat", 1, 1 },
+ { 10, 11, "adat", 1, 1 },
+
+ /* Single or double speed. */
+ { 12, 13, "adat", 1, 1 },
+ { 14, 15, "adat", 1, 1 },
+ { 16, 17, "adat", 1, 1 },
+ { 18, 19, "adat", 1, 1 },
+
+ /* Single speed only. */
+ { 20, 21, "adat", 1, 1 },
+ { 22, 23, "adat", 1, 1 },
+ { 24, 25, "adat", 1, 1 },
+ { 26, 27, "adat", 1, 1 },
+ { 28, 29, "adat", 1, 1 },
+ { 30, 31, "adat", 1, 1 },
+ { 32, 33, "adat", 1, 1 },
+ { 34, 35, "adat", 1, 1 },
+
+ { 0, 0, NULL, 0, 0 },
+};
+
+static void
+hdspe_intr(void *p)
+{
+ struct sc_info *sc = (struct sc_info *)p;
+ struct sc_pcminfo *scp;
+ device_t *devlist;
+ int devcount, status;
+ int i, err;
+
+ snd_mtxlock(sc->lock);
+
+ status = hdspe_read_1(sc, HDSPE_STATUS_REG);
+ if (status & HDSPE_AUDIO_IRQ_PENDING) {
+ if ((err = device_get_children(sc->dev, &devlist, &devcount)) != 0)
+ return;
+
+ for (i = 0; i < devcount; i++) {
+ scp = device_get_ivars(devlist[i]);
+ if (scp->ih != NULL)
+ scp->ih(scp);
+ }
+
+ hdspe_write_1(sc, HDSPE_INTERRUPT_ACK, 0);
+ }
+
+ snd_mtxunlock(sc->lock);
+}
+
+static void
+hdspe_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+#if 0
+ struct sc_info *sc = (struct sc_info *)arg;
+ device_printf(sc->dev, "hdspe_dmapsetmap()\n");
+#endif
+}
+
+static int
+hdspe_alloc_resources(struct sc_info *sc)
+{
+
+ /* Allocate resource. */
+ sc->csid = PCIR_BAR(0);
+ sc->cs = bus_alloc_resource(sc->dev, SYS_RES_MEMORY,
+ &sc->csid, 0, ~0, 1, RF_ACTIVE);
+
+ if (!sc->cs) {
+ device_printf(sc->dev, "Unable to map SYS_RES_MEMORY.\n");
+ return (ENXIO);
+ }
+ sc->cst = rman_get_bustag(sc->cs);
+ sc->csh = rman_get_bushandle(sc->cs);
+
+
+ /* Allocate interrupt resource. */
+ sc->irqid = 0;
+ sc->irq = bus_alloc_resource(sc->dev, SYS_RES_IRQ, &sc->irqid,
+ 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
+
+ if (!sc->irq ||
+ bus_setup_intr(sc->dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV,
+ NULL, hdspe_intr, sc, &sc->ih)) {
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-stable-9
mailing list