aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/sound/pci
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/sound/pci')
-rw-r--r--sys/dev/sound/pci/als4000.c11
-rw-r--r--sys/dev/sound/pci/atiixp.c10
-rw-r--r--sys/dev/sound/pci/cmi.c6
-rw-r--r--sys/dev/sound/pci/cs4281.c6
-rw-r--r--sys/dev/sound/pci/csa.c98
-rw-r--r--sys/dev/sound/pci/csamidi.c1
-rw-r--r--sys/dev/sound/pci/csapcm.c9
-rw-r--r--sys/dev/sound/pci/csareg.h2
-rw-r--r--sys/dev/sound/pci/emu10k1.c5
-rw-r--r--sys/dev/sound/pci/emu10kx-midi.c1
-rw-r--r--sys/dev/sound/pci/emu10kx-pcm.c9
-rw-r--r--sys/dev/sound/pci/emu10kx.c185
-rw-r--r--sys/dev/sound/pci/envy24.c7
-rw-r--r--sys/dev/sound/pci/envy24ht.c8
-rw-r--r--sys/dev/sound/pci/es137x.c6
-rw-r--r--sys/dev/sound/pci/fm801.c15
-rw-r--r--sys/dev/sound/pci/hda/hdaa.c105
-rw-r--r--sys/dev/sound/pci/hda/hdaa_patches.c64
-rw-r--r--sys/dev/sound/pci/hda/hdac.c64
-rw-r--r--sys/dev/sound/pci/hda/hdac.h25
-rw-r--r--sys/dev/sound/pci/hda/hdacc.c10
-rw-r--r--sys/dev/sound/pci/hda/pin_patch_realtek.h15
-rw-r--r--sys/dev/sound/pci/hdsp-pcm.c1136
-rw-r--r--sys/dev/sound/pci/hdsp.c1022
-rw-r--r--sys/dev/sound/pci/hdsp.h266
-rw-r--r--sys/dev/sound/pci/hdspe-pcm.c83
-rw-r--r--sys/dev/sound/pci/hdspe.c239
-rw-r--r--sys/dev/sound/pci/hdspe.h78
-rw-r--r--sys/dev/sound/pci/ich.c9
-rw-r--r--sys/dev/sound/pci/maestro3.c15
-rw-r--r--sys/dev/sound/pci/neomagic.c5
-rw-r--r--sys/dev/sound/pci/solo.c7
-rw-r--r--sys/dev/sound/pci/spicds.c2
-rw-r--r--sys/dev/sound/pci/t4dwave.c6
-rw-r--r--sys/dev/sound/pci/via8233.c6
-rw-r--r--sys/dev/sound/pci/via82c686.c5
-rw-r--r--sys/dev/sound/pci/vibes.c11
37 files changed, 3102 insertions, 450 deletions
diff --git a/sys/dev/sound/pci/als4000.c b/sys/dev/sound/pci/als4000.c
index b1376d2b6e5a..9d86713b379e 100644
--- a/sys/dev/sound/pci/als4000.c
+++ b/sys/dev/sound/pci/als4000.c
@@ -839,10 +839,7 @@ als_pci_attach(device_t dev)
goto bad_attach;
}
- if (pcm_register(dev, sc, 1, 1)) {
- device_printf(dev, "failed to register pcm entries\n");
- goto bad_attach;
- }
+ pcm_init(dev, sc);
pcm_addchan(dev, PCMDIR_PLAY, &alspchan_class, sc);
pcm_addchan(dev, PCMDIR_REC, &alsrchan_class, sc);
@@ -850,7 +847,11 @@ als_pci_attach(device_t dev)
snprintf(status, SND_STATUSLEN, "port 0x%jx irq %jd on %s",
rman_get_start(sc->reg), rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status)) {
+ device_printf(dev, "failed to register pcm entries\n");
+ goto bad_attach;
+ }
+
return 0;
bad_attach:
diff --git a/sys/dev/sound/pci/atiixp.c b/sys/dev/sound/pci/atiixp.c
index dcbf041f9605..90e5742e6523 100644
--- a/sys/dev/sound/pci/atiixp.c
+++ b/sys/dev/sound/pci/atiixp.c
@@ -1084,8 +1084,7 @@ atiixp_chip_post_init(void *arg)
mixer_init(sc->dev, ac97_getmixerclass(), sc->codec);
- if (pcm_register(sc->dev, sc, ATI_IXP_NPCHAN, ATI_IXP_NRCHAN))
- goto postinitbad;
+ pcm_init(sc->dev, sc);
for (i = 0; i < ATI_IXP_NPCHAN; i++)
pcm_addchan(sc->dev, PCMDIR_PLAY, &atiixp_chan_class, sc);
@@ -1101,7 +1100,8 @@ atiixp_chip_post_init(void *arg)
rman_get_start(sc->reg), rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(sc->dev)));
- pcm_setstatus(sc->dev, status);
+ if (pcm_register(sc->dev, status))
+ goto postinitbad;
atiixp_lock(sc);
if (sc->polling == 0)
@@ -1168,12 +1168,12 @@ atiixp_release_resource(struct atiixp_info *sc)
static int
atiixp_pci_probe(device_t dev)
{
- int i;
+ size_t i;
uint16_t devid, vendor;
vendor = pci_get_vendor(dev);
devid = pci_get_device(dev);
- for (i = 0; i < sizeof(atiixp_hw) / sizeof(atiixp_hw[0]); i++) {
+ for (i = 0; i < nitems(atiixp_hw); i++) {
if (vendor == atiixp_hw[i].vendor &&
devid == atiixp_hw[i].devid) {
device_set_desc(dev, atiixp_hw[i].desc);
diff --git a/sys/dev/sound/pci/cmi.c b/sys/dev/sound/pci/cmi.c
index 9a92066c51a4..22f1e76a4d1f 100644
--- a/sys/dev/sound/pci/cmi.c
+++ b/sys/dev/sound/pci/cmi.c
@@ -982,8 +982,7 @@ cmi_attach(device_t dev)
if (mixer_init(dev, &cmi_mixer_class, sc))
goto bad;
- if (pcm_register(dev, sc, 1, 1))
- goto bad;
+ pcm_init(dev, sc);
cmi_initsys(sc);
@@ -993,7 +992,8 @@ cmi_attach(device_t dev)
snprintf(status, SND_STATUSLEN, "port 0x%jx irq %jd on %s",
rman_get_start(sc->reg), rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
DEB(printf("cmi_attach: succeeded\n"));
return 0;
diff --git a/sys/dev/sound/pci/cs4281.c b/sys/dev/sound/pci/cs4281.c
index 972b83efff8f..7a25f7f4c08d 100644
--- a/sys/dev/sound/pci/cs4281.c
+++ b/sys/dev/sound/pci/cs4281.c
@@ -839,8 +839,7 @@ cs4281_pci_attach(device_t dev)
mixer_init(dev, ac97_getmixerclass(), codec);
- if (pcm_register(dev, sc, 1, 1))
- goto bad;
+ pcm_init(dev, sc);
pcm_addchan(dev, PCMDIR_PLAY, &cs4281chan_class, sc);
pcm_addchan(dev, PCMDIR_REC, &cs4281chan_class, sc);
@@ -849,7 +848,8 @@ cs4281_pci_attach(device_t dev)
(sc->regtype == SYS_RES_IOPORT)? "port" : "mem",
rman_get_start(sc->reg), rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return 0;
diff --git a/sys/dev/sound/pci/csa.c b/sys/dev/sound/pci/csa.c
index 7191f0cc4bf9..4bd8ff029f43 100644
--- a/sys/dev/sound/pci/csa.c
+++ b/sys/dev/sound/pci/csa.c
@@ -45,7 +45,6 @@
#endif
#include <dev/sound/pcm/sound.h>
-#include <dev/sound/chip.h>
#include <dev/sound/pci/csareg.h>
#include <dev/sound/pci/csavar.h>
@@ -109,46 +108,25 @@ static int
clkrun_hack(int run)
{
#ifdef __i386__
- devclass_t pci_devclass;
- device_t *pci_devices, *pci_children, *busp, *childp;
- int pci_count = 0, pci_childcount = 0;
- int i, j, port;
+ device_t child;
+ int port;
u_int16_t control;
bus_space_tag_t btag;
- if ((pci_devclass = devclass_find("pci")) == NULL) {
- return ENXIO;
- }
+ child = pci_find_device(0x8086, 0x7113);
+ if (child == NULL)
+ return (ENXIO);
- devclass_get_devices(pci_devclass, &pci_devices, &pci_count);
-
- for (i = 0, busp = pci_devices; i < pci_count; i++, busp++) {
- pci_childcount = 0;
- if (device_get_children(*busp, &pci_children, &pci_childcount))
- continue;
- for (j = 0, childp = pci_children; j < pci_childcount; j++, childp++) {
- if (pci_get_vendor(*childp) == 0x8086 && pci_get_device(*childp) == 0x7113) {
- port = (pci_read_config(*childp, 0x41, 1) << 8) + 0x10;
- /* XXX */
- btag = X86_BUS_SPACE_IO;
-
- control = bus_space_read_2(btag, 0x0, port);
- control &= ~0x2000;
- control |= run? 0 : 0x2000;
- bus_space_write_2(btag, 0x0, port, control);
- free(pci_devices, M_TEMP);
- free(pci_children, M_TEMP);
- return 0;
- }
- }
- free(pci_children, M_TEMP);
- }
+ port = (pci_read_config(child, 0x41, 1) << 8) + 0x10;
+ /* XXX */
+ btag = X86_BUS_SPACE_IO;
- free(pci_devices, M_TEMP);
- return ENXIO;
-#else
- return 0;
+ control = bus_space_read_2(btag, 0x0, port);
+ control &= ~0x2000;
+ control |= run? 0 : 0x2000;
+ bus_space_write_2(btag, 0x0, port, control);
#endif
+ return (0);
}
static struct csa_card cards_4610[] = {
@@ -295,28 +273,20 @@ csa_attach(device_t dev)
/* Attach the children. */
/* PCM Audio */
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto err_teardown;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
func->varinfo = &scp->binfo;
func->func = SCF_PCM;
- scp->pcm = device_add_child(dev, "pcm", -1);
+ scp->pcm = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(scp->pcm, func);
/* Midi Interface */
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto err_teardown;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
func->varinfo = &scp->binfo;
func->func = SCF_MIDI;
- scp->midi = device_add_child(dev, "midi", -1);
+ scp->midi = device_add_child(dev, "midi", DEVICE_UNIT_ANY);
device_set_ivars(scp->midi, func);
- bus_generic_attach(dev);
+ bus_attach_children(dev);
return (0);
@@ -331,43 +301,32 @@ err_io:
return (error);
}
+static void
+csa_child_deleted(device_t dev, device_t child)
+{
+ free(device_get_ivars(child), M_DEVBUF);
+}
+
static int
csa_detach(device_t dev)
{
csa_res *resp;
sc_p scp;
- struct sndcard_func *func;
int err;
scp = device_get_softc(dev);
resp = &scp->res;
- if (scp->midi != NULL) {
- func = device_get_ivars(scp->midi);
- err = device_delete_child(dev, scp->midi);
- if (err != 0)
- return err;
- if (func != NULL)
- free(func, M_DEVBUF);
- scp->midi = NULL;
- }
-
- if (scp->pcm != NULL) {
- func = device_get_ivars(scp->pcm);
- err = device_delete_child(dev, scp->pcm);
- if (err != 0)
- return err;
- if (func != NULL)
- free(func, M_DEVBUF);
- scp->pcm = NULL;
- }
+ err = bus_generic_detach(dev);
+ if (err != 0)
+ return err;
bus_teardown_intr(dev, resp->irq, scp->ih);
bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq);
bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem);
bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io);
- return bus_generic_detach(dev);
+ return (0);
}
static int
@@ -1082,6 +1041,7 @@ static device_method_t csa_methods[] = {
DEVMETHOD(device_resume, csa_resume),
/* Bus interface */
+ DEVMETHOD(bus_child_deleted, csa_child_deleted),
DEVMETHOD(bus_alloc_resource, csa_alloc_resource),
DEVMETHOD(bus_release_resource, csa_release_resource),
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
diff --git a/sys/dev/sound/pci/csamidi.c b/sys/dev/sound/pci/csamidi.c
index df1699092990..29d5548b0954 100644
--- a/sys/dev/sound/pci/csamidi.c
+++ b/sys/dev/sound/pci/csamidi.c
@@ -43,7 +43,6 @@
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
-#include <dev/sound/chip.h>
#include <dev/sound/pcm/sound.h>
#include <dev/sound/midi/midi.h>
diff --git a/sys/dev/sound/pci/csapcm.c b/sys/dev/sound/pci/csapcm.c
index 71b9a0253cdb..a966a2e66402 100644
--- a/sys/dev/sound/pci/csapcm.c
+++ b/sys/dev/sound/pci/csapcm.c
@@ -36,7 +36,6 @@
#include <dev/sound/pcm/sound.h>
#include <dev/sound/pcm/ac97.h>
-#include <dev/sound/chip.h>
#include <dev/sound/pci/csareg.h>
#include <dev/sound/pci/csavar.h>
@@ -833,14 +832,14 @@ pcmcsa_attach(device_t dev)
csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001);
csa_active(csa, -1);
- if (pcm_register(dev, csa, 1, 1)) {
+ pcm_init(dev, csa);
+ pcm_addchan(dev, PCMDIR_REC, &csachan_class, csa);
+ pcm_addchan(dev, PCMDIR_PLAY, &csachan_class, csa);
+ if (pcm_register(dev, status)) {
ac97_destroy(codec);
csa_releaseres(csa, dev);
return (ENXIO);
}
- pcm_addchan(dev, PCMDIR_REC, &csachan_class, csa);
- pcm_addchan(dev, PCMDIR_PLAY, &csachan_class, csa);
- pcm_setstatus(dev, status);
return (0);
}
diff --git a/sys/dev/sound/pci/csareg.h b/sys/dev/sound/pci/csareg.h
index a36e36177f5c..3341390f618b 100644
--- a/sys/dev/sound/pci/csareg.h
+++ b/sys/dev/sound/pci/csareg.h
@@ -34,7 +34,7 @@
#define _CSA_REG_H
/*
- * The following constats are orginally in the sample by Crystal Semiconductor.
+ * The following constats are originally in the sample by Crystal Semiconductor.
* Copyright (c) 1996-1998 Crystal Semiconductor Corp.
*/
diff --git a/sys/dev/sound/pci/emu10k1.c b/sys/dev/sound/pci/emu10k1.c
index 0813f89c87b8..e4b2c22f4f07 100644
--- a/sys/dev/sound/pci/emu10k1.c
+++ b/sys/dev/sound/pci/emu10k1.c
@@ -2130,13 +2130,14 @@ emu_pci_attach(device_t dev)
rman_get_start(sc->reg), rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- if (pcm_register(dev, sc, sc->nchans, gotmic ? 3 : 2)) goto bad;
+ pcm_init(dev, sc);
for (i = 0; i < sc->nchans; i++)
pcm_addchan(dev, PCMDIR_PLAY, &emupchan_class, sc);
for (i = 0; i < (gotmic ? 3 : 2); i++)
pcm_addchan(dev, PCMDIR_REC, &emurchan_class, sc);
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return 0;
diff --git a/sys/dev/sound/pci/emu10kx-midi.c b/sys/dev/sound/pci/emu10kx-midi.c
index 4ed8e6c1dd9c..2a98562f8f39 100644
--- a/sys/dev/sound/pci/emu10kx-midi.c
+++ b/sys/dev/sound/pci/emu10kx-midi.c
@@ -43,7 +43,6 @@
#include "opt_snd.h"
#endif
-#include <dev/sound/chip.h>
#include <dev/sound/pcm/sound.h>
#include <dev/sound/midi/midi.h>
diff --git a/sys/dev/sound/pci/emu10kx-pcm.c b/sys/dev/sound/pci/emu10kx-pcm.c
index 825a39fc4e63..c280b64892f6 100644
--- a/sys/dev/sound/pci/emu10kx-pcm.c
+++ b/sys/dev/sound/pci/emu10kx-pcm.c
@@ -43,7 +43,6 @@
#include "opt_snd.h"
#endif
-#include <dev/sound/chip.h>
#include <dev/sound/pcm/sound.h>
#include <dev/sound/pcm/ac97.h>
@@ -1460,10 +1459,7 @@ emu_pcm_attach(device_t dev)
pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
/* XXX we should better get number of available channels from parent */
- if (pcm_register(dev, sc, (route == RT_FRONT) ? MAX_CHANNELS : 1, (route == RT_FRONT) ? 1 : 0)) {
- device_printf(dev, "can't register PCM channels!\n");
- goto bad;
- }
+ pcm_init(dev, sc);
sc->pnum = 0;
if (route != RT_MCHRECORD)
pcm_addchan(dev, PCMDIR_PLAY, &emupchan_class, sc);
@@ -1477,7 +1473,8 @@ emu_pcm_attach(device_t dev)
snprintf(status, SND_STATUSLEN, "on %s",
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return (0);
diff --git a/sys/dev/sound/pci/emu10kx.c b/sys/dev/sound/pci/emu10kx.c
index d17f5fb16d34..9cd7dbca1cb2 100644
--- a/sys/dev/sound/pci/emu10kx.c
+++ b/sys/dev/sound/pci/emu10kx.c
@@ -49,7 +49,6 @@
#include "opt_snd.h"
#endif
-#include <dev/sound/chip.h>
#include <dev/sound/pcm/sound.h>
#include <dev/sound/pcm/ac97.h>
@@ -3212,120 +3211,72 @@ emu_pci_attach(device_t dev)
sc->pcm[i] = NULL;
/* FRONT */
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto bad;
- }
- pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (pcminfo == NULL) {
- error = ENOMEM;
- goto bad;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
+ pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
pcminfo->card = sc;
pcminfo->route = RT_FRONT;
func->func = SCF_PCM;
func->varinfo = pcminfo;
- sc->pcm[RT_FRONT] = device_add_child(dev, "pcm", -1);
+ sc->pcm[RT_FRONT] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(sc->pcm[RT_FRONT], func);
if (!(sc->mch_disabled)) {
/* REAR */
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto bad;
- }
- pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (pcminfo == NULL) {
- error = ENOMEM;
- goto bad;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
+ pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
pcminfo->card = sc;
pcminfo->route = RT_REAR;
func->func = SCF_PCM;
func->varinfo = pcminfo;
- sc->pcm[RT_REAR] = device_add_child(dev, "pcm", -1);
+ sc->pcm[RT_REAR] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(sc->pcm[RT_REAR], func);
if (sc->has_51) {
/* CENTER */
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto bad;
- }
- pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (pcminfo == NULL) {
- error = ENOMEM;
- goto bad;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
+ pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
pcminfo->card = sc;
pcminfo->route = RT_CENTER;
func->func = SCF_PCM;
func->varinfo = pcminfo;
- sc->pcm[RT_CENTER] = device_add_child(dev, "pcm", -1);
+ sc->pcm[RT_CENTER] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(sc->pcm[RT_CENTER], func);
/* SUB */
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto bad;
- }
- pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (pcminfo == NULL) {
- error = ENOMEM;
- goto bad;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
+ pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
pcminfo->card = sc;
pcminfo->route = RT_SUB;
func->func = SCF_PCM;
func->varinfo = pcminfo;
- sc->pcm[RT_SUB] = device_add_child(dev, "pcm", -1);
+ sc->pcm[RT_SUB] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(sc->pcm[RT_SUB], func);
}
if (sc->has_71) {
/* SIDE */
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto bad;
- }
- pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (pcminfo == NULL) {
- error = ENOMEM;
- goto bad;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
+ pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
pcminfo->card = sc;
pcminfo->route = RT_SIDE;
func->func = SCF_PCM;
func->varinfo = pcminfo;
- sc->pcm[RT_SIDE] = device_add_child(dev, "pcm", -1);
+ sc->pcm[RT_SIDE] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(sc->pcm[RT_SIDE], func);
}
} /* mch_disabled */
if (sc->mch_rec) {
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto bad;
- }
- pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (pcminfo == NULL) {
- error = ENOMEM;
- goto bad;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
+ pcminfo = malloc(sizeof(struct emu_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
pcminfo->card = sc;
pcminfo->route = RT_MCHRECORD;
func->func = SCF_PCM;
func->varinfo = pcminfo;
- sc->pcm[RT_MCHRECORD] = device_add_child(dev, "pcm", -1);
+ sc->pcm[RT_MCHRECORD] = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(sc->pcm[RT_MCHRECORD], func);
} /*mch_rec */
@@ -3336,16 +3287,8 @@ emu_pci_attach(device_t dev)
#if 0
/* Midi Interface 1: Live!, Audigy, Audigy 2 */
if ((sc->is_emu10k1) || (sc->is_emu10k2) || (sc->is_ca0102)) {
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto bad;
- }
- midiinfo = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (midiinfo == NULL) {
- error = ENOMEM;
- goto bad;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
+ midiinfo = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_WAITOK | M_ZERO);
midiinfo->card = sc;
if (sc->is_emu10k2 || (sc->is_ca0102)) {
midiinfo->port = EMU_A_MUDATA1;
@@ -3357,21 +3300,13 @@ emu_pci_attach(device_t dev)
}
func->func = SCF_MIDI;
func->varinfo = midiinfo;
- sc->midi[0] = device_add_child(dev, "midi", -1);
+ sc->midi[0] = device_add_child(dev, "midi", DEVICE_UNIT_ANY);
device_set_ivars(sc->midi[0], func);
}
/* Midi Interface 2: Audigy, Audigy 2 (on AudigyDrive) */
if (sc->is_emu10k2 || (sc->is_ca0102)) {
- func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (func == NULL) {
- error = ENOMEM;
- goto bad;
- }
- midiinfo = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_NOWAIT | M_ZERO);
- if (midiinfo == NULL) {
- error = ENOMEM;
- goto bad;
- }
+ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_WAITOK | M_ZERO);
+ midiinfo = malloc(sizeof(struct emu_midiinfo), M_DEVBUF, M_WAITOK | M_ZERO);
midiinfo->card = sc;
midiinfo->port = EMU_A_MUDATA2;
@@ -3379,11 +3314,12 @@ emu_pci_attach(device_t dev)
func->func = SCF_MIDI;
func->varinfo = midiinfo;
- sc->midi[1] = device_add_child(dev, "midi", -1);
+ sc->midi[1] = device_add_child(dev, "midi", DEVICE_UNIT_ANY);
device_set_ivars(sc->midi[1], func);
}
#endif
- return (bus_generic_attach(dev));
+ bus_attach_children(dev);
+ return (0);
bad:
/* XXX can we just call emu_pci_detach here? */
@@ -3402,65 +3338,29 @@ bad:
return (error);
}
+static void
+emu_pci_child_deleted(device_t dev, device_t child)
+{
+ struct sndcard_func *func;
+
+ func = device_get_ivars(child);
+ if (func != NULL) {
+ free(func->varinfo, M_DEVBUF);
+ free(func, M_DEVBUF);
+ }
+}
+
static int
emu_pci_detach(device_t dev)
{
struct emu_sc_info *sc;
- struct sndcard_func *func;
- int devcount, i;
- device_t *childlist;
int r = 0;
sc = device_get_softc(dev);
- for (i = 0; i < RT_COUNT; i++) {
- if (sc->pcm[i] != NULL) {
- func = device_get_ivars(sc->pcm[i]);
- if (func != NULL && func->func == SCF_PCM) {
- device_set_ivars(sc->pcm[i], NULL);
- free(func->varinfo, M_DEVBUF);
- free(func, M_DEVBUF);
- }
- r = device_delete_child(dev, sc->pcm[i]);
- if (r) return (r);
- }
- }
-
- if (sc->midi[0] != NULL) {
- func = device_get_ivars(sc->midi[0]);
- if (func != NULL && func->func == SCF_MIDI) {
- device_set_ivars(sc->midi[0], NULL);
- free(func->varinfo, M_DEVBUF);
- free(func, M_DEVBUF);
- }
- r = device_delete_child(dev, sc->midi[0]);
- if (r) return (r);
- }
-
- if (sc->midi[1] != NULL) {
- func = device_get_ivars(sc->midi[1]);
- if (func != NULL && func->func == SCF_MIDI) {
- device_set_ivars(sc->midi[1], NULL);
- free(func->varinfo, M_DEVBUF);
- free(func, M_DEVBUF);
- }
- r = device_delete_child(dev, sc->midi[1]);
- if (r) return (r);
- }
-
- if (device_get_children(dev, &childlist, &devcount) == 0)
- for (i = 0; i < devcount - 1; i++) {
- device_printf(dev, "removing stale child %d (unit %d)\n", i, device_get_unit(childlist[i]));
- func = device_get_ivars(childlist[i]);
- if (func != NULL && (func->func == SCF_MIDI || func->func == SCF_PCM)) {
- device_set_ivars(childlist[i], NULL);
- free(func->varinfo, M_DEVBUF);
- free(func, M_DEVBUF);
- }
- device_delete_child(dev, childlist[i]);
- }
- if (childlist != NULL)
- free(childlist, M_TEMP);
+ r = bus_generic_detach(dev);
+ if (r != 0)
+ return (r);
r = emu10kx_dev_uninit(sc);
if (r)
@@ -3480,7 +3380,7 @@ emu_pci_detach(device_t dev)
mtx_destroy(&sc->rw);
mtx_destroy(&sc->lock);
- return (bus_generic_detach(dev));
+ return (0);
}
/* add suspend, resume */
static device_method_t emu_methods[] = {
@@ -3489,6 +3389,7 @@ static device_method_t emu_methods[] = {
DEVMETHOD(device_attach, emu_pci_attach),
DEVMETHOD(device_detach, emu_pci_detach),
/* Bus methods */
+ DEVMETHOD(bus_child_deleted, emu_pci_child_deleted),
DEVMETHOD(bus_read_ivar, emu_read_ivar),
DEVMETHOD(bus_write_ivar, emu_write_ivar),
diff --git a/sys/dev/sound/pci/envy24.c b/sys/dev/sound/pci/envy24.c
index f7cc7ff5724d..51842bfdb480 100644
--- a/sys/dev/sound/pci/envy24.c
+++ b/sys/dev/sound/pci/envy24.c
@@ -2575,9 +2575,7 @@ envy24_pci_attach(device_t dev)
mixer_init(dev, &envy24mixer_class, sc);
/* set channel information */
- err = pcm_register(dev, sc, 5, 2 + sc->adcn);
- if (err)
- goto bad;
+ pcm_init(dev, sc);
sc->chnum = 0;
for (i = 0; i < 5; i++) {
pcm_addchan(dev, PCMDIR_PLAY, &envy24chan_class, sc);
@@ -2601,7 +2599,8 @@ envy24_pci_attach(device_t dev)
rman_get_end(sc->mt) - rman_get_start(sc->mt) + 1,
rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return 0;
diff --git a/sys/dev/sound/pci/envy24ht.c b/sys/dev/sound/pci/envy24ht.c
index 65c67b84ce53..b8202a9fa7cd 100644
--- a/sys/dev/sound/pci/envy24ht.c
+++ b/sys/dev/sound/pci/envy24ht.c
@@ -2480,10 +2480,7 @@ envy24ht_pci_attach(device_t dev)
mixer_init(dev, &envy24htmixer_class, sc);
/* set channel information */
- /* err = pcm_register(dev, sc, 5, 2 + sc->adcn); */
- err = pcm_register(dev, sc, 1, 2 + sc->adcn);
- if (err)
- goto bad;
+ pcm_init(dev, sc);
sc->chnum = 0;
/* for (i = 0; i < 5; i++) { */
pcm_addchan(dev, PCMDIR_PLAY, &envy24htchan_class, sc);
@@ -2503,7 +2500,8 @@ envy24ht_pci_attach(device_t dev)
rman_get_end(sc->mt) - rman_get_start(sc->mt) + 1,
rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return 0;
diff --git a/sys/dev/sound/pci/es137x.c b/sys/dev/sound/pci/es137x.c
index 8f832d899dd3..3c1bea09b5d1 100644
--- a/sys/dev/sound/pci/es137x.c
+++ b/sys/dev/sound/pci/es137x.c
@@ -1861,13 +1861,13 @@ es_pci_attach(device_t dev)
rman_get_start(es->reg), rman_get_start(es->irq),
device_get_nameunit(device_get_parent(dev)));
- if (pcm_register(dev, es, numplay, 1))
- goto bad;
+ pcm_init(dev, es);
for (i = 0; i < numplay; i++)
pcm_addchan(dev, PCMDIR_PLAY, ct, es);
pcm_addchan(dev, PCMDIR_REC, ct, es);
es_init_sysctls(dev);
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
es->escfg = ES_SET_GP(es->escfg, 0);
if (numplay == 1)
device_printf(dev, "<Playback: DAC%d / Record: ADC>\n",
diff --git a/sys/dev/sound/pci/fm801.c b/sys/dev/sound/pci/fm801.c
index cbc74249c04d..3537c7807ded 100644
--- a/sys/dev/sound/pci/fm801.c
+++ b/sys/dev/sound/pci/fm801.c
@@ -642,13 +642,14 @@ fm801_pci_attach(device_t dev)
device_get_nameunit(device_get_parent(dev)));
#define FM801_MAXPLAYCH 1
- if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops;
+ pcm_init(dev, fm801);
pcm_addchan(dev, PCMDIR_PLAY, &fm801ch_class, fm801);
pcm_addchan(dev, PCMDIR_REC, &fm801ch_class, fm801);
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto oops;
- fm801->radio = device_add_child(dev, "radio", -1);
- bus_generic_attach(dev);
+ fm801->radio = device_add_child(dev, "radio", DEVICE_UNIT_ANY);
+ bus_attach_children(dev);
return 0;
@@ -675,12 +676,6 @@ fm801_pci_detach(device_t dev)
r = bus_generic_detach(dev);
if (r)
return r;
- if (fm801->radio != NULL) {
- r = device_delete_child(dev, fm801->radio);
- if (r)
- return r;
- fm801->radio = NULL;
- }
r = pcm_unregister(dev);
if (r)
diff --git a/sys/dev/sound/pci/hda/hdaa.c b/sys/dev/sound/pci/hda/hdaa.c
index dcd10cb36510..5dbb5c4f4453 100644
--- a/sys/dev/sound/pci/hda/hdaa.c
+++ b/sys/dev/sound/pci/hda/hdaa.c
@@ -267,7 +267,8 @@ hdaa_channels_handler(struct hdaa_audio_as *as)
struct hdaa_chan *ch = &devinfo->chans[as->chans[0]];
struct hdaa_widget *w;
uint8_t *eld;
- int i, total, sub, assume, channels;
+ int total, sub, assume, channels;
+ size_t i;
uint16_t cpins, upins, tpins;
cpins = upins = 0;
@@ -347,7 +348,7 @@ hdaa_channels_handler(struct hdaa_audio_as *as)
printf("\n");
);
/* Look for maximal fitting matrix. */
- for (i = 0; i < sizeof(matrixes) / sizeof(struct matrix); i++) {
+ for (i = 0; i < nitems(matrixes); i++) {
if (as->pinset != 0 && matrixes[i].analog == 0)
continue;
if ((matrixes[i].m.mask & ~channels) == 0) {
@@ -531,9 +532,11 @@ static void
hdaa_presence_handler(struct hdaa_widget *w)
{
struct hdaa_devinfo *devinfo = w->devinfo;
- struct hdaa_audio_as *as;
+ struct hdaa_audio_as *as, *asp;
+ char buf[32];
uint32_t res;
- int connected, old;
+ int connected, old, i;
+ bool active;
if (w->enable == 0 || w->type !=
HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)
@@ -551,13 +554,6 @@ hdaa_presence_handler(struct hdaa_widget *w)
if (connected == old)
return;
w->wclass.pin.connected = connected;
- HDA_BOOTVERBOSE(
- if (connected || old != 2) {
- device_printf(devinfo->dev,
- "Pin sense: nid=%d sense=0x%08x (%sconnected)\n",
- w->nid, res, !connected ? "dis" : "");
- }
- );
as = &devinfo->as[w->bindas];
if (as->hpredir >= 0 && as->pins[15] == w->nid)
@@ -566,6 +562,38 @@ hdaa_presence_handler(struct hdaa_widget *w)
hdaa_autorecsrc_handler(as, w);
if (old != 2)
hdaa_channels_handler(as);
+
+ if (connected || old != 2) {
+ HDA_BOOTVERBOSE(
+ device_printf(devinfo->dev,
+ "Pin sense: nid=%d sense=0x%08x (%sconnected)\n",
+ w->nid, res, !connected ? "dis" : "");
+ );
+ if (as->hpredir >= 0)
+ return;
+ for (i = 0, active = false; i < devinfo->num_devs; i++) {
+ if (device_get_unit(devinfo->devs[i].dev) == snd_unit) {
+ active = true;
+ break;
+ }
+ }
+ /* Proceed only if we are currently using this codec. */
+ if (!active)
+ return;
+ for (i = 0; i < devinfo->ascnt; i++) {
+ asp = &devinfo->as[i];
+ if (!asp->enable)
+ continue;
+ if ((connected && asp->index == as->index) ||
+ (!connected && asp->dir == as->dir)) {
+ snprintf(buf, sizeof(buf), "cdev=dsp%d",
+ device_get_unit(asp->pdevinfo->dev));
+ devctl_notify("SND", "CONN",
+ asp->dir == HDAA_CTL_IN ? "IN" : "OUT", buf);
+ break;
+ }
+ }
+ }
}
/*
@@ -1252,7 +1280,8 @@ hdaa_sysctl_config(SYSCTL_HANDLER_ARGS)
static void
hdaa_config_fetch(const char *str, uint32_t *on, uint32_t *off)
{
- int i = 0, j, k, len, inv;
+ size_t k;
+ int i = 0, j, len, inv;
for (;;) {
while (str[i] != '\0' &&
@@ -1292,7 +1321,8 @@ static int
hdaa_sysctl_quirks(SYSCTL_HANDLER_ARGS)
{
char buf[256];
- int error, n = 0, i;
+ int error, n = 0;
+ size_t i;
uint32_t quirks, quirks_off;
quirks = *(uint32_t *)oidp->oid_arg1;
@@ -2909,7 +2939,7 @@ hdaa_dump_gpo(struct hdaa_devinfo *devinfo)
data = hda_command(dev,
HDA_CMD_GET_GPO_DATA(0, devinfo->nid));
for (i = 0; i < HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap); i++) {
- device_printf(dev, " GPO%d: state=%d", i,
+ device_printf(dev, " GPO%d: state=%d\n", i,
(data >> i) & 1);
}
}
@@ -3030,8 +3060,7 @@ hdaa_audio_ctl_parse(struct hdaa_devinfo *devinfo)
if (max < 1)
return;
- ctls = (struct hdaa_audio_ctl *)malloc(
- sizeof(*ctls) * max, M_HDAA, M_ZERO | M_NOWAIT);
+ ctls = malloc(sizeof(*ctls) * max, M_HDAA, M_ZERO | M_NOWAIT);
if (ctls == NULL) {
/* Blekh! */
@@ -3183,8 +3212,7 @@ hdaa_audio_as_parse(struct hdaa_devinfo *devinfo)
if (max < 1)
return;
- as = (struct hdaa_audio_as *)malloc(
- sizeof(*as) * max, M_HDAA, M_ZERO | M_NOWAIT);
+ as = malloc(sizeof(*as) * max, M_HDAA, M_ZERO | M_NOWAIT);
if (as == NULL) {
/* Blekh! */
@@ -3217,7 +3245,7 @@ hdaa_audio_as_parse(struct hdaa_devinfo *devinfo)
continue;
}
KASSERT(cnt < max,
- ("%s: Associations owerflow (%d of %d)",
+ ("%s: Associations overflow (%d of %d)",
__func__, cnt, max));
type = w->wclass.pin.config &
HDA_CONFIG_DEFAULTCONF_DEVICE_MASK;
@@ -4074,8 +4102,7 @@ hdaa_audio_bind_as(struct hdaa_devinfo *devinfo)
cnt += as[j].num_chans;
}
if (devinfo->num_chans == 0) {
- devinfo->chans = (struct hdaa_chan *)malloc(
- sizeof(struct hdaa_chan) * cnt,
+ devinfo->chans = malloc(sizeof(struct hdaa_chan) * cnt,
M_HDAA, M_ZERO | M_NOWAIT);
if (devinfo->chans == NULL) {
device_printf(devinfo->dev,
@@ -5487,10 +5514,8 @@ hdaa_prepare_pcms(struct hdaa_devinfo *devinfo)
}
devinfo->num_devs =
max(ardev, apdev) + max(drdev, dpdev);
- devinfo->devs =
- (struct hdaa_pcm_devinfo *)malloc(
- devinfo->num_devs * sizeof(struct hdaa_pcm_devinfo),
- M_HDAA, M_ZERO | M_NOWAIT);
+ devinfo->devs = malloc(devinfo->num_devs *
+ sizeof(struct hdaa_pcm_devinfo), M_HDAA, M_ZERO | M_NOWAIT);
if (devinfo->devs == NULL) {
device_printf(devinfo->dev,
"Unable to allocate memory for devices\n");
@@ -5539,7 +5564,7 @@ hdaa_create_pcms(struct hdaa_devinfo *devinfo)
for (i = 0; i < devinfo->num_devs; i++) {
struct hdaa_pcm_devinfo *pdevinfo = &devinfo->devs[i];
- pdevinfo->dev = device_add_child(devinfo->dev, "pcm", -1);
+ pdevinfo->dev = device_add_child(devinfo->dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(pdevinfo->dev, (void *)pdevinfo);
}
}
@@ -6196,13 +6221,15 @@ hdaa_configure(device_t dev)
);
hdaa_patch_direct(devinfo);
HDA_BOOTHVERBOSE(
- device_printf(dev, "Pin sense init...\n");
- );
- hdaa_sense_init(devinfo);
- HDA_BOOTHVERBOSE(
device_printf(dev, "Creating PCM devices...\n");
);
+ hdaa_unlock(devinfo);
hdaa_create_pcms(devinfo);
+ hdaa_lock(devinfo);
+ HDA_BOOTHVERBOSE(
+ device_printf(dev, "Pin sense init...\n");
+ );
+ hdaa_sense_init(devinfo);
HDA_BOOTVERBOSE(
if (devinfo->quirks != 0) {
@@ -6470,7 +6497,7 @@ hdaa_sysctl_reconfig(SYSCTL_HANDLER_ARGS)
hdaa_unconfigure(dev);
hdaa_configure(dev);
hdaa_unlock(devinfo);
- bus_generic_attach(dev);
+ bus_attach_children(dev);
HDA_BOOTHVERBOSE(
device_printf(dev, "Reconfiguration done\n");
);
@@ -6622,9 +6649,8 @@ hdaa_attach(device_t dev)
);
if (devinfo->nodecnt > 0)
- devinfo->widget = (struct hdaa_widget *)malloc(
- sizeof(*(devinfo->widget)) * devinfo->nodecnt, M_HDAA,
- M_WAITOK | M_ZERO);
+ devinfo->widget = malloc(sizeof(*(devinfo->widget)) *
+ devinfo->nodecnt, M_HDAA, M_WAITOK | M_ZERO);
else
devinfo->widget = NULL;
@@ -6677,7 +6703,7 @@ hdaa_attach(device_t dev)
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
"init_clear", CTLFLAG_RW,
&devinfo->init_clear, 1,"Clear initial pin widget configuration");
- bus_generic_attach(dev);
+ bus_attach_children(dev);
return (0);
}
@@ -6687,7 +6713,7 @@ hdaa_detach(device_t dev)
struct hdaa_devinfo *devinfo = device_get_softc(dev);
int error;
- if ((error = device_delete_children(dev)) != 0)
+ if ((error = bus_generic_detach(dev)) != 0)
return (error);
hdaa_lock(devinfo);
@@ -7056,9 +7082,7 @@ hdaa_pcm_attach(device_t dev)
HDA_BOOTHVERBOSE(
device_printf(dev, "Registering PCM channels...\n");
);
- if (pcm_register(dev, pdevinfo, (pdevinfo->playas >= 0)?1:0,
- (pdevinfo->recas >= 0)?1:0) != 0)
- device_printf(dev, "Can't register PCM\n");
+ pcm_init(dev, pdevinfo);
pdevinfo->registered++;
@@ -7111,9 +7135,8 @@ hdaa_pcm_attach(device_t dev)
snprintf(status, SND_STATUSLEN, "on %s",
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
- return (0);
+ return (pcm_register(dev, status));
}
static int
diff --git a/sys/dev/sound/pci/hda/hdaa_patches.c b/sys/dev/sound/pci/hda/hdaa_patches.c
index 3a7dfc63637a..91bb244578c7 100644
--- a/sys/dev/sound/pci/hda/hdaa_patches.c
+++ b/sys/dev/sound/pci/hda/hdaa_patches.c
@@ -113,6 +113,12 @@ static const struct {
{ APPLE_MACBOOKAIR31, HDA_CODEC_CS4206, HDA_MATCH_ALL,
0, 0,
HDAA_GPIO_SET(1) | HDAA_GPIO_SET(3) },
+ { HDA_MATCH_ALL, HDA_CODEC_CS4208, APPLE_MACBOOKAIR61,
+ 0, 0,
+ HDAA_GPIO_SET(0) },
+ { HDA_MATCH_ALL, HDA_CODEC_CS4208, APPLE_MACBOOKAIR62,
+ 0, 0,
+ HDAA_GPIO_SET(0) },
{ APPLE_MACBOOKPRO55, HDA_CODEC_CS4206, HDA_MATCH_ALL,
0, 0,
HDAA_GPIO_SET(1) | HDAA_GPIO_SET(3) },
@@ -300,6 +306,22 @@ hdac_pin_patch(struct hdaa_widget *w)
patch_str = "as=2";
break;
}
+ } else if (id == HDA_CODEC_CX20590 &&
+ subid == LENOVO_T420S_SUBVENDOR) {
+ switch (nid) {
+ case 25:
+ patch_str = "as=1 seq=15";
+ break;
+ case 27:
+ patch_str = "as=2 seq=15";
+ break;
+ case 31:
+ patch_str = "as=1 seq=0";
+ break;
+ case 35:
+ patch_str = "as=2 seq=0";
+ break;
+ }
} else if (id == HDA_CODEC_ALC235 && subid == ASUS_GL553VE_SUBVENDOR) {
switch (nid) {
case 33:
@@ -318,7 +340,8 @@ hdac_pin_patch(struct hdaa_widget *w)
}
} else if (id == HDA_CODEC_ALC257 &&
(subid == LENOVO_L5AMD_SUBVENDOR ||
- subid == LENOVO_L5INTEL_SUBVENDOR)) {
+ subid == LENOVO_L5INTEL_SUBVENDOR ||
+ subid == LENOVO_IDEAPAD3_SUBVENDOR)) {
switch (nid) {
case 20:
patch_str = "as=1 seq=0";
@@ -329,7 +352,8 @@ hdac_pin_patch(struct hdaa_widget *w)
}
} else if (id == HDA_CODEC_IDT92HD95B &&
(subid == FRAMEWORK_LAPTOP_0001_SUBVENDOR ||
- subid == FRAMEWORK_LAPTOP_0002_SUBVENDOR)) {
+ subid == FRAMEWORK_LAPTOP_0002_SUBVENDOR ||
+ subid == FRAMEWORK_LAPTOP_0003_SUBVENDOR)) {
switch (nid) {
case 10:
patch_str = "as=1 seq=15 color=Black loc=Left";
@@ -338,8 +362,31 @@ hdac_pin_patch(struct hdaa_widget *w)
patch_str = "as=3 seq=15 color=Black loc=Left";
break;
}
+ } else if ((id == HDA_CODEC_ALC295 &&
+ subid == FRAMEWORK_LAPTOP_0005_SUBVENDOR) ||
+ (id == HDA_CODEC_ALC285 &&
+ subid == FRAMEWORK_LAPTOP_000D_SUBVENDOR)) {
+ switch (nid) {
+ case 20:
+ /*
+ * This pin is a duplicate of pin 23 (both as=1 seq=0),
+ * which ends up in the driver disabling the
+ * association altogether. Since sound quality from pin
+ * 23 seems to be better, configure this one as a back
+ * speaker.
+ */
+ patch_str = "as=1 seq=2";
+ break;
+ }
+ } else if (id == HDA_CODEC_ALC295 &&
+ subid == FRAMEWORK_LAPTOP_0006_SUBVENDOR) {
+ switch (nid) {
+ case 33:
+ patch_str = "as=1 seq=15 color=Black loc=Left";
+ break;
+ }
} else if (id == HDA_CODEC_ALC230 &&
- subid == LENOVO_I330_SUBVENDOR) {
+ subid == LENOVO_IDEAPAD330_SUBVENDOR) {
switch (nid) {
case 20:
patch_str = "as=1 seq=0 device=Speaker";
@@ -358,6 +405,17 @@ hdac_pin_patch(struct hdaa_widget *w)
patch_str = "as=4 seq=15";
break;
}
+ } else if (id == HDA_CODEC_ALC294 &&
+ subid == ASUS_UX331_SUBVENDOR) {
+ switch (nid) {
+ case 25:
+ /* XXX You are not expected to understand this. */
+ config = 0x01a1103c;
+ break;
+ case 33:
+ patch_str = "as=1 seq=15";
+ break;
+ }
} else {
/*
* loop over hdaa_model_pin_patch
diff --git a/sys/dev/sound/pci/hda/hdac.c b/sys/dev/sound/pci/hda/hdac.c
index f3dff2052b51..80028063bb0d 100644
--- a/sys/dev/sound/pci/hda/hdac.c
+++ b/sys/dev/sound/pci/hda/hdac.c
@@ -106,9 +106,8 @@ static const struct {
{ HDA_INTEL_CMLKS, "Intel Comet Lake-S", 0, 0 },
{ HDA_INTEL_CNLK, "Intel Cannon Lake", 0, 0 },
{ HDA_INTEL_ICLK, "Intel Ice Lake", 0, 0 },
- { HDA_INTEL_CMLKLP, "Intel Comet Lake-LP", 0, 0 },
- { HDA_INTEL_CMLKH, "Intel Comet Lake-H", 0, 0 },
{ HDA_INTEL_TGLK, "Intel Tiger Lake", 0, 0 },
+ { HDA_INTEL_TGLKH, "Intel Tiger Lake-H", 0, 0 },
{ HDA_INTEL_GMLK, "Intel Gemini Lake", 0, 0 },
{ HDA_INTEL_ALLK, "Intel Alder Lake", 0, 0 },
{ HDA_INTEL_ALLKM, "Intel Alder Lake-M", 0, 0 },
@@ -118,6 +117,7 @@ static const struct {
{ HDA_INTEL_ALLKPS, "Intel Alder Lake-PS", 0, 0 },
{ HDA_INTEL_RPTLK1, "Intel Raptor Lake-P", 0, 0 },
{ HDA_INTEL_RPTLK2, "Intel Raptor Lake-P", 0, 0 },
+ { HDA_INTEL_RPTLK3, "Intel Raptor Lake-S", 0, 0 },
{ HDA_INTEL_MTL, "Intel Meteor Lake-P", 0, 0 },
{ HDA_INTEL_ARLS, "Intel Arrow Lake-S", 0, 0 },
{ HDA_INTEL_ARL, "Intel Arrow Lake", 0, 0 },
@@ -133,6 +133,7 @@ static const struct {
{ HDA_INTEL_PCH, "Intel Ibex Peak", 0, 0 },
{ HDA_INTEL_PCH2, "Intel Ibex Peak", 0, 0 },
{ HDA_INTEL_ELLK, "Intel Elkhart Lake", 0, 0 },
+ { HDA_INTEL_ELLK2, "Intel Elkhart Lake", 0, 0 },
{ HDA_INTEL_JLK2, "Intel Jasper Lake", 0, 0 },
{ HDA_INTEL_BXTNP, "Intel Broxton-P", 0, 0 },
{ HDA_INTEL_SCH, "Intel SCH", 0, 0 },
@@ -193,6 +194,7 @@ static const struct {
{ HDA_ATI_RV940, "ATI RV940", 0, 0 },
{ HDA_ATI_RV970, "ATI RV970", 0, 0 },
{ HDA_ATI_R1000, "ATI R1000", 0, 0 },
+ { HDA_ATI_OLAND, "ATI Oland", 0, 0 },
{ HDA_ATI_KABINI, "ATI Kabini", 0, 0 },
{ HDA_ATI_TRINITY, "ATI Trinity", 0, 0 },
{ HDA_AMD_X370, "AMD X370", 0, 0 },
@@ -1277,6 +1279,7 @@ hdac_attach(device_t dev)
goto hdac_attach_fail;
/* Get Capabilities */
+ hdac_reset(sc, 1);
result = hdac_get_capabilities(sc);
if (result != 0)
goto hdac_attach_fail;
@@ -1615,7 +1618,7 @@ hdac_attach2(void *arg)
HDA_PARAM_REVISION_ID_REVISION_ID(revisionid);
sc->codecs[i].stepping_id =
HDA_PARAM_REVISION_ID_STEPPING_ID(revisionid);
- child = device_add_child(sc->dev, "hdacc", -1);
+ child = device_add_child(sc->dev, "hdacc", DEVICE_UNIT_ANY);
if (child == NULL) {
device_printf(sc->dev,
"Failed to add CODEC device\n");
@@ -1625,7 +1628,7 @@ hdac_attach2(void *arg)
sc->codecs[i].dev = child;
}
}
- bus_generic_attach(sc->dev);
+ bus_attach_children(sc->dev);
SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev),
SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO,
@@ -1638,6 +1641,35 @@ hdac_attach2(void *arg)
}
/****************************************************************************
+ * int hdac_shutdown(device_t)
+ *
+ * Power down HDA bus and codecs.
+ ****************************************************************************/
+static int
+hdac_shutdown(device_t dev)
+{
+ struct hdac_softc *sc = device_get_softc(dev);
+
+ HDA_BOOTHVERBOSE(
+ device_printf(dev, "Shutdown...\n");
+ );
+ callout_drain(&sc->poll_callout);
+ taskqueue_drain(taskqueue_thread, &sc->unsolq_task);
+ bus_generic_shutdown(dev);
+
+ hdac_lock(sc);
+ HDA_BOOTHVERBOSE(
+ device_printf(dev, "Reset controller...\n");
+ );
+ hdac_reset(sc, false);
+ hdac_unlock(sc);
+ HDA_BOOTHVERBOSE(
+ device_printf(dev, "Shutdown done\n");
+ );
+ return (0);
+}
+
+/****************************************************************************
* int hdac_suspend(device_t)
*
* Suspend and power down HDA bus and codecs.
@@ -1740,26 +1772,19 @@ static int
hdac_detach(device_t dev)
{
struct hdac_softc *sc = device_get_softc(dev);
- device_t *devlist;
- int cad, i, devcount, error;
+ int i, error;
- if ((error = device_get_children(dev, &devlist, &devcount)) != 0)
+ callout_drain(&sc->poll_callout);
+ hdac_irq_free(sc);
+ taskqueue_drain(taskqueue_thread, &sc->unsolq_task);
+
+ error = bus_generic_detach(dev);
+ if (error != 0)
return (error);
- for (i = 0; i < devcount; i++) {
- cad = (intptr_t)device_get_ivars(devlist[i]);
- if ((error = device_delete_child(dev, devlist[i])) != 0) {
- free(devlist, M_TEMP);
- return (error);
- }
- sc->codecs[cad].dev = NULL;
- }
- free(devlist, M_TEMP);
hdac_lock(sc);
hdac_reset(sc, false);
hdac_unlock(sc);
- taskqueue_drain(taskqueue_thread, &sc->unsolq_task);
- hdac_irq_free(sc);
for (i = 0; i < sc->num_ss; i++)
hdac_dma_free(sc, &sc->streams[i].bdl);
@@ -2154,6 +2179,7 @@ static device_method_t hdac_methods[] = {
DEVMETHOD(device_probe, hdac_probe),
DEVMETHOD(device_attach, hdac_attach),
DEVMETHOD(device_detach, hdac_detach),
+ DEVMETHOD(device_shutdown, hdac_shutdown),
DEVMETHOD(device_suspend, hdac_suspend),
DEVMETHOD(device_resume, hdac_resume),
/* Bus interface */
@@ -2181,4 +2207,4 @@ static driver_t hdac_driver = {
sizeof(struct hdac_softc),
};
-DRIVER_MODULE(snd_hda, pci, hdac_driver, NULL, NULL);
+DRIVER_MODULE_ORDERED(snd_hda, pci, hdac_driver, NULL, NULL, SI_ORDER_ANY);
diff --git a/sys/dev/sound/pci/hda/hdac.h b/sys/dev/sound/pci/hda/hdac.h
index 4dd589ed2a09..c11e6b2d6810 100644
--- a/sys/dev/sound/pci/hda/hdac.h
+++ b/sys/dev/sound/pci/hda/hdac.h
@@ -66,6 +66,7 @@
#define HDA_INTEL_PCH HDA_MODEL_CONSTRUCT(INTEL, 0x3b56)
#define HDA_INTEL_PCH2 HDA_MODEL_CONSTRUCT(INTEL, 0x3b57)
#define HDA_INTEL_ELLK HDA_MODEL_CONSTRUCT(INTEL, 0x4b55)
+#define HDA_INTEL_ELLK2 HDA_MODEL_CONSTRUCT(INTEL, 0x4b58)
#define HDA_INTEL_JLK2 HDA_MODEL_CONSTRUCT(INTEL, 0x4dc8)
#define HDA_INTEL_BXTNP HDA_MODEL_CONSTRUCT(INTEL, 0x5a98)
#define HDA_INTEL_MACBOOKPRO92 HDA_MODEL_CONSTRUCT(INTEL, 0x7270)
@@ -77,6 +78,7 @@
#define HDA_INTEL_ALLKPS HDA_MODEL_CONSTRUCT(INTEL, 0x51c9)
#define HDA_INTEL_RPTLK1 HDA_MODEL_CONSTRUCT(INTEL, 0x51ca)
#define HDA_INTEL_RPTLK2 HDA_MODEL_CONSTRUCT(INTEL, 0x51cb)
+#define HDA_INTEL_RPTLK3 HDA_MODEL_CONSTRUCT(INTEL, 0x7a50)
#define HDA_INTEL_SCH HDA_MODEL_CONSTRUCT(INTEL, 0x811b)
#define HDA_INTEL_LPT1 HDA_MODEL_CONSTRUCT(INTEL, 0x8c20)
#define HDA_INTEL_LPT2 HDA_MODEL_CONSTRUCT(INTEL, 0x8c21)
@@ -95,9 +97,8 @@
#define HDA_INTEL_CMLKS HDA_MODEL_CONSTRUCT(INTEL, 0xa3f0)
#define HDA_INTEL_CNLK HDA_MODEL_CONSTRUCT(INTEL, 0x9dc8)
#define HDA_INTEL_ICLK HDA_MODEL_CONSTRUCT(INTEL, 0x34c8)
-#define HDA_INTEL_CMLKLP HDA_MODEL_CONSTRUCT(INTEL, 0x02c8)
-#define HDA_INTEL_CMLKH HDA_MODEL_CONSTRUCT(INTEL, 0x06c8)
#define HDA_INTEL_TGLK HDA_MODEL_CONSTRUCT(INTEL, 0xa0c8)
+#define HDA_INTEL_TGLKH HDA_MODEL_CONSTRUCT(INTEL, 0x43c8)
#define HDA_INTEL_MTL HDA_MODEL_CONSTRUCT(INTEL, 0x7e28)
#define HDA_INTEL_ARLS HDA_MODEL_CONSTRUCT(INTEL, 0x7f50)
#define HDA_INTEL_ARL HDA_MODEL_CONSTRUCT(INTEL, 0x7728)
@@ -178,6 +179,7 @@
#define HDA_ATI_RV930 HDA_MODEL_CONSTRUCT(ATI, 0xaa90)
#define HDA_ATI_RV910 HDA_MODEL_CONSTRUCT(ATI, 0xaa98)
#define HDA_ATI_R1000 HDA_MODEL_CONSTRUCT(ATI, 0xaaa0)
+#define HDA_ATI_OLAND HDA_MODEL_CONSTRUCT(ATI, 0xaab0)
#define HDA_ATI_KABINI HDA_MODEL_CONSTRUCT(ATI, 0x9840)
#define HDA_ATI_TRINITY HDA_MODEL_CONSTRUCT(ATI, 0x9902)
#define HDA_ATI_ALL HDA_MODEL_CONSTRUCT(ATI, 0xffff)
@@ -332,6 +334,7 @@
#define ASUS_G2K_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1339)
#define ASUS_Z550SA_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x13b0)
#define ASUS_A7T_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x13c2)
+#define ASUS_UX331_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x14de)
#define ASUS_UX31A_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1517)
#define ASUS_GL553VE_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x15e0)
#define ASUS_Z71V_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1964)
@@ -371,6 +374,7 @@
#define LENOVO_X300_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x20ac)
#define LENOVO_T400_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x20f2)
#define LENOVO_T420_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x21ce)
+#define LENOVO_T420S_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x21d2)
#define LENOVO_T430_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x21f3)
#define LENOVO_T430S_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x21fb)
#define LENOVO_T520_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x21cf)
@@ -378,11 +382,13 @@
#define LENOVO_X230_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x21fa)
#define LENOVO_X230T_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x2203)
#define LENOVO_T431S_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x2208)
+#define LENOVO_X1CRBNG11_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x2315)
#define LENOVO_G580_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x3977)
#define LENOVO_L5AMD_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x381b)
#define LENOVO_L5INTEL_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x380f)
#define LENOVO_3000_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x384e)
-#define LENOVO_I330_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x3808)
+#define LENOVO_IDEAPAD330_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x3808)
+#define LENOVO_IDEAPAD3_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x3881)
#define LENOVO_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0xffff)
/* Samsung */
@@ -422,6 +428,8 @@
*/
#define APPLE_INTEL_MAC 0x76808384
#define APPLE_MACBOOKAIR31 0x0d9410de
+#define APPLE_MACBOOKAIR61 0x106b7100
+#define APPLE_MACBOOKAIR62 0x106b7200
#define APPLE_MACBOOKPRO55 0xcb7910de
#define APPLE_MACBOOKPRO71 0xcb8910de
@@ -525,6 +533,10 @@
#define FRAMEWORK_VENDORID 0xf111
#define FRAMEWORK_LAPTOP_0001_SUBVENDOR HDA_MODEL_CONSTRUCT(FRAMEWORK, 0x0001)
#define FRAMEWORK_LAPTOP_0002_SUBVENDOR HDA_MODEL_CONSTRUCT(FRAMEWORK, 0x0002)
+#define FRAMEWORK_LAPTOP_0003_SUBVENDOR HDA_MODEL_CONSTRUCT(FRAMEWORK, 0x0003)
+#define FRAMEWORK_LAPTOP_0005_SUBVENDOR HDA_MODEL_CONSTRUCT(FRAMEWORK, 0x0005)
+#define FRAMEWORK_LAPTOP_0006_SUBVENDOR HDA_MODEL_CONSTRUCT(FRAMEWORK, 0x0006)
+#define FRAMEWORK_LAPTOP_000D_SUBVENDOR HDA_MODEL_CONSTRUCT(FRAMEWORK, 0x000d)
/* All codecs you can eat... */
#define HDA_CODEC_CONSTRUCT(vendor, id) \
@@ -534,6 +546,7 @@
#define CIRRUSLOGIC_VENDORID 0x1013
#define HDA_CODEC_CS4206 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4206)
#define HDA_CODEC_CS4207 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4207)
+#define HDA_CODEC_CS4208 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4208)
#define HDA_CODEC_CS4210 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4210)
#define HDA_CODEC_CSXXXX HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0xffff)
@@ -571,6 +584,7 @@
#define HDA_CODEC_ALC284 HDA_CODEC_CONSTRUCT(REALTEK, 0x0284)
#define HDA_CODEC_ALC285 HDA_CODEC_CONSTRUCT(REALTEK, 0x0285)
#define HDA_CODEC_ALC286 HDA_CODEC_CONSTRUCT(REALTEK, 0x0286)
+#define HDA_CODEC_ALC287 HDA_CODEC_CONSTRUCT(REALTEK, 0x0287)
#define HDA_CODEC_ALC288 HDA_CODEC_CONSTRUCT(REALTEK, 0x0288)
#define HDA_CODEC_ALC289 HDA_CODEC_CONSTRUCT(REALTEK, 0x0289)
#define HDA_CODEC_ALC290 HDA_CODEC_CONSTRUCT(REALTEK, 0x0290)
@@ -580,8 +594,6 @@
#define HDA_CODEC_ALC295 HDA_CODEC_CONSTRUCT(REALTEK, 0x0295)
#define HDA_CODEC_ALC298 HDA_CODEC_CONSTRUCT(REALTEK, 0x0298)
#define HDA_CODEC_ALC299 HDA_CODEC_CONSTRUCT(REALTEK, 0x0299)
-#define HDA_CODEC_ALC292 HDA_CODEC_CONSTRUCT(REALTEK, 0x0292)
-#define HDA_CODEC_ALC295 HDA_CODEC_CONSTRUCT(REALTEK, 0x0295)
#define HDA_CODEC_ALC300 HDA_CODEC_CONSTRUCT(REALTEK, 0x0300)
#define HDA_CODEC_ALC623 HDA_CODEC_CONSTRUCT(REALTEK, 0x0623)
#define HDA_CODEC_ALC660 HDA_CODEC_CONSTRUCT(REALTEK, 0x0660)
@@ -605,6 +617,7 @@
#define HDA_CODEC_ALC889 HDA_CODEC_CONSTRUCT(REALTEK, 0x0889)
#define HDA_CODEC_ALC892 HDA_CODEC_CONSTRUCT(REALTEK, 0x0892)
#define HDA_CODEC_ALC897 HDA_CODEC_CONSTRUCT(REALTEK, 0x0897)
+#define HDA_CODEC_ALC898 HDA_CODEC_CONSTRUCT(REALTEK, 0x0898)
#define HDA_CODEC_ALC899 HDA_CODEC_CONSTRUCT(REALTEK, 0x0899)
#define HDA_CODEC_ALC1150 HDA_CODEC_CONSTRUCT(REALTEK, 0x0900)
#define HDA_CODEC_ALCS1200A HDA_CODEC_CONSTRUCT(REALTEK, 0x0b00)
@@ -881,6 +894,7 @@
#define HDA_CODEC_NVIDIATEGRA124 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0028)
#define HDA_CODEC_NVIDIATEGRA210 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0029)
#define HDA_CODEC_NVIDIAMCP67 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0067)
+#define HDA_CODEC_NVIDIAGM204 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0071)
#define HDA_CODEC_NVIDIAMCP73 HDA_CODEC_CONSTRUCT(NVIDIA, 0x8001)
#define HDA_CODEC_NVIDIAXXXX HDA_CODEC_CONSTRUCT(NVIDIA, 0xffff)
@@ -906,6 +920,7 @@
#define HDA_CODEC_INTELGMLK1 HDA_CODEC_CONSTRUCT(INTEL, 0x280d)
#define HDA_CODEC_INTELICLK HDA_CODEC_CONSTRUCT(INTEL, 0x280f)
#define HDA_CODEC_INTELTGLK HDA_CODEC_CONSTRUCT(INTEL, 0x2812)
+#define HDA_CODEC_INTELTGLKH HDA_CODEC_CONSTRUCT(INTEL, 0x2814)
#define HDA_CODEC_INTELALLK HDA_CODEC_CONSTRUCT(INTEL, 0x2815)
#define HDA_CODEC_INTELJLK HDA_CODEC_CONSTRUCT(INTEL, 0x281a)
#define HDA_CODEC_INTELELLK HDA_CODEC_CONSTRUCT(INTEL, 0x281b)
diff --git a/sys/dev/sound/pci/hda/hdacc.c b/sys/dev/sound/pci/hda/hdacc.c
index 009c9098ac3b..4198982c9c2a 100644
--- a/sys/dev/sound/pci/hda/hdacc.c
+++ b/sys/dev/sound/pci/hda/hdacc.c
@@ -74,6 +74,7 @@ static const struct {
} hdacc_codecs[] = {
{ HDA_CODEC_CS4206, 0, "Cirrus Logic CS4206" },
{ HDA_CODEC_CS4207, 0, "Cirrus Logic CS4207" },
+ { HDA_CODEC_CS4208, 0, "Cirrus Logic CS4208" },
{ HDA_CODEC_CS4210, 0, "Cirrus Logic CS4210" },
{ HDA_CODEC_ALC215, 0, "Realtek ALC215" },
{ HDA_CODEC_ALC221, 0, "Realtek ALC221" },
@@ -147,6 +148,7 @@ static const struct {
{ HDA_CODEC_ALC889, 0, "Realtek ALC889" },
{ HDA_CODEC_ALC892, 0, "Realtek ALC892" },
{ HDA_CODEC_ALC897, 0, "Realtek ALC897" },
+ { HDA_CODEC_ALC898, 0, "Realtek ALC898" },
{ HDA_CODEC_ALC899, 0, "Realtek ALC899" },
{ HDA_CODEC_ALC1150, 0, "Realtek ALC1150" },
{ HDA_CODEC_ALCS1200A, 0, "Realtek ALCS1200A" },
@@ -358,6 +360,7 @@ static const struct {
{ HDA_CODEC_NVIDIAMCP78_3, 0, "NVIDIA MCP78" },
{ HDA_CODEC_NVIDIAMCP78_4, 0, "NVIDIA MCP78" },
{ HDA_CODEC_NVIDIAMCP7A, 0, "NVIDIA MCP7A" },
+ { HDA_CODEC_NVIDIAGM204, 0, "NVIDIA GM204" },
{ HDA_CODEC_NVIDIAGT220, 0, "NVIDIA GT220" },
{ HDA_CODEC_NVIDIAGT21X, 0, "NVIDIA GT21x" },
{ HDA_CODEC_NVIDIAMCP89, 0, "NVIDIA MCP89" },
@@ -393,6 +396,7 @@ static const struct {
{ HDA_CODEC_INTELGMLK1, 0, "Intel Gemini Lake" },
{ HDA_CODEC_INTELICLK, 0, "Intel Ice Lake" },
{ HDA_CODEC_INTELTGLK, 0, "Intel Tiger Lake" },
+ { HDA_CODEC_INTELTGLKH, 0, "Intel Tiger Lake-H" },
{ HDA_CODEC_INTELALLK, 0, "Intel Alder Lake" },
{ HDA_CODEC_SII1390, 0, "Silicon Image SiI1390" },
{ HDA_CODEC_SII1392, 0, "Silicon Image SiI1392" },
@@ -520,7 +524,7 @@ hdacc_attach(device_t dev)
codec->fgs[n].subsystem_id = hda_command(dev,
HDA_CMD_GET_SUBSYSTEM_ID(0, i));
hdacc_unlock(codec);
- codec->fgs[n].dev = child = device_add_child(dev, NULL, -1);
+ codec->fgs[n].dev = child = device_add_child(dev, NULL, DEVICE_UNIT_ANY);
if (child == NULL) {
device_printf(dev, "Failed to add function device\n");
continue;
@@ -528,7 +532,7 @@ hdacc_attach(device_t dev)
device_set_ivars(child, &codec->fgs[n]);
}
- bus_generic_attach(dev);
+ bus_attach_children(dev);
return (0);
}
@@ -539,7 +543,7 @@ hdacc_detach(device_t dev)
struct hdacc_softc *codec = device_get_softc(dev);
int error;
- if ((error = device_delete_children(dev)) != 0)
+ if ((error = bus_generic_detach(dev)) != 0)
return (error);
free(codec->fgs, M_HDACC);
return (0);
diff --git a/sys/dev/sound/pci/hda/pin_patch_realtek.h b/sys/dev/sound/pci/hda/pin_patch_realtek.h
index 49afdedea8a1..abb03c92571b 100644
--- a/sys/dev/sound/pci/hda/pin_patch_realtek.h
+++ b/sys/dev/sound/pci/hda/pin_patch_realtek.h
@@ -582,6 +582,21 @@ static struct hdaa_model_pin_patch_t realtek_model_pin_patches[] = {
}
}, { }
}
+ }, { /**** CODEC: HDA_CODEC_ALC287 ****/
+ .id = HDA_CODEC_ALC287,
+ .patches = (struct model_pin_patch_t[]){
+ {
+ .models = (struct pin_machine_model_t[]){
+ PIN_SUBVENDOR(LENOVO_X1CRBNG11_SUBVENDOR),
+ { }
+ },
+ .pin_patches = (struct pin_patch_t[]){
+ PIN_PATCH_NOT_APPLICABLE(20),
+ PIN_PATCH_STRING(33, "as=1 seq=15 device=Headphones loc=Right"),
+ { }
+ }
+ }, { }
+ }
}, { /**** CODEC: HDA_CODEC_ALC288 ****/
.id = HDA_CODEC_ALC288,
.patches = (struct model_pin_patch_t[]){
diff --git a/sys/dev/sound/pci/hdsp-pcm.c b/sys/dev/sound/pci/hdsp-pcm.c
new file mode 100644
index 000000000000..5ac571e64fde
--- /dev/null
+++ b/sys/dev/sound/pci/hdsp-pcm.c
@@ -0,0 +1,1136 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2012-2021 Ruslan Bukin <br@bsdpad.com>
+ * Copyright (c) 2023-2024 Florian Walpen <dev@submerge.ch>
+ * 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 HDSP driver for FreeBSD (pcm-part).
+ * Supported cards: HDSP 9632, HDSP 9652.
+ */
+
+#include <sys/libkern.h>
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pci/hdsp.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <mixer_if.h>
+
+#define HDSP_MATRIX_MAX 8
+
+struct hdsp_latency {
+ uint32_t n;
+ uint32_t period;
+ float ms;
+};
+
+static struct hdsp_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 hdsp_rate {
+ uint32_t speed;
+ uint32_t reg;
+};
+
+static struct hdsp_rate rate_map[] = {
+ { 32000, (HDSP_FREQ_32000) },
+ { 44100, (HDSP_FREQ_44100) },
+ { 48000, (HDSP_FREQ_48000) },
+ { 64000, (HDSP_FREQ_32000 | HDSP_FREQ_DOUBLE) },
+ { 88200, (HDSP_FREQ_44100 | HDSP_FREQ_DOUBLE) },
+ { 96000, (HDSP_FREQ_48000 | HDSP_FREQ_DOUBLE) },
+ { 128000, (HDSP_FREQ_32000 | HDSP_FREQ_QUAD) },
+ { 176400, (HDSP_FREQ_44100 | HDSP_FREQ_QUAD) },
+ { 192000, (HDSP_FREQ_48000 | HDSP_FREQ_QUAD) },
+
+ { 0, 0 },
+};
+
+static uint32_t
+hdsp_adat_slot_map(uint32_t speed)
+{
+ /* ADAT slot bitmap depends on sample rate. */
+ if (speed <= 48000)
+ return (0x000000ff); /* 8 channels single speed. */
+ else if (speed <= 96000)
+ return (0x000000aa); /* 4 channels (1,3,5,7) double speed. */
+ else
+ return (0x00000000); /* ADAT disabled at quad speed. */
+}
+
+static uint32_t
+hdsp_port_slot_map(uint32_t ports, uint32_t speed)
+{
+ uint32_t slot_map = 0;
+
+ if (ports & HDSP_CHAN_9632_ALL) {
+ /* Map HDSP 9632 ports to slot bitmap. */
+ if (ports & HDSP_CHAN_9632_ADAT)
+ slot_map |= (hdsp_adat_slot_map(speed) << 0);
+ if (ports & HDSP_CHAN_9632_SPDIF)
+ slot_map |= (0x03 << 8); /* 2 channels SPDIF. */
+ if (ports & HDSP_CHAN_9632_LINE)
+ slot_map |= (0x03 << 10); /* 2 channels line. */
+ if (ports & HDSP_CHAN_9632_EXT)
+ slot_map |= (0x0f << 12); /* 4 channels extension. */
+ } else if ((ports & HDSP_CHAN_9652_ALL) && (speed <= 96000)) {
+ /* Map HDSP 9652 ports to slot bitmap, no quad speed. */
+ if (ports & HDSP_CHAN_9652_ADAT1)
+ slot_map |= (hdsp_adat_slot_map(speed) << 0);
+ if (ports & HDSP_CHAN_9652_ADAT2)
+ slot_map |= (hdsp_adat_slot_map(speed) << 8);
+ if (ports & HDSP_CHAN_9652_ADAT3)
+ slot_map |= (hdsp_adat_slot_map(speed) << 16);
+ if (ports & HDSP_CHAN_9652_SPDIF)
+ slot_map |= (0x03 << 24); /* 2 channels SPDIF. */
+ }
+
+ return (slot_map);
+}
+
+static uint32_t
+hdsp_slot_first(uint32_t slots)
+{
+ return (slots & (~(slots - 1))); /* Extract first bit set. */
+}
+
+static uint32_t
+hdsp_slot_first_row(uint32_t slots)
+{
+ uint32_t ends;
+
+ /* Ends of slot rows are followed by a slot which is not in the set. */
+ ends = slots & (~(slots >> 1));
+ /* First row of contiguous slots ends in the first row end. */
+ return (slots & (ends ^ (ends - 1)));
+}
+
+static uint32_t
+hdsp_slot_first_n(uint32_t slots, unsigned int n)
+{
+ /* Clear all but the first n slots. */
+ for (uint32_t slot = 1; slot != 0; slot <<= 1) {
+ if ((slots & slot) && n > 0)
+ --n;
+ else
+ slots &= ~slot;
+ }
+ return (slots);
+}
+
+static unsigned int
+hdsp_slot_count(uint32_t slots)
+{
+ return (bitcount32(slots));
+}
+
+static unsigned int
+hdsp_slot_offset(uint32_t slots)
+{
+ return (hdsp_slot_count(hdsp_slot_first(slots) - 1));
+}
+
+static unsigned int
+hdsp_slot_channel_offset(uint32_t subset, uint32_t slots)
+{
+ uint32_t preceding;
+
+ /* Make sure we have a subset of slots. */
+ subset &= slots;
+ /* Include all slots preceding the first one of the subset. */
+ preceding = slots & (hdsp_slot_first(subset) - 1);
+
+ return (hdsp_slot_count(preceding));
+}
+
+static uint32_t
+hdsp_port_first(uint32_t ports)
+{
+ return (ports & (~(ports - 1))); /* Extract first bit set. */
+}
+
+static unsigned int
+hdsp_port_slot_count(uint32_t ports, uint32_t speed)
+{
+ return (hdsp_slot_count(hdsp_port_slot_map(ports, speed)));
+}
+
+static unsigned int
+hdsp_port_slot_count_max(uint32_t ports)
+{
+ return (hdsp_slot_count(hdsp_port_slot_map(ports, 48000)));
+}
+
+static uint32_t
+hdsp_channel_play_ports(struct hdsp_channel *hc)
+{
+ return (hc->ports & (HDSP_CHAN_9632_ALL | HDSP_CHAN_9652_ALL));
+}
+
+static uint32_t
+hdsp_channel_rec_ports(struct hdsp_channel *hc)
+{
+ return (hc->ports & (HDSP_CHAN_9632_ALL | HDSP_CHAN_9652_ALL));
+}
+
+static int
+hdsp_hw_mixer(struct sc_chinfo *ch, unsigned int dst,
+ unsigned int src, unsigned short data)
+{
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ uint32_t value;
+ int offset;
+
+ scp = ch->parent;
+ sc = scp->sc;
+
+ offset = 0;
+ value = (HDSP_MIN_GAIN << 16) | (uint16_t) data;
+
+ if (ch->dir != PCMDIR_PLAY)
+ return (0);
+
+ switch (sc->type) {
+ case HDSP_9632:
+ /* Mixer is 2 rows of sources (inputs, playback) per output. */
+ offset = dst * (2 * HDSP_MIX_SLOTS_9632);
+ /* Source index in the second row (playback). */
+ offset += HDSP_MIX_SLOTS_9632 + src;
+ break;
+ case HDSP_9652:
+ /* Mixer is 2 rows of sources (inputs, playback) per output. */
+ offset = dst * (2 * HDSP_MIX_SLOTS_9652);
+ /* Source index in the second row (playback). */
+ offset += HDSP_MIX_SLOTS_9652 + src;
+ break;
+ default:
+ return (0);
+ }
+
+ /*
+ * We have to write mixer matrix values in pairs, with the second
+ * (odd) value in the upper 16 bits of the 32 bit value.
+ * Make value offset even and shift value accordingly.
+ * Assume the paired value to be silenced, since we only set gain
+ * on the diagonal where src and dst are the same.
+ */
+ if (offset % 2) {
+ offset -= 1;
+ value = (value << 16) | HDSP_MIN_GAIN;
+ }
+
+ hdsp_write_4(sc, HDSP_MIXER_BASE + offset * sizeof(uint16_t), value);
+
+ return (0);
+};
+
+static int
+hdspchan_setgain(struct sc_chinfo *ch)
+{
+ uint32_t port, ports;
+ uint32_t slot, slots;
+ unsigned int offset;
+ unsigned short volume;
+
+ /* Iterate through all physical ports of the channel. */
+ ports = ch->ports;
+ port = hdsp_port_first(ports);
+ while (port != 0) {
+ /*
+ * Get slot map from physical port.
+ * Unlike DMA buffers, the hardware mixer's channel mapping
+ * does not change with double or quad speed sample rates.
+ */
+ slots = hdsp_port_slot_map(port, 48000);
+ slot = hdsp_slot_first(slots);
+
+ /* Treat first slot as left channel. */
+ volume = ch->lvol * HDSP_MAX_GAIN / 100;
+ while (slot != 0) {
+ offset = hdsp_slot_offset(slot);
+ hdsp_hw_mixer(ch, offset, offset, volume);
+
+ slots &= ~slot;
+ slot = hdsp_slot_first(slots);
+
+ /* Subsequent slots all get the right channel volume. */
+ volume = ch->rvol * HDSP_MAX_GAIN / 100;
+ }
+
+ ports &= ~port;
+ port = hdsp_port_first(ports);
+ }
+
+ return (0);
+}
+
+static int
+hdspmixer_init(struct snd_mixer *m)
+{
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ int mask;
+
+ scp = mix_getdevinfo(m);
+ sc = scp->sc;
+ if (sc == NULL)
+ return (-1);
+
+ mask = SOUND_MASK_PCM;
+
+ if (hdsp_channel_play_ports(scp->hc))
+ mask |= SOUND_MASK_VOLUME;
+
+ if (hdsp_channel_rec_ports(scp->hc))
+ 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
+hdspmixer_set(struct snd_mixer *m, unsigned dev,
+ unsigned left, unsigned right)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ int i;
+
+ scp = mix_getdevinfo(m);
+
+#if 0
+ device_printf(scp->dev, "hdspmixer_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)
+ hdspchan_setgain(ch);
+ }
+ }
+
+ return (0);
+}
+
+static kobj_method_t hdspmixer_methods[] = {
+ KOBJMETHOD(mixer_init, hdspmixer_init),
+ KOBJMETHOD(mixer_set, hdspmixer_set),
+ KOBJMETHOD_END
+};
+MIXER_DECLARE(hdspmixer);
+
+static void
+hdspchan_enable(struct sc_chinfo *ch, int value)
+{
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ uint32_t slot, slots;
+ unsigned int offset;
+ int reg;
+
+ scp = ch->parent;
+ sc = scp->sc;
+
+ if (ch->dir == PCMDIR_PLAY)
+ reg = HDSP_OUT_ENABLE_BASE;
+ else
+ reg = HDSP_IN_ENABLE_BASE;
+
+ ch->run = value;
+
+ /* Iterate through all slots of the channel's physical ports. */
+ slots = hdsp_port_slot_map(ch->ports, sc->speed);
+ slot = hdsp_slot_first(slots);
+ while (slot != 0) {
+ /* Set register to enable or disable slot. */
+ offset = hdsp_slot_offset(slot);
+ hdsp_write_1(sc, reg + (4 * offset), value);
+
+ slots &= ~slot;
+ slot = hdsp_slot_first(slots);
+ }
+}
+
+static int
+hdsp_running(struct sc_info *sc)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ device_t *devlist;
+ int devcount;
+ int i, j;
+ int running;
+
+ running = 0;
+
+ devlist = NULL;
+ devcount = 0;
+
+ if (device_get_children(sc->dev, &devlist, &devcount) != 0)
+ running = 1; /* On error, avoid channel config changes. */
+
+ for (i = 0; running == 0 && i < devcount; i++) {
+ scp = device_get_ivars(devlist[i]);
+ for (j = 0; j < scp->chnum; j++) {
+ ch = &scp->chan[j];
+ if (ch->run) {
+ running = 1;
+ break;
+ }
+ }
+ }
+
+#if 0
+ if (running == 1)
+ device_printf(sc->dev, "hdsp is running\n");
+#endif
+
+ free(devlist, M_TEMP);
+
+ return (running);
+}
+
+static void
+hdsp_start_audio(struct sc_info *sc)
+{
+
+ sc->ctrl_register |= (HDSP_AUDIO_INT_ENABLE | HDSP_ENABLE);
+ hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register);
+}
+
+static void
+hdsp_stop_audio(struct sc_info *sc)
+{
+
+ if (hdsp_running(sc) == 1)
+ return;
+
+ sc->ctrl_register &= ~(HDSP_AUDIO_INT_ENABLE | HDSP_ENABLE);
+ hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register);
+}
+
+static void
+buffer_mux_write(uint32_t *dma, uint32_t *pcm, unsigned int pos,
+ unsigned int pos_end, unsigned int width, unsigned int channels)
+{
+ unsigned int slot;
+
+ for (; pos < pos_end; ++pos) {
+ for (slot = 0; slot < width; slot++) {
+ dma[slot * HDSP_CHANBUF_SAMPLES + pos] =
+ pcm[pos * channels + slot];
+ }
+ }
+}
+
+static void
+buffer_mux_port(uint32_t *dma, uint32_t *pcm, uint32_t subset, uint32_t slots,
+ unsigned int pos, unsigned int samples, unsigned int channels)
+{
+ unsigned int slot_offset, width;
+ unsigned int chan_pos;
+
+ /* Translate DMA slot offset to DMA buffer offset. */
+ slot_offset = hdsp_slot_offset(subset);
+ dma += slot_offset * HDSP_CHANBUF_SAMPLES;
+
+ /* Channel position of the slot subset. */
+ chan_pos = hdsp_slot_channel_offset(subset, slots);
+ pcm += chan_pos;
+
+ /* Only copy channels supported by both hardware and pcm format. */
+ width = hdsp_slot_count(subset);
+
+ /* Let the compiler inline and loop unroll common cases. */
+ if (width == 1)
+ buffer_mux_write(dma, pcm, pos, pos + samples, 1, channels);
+ else if (width == 2)
+ buffer_mux_write(dma, pcm, pos, pos + samples, 2, channels);
+ else if (width == 4)
+ buffer_mux_write(dma, pcm, pos, pos + samples, 4, channels);
+ else if (width == 8)
+ buffer_mux_write(dma, pcm, pos, pos + samples, 8, channels);
+ else
+ buffer_mux_write(dma, pcm, pos, pos + samples, width, channels);
+}
+
+static void
+buffer_demux_read(uint32_t *dma, uint32_t *pcm, unsigned int pos,
+ unsigned int pos_end, unsigned int width, unsigned int channels)
+{
+ unsigned int slot;
+
+ for (; pos < pos_end; ++pos) {
+ for (slot = 0; slot < width; slot++) {
+ pcm[pos * channels + slot] =
+ dma[slot * HDSP_CHANBUF_SAMPLES + pos];
+ }
+ }
+}
+
+static void
+buffer_demux_port(uint32_t *dma, uint32_t *pcm, uint32_t subset, uint32_t slots,
+ unsigned int pos, unsigned int samples, unsigned int channels)
+{
+ unsigned int slot_offset, width;
+ unsigned int chan_pos;
+
+ /* Translate DMA slot offset to DMA buffer offset. */
+ slot_offset = hdsp_slot_offset(subset);
+ dma += slot_offset * HDSP_CHANBUF_SAMPLES;
+
+ /* Channel position of the slot subset. */
+ chan_pos = hdsp_slot_channel_offset(subset, slots);
+ pcm += chan_pos;
+
+ /* Only copy channels supported by both hardware and pcm format. */
+ width = hdsp_slot_count(subset);
+
+ /* Let the compiler inline and loop unroll common cases. */
+ if (width == 1)
+ buffer_demux_read(dma, pcm, pos, pos + samples, 1, channels);
+ else if (width == 2)
+ buffer_demux_read(dma, pcm, pos, pos + samples, 2, channels);
+ else if (width == 4)
+ buffer_demux_read(dma, pcm, pos, pos + samples, 4, channels);
+ else if (width == 8)
+ buffer_demux_read(dma, pcm, pos, pos + samples, 8, channels);
+ else
+ buffer_demux_read(dma, pcm, pos, pos + samples, width, channels);
+}
+
+
+/* Copy data between DMA and PCM buffers. */
+static void
+buffer_copy(struct sc_chinfo *ch)
+{
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ uint32_t row, slots;
+ uint32_t dma_pos;
+ unsigned int pos, length, remainder, offset, buffer_size;
+ unsigned int channels;
+
+ scp = ch->parent;
+ sc = scp->sc;
+
+ channels = AFMT_CHANNEL(ch->format); /* Number of PCM channels. */
+
+ /* HDSP cards read / write a double buffer, twice the latency period. */
+ buffer_size = 2 * sc->period * sizeof(uint32_t);
+
+ /* Derive buffer position and length to be copied. */
+ if (ch->dir == PCMDIR_PLAY) {
+ /* Buffer position scaled down to a single channel. */
+ pos = sndbuf_getreadyptr(ch->buffer) / channels;
+ length = sndbuf_getready(ch->buffer) / channels;
+ /* Copy no more than 2 periods in advance. */
+ if (length > buffer_size)
+ length = buffer_size;
+ /* Skip what was already copied last time. */
+ offset = (ch->position + buffer_size) - pos;
+ offset %= buffer_size;
+ if (offset <= length) {
+ pos = (pos + offset) % buffer_size;
+ length -= offset;
+ }
+ } else {
+ /* Buffer position scaled down to a single channel. */
+ pos = sndbuf_getfreeptr(ch->buffer) / channels;
+ /* Get DMA buffer write position. */
+ dma_pos = hdsp_read_2(sc, HDSP_STATUS_REG);
+ dma_pos &= HDSP_BUF_POSITION_MASK;
+ dma_pos %= buffer_size;
+ /* Copy what is newly available. */
+ length = (dma_pos + buffer_size) - pos;
+ length %= buffer_size;
+ }
+
+ /* Position and length in samples (4 bytes). */
+ pos /= 4;
+ length /= 4;
+ buffer_size /= sizeof(uint32_t);
+
+ /* Split copy length to wrap around at buffer end. */
+ remainder = 0;
+ if (pos + length > buffer_size)
+ remainder = (pos + length) - buffer_size;
+
+ /* Iterate through rows of contiguous slots. */
+ slots = hdsp_port_slot_map(ch->ports, sc->speed);
+ slots = hdsp_slot_first_n(slots, channels);
+ row = hdsp_slot_first_row(slots);
+
+ while (row != 0) {
+ if (ch->dir == PCMDIR_PLAY) {
+ buffer_mux_port(sc->pbuf, ch->data, row, slots, pos,
+ length - remainder, channels);
+ buffer_mux_port(sc->pbuf, ch->data, row, slots, 0,
+ remainder, channels);
+ } else {
+ buffer_demux_port(sc->rbuf, ch->data, row, slots, pos,
+ length - remainder, channels);
+ buffer_demux_port(sc->rbuf, ch->data, row, slots, 0,
+ remainder, channels);
+ }
+
+ slots &= ~row;
+ row = hdsp_slot_first_row(slots);
+ }
+
+ ch->position = ((pos + length) * 4) % buffer_size;
+}
+
+static int
+clean(struct sc_chinfo *ch)
+{
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ uint32_t *buf;
+ uint32_t slot, slots;
+ unsigned int offset;
+
+ scp = ch->parent;
+ sc = scp->sc;
+ buf = sc->rbuf;
+
+ if (ch->dir == PCMDIR_PLAY)
+ buf = sc->pbuf;
+
+ /* Iterate through all of the channel's slots. */
+ slots = hdsp_port_slot_map(ch->ports, sc->speed);
+ slot = hdsp_slot_first(slots);
+ while (slot != 0) {
+ /* Clear the slot's buffer. */
+ offset = hdsp_slot_offset(slot);
+ bzero(buf + offset * HDSP_CHANBUF_SAMPLES, HDSP_CHANBUF_SIZE);
+
+ slots &= ~slot;
+ slot = hdsp_slot_first(slots);
+ }
+
+ ch->position = 0;
+
+ return (0);
+}
+
+/* Channel interface. */
+static int
+hdspchan_free(kobj_t obj, void *data)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+
+ ch = data;
+ scp = ch->parent;
+ sc = scp->sc;
+
+#if 0
+ device_printf(scp->dev, "hdspchan_free()\n");
+#endif
+
+ snd_mtxlock(sc->lock);
+ if (ch->data != NULL) {
+ free(ch->data, M_HDSP);
+ ch->data = NULL;
+ }
+ if (ch->caps != NULL) {
+ free(ch->caps, M_HDSP);
+ ch->caps = NULL;
+ }
+ snd_mtxunlock(sc->lock);
+
+ return (0);
+}
+
+static void *
+hdspchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+ int num;
+
+ scp = devinfo;
+ sc = scp->sc;
+
+ snd_mtxlock(sc->lock);
+ num = scp->chnum;
+
+ ch = &scp->chan[num];
+
+ if (dir == PCMDIR_PLAY)
+ ch->ports = hdsp_channel_play_ports(scp->hc);
+ else
+ ch->ports = hdsp_channel_rec_ports(scp->hc);
+
+ ch->run = 0;
+ ch->lvol = 0;
+ ch->rvol = 0;
+
+ /* Support all possible ADAT widths as channel formats. */
+ ch->cap_fmts[0] =
+ SND_FORMAT(AFMT_S32_LE, hdsp_port_slot_count(ch->ports, 48000), 0);
+ ch->cap_fmts[1] =
+ SND_FORMAT(AFMT_S32_LE, hdsp_port_slot_count(ch->ports, 96000), 0);
+ ch->cap_fmts[2] =
+ SND_FORMAT(AFMT_S32_LE, hdsp_port_slot_count(ch->ports, 192000), 0);
+ ch->cap_fmts[3] = 0;
+
+ ch->caps = malloc(sizeof(struct pcmchan_caps), M_HDSP, M_NOWAIT);
+ *(ch->caps) = (struct pcmchan_caps) {32000, 192000, ch->cap_fmts, 0};
+
+ /* HDSP 9652 does not support quad speed sample rates. */
+ if (sc->type == HDSP_9652) {
+ ch->cap_fmts[2] = SND_FORMAT(AFMT_S32_LE, 2, 0);
+ ch->caps->maxspeed = 96000;
+ }
+
+ /* Allocate maximum buffer size. */
+ ch->size = HDSP_CHANBUF_SIZE * hdsp_port_slot_count_max(ch->ports);
+ ch->data = malloc(ch->size, M_HDSP, M_NOWAIT);
+ ch->position = 0;
+
+ 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");
+ hdspchan_free(obj, ch);
+ return (NULL);
+ }
+
+ return (ch);
+}
+
+static int
+hdspchan_trigger(kobj_t obj, void *data, int go)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+
+ ch = data;
+ scp = ch->parent;
+ sc = scp->sc;
+
+ snd_mtxlock(sc->lock);
+ switch (go) {
+ case PCMTRIG_START:
+#if 0
+ device_printf(scp->dev, "hdspchan_trigger(): start\n");
+#endif
+ hdspchan_enable(ch, 1);
+ hdspchan_setgain(ch);
+ hdsp_start_audio(sc);
+ break;
+
+ case PCMTRIG_STOP:
+ case PCMTRIG_ABORT:
+#if 0
+ device_printf(scp->dev, "hdspchan_trigger(): stop or abort\n");
+#endif
+ clean(ch);
+ hdspchan_enable(ch, 0);
+ hdsp_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
+hdspchan_getptr(kobj_t obj, void *data)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+ uint32_t ret, pos;
+
+ ch = data;
+ scp = ch->parent;
+ sc = scp->sc;
+
+ snd_mtxlock(sc->lock);
+ ret = hdsp_read_2(sc, HDSP_STATUS_REG);
+ snd_mtxunlock(sc->lock);
+
+ pos = ret & HDSP_BUF_POSITION_MASK;
+ pos %= (2 * sc->period * sizeof(uint32_t)); /* Double buffer. */
+ pos *= AFMT_CHANNEL(ch->format); /* Hardbuf with multiple channels. */
+
+ return (pos);
+}
+
+static int
+hdspchan_setformat(kobj_t obj, void *data, uint32_t format)
+{
+ struct sc_chinfo *ch;
+
+ ch = data;
+
+#if 0
+ struct sc_pcminfo *scp = ch->parent;
+ device_printf(scp->dev, "hdspchan_setformat(%d)\n", format);
+#endif
+
+ ch->format = format;
+
+ return (0);
+}
+
+static uint32_t
+hdspchan_setspeed(kobj_t obj, void *data, uint32_t speed)
+{
+ struct sc_pcminfo *scp;
+ struct hdsp_rate *hr;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+ int threshold;
+ int i;
+
+ ch = data;
+ scp = ch->parent;
+ sc = scp->sc;
+ hr = NULL;
+
+#if 0
+ device_printf(scp->dev, "hdspchan_setspeed(%d)\n", speed);
+#endif
+
+ if (hdsp_running(sc) == 1)
+ goto end;
+
+ /* HDSP 9652 only supports sample rates up to 96kHz. */
+ if (sc->type == HDSP_9652 && speed > 96000)
+ speed = 96000;
+
+ if (sc->force_speed > 0)
+ speed = sc->force_speed;
+
+ /* 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;
+ }
+ }
+
+ /* Write frequency on the device. */
+ sc->ctrl_register &= ~HDSP_FREQ_MASK;
+ sc->ctrl_register |= hr->reg;
+ hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register);
+
+ if (sc->type == HDSP_9632) {
+ /* Set DDS value. */
+ hdsp_write_4(sc, HDSP_FREQ_REG, hdsp_freq_reg_value(hr->speed));
+ }
+
+ sc->speed = hr->speed;
+end:
+
+ return (sc->speed);
+}
+
+static uint32_t
+hdspchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
+{
+ struct hdsp_latency *hl;
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+ int threshold;
+ int i;
+
+ ch = data;
+ scp = ch->parent;
+ sc = scp->sc;
+ hl = NULL;
+
+#if 0
+ device_printf(scp->dev, "hdspchan_setblocksize(%d)\n", blocksize);
+#endif
+
+ if (hdsp_running(sc) == 1)
+ goto end;
+
+ if (blocksize > HDSP_LAT_BYTES_MAX)
+ blocksize = HDSP_LAT_BYTES_MAX;
+ else if (blocksize < HDSP_LAT_BYTES_MIN)
+ blocksize = HDSP_LAT_BYTES_MIN;
+
+ blocksize /= 4 /* samples */;
+
+ if (sc->force_period > 0)
+ blocksize = sc->force_period;
+
+ /* 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 &= ~HDSP_LAT_MASK;
+ sc->ctrl_register |= hdsp_encode_latency(hl->n);
+ hdsp_write_4(sc, HDSP_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, 2,
+ (sc->period * AFMT_CHANNEL(ch->format) * sizeof(uint32_t)));
+
+ /* Reset pointer, rewrite frequency (same register) for 9632. */
+ hdsp_write_4(sc, HDSP_RESET_POINTER, 0);
+ if (sc->type == HDSP_9632)
+ hdsp_write_4(sc, HDSP_FREQ_REG, hdsp_freq_reg_value(sc->speed));
+end:
+
+ return (sndbuf_getblksz(ch->buffer));
+}
+
+static uint32_t hdsp_bkp_fmt[] = {
+ SND_FORMAT(AFMT_S32_LE, 2, 0),
+ 0
+};
+
+/* Capabilities fallback, no quad speed for HDSP 9652 compatibility. */
+static struct pcmchan_caps hdsp_bkp_caps = {32000, 96000, hdsp_bkp_fmt, 0};
+
+static struct pcmchan_caps *
+hdspchan_getcaps(kobj_t obj, void *data)
+{
+ struct sc_chinfo *ch;
+
+ ch = data;
+
+#if 0
+ device_printf(ch->parent->dev, "hdspchan_getcaps()\n");
+#endif
+
+ if (ch->caps != NULL)
+ return (ch->caps);
+
+ return (&hdsp_bkp_caps);
+}
+
+static kobj_method_t hdspchan_methods[] = {
+ KOBJMETHOD(channel_init, hdspchan_init),
+ KOBJMETHOD(channel_free, hdspchan_free),
+ KOBJMETHOD(channel_setformat, hdspchan_setformat),
+ KOBJMETHOD(channel_setspeed, hdspchan_setspeed),
+ KOBJMETHOD(channel_setblocksize, hdspchan_setblocksize),
+ KOBJMETHOD(channel_trigger, hdspchan_trigger),
+ KOBJMETHOD(channel_getptr, hdspchan_getptr),
+ KOBJMETHOD(channel_getcaps, hdspchan_getcaps),
+ KOBJMETHOD_END
+};
+CHANNEL_DECLARE(hdspchan);
+
+static int
+hdsp_pcm_probe(device_t dev)
+{
+
+#if 0
+ device_printf(dev,"hdsp_pcm_probe()\n");
+#endif
+
+ return (0);
+}
+
+static uint32_t
+hdsp_pcm_intr(struct sc_pcminfo *scp)
+{
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+ int i;
+
+ sc = scp->sc;
+
+ 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
+hdsp_pcm_attach(device_t dev)
+{
+ char status[SND_STATUSLEN];
+ struct sc_pcminfo *scp;
+ const char *buf;
+ uint32_t pcm_flags;
+ int err;
+ int play, rec;
+
+ scp = device_get_ivars(dev);
+ scp->ih = &hdsp_pcm_intr;
+
+ if (scp->hc->ports & HDSP_CHAN_9632_ALL)
+ buf = "9632";
+ else if (scp->hc->ports & HDSP_CHAN_9652_ALL)
+ buf = "9652";
+ else
+ buf = "?";
+ device_set_descf(dev, "HDSP %s [%s]", buf, scp->hc->descr);
+
+ /*
+ * We don't register interrupt handler with snd_setup_intr
+ * in pcm device. Mark pcm device as MPSAFE manually.
+ */
+ pcm_flags = pcm_getflags(dev) | SD_F_MPSAFE;
+ if (hdsp_port_slot_count_max(scp->hc->ports) > HDSP_MATRIX_MAX)
+ /* Disable vchan conversion, too many channels. */
+ pcm_flags |= SD_F_BITPERFECT;
+ pcm_setflags(dev, pcm_flags);
+
+ pcm_init(dev, scp);
+
+ play = (hdsp_channel_play_ports(scp->hc)) ? 1 : 0;
+ rec = (hdsp_channel_rec_ports(scp->hc)) ? 1 : 0;
+
+ scp->chnum = 0;
+ if (play) {
+ pcm_addchan(dev, PCMDIR_PLAY, &hdspchan_class, scp);
+ scp->chnum++;
+ }
+
+ if (rec) {
+ pcm_addchan(dev, PCMDIR_REC, &hdspchan_class, scp);
+ scp->chnum++;
+ }
+
+ snprintf(status, SND_STATUSLEN, "port 0x%jx irq %jd on %s",
+ rman_get_start(scp->sc->cs),
+ rman_get_start(scp->sc->irq),
+ device_get_nameunit(device_get_parent(dev)));
+ err = pcm_register(dev, status);
+ if (err) {
+ device_printf(dev, "Can't register pcm.\n");
+ return (ENXIO);
+ }
+
+ mixer_init(dev, &hdspmixer_class, scp);
+
+ return (0);
+}
+
+static int
+hdsp_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 hdsp_pcm_methods[] = {
+ DEVMETHOD(device_probe, hdsp_pcm_probe),
+ DEVMETHOD(device_attach, hdsp_pcm_attach),
+ DEVMETHOD(device_detach, hdsp_pcm_detach),
+ { 0, 0 }
+};
+
+static driver_t hdsp_pcm_driver = {
+ "pcm",
+ hdsp_pcm_methods,
+ PCM_SOFTC_SIZE,
+};
+
+DRIVER_MODULE(snd_hdsp_pcm, hdsp, hdsp_pcm_driver, 0, 0);
+MODULE_DEPEND(snd_hdsp, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
+MODULE_VERSION(snd_hdsp, 1);
diff --git a/sys/dev/sound/pci/hdsp.c b/sys/dev/sound/pci/hdsp.c
new file mode 100644
index 000000000000..4ba23d22ebce
--- /dev/null
+++ b/sys/dev/sound/pci/hdsp.c
@@ -0,0 +1,1022 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2012-2016 Ruslan Bukin <br@bsdpad.com>
+ * Copyright (c) 2023-2024 Florian Walpen <dev@submerge.ch>
+ * 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 HDSP driver for FreeBSD.
+ * Supported cards: HDSP 9632, HDSP 9652.
+ */
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include <dev/sound/pcm/sound.h>
+#include <dev/sound/pci/hdsp.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <mixer_if.h>
+
+static bool hdsp_unified_pcm = false;
+
+static SYSCTL_NODE(_hw, OID_AUTO, hdsp, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
+ "PCI HDSP");
+
+SYSCTL_BOOL(_hw_hdsp, OID_AUTO, unified_pcm, CTLFLAG_RWTUN,
+ &hdsp_unified_pcm, 0, "Combine physical ports in one unified pcm device");
+
+static struct hdsp_clock_source hdsp_clock_source_table_9632[] = {
+ { "internal", HDSP_CLOCK_INTERNAL },
+ { "adat", HDSP_CLOCK_ADAT1 },
+ { "spdif", HDSP_CLOCK_SPDIF },
+ { "word", HDSP_CLOCK_WORD },
+ { NULL, HDSP_CLOCK_INTERNAL }
+};
+
+static struct hdsp_clock_source hdsp_clock_source_table_9652[] = {
+ { "internal", HDSP_CLOCK_INTERNAL },
+ { "adat1", HDSP_CLOCK_ADAT1 },
+ { "adat2", HDSP_CLOCK_ADAT2 },
+ { "adat3", HDSP_CLOCK_ADAT3 },
+ { "spdif", HDSP_CLOCK_SPDIF },
+ { "word", HDSP_CLOCK_WORD },
+ { "adat_sync", HDSP_CLOCK_ADAT_SYNC },
+ { NULL, HDSP_CLOCK_INTERNAL }
+};
+
+static struct hdsp_channel chan_map_9632[] = {
+ { HDSP_CHAN_9632_ADAT, "adat" },
+ { HDSP_CHAN_9632_SPDIF, "s/pdif" },
+ { HDSP_CHAN_9632_LINE, "line" },
+ { HDSP_CHAN_9632_EXT, "ext" },
+ { 0, NULL },
+};
+
+static struct hdsp_channel chan_map_9632_uni[] = {
+ { HDSP_CHAN_9632_ALL, "all" },
+ { 0, NULL },
+};
+
+static struct hdsp_channel chan_map_9652[] = {
+ { HDSP_CHAN_9652_ADAT1, "adat1" },
+ { HDSP_CHAN_9652_ADAT2, "adat2" },
+ { HDSP_CHAN_9652_ADAT3, "adat3" },
+ { HDSP_CHAN_9652_SPDIF, "s/pdif" },
+ { 0, NULL },
+};
+
+static struct hdsp_channel chan_map_9652_uni[] = {
+ { HDSP_CHAN_9652_ALL, "all" },
+ { 0, NULL },
+};
+
+static void
+hdsp_intr(void *p)
+{
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ device_t *devlist;
+ int devcount;
+ int status;
+ int err;
+ int i;
+
+ sc = (struct sc_info *)p;
+
+ snd_mtxlock(sc->lock);
+
+ status = hdsp_read_1(sc, HDSP_STATUS_REG);
+ if (status & HDSP_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);
+ }
+
+ hdsp_write_1(sc, HDSP_INTERRUPT_ACK, 0);
+ free(devlist, M_TEMP);
+ }
+
+ snd_mtxunlock(sc->lock);
+}
+
+static void
+hdsp_dmapsetmap(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+#if 0
+ device_printf(sc->dev, "hdsp_dmapsetmap()\n");
+#endif
+}
+
+static int
+hdsp_alloc_resources(struct sc_info *sc)
+{
+
+ /* Allocate resource. */
+ sc->csid = PCIR_BAR(0);
+ sc->cs = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
+ &sc->csid, 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_any(sc->dev, SYS_RES_IRQ, &sc->irqid,
+ RF_ACTIVE | RF_SHAREABLE);
+
+ if (!sc->irq ||
+ bus_setup_intr(sc->dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV,
+ NULL, hdsp_intr, sc, &sc->ih)) {
+ device_printf(sc->dev, "Unable to alloc interrupt resource.\n");
+ return (ENXIO);
+ }
+
+ /* Allocate DMA resources. */
+ if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(sc->dev),
+ /*alignment*/4,
+ /*boundary*/0,
+ /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
+ /*highaddr*/BUS_SPACE_MAXADDR,
+ /*filter*/NULL,
+ /*filterarg*/NULL,
+ /*maxsize*/2 * HDSP_DMASEGSIZE,
+ /*nsegments*/2,
+ /*maxsegsz*/HDSP_DMASEGSIZE,
+ /*flags*/0,
+ /*lockfunc*/NULL,
+ /*lockarg*/NULL,
+ /*dmatag*/&sc->dmat) != 0) {
+ device_printf(sc->dev, "Unable to create dma tag.\n");
+ return (ENXIO);
+ }
+
+ sc->bufsize = HDSP_DMASEGSIZE;
+
+ /* pbuf (play buffer). */
+ if (bus_dmamem_alloc(sc->dmat, (void **)&sc->pbuf, BUS_DMA_WAITOK,
+ &sc->pmap)) {
+ device_printf(sc->dev, "Can't alloc pbuf.\n");
+ return (ENXIO);
+ }
+
+ if (bus_dmamap_load(sc->dmat, sc->pmap, sc->pbuf, sc->bufsize,
+ hdsp_dmapsetmap, sc, BUS_DMA_NOWAIT)) {
+ device_printf(sc->dev, "Can't load pbuf.\n");
+ return (ENXIO);
+ }
+
+ /* rbuf (rec buffer). */
+ if (bus_dmamem_alloc(sc->dmat, (void **)&sc->rbuf, BUS_DMA_WAITOK,
+ &sc->rmap)) {
+ device_printf(sc->dev, "Can't alloc rbuf.\n");
+ return (ENXIO);
+ }
+
+ if (bus_dmamap_load(sc->dmat, sc->rmap, sc->rbuf, sc->bufsize,
+ hdsp_dmapsetmap, sc, BUS_DMA_NOWAIT)) {
+ device_printf(sc->dev, "Can't load rbuf.\n");
+ return (ENXIO);
+ }
+
+ bzero(sc->pbuf, sc->bufsize);
+ bzero(sc->rbuf, sc->bufsize);
+
+ return (0);
+}
+
+static void
+hdsp_map_dmabuf(struct sc_info *sc)
+{
+ uint32_t paddr, raddr;
+
+ paddr = vtophys(sc->pbuf);
+ raddr = vtophys(sc->rbuf);
+
+ hdsp_write_4(sc, HDSP_PAGE_ADDR_BUF_OUT, paddr);
+ hdsp_write_4(sc, HDSP_PAGE_ADDR_BUF_IN, raddr);
+}
+
+static const char *
+hdsp_control_input_level(uint32_t control)
+{
+ switch (control & HDSP_INPUT_LEVEL_MASK) {
+ case HDSP_INPUT_LEVEL_LOWGAIN:
+ return ("LowGain");
+ case HDSP_INPUT_LEVEL_PLUS4DBU:
+ return ("+4dBu");
+ case HDSP_INPUT_LEVEL_MINUS10DBV:
+ return ("-10dBV");
+ default:
+ return (NULL);
+ }
+}
+
+static int
+hdsp_sysctl_input_level(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ const char *label;
+ char buf[16] = "invalid";
+ int error;
+ uint32_t control;
+
+ sc = oidp->oid_arg1;
+
+ /* Only available on HDSP 9632. */
+ if (sc->type != HDSP_9632)
+ return (ENXIO);
+
+ /* Extract current input level from control register. */
+ control = sc->ctrl_register & HDSP_INPUT_LEVEL_MASK;
+ label = hdsp_control_input_level(control);
+ if (label != NULL)
+ strlcpy(buf, label, sizeof(buf));
+
+ /* Process sysctl string request. */
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Find input level matching the sysctl string. */
+ label = hdsp_control_input_level(HDSP_INPUT_LEVEL_LOWGAIN);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_INPUT_LEVEL_LOWGAIN;
+ label = hdsp_control_input_level(HDSP_INPUT_LEVEL_PLUS4DBU);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_INPUT_LEVEL_PLUS4DBU;
+ label = hdsp_control_input_level(HDSP_INPUT_LEVEL_MINUS10DBV);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_INPUT_LEVEL_MINUS10DBV;
+
+ /* Set input level in control register. */
+ control &= HDSP_INPUT_LEVEL_MASK;
+ if (control != (sc->ctrl_register & HDSP_INPUT_LEVEL_MASK)) {
+ snd_mtxlock(sc->lock);
+ sc->ctrl_register &= ~HDSP_INPUT_LEVEL_MASK;
+ sc->ctrl_register |= control;
+ hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register);
+ snd_mtxunlock(sc->lock);
+ }
+ return (0);
+}
+
+static const char *
+hdsp_control_output_level(uint32_t control)
+{
+ switch (control & HDSP_OUTPUT_LEVEL_MASK) {
+ case HDSP_OUTPUT_LEVEL_MINUS10DBV:
+ return ("-10dBV");
+ case HDSP_OUTPUT_LEVEL_PLUS4DBU:
+ return ("+4dBu");
+ case HDSP_OUTPUT_LEVEL_HIGHGAIN:
+ return ("HighGain");
+ default:
+ return (NULL);
+ }
+}
+
+static int
+hdsp_sysctl_output_level(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ const char *label;
+ char buf[16] = "invalid";
+ int error;
+ uint32_t control;
+
+ sc = oidp->oid_arg1;
+
+ /* Only available on HDSP 9632. */
+ if (sc->type != HDSP_9632)
+ return (ENXIO);
+
+ /* Extract current output level from control register. */
+ control = sc->ctrl_register & HDSP_OUTPUT_LEVEL_MASK;
+ label = hdsp_control_output_level(control);
+ if (label != NULL)
+ strlcpy(buf, label, sizeof(buf));
+
+ /* Process sysctl string request. */
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Find output level matching the sysctl string. */
+ label = hdsp_control_output_level(HDSP_OUTPUT_LEVEL_MINUS10DBV);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_OUTPUT_LEVEL_MINUS10DBV;
+ label = hdsp_control_output_level(HDSP_OUTPUT_LEVEL_PLUS4DBU);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_OUTPUT_LEVEL_PLUS4DBU;
+ label = hdsp_control_output_level(HDSP_OUTPUT_LEVEL_HIGHGAIN);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_OUTPUT_LEVEL_HIGHGAIN;
+
+ /* Set output level in control register. */
+ control &= HDSP_OUTPUT_LEVEL_MASK;
+ if (control != (sc->ctrl_register & HDSP_OUTPUT_LEVEL_MASK)) {
+ snd_mtxlock(sc->lock);
+ sc->ctrl_register &= ~HDSP_OUTPUT_LEVEL_MASK;
+ sc->ctrl_register |= control;
+ hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register);
+ snd_mtxunlock(sc->lock);
+ }
+ return (0);
+}
+
+static const char *
+hdsp_control_phones_level(uint32_t control)
+{
+ switch (control & HDSP_PHONES_LEVEL_MASK) {
+ case HDSP_PHONES_LEVEL_MINUS12DB:
+ return ("-12dB");
+ case HDSP_PHONES_LEVEL_MINUS6DB:
+ return ("-6dB");
+ case HDSP_PHONES_LEVEL_0DB:
+ return ("0dB");
+ default:
+ return (NULL);
+ }
+}
+
+static int
+hdsp_sysctl_phones_level(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ const char *label;
+ char buf[16] = "invalid";
+ int error;
+ uint32_t control;
+
+ sc = oidp->oid_arg1;
+
+ /* Only available on HDSP 9632. */
+ if (sc->type != HDSP_9632)
+ return (ENXIO);
+
+ /* Extract current phones level from control register. */
+ control = sc->ctrl_register & HDSP_PHONES_LEVEL_MASK;
+ label = hdsp_control_phones_level(control);
+ if (label != NULL)
+ strlcpy(buf, label, sizeof(buf));
+
+ /* Process sysctl string request. */
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Find phones level matching the sysctl string. */
+ label = hdsp_control_phones_level(HDSP_PHONES_LEVEL_MINUS12DB);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_PHONES_LEVEL_MINUS12DB;
+ label = hdsp_control_phones_level(HDSP_PHONES_LEVEL_MINUS6DB);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_PHONES_LEVEL_MINUS6DB;
+ label = hdsp_control_phones_level(HDSP_PHONES_LEVEL_0DB);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ control = HDSP_PHONES_LEVEL_0DB;
+
+ /* Set phones level in control register. */
+ control &= HDSP_PHONES_LEVEL_MASK;
+ if (control != (sc->ctrl_register & HDSP_PHONES_LEVEL_MASK)) {
+ snd_mtxlock(sc->lock);
+ sc->ctrl_register &= ~HDSP_PHONES_LEVEL_MASK;
+ sc->ctrl_register |= control;
+ hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register);
+ snd_mtxunlock(sc->lock);
+ }
+ return (0);
+}
+
+static int
+hdsp_sysctl_sample_rate(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc = oidp->oid_arg1;
+ int error;
+ unsigned int speed, multiplier;
+
+ speed = sc->force_speed;
+
+ /* Process sysctl (unsigned) integer request. */
+ error = sysctl_handle_int(oidp, &speed, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Speed from 32000 to 192000, 0 falls back to pcm speed setting. */
+ sc->force_speed = 0;
+ if (speed > 0) {
+ multiplier = 1;
+ if ((speed > (96000 + 128000) / 2) && sc->type == HDSP_9632)
+ multiplier = 4;
+ else if (speed > (48000 + 64000) / 2)
+ multiplier = 2;
+
+ if (speed < ((32000 + 44100) / 2) * multiplier)
+ sc->force_speed = 32000 * multiplier;
+ else if (speed < ((44100 + 48000) / 2) * multiplier)
+ sc->force_speed = 44100 * multiplier;
+ else
+ sc->force_speed = 48000 * multiplier;
+ }
+
+ return (0);
+}
+
+
+static int
+hdsp_sysctl_period(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc = oidp->oid_arg1;
+ int error;
+ unsigned int period;
+
+ period = sc->force_period;
+
+ /* Process sysctl (unsigned) integer request. */
+ error = sysctl_handle_int(oidp, &period, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Period is from 2^5 to 2^14, 0 falls back to pcm latency settings. */
+ sc->force_period = 0;
+ if (period > 0) {
+ sc->force_period = 32;
+ while (sc->force_period < period && sc->force_period < 4096)
+ sc->force_period <<= 1;
+ }
+
+ return (0);
+}
+
+static uint32_t
+hdsp_control_clock_preference(enum hdsp_clock_type type)
+{
+ switch (type) {
+ case HDSP_CLOCK_INTERNAL:
+ return (HDSP_CONTROL_MASTER);
+ case HDSP_CLOCK_ADAT1:
+ return (HDSP_CONTROL_CLOCK(0));
+ case HDSP_CLOCK_ADAT2:
+ return (HDSP_CONTROL_CLOCK(1));
+ case HDSP_CLOCK_ADAT3:
+ return (HDSP_CONTROL_CLOCK(2));
+ case HDSP_CLOCK_SPDIF:
+ return (HDSP_CONTROL_CLOCK(3));
+ case HDSP_CLOCK_WORD:
+ return (HDSP_CONTROL_CLOCK(4));
+ case HDSP_CLOCK_ADAT_SYNC:
+ return (HDSP_CONTROL_CLOCK(5));
+ default:
+ return (HDSP_CONTROL_MASTER);
+ }
+}
+
+static int
+hdsp_sysctl_clock_preference(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ struct hdsp_clock_source *clock_table, *clock;
+ char buf[16] = "invalid";
+ int error;
+ uint32_t control;
+
+ sc = oidp->oid_arg1;
+
+ /* Select sync ports table for device type. */
+ if (sc->type == HDSP_9632)
+ clock_table = hdsp_clock_source_table_9632;
+ else if (sc->type == HDSP_9652)
+ clock_table = hdsp_clock_source_table_9652;
+ else
+ return (ENXIO);
+
+ /* Extract preferred clock source from control register. */
+ control = sc->ctrl_register & HDSP_CONTROL_CLOCK_MASK;
+ for (clock = clock_table; clock->name != NULL; ++clock) {
+ if (hdsp_control_clock_preference(clock->type) == control)
+ break;
+ }
+ if (clock->name != NULL)
+ strlcpy(buf, clock->name, sizeof(buf));
+
+ /* Process sysctl string request. */
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Find clock source matching the sysctl string. */
+ for (clock = clock_table; clock->name != NULL; ++clock) {
+ if (strncasecmp(buf, clock->name, sizeof(buf)) == 0)
+ break;
+ }
+
+ /* Set preferred clock source in control register. */
+ if (clock->name != NULL) {
+ control = hdsp_control_clock_preference(clock->type);
+ control &= HDSP_CONTROL_CLOCK_MASK;
+ snd_mtxlock(sc->lock);
+ sc->ctrl_register &= ~HDSP_CONTROL_CLOCK_MASK;
+ sc->ctrl_register |= control;
+ hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register);
+ snd_mtxunlock(sc->lock);
+ }
+ return (0);
+}
+
+static uint32_t
+hdsp_status2_clock_source(enum hdsp_clock_type type)
+{
+ switch (type) {
+ case HDSP_CLOCK_INTERNAL:
+ return (0);
+ case HDSP_CLOCK_ADAT1:
+ return (HDSP_STATUS2_CLOCK(0));
+ case HDSP_CLOCK_ADAT2:
+ return (HDSP_STATUS2_CLOCK(1));
+ case HDSP_CLOCK_ADAT3:
+ return (HDSP_STATUS2_CLOCK(2));
+ case HDSP_CLOCK_SPDIF:
+ return (HDSP_STATUS2_CLOCK(3));
+ case HDSP_CLOCK_WORD:
+ return (HDSP_STATUS2_CLOCK(4));
+ case HDSP_CLOCK_ADAT_SYNC:
+ return (HDSP_STATUS2_CLOCK(5));
+ default:
+ return (0);
+ }
+}
+
+static int
+hdsp_sysctl_clock_source(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ struct hdsp_clock_source *clock_table, *clock;
+ char buf[16] = "invalid";
+ uint32_t status2;
+
+ sc = oidp->oid_arg1;
+
+ /* Select sync ports table for device type. */
+ if (sc->type == HDSP_9632)
+ clock_table = hdsp_clock_source_table_9632;
+ else if (sc->type == HDSP_9652)
+ clock_table = hdsp_clock_source_table_9652;
+ else
+ return (ENXIO);
+
+ /* Read current (autosync) clock source from status2 register. */
+ snd_mtxlock(sc->lock);
+ status2 = hdsp_read_4(sc, HDSP_STATUS2_REG);
+ status2 &= HDSP_STATUS2_CLOCK_MASK;
+ snd_mtxunlock(sc->lock);
+
+ /* Translate status2 register value to clock source. */
+ for (clock = clock_table; clock->name != NULL; ++clock) {
+ /* In clock master mode, override with internal clock source. */
+ if (sc->ctrl_register & HDSP_CONTROL_MASTER) {
+ if (clock->type == HDSP_CLOCK_INTERNAL)
+ break;
+ } else if (hdsp_status2_clock_source(clock->type) == status2)
+ break;
+ }
+
+ /* Process sysctl string request. */
+ if (clock->name != NULL)
+ strlcpy(buf, clock->name, sizeof(buf));
+ return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
+}
+
+static int
+hdsp_sysctl_clock_list(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ struct hdsp_clock_source *clock_table, *clock;
+ char buf[256];
+ int n;
+
+ sc = oidp->oid_arg1;
+ n = 0;
+
+ /* Select clock source table for device type. */
+ if (sc->type == HDSP_9632)
+ clock_table = hdsp_clock_source_table_9632;
+ else if (sc->type == HDSP_9652)
+ clock_table = hdsp_clock_source_table_9652;
+ else
+ return (ENXIO);
+
+ /* List available clock sources. */
+ buf[0] = 0;
+ for (clock = clock_table; clock->name != NULL; ++clock) {
+ if (n > 0)
+ n += strlcpy(buf + n, ",", sizeof(buf) - n);
+ n += strlcpy(buf + n, clock->name, sizeof(buf) - n);
+ }
+ return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
+}
+
+static bool
+hdsp_clock_source_locked(enum hdsp_clock_type type, uint32_t status,
+ uint32_t status2)
+{
+ switch (type) {
+ case HDSP_CLOCK_INTERNAL:
+ return (true);
+ case HDSP_CLOCK_ADAT1:
+ return ((status >> 3) & 0x01);
+ case HDSP_CLOCK_ADAT2:
+ return ((status >> 2) & 0x01);
+ case HDSP_CLOCK_ADAT3:
+ return ((status >> 1) & 0x01);
+ case HDSP_CLOCK_SPDIF:
+ return (!((status >> 25) & 0x01));
+ case HDSP_CLOCK_WORD:
+ return ((status2 >> 3) & 0x01);
+ case HDSP_CLOCK_ADAT_SYNC:
+ return ((status >> 5) & 0x01);
+ default:
+ return (false);
+ }
+}
+
+static bool
+hdsp_clock_source_synced(enum hdsp_clock_type type, uint32_t status,
+ uint32_t status2)
+{
+ switch (type) {
+ case HDSP_CLOCK_INTERNAL:
+ return (true);
+ case HDSP_CLOCK_ADAT1:
+ return ((status >> 18) & 0x01);
+ case HDSP_CLOCK_ADAT2:
+ return ((status >> 17) & 0x01);
+ case HDSP_CLOCK_ADAT3:
+ return ((status >> 16) & 0x01);
+ case HDSP_CLOCK_SPDIF:
+ return (((status >> 4) & 0x01) && !((status >> 25) & 0x01));
+ case HDSP_CLOCK_WORD:
+ return ((status2 >> 4) & 0x01);
+ case HDSP_CLOCK_ADAT_SYNC:
+ return ((status >> 27) & 0x01);
+ default:
+ return (false);
+ }
+}
+
+static int
+hdsp_sysctl_sync_status(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ struct hdsp_clock_source *clock_table, *clock;
+ char buf[256];
+ char *state;
+ int n;
+ uint32_t status, status2;
+
+ sc = oidp->oid_arg1;
+ n = 0;
+
+ /* Select sync ports table for device type. */
+ if (sc->type == HDSP_9632)
+ clock_table = hdsp_clock_source_table_9632;
+ else if (sc->type == HDSP_9652)
+ clock_table = hdsp_clock_source_table_9652;
+ else
+ return (ENXIO);
+
+ /* Read current lock and sync bits from status registers. */
+ snd_mtxlock(sc->lock);
+ status = hdsp_read_4(sc, HDSP_STATUS_REG);
+ status2 = hdsp_read_4(sc, HDSP_STATUS2_REG);
+ snd_mtxunlock(sc->lock);
+
+ /* List clock sources with lock and sync state. */
+ for (clock = clock_table; clock->name != NULL; ++clock) {
+ if (clock->type == HDSP_CLOCK_INTERNAL)
+ continue;
+ if (n > 0)
+ n += strlcpy(buf + n, ",", sizeof(buf) - n);
+ state = "none";
+ if (hdsp_clock_source_locked(clock->type, status, status2)) {
+ if (hdsp_clock_source_synced(clock->type, status,
+ status2))
+ state = "sync";
+ else
+ state = "lock";
+ }
+ n += snprintf(buf + n, sizeof(buf) - n, "%s(%s)",
+ clock->name, state);
+ }
+ return (sysctl_handle_string(oidp, buf, sizeof(buf), req));
+}
+
+static int
+hdsp_probe(device_t dev)
+{
+ uint32_t rev;
+
+ if (pci_get_vendor(dev) == PCI_VENDOR_XILINX &&
+ pci_get_device(dev) == PCI_DEVICE_XILINX_HDSP) {
+ rev = pci_get_revid(dev);
+ switch (rev) {
+ case PCI_REVISION_9632:
+ device_set_desc(dev, "RME HDSP 9632");
+ return (0);
+ case PCI_REVISION_9652:
+ device_set_desc(dev, "RME HDSP 9652");
+ return (0);
+ }
+ }
+
+ return (ENXIO);
+}
+
+static int
+hdsp_init(struct sc_info *sc)
+{
+ unsigned mixer_controls;
+
+ /* Set latency. */
+ sc->period = 256;
+ /*
+ * The pcm channel latency settings propagate unreliable blocksizes,
+ * different for recording and playback, and skewed due to rounding
+ * and total buffer size limits.
+ * Force period to a consistent default until these issues are fixed.
+ */
+ sc->force_period = 256;
+ sc->ctrl_register = hdsp_encode_latency(2);
+
+ /* Set rate. */
+ sc->speed = HDSP_SPEED_DEFAULT;
+ sc->force_speed = 0;
+ sc->ctrl_register &= ~HDSP_FREQ_MASK;
+ sc->ctrl_register |= HDSP_FREQ_MASK_DEFAULT;
+
+ /* Set internal clock source (master). */
+ sc->ctrl_register &= ~HDSP_CONTROL_CLOCK_MASK;
+ sc->ctrl_register |= HDSP_CONTROL_MASTER;
+
+ /* SPDIF from coax in, line out. */
+ sc->ctrl_register &= ~HDSP_CONTROL_SPDIF_COAX;
+ sc->ctrl_register |= HDSP_CONTROL_SPDIF_COAX;
+ sc->ctrl_register &= ~HDSP_CONTROL_LINE_OUT;
+ sc->ctrl_register |= HDSP_CONTROL_LINE_OUT;
+
+ /* Default gain levels. */
+ sc->ctrl_register &= ~HDSP_INPUT_LEVEL_MASK;
+ sc->ctrl_register |= HDSP_INPUT_LEVEL_LOWGAIN;
+ sc->ctrl_register &= ~HDSP_OUTPUT_LEVEL_MASK;
+ sc->ctrl_register |= HDSP_OUTPUT_LEVEL_MINUS10DBV;
+ sc->ctrl_register &= ~HDSP_PHONES_LEVEL_MASK;
+ sc->ctrl_register |= HDSP_PHONES_LEVEL_MINUS12DB;
+
+ hdsp_write_4(sc, HDSP_CONTROL_REG, sc->ctrl_register);
+
+ if (sc->type == HDSP_9652)
+ hdsp_write_4(sc, HDSP_CONTROL2_REG, HDSP_CONTROL2_9652_MIXER);
+ else
+ hdsp_write_4(sc, HDSP_CONTROL2_REG, 0);
+
+ switch (sc->type) {
+ case HDSP_9632:
+ /* Mixer matrix is 2 source rows (input, playback) per output. */
+ mixer_controls = 2 * HDSP_MIX_SLOTS_9632 * HDSP_MIX_SLOTS_9632;
+ break;
+ case HDSP_9652:
+ /* Mixer matrix is 2 source rows (input, playback) per output. */
+ mixer_controls = 2 * HDSP_MIX_SLOTS_9652 * HDSP_MIX_SLOTS_9652;
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ /* Initialize mixer matrix by silencing all controls. */
+ for (unsigned offset = 0; offset < mixer_controls * 2; offset += 4) {
+ /* Only accepts 4 byte values, pairs of 16 bit volume controls. */
+ hdsp_write_4(sc, HDSP_MIXER_BASE + offset,
+ (HDSP_MIN_GAIN << 16) | HDSP_MIN_GAIN);
+ }
+
+ /* Reset pointer, rewrite frequency (same register) for 9632. */
+ hdsp_write_4(sc, HDSP_RESET_POINTER, 0);
+ if (sc->type == HDSP_9632) {
+ /* Set DDS value. */
+ hdsp_write_4(sc, HDSP_FREQ_REG, hdsp_freq_reg_value(sc->speed));
+ }
+
+ return (0);
+}
+
+static int
+hdsp_attach(device_t dev)
+{
+ struct hdsp_channel *chan_map;
+ struct sc_pcminfo *scp;
+ struct sc_info *sc;
+ uint32_t rev;
+ int i, err;
+
+#if 0
+ device_printf(dev, "hdsp_attach()\n");
+#endif
+
+ sc = device_get_softc(dev);
+ sc->lock = snd_mtxcreate(device_get_nameunit(dev),
+ "snd_hdsp softc");
+ sc->dev = dev;
+
+ pci_enable_busmaster(dev);
+ rev = pci_get_revid(dev);
+ switch (rev) {
+ case PCI_REVISION_9632:
+ sc->type = HDSP_9632;
+ chan_map = hdsp_unified_pcm ? chan_map_9632_uni : chan_map_9632;
+ break;
+ case PCI_REVISION_9652:
+ sc->type = HDSP_9652;
+ chan_map = hdsp_unified_pcm ? chan_map_9652_uni : chan_map_9652;
+ break;
+ default:
+ return (ENXIO);
+ }
+
+ /* Allocate resources. */
+ err = hdsp_alloc_resources(sc);
+ if (err) {
+ device_printf(dev, "Unable to allocate system resources.\n");
+ return (ENXIO);
+ }
+
+ if (hdsp_init(sc) != 0)
+ return (ENXIO);
+
+ for (i = 0; i < HDSP_MAX_CHANS && chan_map[i].descr != NULL; i++) {
+ scp = malloc(sizeof(struct sc_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
+ scp->hc = &chan_map[i];
+ scp->sc = sc;
+ scp->dev = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
+ device_set_ivars(scp->dev, scp);
+ }
+
+ hdsp_map_dmabuf(sc);
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "sync_status", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_sync_status, "A",
+ "List clock source signal lock and sync status");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "clock_source", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_clock_source, "A",
+ "Currently effective clock source");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "clock_preference", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_clock_preference, "A",
+ "Set 'internal' (master) or preferred autosync clock source");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "clock_list", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_clock_list, "A",
+ "List of supported clock sources");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "period", CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_period, "A",
+ "Force period of samples per interrupt (32, 64, ... 4096)");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "sample_rate", CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_sample_rate, "A",
+ "Force sample rate (32000, 44100, 48000, ... 192000)");
+
+ if (sc->type == HDSP_9632) {
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "phones_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_phones_level, "A",
+ "Phones output level ('0dB', '-6dB', '-12dB')");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "output_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_output_level, "A",
+ "Analog output level ('HighGain', '+4dBU', '-10dBV')");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "input_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdsp_sysctl_input_level, "A",
+ "Analog input level ('LowGain', '+4dBU', '-10dBV')");
+ }
+
+ bus_attach_children(dev);
+ return (0);
+}
+
+static void
+hdsp_child_deleted(device_t dev, device_t child)
+{
+ free(device_get_ivars(child), M_DEVBUF);
+}
+
+static void
+hdsp_dmafree(struct sc_info *sc)
+{
+
+ bus_dmamap_unload(sc->dmat, sc->rmap);
+ bus_dmamap_unload(sc->dmat, sc->pmap);
+ bus_dmamem_free(sc->dmat, sc->rbuf, sc->rmap);
+ bus_dmamem_free(sc->dmat, sc->pbuf, sc->pmap);
+ sc->rbuf = sc->pbuf = NULL;
+}
+
+static int
+hdsp_detach(device_t dev)
+{
+ struct sc_info *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+ if (sc == NULL) {
+ device_printf(dev,"Can't detach: softc is null.\n");
+ return (0);
+ }
+
+ err = bus_generic_detach(dev);
+ if (err)
+ return (err);
+
+ hdsp_dmafree(sc);
+
+ if (sc->ih)
+ bus_teardown_intr(dev, sc->irq, sc->ih);
+ if (sc->dmat)
+ bus_dma_tag_destroy(sc->dmat);
+ if (sc->irq)
+ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq);
+ if (sc->cs)
+ bus_release_resource(dev, SYS_RES_MEMORY, PCIR_BAR(0), sc->cs);
+ if (sc->lock)
+ snd_mtxfree(sc->lock);
+
+ return (0);
+}
+
+static device_method_t hdsp_methods[] = {
+ DEVMETHOD(device_probe, hdsp_probe),
+ DEVMETHOD(device_attach, hdsp_attach),
+ DEVMETHOD(device_detach, hdsp_detach),
+ DEVMETHOD(bus_child_deleted, hdsp_child_deleted),
+ { 0, 0 }
+};
+
+static driver_t hdsp_driver = {
+ "hdsp",
+ hdsp_methods,
+ PCM_SOFTC_SIZE,
+};
+
+DRIVER_MODULE(snd_hdsp, pci, hdsp_driver, 0, 0);
diff --git a/sys/dev/sound/pci/hdsp.h b/sys/dev/sound/pci/hdsp.h
new file mode 100644
index 000000000000..8ac438cd79f9
--- /dev/null
+++ b/sys/dev/sound/pci/hdsp.h
@@ -0,0 +1,266 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2012-2016 Ruslan Bukin <br@bsdpad.com>
+ * Copyright (c) 2023-2024 Florian Walpen <dev@submerge.ch>
+ * 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.
+ */
+
+#define PCI_VENDOR_XILINX 0x10ee
+#define PCI_DEVICE_XILINX_HDSP 0x3fc5 /* HDSP 9652 */
+#define PCI_REVISION_9632 0x9b
+#define PCI_REVISION_9652 0x6c
+
+#define HDSP_9632 0
+#define HDSP_9652 1
+
+/* Hardware mixer */
+#define HDSP_OUT_ENABLE_BASE 128
+#define HDSP_IN_ENABLE_BASE 384
+#define HDSP_MIXER_BASE 4096
+#define HDSP_MAX_GAIN 32768
+#define HDSP_MIN_GAIN 0
+#define HDSP_MIX_SLOTS_9632 16
+#define HDSP_MIX_SLOTS_9652 26
+#define HDSP_CONTROL2_9652_MIXER (1 << 11)
+
+/* Buffer */
+#define HDSP_PAGE_ADDR_BUF_OUT 32
+#define HDSP_PAGE_ADDR_BUF_IN 36
+#define HDSP_BUF_POSITION_MASK 0x000FFC0
+
+/* Frequency */
+#define HDSP_FREQ_0 (1 << 6)
+#define HDSP_FREQ_1 (1 << 7)
+#define HDSP_FREQ_DOUBLE (1 << 8)
+#define HDSP_FREQ_QUAD (1 << 31)
+
+#define HDSP_FREQ_32000 HDSP_FREQ_0
+#define HDSP_FREQ_44100 HDSP_FREQ_1
+#define HDSP_FREQ_48000 (HDSP_FREQ_0 | HDSP_FREQ_1)
+#define HDSP_FREQ_MASK (HDSP_FREQ_0 | HDSP_FREQ_1 | \
+ HDSP_FREQ_DOUBLE | HDSP_FREQ_QUAD)
+#define HDSP_FREQ_MASK_DEFAULT HDSP_FREQ_48000
+#define HDSP_FREQ_REG 0
+#define HDSP_FREQ_9632 104857600000000ULL
+#define hdsp_freq_multiplier(s) (((s) > 96000) ? 4 : \
+ (((s) > 48000) ? 2 : 1))
+#define hdsp_freq_single(s) ((s) / hdsp_freq_multiplier(s))
+#define hdsp_freq_reg_value(s) (HDSP_FREQ_9632 / hdsp_freq_single(s))
+
+#define HDSP_SPEED_DEFAULT 48000
+
+/* Latency */
+#define HDSP_LAT_0 (1 << 1)
+#define HDSP_LAT_1 (1 << 2)
+#define HDSP_LAT_2 (1 << 3)
+#define HDSP_LAT_MASK (HDSP_LAT_0 | HDSP_LAT_1 | HDSP_LAT_2)
+#define HDSP_LAT_BYTES_MAX (4096 * 4)
+#define HDSP_LAT_BYTES_MIN (32 * 4)
+#define hdsp_encode_latency(x) (((x)<<1) & HDSP_LAT_MASK)
+
+/* Register addresses */
+#define HDSP_RESET_POINTER 0
+#define HDSP_CONTROL_REG 64
+#define HDSP_CONTROL2_REG 256
+#define HDSP_STATUS_REG 0
+#define HDSP_STATUS2_REG 192
+
+/* Control register flags */
+#define HDSP_ENABLE (1 << 0)
+#define HDSP_CONTROL_SPDIF_COAX (1 << 14)
+#define HDSP_CONTROL_LINE_OUT (1 << 24)
+#define HDSP_CONTROL_INPUT_GAIN0 (1 << 25)
+#define HDSP_CONTROL_INPUT_GAIN1 (1 << 26)
+#define HDSP_CONTROL_OUTPUT_GAIN0 (1 << 27)
+#define HDSP_CONTROL_OUTPUT_GAIN1 (1 << 28)
+#define HDSP_CONTROL_PHONES_GAIN0 (1 << 29)
+#define HDSP_CONTROL_PHONES_GAIN1 (1 << 30)
+
+/* Analog input gain level */
+#define HDSP_INPUT_LEVEL_MASK (HDSP_CONTROL_INPUT_GAIN0 | \
+ HDSP_CONTROL_INPUT_GAIN1)
+#define HDSP_INPUT_LEVEL_LOWGAIN 0
+#define HDSP_INPUT_LEVEL_PLUS4DBU (HDSP_CONTROL_INPUT_GAIN0)
+#define HDSP_INPUT_LEVEL_MINUS10DBV (HDSP_CONTROL_INPUT_GAIN0 | \
+ HDSP_CONTROL_INPUT_GAIN1)
+
+/* Analog output gain level */
+#define HDSP_OUTPUT_LEVEL_MASK (HDSP_CONTROL_OUTPUT_GAIN0 | \
+ HDSP_CONTROL_OUTPUT_GAIN1)
+#define HDSP_OUTPUT_LEVEL_MINUS10DBV 0
+#define HDSP_OUTPUT_LEVEL_PLUS4DBU (HDSP_CONTROL_OUTPUT_GAIN0)
+#define HDSP_OUTPUT_LEVEL_HIGHGAIN (HDSP_CONTROL_OUTPUT_GAIN0 | \
+ HDSP_CONTROL_OUTPUT_GAIN1)
+
+/* Phones output gain level */
+#define HDSP_PHONES_LEVEL_MASK (HDSP_CONTROL_PHONES_GAIN0 | \
+ HDSP_CONTROL_PHONES_GAIN1)
+#define HDSP_PHONES_LEVEL_MINUS12DB 0
+#define HDSP_PHONES_LEVEL_MINUS6DB (HDSP_CONTROL_PHONES_GAIN0)
+#define HDSP_PHONES_LEVEL_0DB (HDSP_CONTROL_PHONES_GAIN0 | \
+ HDSP_CONTROL_PHONES_GAIN1)
+
+/* Interrupts */
+#define HDSP_AUDIO_IRQ_PENDING (1 << 0)
+#define HDSP_AUDIO_INT_ENABLE (1 << 5)
+#define HDSP_INTERRUPT_ACK 96
+
+/* Channels */
+#define HDSP_MAX_SLOTS 64 /* Mono channels */
+#define HDSP_MAX_CHANS (HDSP_MAX_SLOTS / 2) /* Stereo pairs */
+
+#define HDSP_CHANBUF_SAMPLES (16 * 1024)
+#define HDSP_CHANBUF_SIZE (4 * HDSP_CHANBUF_SAMPLES)
+#define HDSP_DMASEGSIZE (HDSP_CHANBUF_SIZE * HDSP_MAX_SLOTS)
+
+#define HDSP_CHAN_9632_ADAT (1 << 0)
+#define HDSP_CHAN_9632_SPDIF (1 << 1)
+#define HDSP_CHAN_9632_LINE (1 << 2)
+#define HDSP_CHAN_9632_EXT (1 << 3) /* Extension boards */
+#define HDSP_CHAN_9632_ALL (HDSP_CHAN_9632_ADAT | \
+ HDSP_CHAN_9632_SPDIF | \
+ HDSP_CHAN_9632_LINE | \
+ HDSP_CHAN_9632_EXT)
+
+#define HDSP_CHAN_9652_ADAT1 (1 << 5)
+#define HDSP_CHAN_9652_ADAT2 (1 << 6)
+#define HDSP_CHAN_9652_ADAT3 (1 << 7)
+#define HDSP_CHAN_9652_ADAT_ALL (HDSP_CHAN_9652_ADAT1 | \
+ HDSP_CHAN_9652_ADAT2 | \
+ HDSP_CHAN_9652_ADAT3)
+#define HDSP_CHAN_9652_SPDIF (1 << 8)
+#define HDSP_CHAN_9652_ALL (HDSP_CHAN_9652_ADAT_ALL | \
+ HDSP_CHAN_9652_SPDIF)
+
+struct hdsp_channel {
+ uint32_t ports;
+ char *descr;
+};
+
+enum hdsp_clock_type {
+ HDSP_CLOCK_INTERNAL,
+ HDSP_CLOCK_ADAT1,
+ HDSP_CLOCK_ADAT2,
+ HDSP_CLOCK_ADAT3,
+ HDSP_CLOCK_SPDIF,
+ HDSP_CLOCK_WORD,
+ HDSP_CLOCK_ADAT_SYNC
+};
+
+/* Preferred clock source. */
+#define HDSP_CONTROL_MASTER (1 << 4)
+#define HDSP_CONTROL_CLOCK_MASK (HDSP_CONTROL_MASTER | (1 << 13) | \
+ (1 << 16) | (1 << 17))
+#define HDSP_CONTROL_CLOCK(n) (((n & 0x04) << 11) | ((n & 0x03) << 16))
+
+/* Autosync selected clock source. */
+#define HDSP_STATUS2_CLOCK(n) ((n & 0x07) << 8)
+#define HDSP_STATUS2_CLOCK_MASK HDSP_STATUS2_CLOCK(0x07);
+
+struct hdsp_clock_source {
+ char *name;
+ enum hdsp_clock_type type;
+};
+
+static MALLOC_DEFINE(M_HDSP, "hdsp", "hdsp audio");
+
+/* Channel registers */
+struct sc_chinfo {
+ struct snd_dbuf *buffer;
+ struct pcm_channel *channel;
+ struct sc_pcminfo *parent;
+
+ /* Channel information */
+ struct pcmchan_caps *caps;
+ uint32_t cap_fmts[4];
+ uint32_t dir;
+ uint32_t format;
+ uint32_t ports;
+ uint32_t lvol;
+ uint32_t rvol;
+
+ /* Buffer */
+ uint32_t *data;
+ uint32_t size;
+ uint32_t position;
+
+ /* Flags */
+ uint32_t run;
+};
+
+/* PCM device private data */
+struct sc_pcminfo {
+ device_t dev;
+ uint32_t (*ih) (struct sc_pcminfo *scp);
+ uint32_t chnum;
+ struct sc_chinfo chan[HDSP_MAX_CHANS];
+ struct sc_info *sc;
+ struct hdsp_channel *hc;
+};
+
+/* HDSP device private data */
+struct sc_info {
+ device_t dev;
+ struct mtx *lock;
+
+ uint32_t ctrl_register;
+ uint32_t type;
+
+ /* Control/Status register */
+ struct resource *cs;
+ int csid;
+ bus_space_tag_t cst;
+ bus_space_handle_t csh;
+
+ struct resource *irq;
+ int irqid;
+ void *ih;
+ bus_dma_tag_t dmat;
+
+ /* Play/Record DMA buffers */
+ uint32_t *pbuf;
+ uint32_t *rbuf;
+ uint32_t bufsize;
+ bus_dmamap_t pmap;
+ bus_dmamap_t rmap;
+ uint32_t period;
+ uint32_t speed;
+ uint32_t force_period;
+ uint32_t force_speed;
+};
+
+#define hdsp_read_1(sc, regno) \
+ bus_space_read_1((sc)->cst, (sc)->csh, (regno))
+#define hdsp_read_2(sc, regno) \
+ bus_space_read_2((sc)->cst, (sc)->csh, (regno))
+#define hdsp_read_4(sc, regno) \
+ bus_space_read_4((sc)->cst, (sc)->csh, (regno))
+
+#define hdsp_write_1(sc, regno, data) \
+ bus_space_write_1((sc)->cst, (sc)->csh, (regno), (data))
+#define hdsp_write_2(sc, regno, data) \
+ bus_space_write_2((sc)->cst, (sc)->csh, (regno), (data))
+#define hdsp_write_4(sc, regno, data) \
+ bus_space_write_4((sc)->cst, (sc)->csh, (regno), (data))
diff --git a/sys/dev/sound/pci/hdspe-pcm.c b/sys/dev/sound/pci/hdspe-pcm.c
index 0e78be113a66..09bbbe22dacf 100644
--- a/sys/dev/sound/pci/hdspe-pcm.c
+++ b/sys/dev/sound/pci/hdspe-pcm.c
@@ -34,7 +34,6 @@
#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>
@@ -115,10 +114,8 @@ hdspe_port_first_row(uint32_t ports)
uint32_t ends;
/* Restrict ports to one set with contiguous slots. */
- if (ports & HDSPE_CHAN_AIO_LINE)
- ports = HDSPE_CHAN_AIO_LINE; /* Gap in the AIO slots here. */
- else if (ports & HDSPE_CHAN_AIO_ALL)
- ports &= HDSPE_CHAN_AIO_ALL; /* Rest of the AIO slots. */
+ if (ports & HDSPE_CHAN_AIO_ALL)
+ ports &= HDSPE_CHAN_AIO_ALL; /* All AIO slots. */
else if (ports & HDSPE_CHAN_RAY_ALL)
ports &= HDSPE_CHAN_RAY_ALL; /* All RayDAT slots. */
@@ -137,6 +134,8 @@ hdspe_channel_count(uint32_t ports, uint32_t adat_width)
/* AIO ports. */
if (ports & HDSPE_CHAN_AIO_LINE)
count += 2;
+ if (ports & HDSPE_CHAN_AIO_EXT)
+ count += 4;
if (ports & HDSPE_CHAN_AIO_PHONE)
count += 2;
if (ports & HDSPE_CHAN_AIO_AES)
@@ -190,6 +189,8 @@ hdspe_port_slot_offset(uint32_t port, unsigned int adat_width)
/* AIO ports */
case HDSPE_CHAN_AIO_LINE:
return (0);
+ case HDSPE_CHAN_AIO_EXT:
+ return (2);
case HDSPE_CHAN_AIO_PHONE:
return (6);
case HDSPE_CHAN_AIO_AES:
@@ -651,6 +652,35 @@ clean(struct sc_chinfo *ch)
}
/* Channel interface. */
+static int
+hdspechan_free(kobj_t obj, void *data)
+{
+ struct sc_pcminfo *scp;
+ struct sc_chinfo *ch;
+ struct sc_info *sc;
+
+ ch = data;
+ scp = ch->parent;
+ 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;
+ }
+ if (ch->caps != NULL) {
+ free(ch->caps, M_HDSPE);
+ ch->caps = NULL;
+ }
+ snd_mtxunlock(sc->lock);
+
+ return (0);
+}
+
static void *
hdspechan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
struct pcm_channel *c, int dir)
@@ -703,6 +733,7 @@ hdspechan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
if (sndbuf_setup(ch->buffer, ch->data, ch->size) != 0) {
device_printf(scp->dev, "Can't setup sndbuf.\n");
+ hdspechan_free(obj, ch);
return (NULL);
}
@@ -776,35 +807,6 @@ hdspechan_getptr(kobj_t obj, void *data)
}
static int
-hdspechan_free(kobj_t obj, void *data)
-{
- struct sc_pcminfo *scp;
- struct sc_chinfo *ch;
- struct sc_info *sc;
-
- ch = data;
- scp = ch->parent;
- 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;
- }
- if (ch->caps != NULL) {
- free(ch->caps, M_HDSPE);
- ch->caps = NULL;
- }
- snd_mtxunlock(sc->lock);
-
- return (0);
-}
-
-static int
hdspechan_setformat(kobj_t obj, void *data, uint32_t format)
{
struct sc_chinfo *ch;
@@ -1062,13 +1064,10 @@ hdspe_pcm_attach(device_t dev)
pcm_flags |= SD_F_BITPERFECT;
pcm_setflags(dev, pcm_flags);
+ pcm_init(dev, scp);
+
play = (hdspe_channel_play_ports(scp->hc)) ? 1 : 0;
rec = (hdspe_channel_rec_ports(scp->hc)) ? 1 : 0;
- err = pcm_register(dev, scp, play, rec);
- if (err) {
- device_printf(dev, "Can't register pcm.\n");
- return (ENXIO);
- }
scp->chnum = 0;
if (play) {
@@ -1085,7 +1084,11 @@ hdspe_pcm_attach(device_t dev)
rman_get_start(scp->sc->cs),
rman_get_start(scp->sc->irq),
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
+ err = pcm_register(dev, status);
+ if (err) {
+ device_printf(dev, "Can't register pcm.\n");
+ return (ENXIO);
+ }
mixer_init(dev, &hdspemixer_class, scp);
diff --git a/sys/dev/sound/pci/hdspe.c b/sys/dev/sound/pci/hdspe.c
index f9c36df359e3..c292b2ddef56 100644
--- a/sys/dev/sound/pci/hdspe.c
+++ b/sys/dev/sound/pci/hdspe.c
@@ -37,7 +37,6 @@
#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>
@@ -79,6 +78,7 @@ static struct hdspe_clock_source hdspe_clock_source_table_aio[] = {
static struct hdspe_channel chan_map_aio[] = {
{ HDSPE_CHAN_AIO_LINE, "line" },
+ { HDSPE_CHAN_AIO_EXT, "ext" },
{ HDSPE_CHAN_AIO_PHONE, "phone" },
{ HDSPE_CHAN_AIO_AES, "aes" },
{ HDSPE_CHAN_AIO_SPDIF, "s/pdif" },
@@ -246,6 +246,198 @@ hdspe_map_dmabuf(struct sc_info *sc)
}
}
+static const char *
+hdspe_settings_input_level(uint32_t settings)
+{
+ switch (settings & HDSPE_INPUT_LEVEL_MASK) {
+ case HDSPE_INPUT_LEVEL_LOWGAIN:
+ return ("LowGain");
+ case HDSPE_INPUT_LEVEL_PLUS4DBU:
+ return ("+4dBu");
+ case HDSPE_INPUT_LEVEL_MINUS10DBV:
+ return ("-10dBV");
+ default:
+ return (NULL);
+ }
+}
+
+static int
+hdspe_sysctl_input_level(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ const char *label;
+ char buf[16] = "invalid";
+ int error;
+ uint32_t settings;
+
+ sc = oidp->oid_arg1;
+
+ /* Only available on HDSPE AIO. */
+ if (sc->type != HDSPE_AIO)
+ return (ENXIO);
+
+ /* Extract current input level from settings register. */
+ settings = sc->settings_register & HDSPE_INPUT_LEVEL_MASK;
+ label = hdspe_settings_input_level(settings);
+ if (label != NULL)
+ strlcpy(buf, label, sizeof(buf));
+
+ /* Process sysctl string request. */
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Find input level matching the sysctl string. */
+ label = hdspe_settings_input_level(HDSPE_INPUT_LEVEL_LOWGAIN);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_INPUT_LEVEL_LOWGAIN;
+ label = hdspe_settings_input_level(HDSPE_INPUT_LEVEL_PLUS4DBU);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_INPUT_LEVEL_PLUS4DBU;
+ label = hdspe_settings_input_level(HDSPE_INPUT_LEVEL_MINUS10DBV);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_INPUT_LEVEL_MINUS10DBV;
+
+ /* Set input level in settings register. */
+ settings &= HDSPE_INPUT_LEVEL_MASK;
+ if (settings != (sc->settings_register & HDSPE_INPUT_LEVEL_MASK)) {
+ snd_mtxlock(sc->lock);
+ sc->settings_register &= ~HDSPE_INPUT_LEVEL_MASK;
+ sc->settings_register |= settings;
+ hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register);
+ snd_mtxunlock(sc->lock);
+ }
+ return (0);
+}
+
+static const char *
+hdspe_settings_output_level(uint32_t settings)
+{
+ switch (settings & HDSPE_OUTPUT_LEVEL_MASK) {
+ case HDSPE_OUTPUT_LEVEL_HIGHGAIN:
+ return ("HighGain");
+ case HDSPE_OUTPUT_LEVEL_PLUS4DBU:
+ return ("+4dBu");
+ case HDSPE_OUTPUT_LEVEL_MINUS10DBV:
+ return ("-10dBV");
+ default:
+ return (NULL);
+ }
+}
+
+static int
+hdspe_sysctl_output_level(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ const char *label;
+ char buf[16] = "invalid";
+ int error;
+ uint32_t settings;
+
+ sc = oidp->oid_arg1;
+
+ /* Only available on HDSPE AIO. */
+ if (sc->type != HDSPE_AIO)
+ return (ENXIO);
+
+ /* Extract current output level from settings register. */
+ settings = sc->settings_register & HDSPE_OUTPUT_LEVEL_MASK;
+ label = hdspe_settings_output_level(settings);
+ if (label != NULL)
+ strlcpy(buf, label, sizeof(buf));
+
+ /* Process sysctl string request. */
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Find output level matching the sysctl string. */
+ label = hdspe_settings_output_level(HDSPE_OUTPUT_LEVEL_HIGHGAIN);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_OUTPUT_LEVEL_HIGHGAIN;
+ label = hdspe_settings_output_level(HDSPE_OUTPUT_LEVEL_PLUS4DBU);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_OUTPUT_LEVEL_PLUS4DBU;
+ label = hdspe_settings_output_level(HDSPE_OUTPUT_LEVEL_MINUS10DBV);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_OUTPUT_LEVEL_MINUS10DBV;
+
+ /* Set output level in settings register. */
+ settings &= HDSPE_OUTPUT_LEVEL_MASK;
+ if (settings != (sc->settings_register & HDSPE_OUTPUT_LEVEL_MASK)) {
+ snd_mtxlock(sc->lock);
+ sc->settings_register &= ~HDSPE_OUTPUT_LEVEL_MASK;
+ sc->settings_register |= settings;
+ hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register);
+ snd_mtxunlock(sc->lock);
+ }
+ return (0);
+}
+
+static const char *
+hdspe_settings_phones_level(uint32_t settings)
+{
+ switch (settings & HDSPE_PHONES_LEVEL_MASK) {
+ case HDSPE_PHONES_LEVEL_HIGHGAIN:
+ return ("HighGain");
+ case HDSPE_PHONES_LEVEL_PLUS4DBU:
+ return ("+4dBu");
+ case HDSPE_PHONES_LEVEL_MINUS10DBV:
+ return ("-10dBV");
+ default:
+ return (NULL);
+ }
+}
+
+static int
+hdspe_sysctl_phones_level(SYSCTL_HANDLER_ARGS)
+{
+ struct sc_info *sc;
+ const char *label;
+ char buf[16] = "invalid";
+ int error;
+ uint32_t settings;
+
+ sc = oidp->oid_arg1;
+
+ /* Only available on HDSPE AIO. */
+ if (sc->type != HDSPE_AIO)
+ return (ENXIO);
+
+ /* Extract current phones level from settings register. */
+ settings = sc->settings_register & HDSPE_PHONES_LEVEL_MASK;
+ label = hdspe_settings_phones_level(settings);
+ if (label != NULL)
+ strlcpy(buf, label, sizeof(buf));
+
+ /* Process sysctl string request. */
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ /* Find phones level matching the sysctl string. */
+ label = hdspe_settings_phones_level(HDSPE_PHONES_LEVEL_HIGHGAIN);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_PHONES_LEVEL_HIGHGAIN;
+ label = hdspe_settings_phones_level(HDSPE_PHONES_LEVEL_PLUS4DBU);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_PHONES_LEVEL_PLUS4DBU;
+ label = hdspe_settings_phones_level(HDSPE_PHONES_LEVEL_MINUS10DBV);
+ if (strncasecmp(buf, label, sizeof(buf)) == 0)
+ settings = HDSPE_PHONES_LEVEL_MINUS10DBV;
+
+ /* Set phones level in settings register. */
+ settings &= HDSPE_PHONES_LEVEL_MASK;
+ if (settings != (sc->settings_register & HDSPE_PHONES_LEVEL_MASK)) {
+ snd_mtxlock(sc->lock);
+ sc->settings_register &= ~HDSPE_PHONES_LEVEL_MASK;
+ sc->settings_register |= settings;
+ hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register);
+ snd_mtxunlock(sc->lock);
+ }
+ return (0);
+}
+
static int
hdspe_sysctl_sample_rate(SYSCTL_HANDLER_ARGS)
{
@@ -529,6 +721,15 @@ hdspe_init(struct sc_info *sc)
/* Other settings. */
sc->settings_register = 0;
+
+ /* Default gain levels. */
+ sc->settings_register &= ~HDSPE_INPUT_LEVEL_MASK;
+ sc->settings_register |= HDSPE_INPUT_LEVEL_LOWGAIN;
+ sc->settings_register &= ~HDSPE_OUTPUT_LEVEL_MASK;
+ sc->settings_register |= HDSPE_OUTPUT_LEVEL_MINUS10DBV;
+ sc->settings_register &= ~HDSPE_PHONES_LEVEL_MASK;
+ sc->settings_register |= HDSPE_PHONES_LEVEL_MINUS10DBV;
+
hdspe_write_4(sc, HDSPE_SETTINGS_REG, sc->settings_register);
return (0);
@@ -578,10 +779,10 @@ hdspe_attach(device_t dev)
return (ENXIO);
for (i = 0; i < HDSPE_MAX_CHANS && chan_map[i].descr != NULL; i++) {
- scp = malloc(sizeof(struct sc_pcminfo), M_DEVBUF, M_NOWAIT | M_ZERO);
+ scp = malloc(sizeof(struct sc_pcminfo), M_DEVBUF, M_WAITOK | M_ZERO);
scp->hc = &chan_map[i];
scp->sc = sc;
- scp->dev = device_add_child(dev, "pcm", -1);
+ scp->dev = device_add_child(dev, "pcm", DEVICE_UNIT_ANY);
device_set_ivars(scp->dev, scp);
}
@@ -623,7 +824,34 @@ hdspe_attach(device_t dev)
sc, 0, hdspe_sysctl_sample_rate, "A",
"Force sample rate (32000, 44100, 48000, ... 192000)");
- return (bus_generic_attach(dev));
+ if (sc->type == HDSPE_AIO) {
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "phones_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdspe_sysctl_phones_level, "A",
+ "Phones output level ('HighGain', '+4dBU', '-10dBV')");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "output_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdspe_sysctl_output_level, "A",
+ "Analog output level ('HighGain', '+4dBU', '-10dBV')");
+
+ SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
+ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+ "input_level", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE,
+ sc, 0, hdspe_sysctl_input_level, "A",
+ "Analog input level ('LowGain', '+4dBU', '-10dBV')");
+ }
+
+ bus_attach_children(dev);
+ return (0);
+}
+
+static void
+hdspe_child_deleted(device_t dev, device_t child)
+{
+ free(device_get_ivars(child), M_DEVBUF);
}
static void
@@ -649,7 +877,7 @@ hdspe_detach(device_t dev)
return (0);
}
- err = device_delete_children(dev);
+ err = bus_generic_detach(dev);
if (err)
return (err);
@@ -673,6 +901,7 @@ static device_method_t hdspe_methods[] = {
DEVMETHOD(device_probe, hdspe_probe),
DEVMETHOD(device_attach, hdspe_attach),
DEVMETHOD(device_detach, hdspe_detach),
+ DEVMETHOD(bus_child_deleted, hdspe_child_deleted),
{ 0, 0 }
};
diff --git a/sys/dev/sound/pci/hdspe.h b/sys/dev/sound/pci/hdspe.h
index daffeb4ddebc..bced78758068 100644
--- a/sys/dev/sound/pci/hdspe.h
+++ b/sys/dev/sound/pci/hdspe.h
@@ -74,35 +74,43 @@
#define HDSPE_LAT_BYTES_MIN (32 * 4)
#define hdspe_encode_latency(x) (((x)<<1) & HDSPE_LAT_MASK)
-/* Gain */
-#define HDSP_ADGain0 (1 << 25)
-#define HDSP_ADGain1 (1 << 26)
-#define HDSP_DAGain0 (1 << 27)
-#define HDSP_DAGain1 (1 << 28)
-#define HDSP_PhoneGain0 (1 << 29)
-#define HDSP_PhoneGain1 (1 << 30)
-
-#define HDSP_ADGainMask (HDSP_ADGain0 | HDSP_ADGain1)
-#define HDSP_ADGainMinus10dBV (HDSP_ADGainMask)
-#define HDSP_ADGainPlus4dBu (HDSP_ADGain0)
-#define HDSP_ADGainLowGain 0
-
-#define HDSP_DAGainMask (HDSP_DAGain0 | HDSP_DAGain1)
-#define HDSP_DAGainHighGain (HDSP_DAGainMask)
-#define HDSP_DAGainPlus4dBu (HDSP_DAGain0)
-#define HDSP_DAGainMinus10dBV 0
-
-#define HDSP_PhoneGainMask (HDSP_PhoneGain0|HDSP_PhoneGain1)
-#define HDSP_PhoneGain0dB HDSP_PhoneGainMask
-#define HDSP_PhoneGainMinus6dB (HDSP_PhoneGain0)
-#define HDSP_PhoneGainMinus12dB 0
-
-/* Settings */
+/* Register addresses */
#define HDSPE_SETTINGS_REG 0
#define HDSPE_CONTROL_REG 64
#define HDSPE_STATUS_REG 0
#define HDSPE_STATUS1_REG 64
#define HDSPE_STATUS2_REG 192
+
+/* Settings register flags */
+#define HDSPE_SETTINGS_INPUT_GAIN0 (1 << 20)
+#define HDSPE_SETTINGS_INPUT_GAIN1 (1 << 21)
+#define HDSPE_SETTINGS_OUTPUT_GAIN0 (1 << 22)
+#define HDSPE_SETTINGS_OUTPUT_GAIN1 (1 << 23)
+#define HDSPE_SETTINGS_PHONES_GAIN0 (1 << 24)
+#define HDSPE_SETTINGS_PHONES_GAIN1 (1 << 25)
+
+/* Analog input gain level */
+#define HDSPE_INPUT_LEVEL_MASK (HDSPE_SETTINGS_INPUT_GAIN0 | \
+ HDSPE_SETTINGS_INPUT_GAIN1)
+#define HDSPE_INPUT_LEVEL_LOWGAIN 0
+#define HDSPE_INPUT_LEVEL_PLUS4DBU (HDSPE_SETTINGS_INPUT_GAIN0)
+#define HDSPE_INPUT_LEVEL_MINUS10DBV (HDSPE_SETTINGS_INPUT_GAIN1)
+
+/* Analog output gain level */
+#define HDSPE_OUTPUT_LEVEL_MASK (HDSPE_SETTINGS_OUTPUT_GAIN0 | \
+ HDSPE_SETTINGS_OUTPUT_GAIN1)
+#define HDSPE_OUTPUT_LEVEL_HIGHGAIN 0
+#define HDSPE_OUTPUT_LEVEL_PLUS4DBU (HDSPE_SETTINGS_OUTPUT_GAIN0)
+#define HDSPE_OUTPUT_LEVEL_MINUS10DBV (HDSPE_SETTINGS_OUTPUT_GAIN1)
+
+/* Phones output gain level */
+#define HDSPE_PHONES_LEVEL_MASK (HDSPE_SETTINGS_PHONES_GAIN0 | \
+ HDSPE_SETTINGS_PHONES_GAIN1)
+#define HDSPE_PHONES_LEVEL_HIGHGAIN 0
+#define HDSPE_PHONES_LEVEL_PLUS4DBU (HDSPE_SETTINGS_PHONES_GAIN0)
+#define HDSPE_PHONES_LEVEL_MINUS10DBV (HDSPE_SETTINGS_PHONES_GAIN1)
+
+/* Control register flags */
#define HDSPE_ENABLE (1 << 0)
/* Interrupts */
@@ -119,23 +127,25 @@
#define HDSPE_DMASEGSIZE (HDSPE_CHANBUF_SIZE * HDSPE_MAX_SLOTS)
#define HDSPE_CHAN_AIO_LINE (1 << 0)
-#define HDSPE_CHAN_AIO_PHONE (1 << 1)
-#define HDSPE_CHAN_AIO_AES (1 << 2)
-#define HDSPE_CHAN_AIO_SPDIF (1 << 3)
-#define HDSPE_CHAN_AIO_ADAT (1 << 4)
+#define HDSPE_CHAN_AIO_EXT (1 << 1)
+#define HDSPE_CHAN_AIO_PHONE (1 << 2)
+#define HDSPE_CHAN_AIO_AES (1 << 3)
+#define HDSPE_CHAN_AIO_SPDIF (1 << 4)
+#define HDSPE_CHAN_AIO_ADAT (1 << 5)
#define HDSPE_CHAN_AIO_ALL_REC (HDSPE_CHAN_AIO_LINE | \
+ HDSPE_CHAN_AIO_EXT | \
HDSPE_CHAN_AIO_AES | \
HDSPE_CHAN_AIO_SPDIF | \
HDSPE_CHAN_AIO_ADAT)
#define HDSPE_CHAN_AIO_ALL (HDSPE_CHAN_AIO_ALL_REC | \
HDSPE_CHAN_AIO_PHONE) \
-#define HDSPE_CHAN_RAY_AES (1 << 5)
-#define HDSPE_CHAN_RAY_SPDIF (1 << 6)
-#define HDSPE_CHAN_RAY_ADAT1 (1 << 7)
-#define HDSPE_CHAN_RAY_ADAT2 (1 << 8)
-#define HDSPE_CHAN_RAY_ADAT3 (1 << 9)
-#define HDSPE_CHAN_RAY_ADAT4 (1 << 10)
+#define HDSPE_CHAN_RAY_AES (1 << 6)
+#define HDSPE_CHAN_RAY_SPDIF (1 << 7)
+#define HDSPE_CHAN_RAY_ADAT1 (1 << 8)
+#define HDSPE_CHAN_RAY_ADAT2 (1 << 9)
+#define HDSPE_CHAN_RAY_ADAT3 (1 << 10)
+#define HDSPE_CHAN_RAY_ADAT4 (1 << 11)
#define HDSPE_CHAN_RAY_ALL (HDSPE_CHAN_RAY_AES | \
HDSPE_CHAN_RAY_SPDIF | \
HDSPE_CHAN_RAY_ADAT1 | \
diff --git a/sys/dev/sound/pci/ich.c b/sys/dev/sound/pci/ich.c
index fbde0accfd28..500d6d95daac 100644
--- a/sys/dev/sound/pci/ich.c
+++ b/sys/dev/sound/pci/ich.c
@@ -695,7 +695,7 @@ ich_setstatus(struct sc_info *sc)
device_printf(sc->dev,
"PCI Master abort workaround enabled\n");
- pcm_setstatus(sc->dev, status);
+ pcm_register(sc->dev, status);
}
/* -------------------------------------------------------------------- */
@@ -860,12 +860,12 @@ ich_init(struct sc_info *sc)
static int
ich_pci_probe(device_t dev)
{
- int i;
+ size_t i;
uint16_t devid, vendor;
vendor = pci_get_vendor(dev);
devid = pci_get_device(dev);
- for (i = 0; i < sizeof(ich_devs)/sizeof(ich_devs[0]); i++) {
+ for (i = 0; i < nitems(ich_devs); i++) {
if (vendor == ich_devs[i].vendor &&
devid == ich_devs[i].devid) {
device_set_desc(dev, ich_devs[i].name);
@@ -1066,8 +1066,7 @@ ich_pci_attach(device_t dev)
ich_setmap, sc, 0))
goto bad;
- if (pcm_register(dev, sc, 1, (sc->hasmic) ? 2 : 1))
- goto bad;
+ pcm_init(dev, sc);
pcm_addchan(dev, PCMDIR_PLAY, &ichchan_class, sc); /* play */
pcm_addchan(dev, PCMDIR_REC, &ichchan_class, sc); /* record */
diff --git a/sys/dev/sound/pci/maestro3.c b/sys/dev/sound/pci/maestro3.c
index 6dd54a66f683..2d102fcd6dbe 100644
--- a/sys/dev/sound/pci/maestro3.c
+++ b/sys/dev/sound/pci/maestro3.c
@@ -488,7 +488,7 @@ m3_pchan_init(kobj_t kobj, void *devinfo, struct snd_dbuf *b, struct pcm_channel
DMAC_BLOCKF_SELECTOR);
/* set an armload of static initializers */
- for(i = 0 ; i < (sizeof(pv) / sizeof(pv[0])) ; i++) {
+ for(i = 0 ; i < nitems(pv); i++) {
m3_wr_assp_data(sc, ch->dac_data + pv[i].addr, pv[i].val);
}
@@ -862,7 +862,7 @@ m3_rchan_init(kobj_t kobj, void *devinfo, struct snd_dbuf *b, struct pcm_channel
DMAC_PAGE3_SELECTOR + DMAC_BLOCKF_SELECTOR);
/* set an armload of static initializers */
- for(i = 0 ; i < (sizeof(rv) / sizeof(rv[0])) ; i++) {
+ for(i = 0 ; i < nitems(rv); i++) {
m3_wr_assp_data(sc, ch->adc_data + rv[i].addr, rv[i].val);
}
@@ -1423,10 +1423,7 @@ m3_pci_attach(device_t dev)
m3_enable_ints(sc);
- if (pcm_register(dev, sc, dacn, adcn)) {
- device_printf(dev, "pcm_register error\n");
- goto bad;
- }
+ pcm_init(dev, sc);
for (i=0 ; i<dacn ; i++) {
if (pcm_addchan(dev, PCMDIR_PLAY, &m3_pch_class, sc)) {
device_printf(dev, "pcm_addchan (play) error\n");
@@ -1443,8 +1440,8 @@ m3_pci_attach(device_t dev)
(sc->regtype == SYS_RES_IOPORT)? "port" : "mem",
rman_get_start(sc->reg), rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- if (pcm_setstatus(dev, status)) {
- device_printf(dev, "attach: pcm_setstatus error\n");
+ if (pcm_register(dev, status)) {
+ device_printf(dev, "pcm_register error\n");
goto bad;
}
@@ -1453,7 +1450,7 @@ m3_pci_attach(device_t dev)
/* Create the buffer for saving the card state during suspend */
len = sizeof(u_int16_t) * (REV_B_CODE_MEMORY_LENGTH +
REV_B_DATA_MEMORY_LENGTH);
- sc->savemem = (u_int16_t*)malloc(len, M_DEVBUF, M_WAITOK | M_ZERO);
+ sc->savemem = malloc(len, M_DEVBUF, M_WAITOK | M_ZERO);
return 0;
diff --git a/sys/dev/sound/pci/neomagic.c b/sys/dev/sound/pci/neomagic.c
index 25273633ff18..d7824c990a52 100644
--- a/sys/dev/sound/pci/neomagic.c
+++ b/sys/dev/sound/pci/neomagic.c
@@ -707,10 +707,11 @@ nm_pci_attach(device_t dev)
rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- if (pcm_register(dev, sc, 1, 1)) goto bad;
+ pcm_init(dev, sc);
pcm_addchan(dev, PCMDIR_REC, &nmchan_class, sc);
pcm_addchan(dev, PCMDIR_PLAY, &nmchan_class, sc);
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return 0;
diff --git a/sys/dev/sound/pci/solo.c b/sys/dev/sound/pci/solo.c
index bee79e723696..90dd2e26ad41 100644
--- a/sys/dev/sound/pci/solo.c
+++ b/sys/dev/sound/pci/solo.c
@@ -35,7 +35,6 @@
#include <dev/pci/pcivar.h>
#include <dev/sound/isa/sb.h>
-#include <dev/sound/chip.h>
#include "mixer_if.h"
@@ -1027,11 +1026,11 @@ ess_attach(device_t dev)
rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- if (pcm_register(dev, sc, 1, 1))
- goto no;
+ pcm_init(dev, sc);
pcm_addchan(dev, PCMDIR_REC, &esschan_class, sc);
pcm_addchan(dev, PCMDIR_PLAY, &esschan_class, sc);
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto no;
return 0;
diff --git a/sys/dev/sound/pci/spicds.c b/sys/dev/sound/pci/spicds.c
index 3b67101b8df0..da0e8d9da6d5 100644
--- a/sys/dev/sound/pci/spicds.c
+++ b/sys/dev/sound/pci/spicds.c
@@ -144,7 +144,7 @@ spicds_create(device_t dev, void *devinfo, int num, spicds_ctrl ctrl)
#if(0)
device_printf(dev, "spicds_create(dev, devinfo, %d, ctrl)\n", num);
#endif
- codec = (struct spicds_info *)malloc(sizeof *codec, M_SPICDS, M_NOWAIT);
+ codec = malloc(sizeof(*codec), M_SPICDS, M_NOWAIT);
if (codec == NULL)
return NULL;
diff --git a/sys/dev/sound/pci/t4dwave.c b/sys/dev/sound/pci/t4dwave.c
index 653e610febbe..07b9e1004573 100644
--- a/sys/dev/sound/pci/t4dwave.c
+++ b/sys/dev/sound/pci/t4dwave.c
@@ -921,12 +921,12 @@ tr_pci_attach(device_t dev)
rman_get_start(tr->reg), rman_get_start(tr->irq),
device_get_nameunit(device_get_parent(dev)));
- if (pcm_register(dev, tr, dacn, 1))
- goto bad;
+ pcm_init(dev, tr);
pcm_addchan(dev, PCMDIR_REC, &trrchan_class, tr);
for (i = 0; i < dacn; i++)
pcm_addchan(dev, PCMDIR_PLAY, &trpchan_class, tr);
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return 0;
diff --git a/sys/dev/sound/pci/via8233.c b/sys/dev/sound/pci/via8233.c
index 9f3b312e4365..243353805b94 100644
--- a/sys/dev/sound/pci/via8233.c
+++ b/sys/dev/sound/pci/via8233.c
@@ -1352,8 +1352,7 @@ via_attach(device_t dev)
device_get_nameunit(device_get_parent(dev)));
/* Register */
- if (pcm_register(dev, via, via_dxs_chnum + via_sgd_chnum, NWRCHANS))
- goto bad;
+ pcm_init(dev, via);
for (i = 0; i < via_dxs_chnum; i++)
pcm_addchan(dev, PCMDIR_PLAY, &via8233dxs_class, via);
for (i = 0; i < via_sgd_chnum; i++)
@@ -1366,7 +1365,8 @@ via_attach(device_t dev)
(via_dxs_chnum > 0) ? "En" : "Dis", (via->dxs_src) ? "(SRC)" : "",
via_dxs_chnum, via_sgd_chnum, NWRCHANS);
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return (0);
bad:
diff --git a/sys/dev/sound/pci/via82c686.c b/sys/dev/sound/pci/via82c686.c
index 44f846b684d7..40f3521a57a2 100644
--- a/sys/dev/sound/pci/via82c686.c
+++ b/sys/dev/sound/pci/via82c686.c
@@ -585,10 +585,11 @@ via_attach(device_t dev)
device_get_nameunit(device_get_parent(dev)));
/* Register */
- if (pcm_register(dev, via, 1, 1)) goto bad;
+ pcm_init(dev, via);
pcm_addchan(dev, PCMDIR_PLAY, &viachan_class, via);
pcm_addchan(dev, PCMDIR_REC, &viachan_class, via);
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status))
+ goto bad;
return 0;
bad:
if (via->codec) ac97_destroy(via->codec);
diff --git a/sys/dev/sound/pci/vibes.c b/sys/dev/sound/pci/vibes.c
index e587f0113b5d..7e908f188614 100644
--- a/sys/dev/sound/pci/vibes.c
+++ b/sys/dev/sound/pci/vibes.c
@@ -866,18 +866,17 @@ sv_attach(device_t dev) {
if (bootverbose)
printf("Sonicvibes: revision %d.\n", sc->rev);
- if (pcm_register(dev, sc, 1, 1)) {
- device_printf(dev, "sv_attach: pcm_register fail\n");
- goto fail;
- }
-
+ pcm_init(dev, sc);
pcm_addchan(dev, PCMDIR_PLAY, &svpchan_class, sc);
pcm_addchan(dev, PCMDIR_REC, &svrchan_class, sc);
snprintf(status, SND_STATUSLEN, "port 0x%jx irq %jd on %s",
rman_get_start(sc->enh_reg), rman_get_start(sc->irq),
device_get_nameunit(device_get_parent(dev)));
- pcm_setstatus(dev, status);
+ if (pcm_register(dev, status)) {
+ device_printf(dev, "sv_attach: pcm_register fail\n");
+ goto fail;
+ }
DEB(printf("sv_attach: succeeded\n"));