diff options
Diffstat (limited to 'sys/dev/sound/pci')
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")); |
