summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorJason Wright <jason@cvs.openbsd.org>2006-04-26 15:53:09 +0000
committerJason Wright <jason@cvs.openbsd.org>2006-04-26 15:53:09 +0000
commitf62a1b8a44fa545bbc307cdd16654db36365758b (patch)
tree475ce39ff01571496f5f1201790773b4a20e94ef /sys
parentec57674c7e81f9893811d46d8896db00f5a6ca87 (diff)
not quite working driver for azalia audio (this is essentially AC97 for > 2005)
Just as brain damaged and ill specified. From NetBSD.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/pci/azalia.c3122
-rw-r--r--sys/dev/pci/azalia.h546
-rw-r--r--sys/dev/pci/azalia_codec.c383
-rw-r--r--sys/dev/pci/files.pci8
4 files changed, 4058 insertions, 1 deletions
diff --git a/sys/dev/pci/azalia.c b/sys/dev/pci/azalia.c
new file mode 100644
index 00000000000..f41ebbddb8b
--- /dev/null
+++ b/sys/dev/pci/azalia.c
@@ -0,0 +1,3122 @@
+/* $OpenBSD: azalia.c,v 1.1 2006/04/26 15:53:08 jason Exp $ */
+/* $NetBSD: azalia.c,v 1.15 2005/09/29 04:14:03 kent Exp $ */
+
+/*-
+ * Copyright (c) 2005 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by TAMURA Kent
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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 AZALIA_DEBUG
+/*
+ * High Definition Audio Specification
+ * ftp://download.intel.com/standards/hdaudio/pdf/HDAudio_03.pdf
+ *
+ *
+ * TO DO:
+ * - S/PDIF
+ * - power hook
+ * - multiple codecs (needed?)
+ * - multiple streams (needed?)
+ */
+
+#include <sys/cdefs.h>
+#ifdef NETBSD_GOOP
+__KERNEL_RCSID(0, "$NetBSD: azalia.c,v 1.15 2005/09/29 04:14:03 kent Exp $");
+#endif
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <uvm/uvm_param.h>
+#include <dev/audio_if.h>
+#include <dev/auconv.h>
+#include <dev/pci/pcidevs.h>
+#include <dev/pci/pcivar.h>
+
+#include <dev/pci/azalia.h>
+
+typedef struct audio_params audio_params_t;
+#ifndef BUS_DMA_NOCACHE
+#define BUS_DMA_NOCACHE 0
+#endif
+#define auconv_delete_encodings(x...)
+#define auconv_query_encoding(x...) (EINVAL)
+#define auconv_create_encodings(x...) (0)
+
+struct audio_format {
+ void *driver_data;
+ int32_t mode;
+ u_int encoding;
+ u_int validbits;
+ u_int precision;
+ u_int channels;
+ u_int channel_mask;
+#define AUFMT_UNKNOWN_POSITION 0U
+#define AUFMT_FRONT_LEFT 0x00001U /* USB audio compatible */
+#define AUFMT_FRONT_RIGHT 0x00002U /* USB audio compatible */
+#define AUFMT_FRONT_CENTER 0x00004U /* USB audio compatible */
+#define AUFMT_LOW_FREQUENCY 0x00008U /* USB audio compatible */
+#define AUFMT_BACK_LEFT 0x00010U /* USB audio compatible */
+#define AUFMT_BACK_RIGHT 0x00020U /* USB audio compatible */
+#define AUFMT_FRONT_LEFT_OF_CENTER 0x00040U /* USB audio compatible */
+#define AUFMT_FRONT_RIGHT_OF_CENTER 0x00080U /* USB audio compatible */
+#define AUFMT_BACK_CENTER 0x00100U /* USB audio compatible */
+#define AUFMT_SIDE_LEFT 0x00200U /* USB audio compatible */
+#define AUFMT_SIDE_RIGHT 0x00400U /* USB audio compatible */
+#define AUFMT_TOP_CENTER 0x00800U /* USB audio compatible */
+#define AUFMT_TOP_FRONT_LEFT 0x01000U
+#define AUFMT_TOP_FRONT_CENTER 0x02000U
+#define AUFMT_TOP_FRONT_RIGHT 0x04000U
+#define AUFMT_TOP_BACK_LEFT 0x08000U
+#define AUFMT_TOP_BACK_CENTER 0x10000U
+#define AUFMT_TOP_BACK_RIGHT 0x20000U
+
+#define AUFMT_MONAURAL AUFMT_FRONT_CENTER
+#define AUFMT_STEREO (AUFMT_FRONT_LEFT | AUFMT_FRONT_RIGHT)
+#define AUFMT_SURROUND4 (AUFMT_STEREO | AUFMT_BACK_LEFT \
+ | AUFMT_BACK_RIGHT)
+#define AUFMT_DOLBY_5_1 (AUFMT_SURROUND4 | AUFMT_FRONT_CENTER \
+ | AUFMT_LOW_FREQUENCY)
+
+ /**
+ * 0: frequency[0] is lower limit, and frequency[1] is higher limit.
+ * 1-16: frequency[0] to frequency[frequency_type-1] are valid.
+ */
+ u_int frequency_type;
+
+#define AUFMT_MAX_FREQUENCIES 16
+ /**
+ * sampling rates
+ */
+ u_int frequency[AUFMT_MAX_FREQUENCIES];
+};
+
+#define AUFMT_INVALIDATE(fmt) (fmt)->mode |= 0x80000000
+#define AUFMT_VALIDATE(fmt) (fmt)->mode &= 0x7fffffff
+#define AUFMT_IS_VALID(fmt) (((fmt)->mode & 0x80000000) == 0)
+
+/* ----------------------------------------------------------------
+ * ICH6/ICH7 constant values
+ * ---------------------------------------------------------------- */
+
+/* PCI registers */
+#define ICH_PCI_HDBARL 0x10
+#define ICH_PCI_HDBARU 0x14
+#define ICH_PCI_HDCTL 0x40
+#define ICH_PCI_HDCTL_CLKDETCLR 0x08
+#define ICH_PCI_HDCTL_CLKDETEN 0x04
+#define ICH_PCI_HDCTL_CLKDETINV 0x02
+#define ICH_PCI_HDCTL_SIGNALMODE 0x01
+
+/* internal types */
+
+typedef struct {
+ bus_dmamap_t map;
+ caddr_t addr; /* kernel virtual address */
+ bus_dma_segment_t segments[1];
+ size_t size;
+} azalia_dma_t;
+#define AZALIA_DMA_DMAADDR(p) ((p)->map->dm_segs[0].ds_addr)
+
+typedef struct {
+ struct azalia_t *az;
+ int regbase;
+ int number;
+ int dir; /* AUMODE_PLAY or AUMODE_RECORD */
+ uint32_t intr_bit;
+ azalia_dma_t bdlist;
+ azalia_dma_t buffer;
+ void (*intr)(void*);
+ void *intr_arg;
+ bus_addr_t dmaend, dmanext; /* XXX needed? */
+} stream_t;
+#define STR_READ_1(s, r) \
+ bus_space_read_1((s)->az->iot, (s)->az->ioh, (s)->regbase + HDA_SD_##r)
+#define STR_READ_2(s, r) \
+ bus_space_read_2((s)->az->iot, (s)->az->ioh, (s)->regbase + HDA_SD_##r)
+#define STR_READ_4(s, r) \
+ bus_space_read_4((s)->az->iot, (s)->az->ioh, (s)->regbase + HDA_SD_##r)
+#define STR_WRITE_1(s, r, v) \
+ bus_space_write_1((s)->az->iot, (s)->az->ioh, (s)->regbase + HDA_SD_##r, v)
+#define STR_WRITE_2(s, r, v) \
+ bus_space_write_2((s)->az->iot, (s)->az->ioh, (s)->regbase + HDA_SD_##r, v)
+#define STR_WRITE_4(s, r, v) \
+ bus_space_write_4((s)->az->iot, (s)->az->ioh, (s)->regbase + HDA_SD_##r, v)
+
+typedef struct azalia_t {
+ struct device dev;
+ struct device *audiodev;
+
+ pci_chipset_tag_t pc;
+ void *ih;
+ bus_space_tag_t iot;
+ bus_space_handle_t ioh;
+ bus_size_t map_size;
+ bus_dma_tag_t dmat;
+
+ codec_t codecs[15];
+ int ncodecs; /* number of codecs */
+ int codecno; /* index of the using codec */
+
+ azalia_dma_t corb_dma;
+ int corb_size;
+ azalia_dma_t rirb_dma;
+ int rirb_size;
+ int rirb_rp;
+
+ boolean_t ok64;
+ int nistreams, nostreams, nbstreams;
+ stream_t pstream;
+ stream_t rstream;
+
+ int running;
+} azalia_t;
+#define XNAME(sc) ((sc)->dev.dv_xname)
+#define AZ_READ_1(z, r) bus_space_read_1((z)->iot, (z)->ioh, HDA_##r)
+#define AZ_READ_2(z, r) bus_space_read_2((z)->iot, (z)->ioh, HDA_##r)
+#define AZ_READ_4(z, r) bus_space_read_4((z)->iot, (z)->ioh, HDA_##r)
+#define AZ_WRITE_1(z, r, v) bus_space_write_1((z)->iot, (z)->ioh, HDA_##r, v)
+#define AZ_WRITE_2(z, r, v) bus_space_write_2((z)->iot, (z)->ioh, HDA_##r, v)
+#define AZ_WRITE_4(z, r, v) bus_space_write_4((z)->iot, (z)->ioh, HDA_##r, v)
+
+
+/* prototypes */
+int azalia_pci_match(struct device *, void *, void *);
+void azalia_pci_attach(struct device *, struct device *, void *);
+int azalia_pci_activate(struct device *, enum devact);
+int azalia_pci_detach(struct device *, int);
+int azalia_intr(void *);
+int azalia_attach(azalia_t *);
+void azalia_attach_intr(struct device *);
+int azalia_init_corb(azalia_t *);
+int azalia_delete_corb(azalia_t *);
+int azalia_init_rirb(azalia_t *);
+int azalia_delete_rirb(azalia_t *);
+int azalia_set_command(const azalia_t *, nid_t, int, uint32_t,
+ uint32_t);
+int azalia_get_response(azalia_t *, uint32_t *);
+int azalia_alloc_dmamem(azalia_t *, size_t, size_t, azalia_dma_t *);
+int azalia_free_dmamem(const azalia_t *, azalia_dma_t*);
+
+int azalia_codec_init(codec_t *);
+int azalia_codec_delete(codec_t *);
+int azalia_codec_construct_format(codec_t *);
+void azalia_codec_add_bits(codec_t *, int, uint32_t, int);
+void azalia_codec_add_format(codec_t *, int, int, int, uint32_t,
+ int32_t);
+int azalia_codec_comresp(const codec_t *, nid_t, uint32_t,
+ uint32_t, uint32_t *);
+int azalia_codec_connect_stream(codec_t *, int, uint16_t, int);
+
+int azalia_mixer_init(codec_t *);
+int azalia_mixer_delete(codec_t *);
+int azalia_mixer_get(const codec_t *, mixer_ctrl_t *);
+int azalia_mixer_set(codec_t *, const mixer_ctrl_t *);
+int azalia_mixer_ensure_capacity(codec_t *, size_t);
+u_char azalia_mixer_from_device_value(const codec_t *,
+ const mixer_item_t *, uint32_t );
+uint32_t azalia_mixer_to_device_value(const codec_t *,
+ const mixer_item_t *, u_char);
+boolean_t azalia_mixer_validate_value(const codec_t *,
+ const mixer_item_t *, u_char);
+
+int azalia_widget_init(widget_t *, const codec_t *, int);
+int azalia_widget_init_audio(widget_t *, const codec_t *);
+int azalia_widget_print_audio(const widget_t *, const char *);
+int azalia_widget_init_pin(widget_t *, const codec_t *);
+int azalia_widget_print_pin(const widget_t *);
+int azalia_widget_init_connection(widget_t *, const codec_t *);
+
+int azalia_stream_init(stream_t *, azalia_t *, int, int, int);
+int azalia_stream_delete(stream_t *, azalia_t *);
+int azalia_stream_reset(stream_t *);
+int azalia_stream_start(stream_t *, void *, void *, int,
+ void (*)(void *), void *, uint16_t);
+int azalia_stream_halt(stream_t *);
+int azalia_stream_intr(stream_t *, uint32_t);
+
+int azalia_open(void *, int);
+void azalia_close(void *);
+int azalia_query_encoding(void *, audio_encoding_t *);
+int azalia_set_params(void *, int, int, audio_params_t *,
+ audio_params_t *);
+int azalia_round_blocksize(void *, int);
+int azalia_halt_output(void *);
+int azalia_halt_input(void *);
+int azalia_getdev(void *, struct audio_device *);
+int azalia_set_port(void *, mixer_ctrl_t *);
+int azalia_get_port(void *, mixer_ctrl_t *);
+int azalia_query_devinfo(void *, mixer_devinfo_t *);
+void *azalia_allocm(void *, int, size_t, int, int);
+void azalia_freem(void *, void *, int);
+size_t azalia_round_buffersize(void *, int, size_t);
+int azalia_get_props(void *);
+int azalia_trigger_output(void *, void *, void *, int,
+ void (*)(void *), void *, audio_params_t *);
+int azalia_trigger_input(void *, void *, void *, int,
+ void (*)(void *), void *, audio_params_t *);
+
+int azalia_params2fmt(const audio_params_t *, uint16_t *);
+int azalia_create_encodings(struct audio_format *, int,
+ struct audio_encoding_set **);
+
+
+/* variables */
+struct cfattach azalia_ca = {
+ sizeof(azalia_t), azalia_pci_match, azalia_pci_attach,
+ azalia_pci_detach, azalia_pci_activate
+};
+
+struct cfdriver azalia_cd = {
+ NULL, "azalia", DV_DULL
+};
+
+struct audio_hw_if azalia_hw_if = {
+ azalia_open,
+ azalia_close,
+ NULL, /* drain */
+ azalia_query_encoding,
+ azalia_set_params,
+ azalia_round_blocksize,
+ NULL, /* commit_settings */
+ NULL, /* init_output */
+ NULL, /* init_input */
+ NULL, /* start_output */
+ NULL, /* satart_inpu */
+ azalia_halt_output,
+ azalia_halt_input,
+ NULL, /* speaker_ctl */
+ azalia_getdev,
+ NULL, /* setfd */
+ azalia_set_port,
+ azalia_get_port,
+ azalia_query_devinfo,
+ azalia_allocm,
+ azalia_freem,
+ azalia_round_buffersize,
+ NULL, /* mappage */
+ azalia_get_props,
+ azalia_trigger_output,
+ azalia_trigger_input,
+};
+
+static const char *pin_colors[16] = {
+ "unknown", "black", "gray", "blue",
+ "green", "red", "orange", "yellow",
+ "purple", "pink", "col0a", "col0b",
+ "col0c", "col0d", "white", "other"};
+#ifdef AZALIA_DEBUG
+static const char *pin_devices[16] = {
+ "line-out", AudioNspeaker, AudioNheadphone, AudioNcd,
+ "SPDIF-out", "digital-out", "modem-line", "modem-handset",
+ "line-in", AudioNaux, AudioNmicrophone, "telephony",
+ "SPDIF-in", "digital-in", "dev0e", "other"};
+#endif
+
+/* ================================================================
+ * PCI functions
+ * ================================================================ */
+
+int
+azalia_pci_match(struct device *parent, void *match, void *aux)
+{
+ struct pci_attach_args *pa;
+
+ pa = aux;
+ if (PCI_CLASS(pa->pa_class) == PCI_CLASS_MULTIMEDIA
+ && PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_HDAUDIO)
+ return 1;
+ return 0;
+}
+
+void
+azalia_pci_attach(struct device *parent, struct device *self, void *aux)
+{
+ azalia_t *sc;
+ struct pci_attach_args *pa;
+ pcireg_t v;
+ pci_intr_handle_t ih;
+ const char *intrrupt_str;
+
+ sc = (azalia_t*)self;
+ pa = aux;
+
+ sc->dmat = pa->pa_dmat;
+
+ printf(": Generic High Definition Audio Controller\n");
+ if (pci_mapreg_map(pa, ICH_PCI_HDBARL, PCI_MAPREG_MEM_TYPE_64BIT, 0,
+ &sc->iot, &sc->ioh, NULL, &sc->map_size, 0)) {
+ printf("%s: can't map device i/o space\n", XNAME(sc));
+ return;
+ }
+
+ /* enable bus mastering */
+ v = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
+ pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
+ v | PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_BACKTOBACK_ENABLE);
+
+ /* interrupt */
+ if (pci_intr_map(pa, &ih)) {
+ printf("%s: can't map interrupt\n", XNAME(sc));
+ return;
+ }
+ sc->pc = pa->pa_pc;
+ intrrupt_str = pci_intr_string(pa->pa_pc, ih);
+ sc->ih = pci_intr_establish(pa->pa_pc, ih, IPL_AUDIO, azalia_intr,
+ sc, sc->dev.dv_xname);
+ if (sc->ih == NULL) {
+ printf("%s: can't establish interrupt", XNAME(sc));
+ if (intrrupt_str != NULL)
+ printf(" at %s", intrrupt_str);
+ printf("\n");
+ return;
+ }
+ printf("%s: interrupting at %s\n", XNAME(sc), intrrupt_str);
+
+ if (azalia_attach(sc)) {
+ printf("%s: initialization failure\n", XNAME(sc));
+ azalia_pci_detach(self, 0);
+ return;
+ }
+ for (v = 0; v < 10; v++)
+ DELAY(1000000);
+ azalia_attach_intr(self);
+}
+
+int
+azalia_pci_activate(struct device *self, enum devact act)
+{
+ azalia_t *sc;
+ int ret;
+
+ sc = (azalia_t*)self;
+ ret = 0;
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return EOPNOTSUPP;
+ case DVACT_DEACTIVATE:
+ if (sc->audiodev != NULL)
+ ret = config_deactivate(sc->audiodev);
+ return ret;
+ }
+ return EOPNOTSUPP;
+}
+
+int
+azalia_pci_detach(struct device *self, int flags)
+{
+ azalia_t *az;
+ int i;
+
+ az = (azalia_t*)self;
+ if (az->audiodev != NULL) {
+ config_detach(az->audiodev, flags);
+ az->audiodev = NULL;
+ }
+ azalia_stream_delete(&az->rstream, az);
+ azalia_stream_delete(&az->pstream, az);
+ for (i = 0; i < az->ncodecs; i++) {
+ azalia_codec_delete(&az->codecs[i]);
+ }
+ az->ncodecs = 0;
+ azalia_delete_corb(az);
+ azalia_delete_rirb(az);
+ if (az->ih != NULL) {
+ pci_intr_disestablish(az->pc, az->ih);
+ az->ih = NULL;
+ }
+ if (az->map_size != 0) {
+ bus_space_unmap(az->iot, az->ioh, az->map_size);
+ az->map_size = 0;
+ }
+ return 0;
+}
+
+int
+azalia_intr(void *v)
+{
+ azalia_t *az;
+ int ret;
+ uint32_t intsts;
+ uint8_t rirbsts;
+
+ az = v;
+ ret = 0;
+ //printf("[i]");
+
+ intsts = AZ_READ_4(az, INTSTS);
+ if (intsts == 0)
+ return ret;
+
+ ret += azalia_stream_intr(&az->pstream, intsts);
+#if 0
+ ret += azalia_stream_intr(&az->rstream, intsts);
+#endif
+
+ rirbsts = AZ_READ_1(az, RIRBSTS);
+ if (rirbsts & (HDA_RIRBSTS_RIRBOIS | HDA_RIRBSTS_RINTFL)) {
+ if (rirbsts & HDA_RIRBSTS_RINTFL) {
+ //printf("[R]");
+ } else {
+ //printf("[O]");
+ }
+ AZ_WRITE_1(az, RIRBSTS,
+ rirbsts | HDA_RIRBSTS_RIRBOIS | HDA_RIRBSTS_RINTFL);
+ ret++;
+ }
+ return ret;
+}
+
+/* ================================================================
+ * HDA controller functions
+ * ================================================================ */
+
+int
+azalia_attach(azalia_t *az)
+{
+ int i, n;
+ uint32_t gctl;
+ uint16_t gcap;
+ uint16_t statests;
+
+ printf("%s: host: High Definition Audio rev. %d.%d\n",
+ XNAME(az), AZ_READ_1(az, VMAJ), AZ_READ_1(az, VMIN));
+ gcap = AZ_READ_2(az, GCAP);
+ az->nistreams = HDA_GCAP_ISS(gcap);
+ az->nostreams = HDA_GCAP_OSS(gcap);
+ az->nbstreams = HDA_GCAP_BSS(gcap);
+ az->ok64 = (gcap & HDA_GCAP_64OK) != 0;
+ DPRINTF(("%s: host: %d output, %d input, and %d bidi streams\n",
+ XNAME(az), az->nostreams, az->nistreams, az->nbstreams));
+
+ /* 4.2.2 Starting the High Definition Audio Controller */
+ DPRINTF(("%s: resetting\n", __func__));
+ gctl = AZ_READ_4(az, GCTL);
+ AZ_WRITE_4(az, GCTL, gctl & ~HDA_GCTL_CRST);
+ for (i = 5000; i >= 0; i--) {
+ DELAY(10);
+ if ((AZ_READ_4(az, GCTL) & HDA_GCTL_CRST) == 0)
+ break;
+ }
+ DPRINTF(("%s: reset counter = %d\n", __func__, i));
+ if (i <= 0) {
+ printf("%s: reset failure\n", XNAME(az));
+ return ETIMEDOUT;
+ }
+ DELAY(1000);
+ gctl = AZ_READ_4(az, GCTL);
+ AZ_WRITE_4(az, GCTL, gctl | HDA_GCTL_CRST);
+ for (i = 5000; i >= 0; i--) {
+ DELAY(10);
+ if (AZ_READ_4(az, GCTL) & HDA_GCTL_CRST)
+ break;
+ }
+ DPRINTF(("%s: reset counter = %d\n", __func__, i));
+ if (i <= 0) {
+ printf("%s: reset-exit failure\n", XNAME(az));
+ return ETIMEDOUT;
+ }
+
+ /* 4.3 Codec discovery */
+ DELAY(1000);
+ statests = AZ_READ_2(az, STATESTS);
+ for (i = 0, n = 0; i < 15; i++) {
+ if ((statests >> i) & 1) {
+ DPRINTF(("%s: found a codec at #%d\n", XNAME(az), i));
+ az->codecs[n].address = i;
+ az->codecs[n++].az = az;
+ }
+ }
+ az->ncodecs = n;
+ if (az->ncodecs < 1) {
+ printf("%s: No HD-Audio codecs\n", XNAME(az));
+ return -1;
+ }
+ return 0;
+}
+
+void
+azalia_attach_intr(struct device *self)
+{
+ azalia_t *az;
+ int err, i, c;
+
+ az = (azalia_t*)self;
+
+ AZ_WRITE_2(az, STATESTS, HDA_STATESTS_SDIWAKE);
+ AZ_WRITE_1(az, RIRBSTS, HDA_RIRBSTS_RINTFL | HDA_RIRBSTS_RIRBOIS);
+ AZ_WRITE_4(az, INTSTS, HDA_INTSTS_CIS | HDA_INTSTS_GIS);
+ AZ_WRITE_4(az, DPLBASE, 0);
+ AZ_WRITE_4(az, DPUBASE, 0);
+
+ /* 4.4.1 Command Outbound Ring Buffer */
+ azalia_init_corb(az);
+ /* 4.4.2 Response Inbound Ring Buffer */
+ azalia_init_rirb(az);
+
+ AZ_WRITE_4(az, INTCTL,
+ AZ_READ_4(az, INTCTL) | HDA_INTCTL_CIE | HDA_INTCTL_GIE);
+
+ c = -1;
+ for (i = 0; i < az->ncodecs; i++) {
+ err = azalia_codec_init(&az->codecs[i]);
+ if (!err && c < 0)
+ c = i;
+ }
+ if (c < 0)
+ goto err_exit;
+ /* Use the first audio codec */
+ az->codecno = c;
+ DPRINTF(("%s: using the #%d codec\n", XNAME(az), az->codecno));
+
+ if (azalia_stream_init(&az->pstream, az, az->nistreams + 0,
+ 1, AUMODE_PLAY))
+ goto err_exit;
+ if (azalia_stream_init(&az->rstream, az, 0, 2, AUMODE_RECORD))
+ goto err_exit;
+
+ az->audiodev = audio_attach_mi(&azalia_hw_if, az, &az->dev);
+ return;
+err_exit:
+ azalia_pci_detach(self, 0);
+ return;
+}
+
+int
+azalia_init_corb(azalia_t *az)
+{
+ int entries, err, i;
+ uint16_t corbrp, corbwp;
+ uint8_t corbsize, cap, corbctl;
+
+ /* stop the CORB */
+ corbctl = AZ_READ_1(az, CORBCTL);
+ if (corbctl & HDA_CORBCTL_CORBRUN) { /* running? */
+ AZ_WRITE_1(az, CORBCTL, corbctl & ~HDA_CORBCTL_CORBRUN);
+ for (i = 5000; i >= 0; i--) {
+ DELAY(10);
+ corbctl = AZ_READ_1(az, CORBCTL);
+ if ((corbctl & HDA_CORBCTL_CORBRUN) == 0)
+ break;
+ }
+ if (i <= 0) {
+ printf("%s: CORB is running\n", XNAME(az));
+ return EBUSY;
+ }
+ }
+
+ /* determine CORB size */
+ corbsize = AZ_READ_1(az, CORBSIZE);
+ cap = corbsize & HDA_CORBSIZE_CORBSZCAP_MASK;
+ corbsize &= ~HDA_CORBSIZE_CORBSIZE_MASK;
+ if (cap & HDA_CORBSIZE_CORBSZCAP_256) {
+ entries = 256;
+ corbsize |= HDA_CORBSIZE_CORBSIZE_256;
+ } else if (cap & HDA_CORBSIZE_CORBSZCAP_16) {
+ entries = 16;
+ corbsize |= HDA_CORBSIZE_CORBSIZE_16;
+ } else if (cap & HDA_CORBSIZE_CORBSZCAP_2) {
+ entries = 2;
+ corbsize |= HDA_CORBSIZE_CORBSIZE_2;
+ } else {
+ printf("%s: Invalid CORBSZCAP: 0x%2x\n", XNAME(az), cap);
+ return -1;
+ }
+
+ err = azalia_alloc_dmamem(az, entries * sizeof(corb_entry_t),
+ 128, &az->corb_dma);
+ if (err) {
+ printf("%s: can't allocate CORB buffer\n", XNAME(az));
+ return err;
+ }
+ AZ_WRITE_4(az, CORBLBASE, (uint32_t)AZALIA_DMA_DMAADDR(&az->corb_dma));
+ AZ_WRITE_4(az, CORBUBASE, PTR_UPPER32(AZALIA_DMA_DMAADDR(&az->corb_dma)));
+ AZ_WRITE_1(az, CORBSIZE, corbsize);
+ az->corb_size = entries;
+
+ DPRINTF(("%s: CORB allocation succeeded.\n", __func__));
+
+ /* reset CORBRP */
+ corbrp = AZ_READ_2(az, CORBRP);
+ AZ_WRITE_2(az, CORBRP, corbrp | HDA_CORBRP_CORBRPRST);
+ for (i = 5000; i >= 0; i--) {
+ DELAY(10);
+ corbrp = AZ_READ_2(az, CORBRP);
+ if (corbrp & HDA_CORBRP_CORBRPRST)
+ break;
+ }
+ if (i <= 0) {
+ printf("%s: CORBRP reset failure\n", XNAME(az));
+ return -1;
+ }
+ AZ_WRITE_2(az, CORBRP, corbrp & ~HDA_CORBRP_CORBRPRST);
+ for (i = 5000; i >= 0; i--) {
+ DELAY(10);
+ corbrp = AZ_READ_2(az, CORBRP);
+ if ((corbrp & HDA_CORBRP_CORBRPRST) == 0)
+ break;
+ }
+ if (i <= 0) {
+ printf("%s: CORBRP reset failure 2\n", XNAME(az));
+ return -1;
+ }
+ DPRINTF(("%s: CORBWP=%d; size=%d\n", __func__,
+ AZ_READ_2(az, CORBRP) & HDA_CORBRP_CORBRP, az->corb_size));
+
+ /* clear CORBWP */
+ corbwp = AZ_READ_2(az, CORBWP);
+ AZ_WRITE_2(az, CORBWP, corbwp & ~HDA_CORBWP_CORBWP);
+
+ /* Run! */
+ corbctl = AZ_READ_1(az, CORBCTL);
+ AZ_WRITE_1(az, CORBCTL, corbctl | HDA_CORBCTL_CORBRUN);
+ return 0;
+}
+
+int
+azalia_delete_corb(azalia_t *az)
+{
+ int i;
+ uint8_t corbctl;
+
+ if (az->corb_dma.addr == NULL)
+ return 0;
+ /* stop the CORB */
+ corbctl = AZ_READ_1(az, CORBCTL);
+ AZ_WRITE_1(az, CORBCTL, corbctl & ~HDA_CORBCTL_CORBRUN);
+ for (i = 5000; i >= 0; i--) {
+ DELAY(10);
+ corbctl = AZ_READ_1(az, CORBCTL);
+ if ((corbctl & HDA_CORBCTL_CORBRUN) == 0)
+ break;
+ }
+ azalia_free_dmamem(az, &az->corb_dma);
+ return 0;
+}
+
+int
+azalia_init_rirb(azalia_t *az)
+{
+ int entries, err, i;
+ uint16_t rirbwp;
+ uint8_t rirbsize, cap, rirbctl;
+
+ /* stop the RIRB */
+ rirbctl = AZ_READ_1(az, RIRBCTL);
+ if (rirbctl & HDA_RIRBCTL_RIRBDMAEN) { /* running? */
+ AZ_WRITE_1(az, RIRBCTL, rirbctl & ~HDA_RIRBCTL_RIRBDMAEN);
+ for (i = 5000; i >= 0; i--) {
+ DELAY(10);
+ rirbctl = AZ_READ_1(az, RIRBCTL);
+ if ((rirbctl & HDA_RIRBCTL_RIRBDMAEN) == 0)
+ break;
+ }
+ if (i <= 0) {
+ printf("%s: RIRB is running\n", XNAME(az));
+ return EBUSY;
+ }
+ }
+
+ /* determine RIRB size */
+ rirbsize = AZ_READ_1(az, RIRBSIZE);
+ cap = rirbsize & HDA_RIRBSIZE_RIRBSZCAP_MASK;
+ rirbsize &= ~HDA_RIRBSIZE_RIRBSIZE_MASK;
+ if (cap & HDA_RIRBSIZE_RIRBSZCAP_256) {
+ entries = 256;
+ rirbsize |= HDA_RIRBSIZE_RIRBSIZE_256;
+ } else if (cap & HDA_RIRBSIZE_RIRBSZCAP_16) {
+ entries = 16;
+ rirbsize |= HDA_RIRBSIZE_RIRBSIZE_16;
+ } else if (cap & HDA_RIRBSIZE_RIRBSZCAP_2) {
+ entries = 2;
+ rirbsize |= HDA_RIRBSIZE_RIRBSIZE_2;
+ } else {
+ printf("%s: Invalid RIRBSZCAP: 0x%2x\n", XNAME(az), cap);
+ return -1;
+ }
+
+ err = azalia_alloc_dmamem(az, entries * sizeof(rirb_entry_t),
+ 128, &az->rirb_dma);
+ if (err) {
+ printf("%s: can't allocate RIRB buffer\n", XNAME(az));
+ return err;
+ }
+ AZ_WRITE_4(az, RIRBLBASE, (uint32_t)AZALIA_DMA_DMAADDR(&az->rirb_dma));
+ AZ_WRITE_4(az, RIRBUBASE, PTR_UPPER32(AZALIA_DMA_DMAADDR(&az->rirb_dma)));
+ AZ_WRITE_1(az, RIRBSIZE, rirbsize);
+ az->rirb_size = entries;
+
+ DPRINTF(("%s: RIRB allocation succeeded.\n", __func__));
+
+ //rirbctl = AZ_READ_1(az, RIRBCTL);
+ //AZ_WRITE_1(az, RIRBCTL, rirbctl & ~HDA_RIRBCTL_RINTCTL);
+
+ /* reset the write pointer */
+ rirbwp = AZ_READ_2(az, RIRBWP);
+ AZ_WRITE_2(az, RIRBWP, rirbwp | HDA_RIRBWP_RIRBWPRST);
+
+ /* clear the read pointer */
+ az->rirb_rp = AZ_READ_2(az, RIRBWP) & HDA_RIRBWP_RIRBWP;
+ DPRINTF(("%s: RIRBRP=%d, size=%d\n", __func__, az->rirb_rp, az->rirb_size));
+
+ AZ_WRITE_2(az, RINTCNT, 1);
+
+ /* Run! */
+ rirbctl = AZ_READ_1(az, RIRBCTL);
+ AZ_WRITE_1(az, RIRBCTL, rirbctl | HDA_RIRBCTL_RIRBDMAEN | HDA_RIRBCTL_RINTCTL);
+ return 0;
+}
+
+int
+azalia_delete_rirb(azalia_t *az)
+{
+ int i;
+ uint8_t rirbctl;
+
+ if (az->rirb_dma.addr == NULL)
+ return 0;
+ /* stop the RIRB */
+ rirbctl = AZ_READ_1(az, RIRBCTL);
+ AZ_WRITE_1(az, RIRBCTL, rirbctl & ~HDA_RIRBCTL_RIRBDMAEN);
+ for (i = 5000; i >= 0; i--) {
+ DELAY(10);
+ rirbctl = AZ_READ_1(az, RIRBCTL);
+ if ((rirbctl & HDA_RIRBCTL_RIRBDMAEN) == 0)
+ break;
+ }
+ azalia_free_dmamem(az, &az->rirb_dma);
+ return 0;
+}
+
+int
+azalia_set_command(const azalia_t *az, int caddr, nid_t nid, uint32_t control,
+ uint32_t param)
+{
+ corb_entry_t *corb;
+ int wp;
+ uint32_t verb;
+ uint16_t corbwp;
+
+#ifdef DIAGNOSTIC
+ if ((AZ_READ_1(az, CORBCTL) & HDA_CORBCTL_CORBRUN) == 0) {
+ printf("%s: CORB is not running.\n", XNAME(az));
+ return -1;
+ }
+#endif
+ verb = (caddr << 28) | (nid << 20) | (control << 8) | param;
+ corbwp = AZ_READ_2(az, CORBWP);
+ wp = corbwp & HDA_CORBWP_CORBWP;
+ corb = (corb_entry_t*)az->corb_dma.addr;
+ if (++wp >= az->corb_size)
+ wp = 0;
+ corb[wp] = verb;
+ AZ_WRITE_2(az, CORBWP, (corbwp & ~HDA_CORBWP_CORBWP) | wp);
+#if 0
+ DPRINTF(("%s: caddr=%d nid=%d control=0x%x param=0x%x verb=0x%8.8x wp=%d\n",
+ __func__, caddr, nid, control, param, verb, wp));
+#endif
+ return 0;
+}
+
+int
+azalia_get_response(azalia_t *az, uint32_t *result)
+{
+ const rirb_entry_t *rirb;
+ int i;
+ uint16_t wp;
+
+#ifdef DIAGNOSTIC
+ if ((AZ_READ_1(az, RIRBCTL) & HDA_RIRBCTL_RIRBDMAEN) == 0) {
+ printf("%s: RIRB is not running.\n", XNAME(az));
+ return -1;
+ }
+#endif
+ for (i = 5000; i >= 0; i--) {
+ wp = AZ_READ_2(az, RIRBWP) & HDA_RIRBWP_RIRBWP;
+ if (az->rirb_rp != wp)
+ break;
+ DELAY(10);
+ }
+ if (i <= 0) {
+ printf("%s: RIRB time out\n", XNAME(az));
+ return ETIMEDOUT;
+ }
+ rirb = (rirb_entry_t*)az->rirb_dma.addr;
+ for (;;) {
+ if (++az->rirb_rp >= az->rirb_size)
+ az->rirb_rp = 0;
+ if (rirb[az->rirb_rp].resp_ex & RIRB_UNSOLICITED_RESPONSE) {
+ DPRINTF(("%s: unsolicited response\n", __func__));
+ } else
+ break;
+ }
+ if (result != NULL)
+ *result = rirb[az->rirb_rp].resp;
+#if 0
+ DPRINTF(("%s: rirbwp=%d rp=%d resp1=0x%8.8x resp2=0x%8.8x\n",
+ __func__, wp, az->rirb_rp, rirb[az->rirb_rp].resp,
+ rirb[az->rirb_rp].resp_ex));
+#endif
+#if 0
+ for (i = 0; i < 16 /*az->rirb_size*/; i++) {
+ DPRINTF(("rirb[%d] 0x%8.8x:0x%8.8x ", i, rirb[i].resp, rirb[i].resp_ex));
+ if ((i % 2) == 1)
+ DPRINTF(("\n"));
+ }
+#endif
+ return 0;
+}
+
+int
+azalia_alloc_dmamem(azalia_t *az, size_t size, size_t align, azalia_dma_t *d)
+{
+ int err;
+ int nsegs;
+
+ d->size = size;
+ err = bus_dmamem_alloc(az->dmat, size, align, 0, d->segments, 1,
+ &nsegs, BUS_DMA_NOWAIT);
+ if (err)
+ return err;
+ if (nsegs != 1)
+ goto free;
+ err = bus_dmamem_map(az->dmat, d->segments, 1, size,
+ &d->addr, BUS_DMA_NOWAIT | BUS_DMA_COHERENT | BUS_DMA_NOCACHE);
+ if (err)
+ goto free;
+ err = bus_dmamap_create(az->dmat, size, 1, size, 0,
+ BUS_DMA_NOWAIT, &d->map);
+ if (err)
+ goto unmap;
+ err = bus_dmamap_load(az->dmat, d->map, d->addr, size,
+ NULL, BUS_DMA_NOWAIT);
+ if (err)
+ goto destroy;
+
+ if (!az->ok64 && PTR_UPPER32(AZALIA_DMA_DMAADDR(d)) != 0) {
+ azalia_free_dmamem(az, d);
+ return -1;
+ }
+ return 0;
+
+destroy:
+ bus_dmamap_destroy(az->dmat, d->map);
+unmap:
+ bus_dmamem_unmap(az->dmat, d->addr, size);
+free:
+ bus_dmamem_free(az->dmat, d->segments, 1);
+ d->addr = NULL;
+ return err;
+}
+
+int
+azalia_free_dmamem(const azalia_t *az, azalia_dma_t* d)
+{
+ if (d->addr == NULL)
+ return 0;
+ bus_dmamap_unload(az->dmat, d->map);
+ bus_dmamap_destroy(az->dmat, d->map);
+ bus_dmamem_unmap(az->dmat, d->addr, d->size);
+ bus_dmamem_free(az->dmat, d->segments, 1);
+ d->addr = NULL;
+ return 0;
+}
+
+/* ================================================================
+ * HDA codec functions
+ * ================================================================ */
+
+int
+azalia_codec_init(codec_t *this)
+{
+ uint32_t rev, result;
+ int err, addr, n, i;
+
+ this->comresp = azalia_codec_comresp;
+ addr = this->address;
+ DPRINTF(("%s: information of codec[%d] follows:\n",
+ XNAME(this->az), addr));
+ /* codec vendor/device/revision */
+ err = this->comresp(this, CORB_NID_ROOT, CORB_GET_PARAMETER,
+ COP_REVISION_ID, &rev);
+ if (err)
+ return err;
+ err = this->comresp(this, CORB_NID_ROOT, CORB_GET_PARAMETER,
+ COP_VENDOR_ID, &result);
+ if (err)
+ return err;
+ azalia_codec_init_vtbl(this, result);
+
+ if (this->name == NULL) {
+ printf("%s: codec: 0x%4.4x/0x%4.4x (rev. %u.%u)\n",
+ XNAME(this->az), result >> 16, result & 0xffff,
+ COP_RID_REVISION(rev), COP_RID_STEPPING(rev));
+ } else {
+ printf("%s: codec: %s (rev. %u.%u)\n",
+ XNAME(this->az), this->name,
+ COP_RID_REVISION(rev), COP_RID_STEPPING(rev));
+ }
+ printf("%s: codec: High Definition Audio rev. %u.%u\n",
+ XNAME(this->az), COP_RID_MAJ(rev), COP_RID_MIN(rev));
+
+ /* identify function nodes */
+ err = this->comresp(this, CORB_NID_ROOT, CORB_GET_PARAMETER,
+ COP_SUBORDINATE_NODE_COUNT, &result);
+ if (err)
+ return err;
+ this->nfunctions = COP_NSUBNODES(result);
+ if (COP_NSUBNODES(result) <= 0) {
+ printf("%s: No function groups\n", XNAME(this->az));
+ return -1;
+ }
+ /* iterate function nodes and find an audio function */
+ n = COP_START_NID(result);
+ DPRINTF(("%s: nidstart=%d #functions=%d\n",
+ __func__, n, this->nfunctions));
+ this->audiofunc = -1;
+ for (i = 0; i < this->nfunctions; i++) {
+ err = this->comresp(this, n + i, CORB_GET_PARAMETER,
+ COP_FUNCTION_GROUP_TYPE, &result);
+ if (err)
+ continue;
+ DPRINTF(("%s: FTYPE result = 0x%8.8x\n", __func__, result));
+ if (COP_FTYPE(result) == COP_FTYPE_AUDIO) {
+ this->audiofunc = n + i;
+ break; /* XXX multiple audio functions? */
+ }
+ }
+ if (this->audiofunc < 0) {
+ printf("%s: codec[%d]: No audio functions\n",
+ XNAME(this->az), addr);
+ return -1;
+ }
+
+ /* power the audio function */
+ this->comresp(this, this->audiofunc, CORB_SET_POWER_STATE, CORB_PS_D0, &result);
+ DELAY(100);
+
+ /* check widgets in the audio function */
+ err = this->comresp(this, this->audiofunc,
+ CORB_GET_PARAMETER, COP_SUBORDINATE_NODE_COUNT, &result);
+ if (err)
+ return err;
+ DPRINTF(("%s: There are %d widgets in the audio function.\n",
+ __func__, COP_NSUBNODES(result)));
+ this->wstart = COP_START_NID(result);
+ if (this->wstart < 2) {
+ printf("%s: invalid node structure\n", XNAME(this->az));
+ return -1;
+ }
+ this->wend = this->wstart + COP_NSUBNODES(result);
+ this->w = malloc(sizeof(widget_t) * this->wend, M_DEVBUF, M_NOWAIT);
+ if (this->w == NULL) {
+ printf("%s: out of memory\n", XNAME(this->az));
+ return ENOMEM;
+ }
+ bzero(this->w, sizeof(widget_t) * this->wend);
+
+ /* query the base parameters */
+ this->comresp(this, this->audiofunc, CORB_GET_PARAMETER,
+ COP_STREAM_FORMATS, &result);
+ this->w[this->audiofunc].d.audio.encodings = result;
+ this->comresp(this, this->audiofunc, CORB_GET_PARAMETER,
+ COP_PCM, &result);
+ this->w[this->audiofunc].d.audio.bits_rates = result;
+ this->comresp(this, this->audiofunc, CORB_GET_PARAMETER,
+ COP_INPUT_AMPCAP, &result);
+ this->w[this->audiofunc].inamp_cap = result;
+ this->comresp(this, this->audiofunc, CORB_GET_PARAMETER,
+ COP_OUTPUT_AMPCAP, &result);
+ this->w[this->audiofunc].outamp_cap = result;
+#ifdef AZALIA_DEBUG
+ azalia_widget_print_audio(&this->w[this->audiofunc], "\t");
+ result = this->w[this->audiofunc].inamp_cap;
+ DPRINTF(("\tinamp: mute=%u size=%u steps=%u offset=%u\n",
+ (result & COP_AMPCAP_MUTE) != 0, COP_AMPCAP_STEPSIZE(result),
+ COP_AMPCAP_NUMSTEPS(result), COP_AMPCAP_OFFSET(result)));
+ result = this->w[this->audiofunc].outamp_cap;
+ DPRINTF(("\toutamp: mute=%u size=%u steps=%u offset=%u\n",
+ (result & COP_AMPCAP_MUTE) != 0, COP_AMPCAP_STEPSIZE(result),
+ COP_AMPCAP_NUMSTEPS(result), COP_AMPCAP_OFFSET(result)));
+#endif
+
+ FOR_EACH_WIDGET(this, i) {
+ err = azalia_widget_init(&this->w[i], this, i);
+ if (err)
+ return err;
+ }
+
+ err = this->init_dacgroup(this);
+ if (err)
+ return err;
+#ifdef AZALIA_DEBUGGGG
+ for (i = 0; i < this->ndacgroups; i++) {
+ DPRINTF(("%s: dacgroup[%d]:", __func__, i));
+ for (n = 0; n < this->dacgroups[group].nconv; n++) {
+ DPRINTF((" %2.2x", this->dacgroups[group].conv[n]));
+ }
+ DPRINTF(("\n"));
+ }
+#endif
+ this->cur_dac = 0;
+ this->cur_adc = 0;
+
+ err = azalia_codec_construct_format(this);
+ if (err)
+ return err;
+
+ return azalia_mixer_init(this);
+}
+
+int
+azalia_codec_delete(codec_t *this)
+{
+ azalia_mixer_delete(this);
+ if (this->formats != NULL) {
+ free(this->formats, M_DEVBUF);
+ this->formats = NULL;
+ }
+ printf("delete_encodings...\n");
+ auconv_delete_encodings(this->encodings);
+ this->encodings = NULL;
+ return 0;
+}
+
+int
+azalia_codec_construct_format(codec_t *this)
+{
+ char flagbuf[FLAGBUFLEN];
+ const convgroup_t *group;
+ uint32_t bits_rates;
+ int pvariation, rvariation;
+ int nbits, dac, chan, i, err;
+ nid_t nid;
+
+ group = &this->dacgroups[this->cur_dac];
+ bits_rates = this->w[group->conv[0]].d.audio.bits_rates;
+ nbits = 0;
+ if (bits_rates & COP_PCM_B8)
+ nbits++;
+ if (bits_rates & COP_PCM_B16)
+ nbits++;
+ if (bits_rates & COP_PCM_B20)
+ nbits++;
+ if (bits_rates & COP_PCM_B24)
+ nbits++;
+ if (bits_rates & COP_PCM_B32)
+ nbits++;
+ if (nbits == 0) {
+ printf("%s: %s/%d invalid PCM format: 0x%8.8x\n",
+ XNAME(this->az), __FILE__, __LINE__, bits_rates);
+ return -1;
+ }
+ pvariation = group->nconv * nbits;
+
+ bits_rates = this->w[this->adcs[this->cur_adc]].d.audio.bits_rates;
+ nbits = 0;
+ if (bits_rates & COP_PCM_B8)
+ nbits++;
+ if (bits_rates & COP_PCM_B16)
+ nbits++;
+ if (bits_rates & COP_PCM_B20)
+ nbits++;
+ if (bits_rates & COP_PCM_B24)
+ nbits++;
+ if (bits_rates & COP_PCM_B32)
+ nbits++;
+ if (nbits == 0) {
+ printf("%s: %s/%d invalid PCM format: 0x%8.8x\n",
+ XNAME(this->az), __FILE__, __LINE__, bits_rates);
+#if 0
+ return -1;
+#endif
+ }
+ rvariation = nbits;
+
+ if (this->formats != NULL)
+ free(this->formats, M_DEVBUF);
+ this->nformats = 0;
+ this->formats = malloc(sizeof(struct audio_format) *
+ (pvariation + rvariation), M_DEVBUF, M_NOWAIT);
+ if (this->formats == NULL) {
+ printf("%s: out of memory in %s\n",
+ XNAME(this->az), __func__);
+ return ENOMEM;
+ }
+ bzero(this->formats, sizeof(struct audio_format) *
+ (pvariation + rvariation));
+
+ /* register formats for playback */
+ nid = group->conv[0];
+ chan = 0;
+ bits_rates = this->w[nid].d.audio.bits_rates;
+ for (dac = 0; dac < group->nconv; dac++) {
+ for (chan = 0, i = 0; i <= dac; i++)
+ chan += this->w[group->conv[dac]].widgetcap
+ & COP_AWCAP_STEREO ? 2 : 1;
+ azalia_codec_add_bits(this, chan, bits_rates, AUMODE_PLAY);
+ }
+ /* print playback capability */
+ snprintf(flagbuf, FLAGBUFLEN, "%s: playback: ", XNAME(this->az));
+ azalia_widget_print_audio(&this->w[nid], flagbuf);
+ if (this->w[group->conv[0]].widgetcap & COP_AWCAP_DIGITAL) {
+ printf("%s: playback: max channels=%d, DIGITAL\n",
+ XNAME(this->az), chan);
+ } else {
+ printf("%s: playback: max channels=%d\n",
+ XNAME(this->az), chan);
+ }
+
+ /* register formats for recording */
+ nid = this->adcs[this->cur_adc];
+ chan = this->w[nid].widgetcap & COP_AWCAP_STEREO ? 2 : 1;
+ bits_rates = this->w[nid].d.audio.bits_rates;
+ azalia_codec_add_bits(this, chan, bits_rates, AUMODE_RECORD);
+ /* print recording capability */
+ snprintf(flagbuf, FLAGBUFLEN, "%s: recording: ", XNAME(this->az));
+ azalia_widget_print_audio(&this->w[nid], flagbuf);
+ if (this->w[nid].widgetcap & COP_AWCAP_DIGITAL) {
+ printf("%s: recording: max channels=%d, DIGITAL\n",
+ XNAME(this->az), chan);
+ } else {
+ printf("%s: recording: max channels=%d\n",
+ XNAME(this->az), chan);
+ }
+
+ printf("create encodings...\n");
+ err = azalia_create_encodings(this->formats, this->nformats,
+ &this->encodings);
+ if (err)
+ return err;
+ return 0;
+}
+
+void
+azalia_codec_add_bits(codec_t *this, int chan, uint32_t bits_rates, int mode)
+{
+ if (bits_rates & COP_PCM_B8)
+ azalia_codec_add_format(this, chan, 8, 16, bits_rates, mode);
+ if (bits_rates & COP_PCM_B16)
+ azalia_codec_add_format(this, chan, 16, 16, bits_rates, mode);
+ if (bits_rates & COP_PCM_B20)
+ azalia_codec_add_format(this, chan, 20, 32, bits_rates, mode);
+ if (bits_rates & COP_PCM_B24)
+ azalia_codec_add_format(this, chan, 24, 32, bits_rates, mode);
+ if (bits_rates & COP_PCM_B32)
+ azalia_codec_add_format(this, chan, 32, 32, bits_rates, mode);
+}
+
+void
+azalia_codec_add_format(codec_t *this, int chan, int valid, int prec,
+ uint32_t rates, int32_t mode)
+{
+ struct audio_format *f;
+
+ f = &this->formats[this->nformats++];
+ f->mode = mode;
+ f->encoding = AUDIO_ENCODING_SLINEAR_LE;
+ if (valid == 8 && prec == 8)
+ f->encoding = AUDIO_ENCODING_ULINEAR_LE;
+ f->validbits = valid;
+ f->precision = prec;
+ f->channels = chan;
+ switch (chan) {
+ case 1:
+ f->channel_mask = AUFMT_MONAURAL;
+ break;
+ case 2:
+ f->channel_mask = AUFMT_STEREO;
+ break;
+ case 4:
+ f->channel_mask = AUFMT_SURROUND4;
+ break;
+ case 6:
+ f->channel_mask = AUFMT_DOLBY_5_1;
+ break;
+ case 8:
+ f->channel_mask = AUFMT_DOLBY_5_1
+ | AUFMT_SIDE_LEFT | AUFMT_SIDE_RIGHT;
+ break;
+ default:
+ f->channel_mask = 0;
+ }
+ if (rates & COP_PCM_R80)
+ f->frequency[f->frequency_type++] = 8000;
+ if (rates & COP_PCM_R110)
+ f->frequency[f->frequency_type++] = 11025;
+ if (rates & COP_PCM_R160)
+ f->frequency[f->frequency_type++] = 16000;
+ if (rates & COP_PCM_R220)
+ f->frequency[f->frequency_type++] = 22050;
+ if (rates & COP_PCM_R320)
+ f->frequency[f->frequency_type++] = 32000;
+ if (rates & COP_PCM_R441)
+ f->frequency[f->frequency_type++] = 44100;
+ if (rates & COP_PCM_R480)
+ f->frequency[f->frequency_type++] = 48000;
+ if (rates & COP_PCM_R882)
+ f->frequency[f->frequency_type++] = 88200;
+ if (rates & COP_PCM_R960)
+ f->frequency[f->frequency_type++] = 96000;
+ if (rates & COP_PCM_R1764)
+ f->frequency[f->frequency_type++] = 176400;
+ if (rates & COP_PCM_R1920)
+ f->frequency[f->frequency_type++] = 192000;
+ if (rates & COP_PCM_R3840)
+ f->frequency[f->frequency_type++] = 384000;
+}
+
+int
+azalia_codec_comresp(const codec_t *codec, nid_t nid, uint32_t control,
+ uint32_t param, uint32_t* result)
+{
+ int err;
+
+ err = azalia_set_command(codec->az, codec->address, nid, control, param);
+ if (err)
+ return err;
+ return azalia_get_response(codec->az, result);
+}
+
+int
+azalia_codec_connect_stream(codec_t *this, int dir, uint16_t fmt, int number)
+{
+ const convgroup_t *group;
+ int i, err, startchan, nchan;
+ nid_t nid;
+ boolean_t flag222;
+
+ DPRINTF(("%s: fmt=0x%4.4x number=%d\n", __func__, fmt, number));
+ err = 0;
+ if (dir == AUMODE_RECORD) {
+ nid = this->adcs[this->cur_adc];
+ DPRINTF(("%s: record: nid=0x%.2x\n", __func__, nid));
+ err = this->comresp(this, nid, CORB_SET_CONVERTER_FORMAT, fmt, NULL);
+ if (err)
+ goto exit;
+ err = this->comresp(this, nid, CORB_SET_CONVERTER_STREAM_CHANNEL,
+ (number << 4) | 0, NULL);
+ goto exit;
+ }
+ group = &this->dacgroups[this->cur_dac];
+ flag222 = group->nconv >= 3 &&
+ (this->w[group->conv[0]].widgetcap & COP_AWCAP_STEREO) &&
+ (this->w[group->conv[1]].widgetcap & COP_AWCAP_STEREO) &&
+ (this->w[group->conv[2]].widgetcap & COP_AWCAP_STEREO);
+ nchan = (fmt & HDA_SD_FMT_CHAN) + 1;
+ startchan = 0;
+ for (i = 0; i < group->nconv; i++) {
+ nid = group->conv[i];
+
+ /* surround and c/lfe handling */
+ if (nchan >= 6 && flag222 && i == 1) {
+ nid = group->conv[2];
+ } else if (nchan >= 6 && flag222 && i == 2) {
+ nid = group->conv[1];
+ }
+
+ err = this->comresp(this, nid, CORB_SET_CONVERTER_FORMAT, fmt, NULL);
+ if (err)
+ goto exit;
+ err = this->comresp(this, nid, CORB_SET_CONVERTER_STREAM_CHANNEL,
+ (number << 4) | startchan, NULL);
+ if (err)
+ goto exit;
+ startchan += this->w[nid].widgetcap & COP_AWCAP_STEREO ? 2 : 1;
+ }
+
+exit:
+ DPRINTF(("%s: leave with %d\n", __func__, err));
+ return err;
+}
+
+/* ================================================================
+ * HDA mixer functions
+ * ================================================================ */
+
+int
+azalia_mixer_init(codec_t *this)
+{
+ /*
+ * pin "<color>%2.2x"
+ * audio output "dac%2.2x"
+ * audio input "adc%2.2x"
+ * mixer "mixer%2.2x"
+ * selector "sel%2.2x"
+ */
+ mixer_item_t *m;
+ int nadcs;
+ int err, i, j;
+
+ nadcs = 0;
+ this->maxmixers = 10;
+ this->nmixers = 0;
+ this->mixers = malloc(sizeof(mixer_item_t) * this->maxmixers,
+ M_DEVBUF, M_NOWAIT);
+ if (this->mixers == NULL) {
+ printf("%s: out of memory in %s\n", XNAME(this->az),
+ __func__);
+ return ENOMEM;
+ }
+ bzero(this->mixers, sizeof(mixer_item_t) * this->maxmixers);
+
+ /* register classes */
+#define AZ_CLASS_INPUT 0
+#define AZ_CLASS_OUTPUT 1
+#define AZ_CLASS_RECORD 2
+ m = &this->mixers[AZ_CLASS_INPUT];
+ m->devinfo.index = AZ_CLASS_INPUT;
+ strlcpy(m->devinfo.label.name, AudioCinputs,
+ sizeof(m->devinfo.label.name));
+ m->devinfo.type = AUDIO_MIXER_CLASS;
+ m->devinfo.mixer_class = AZ_CLASS_INPUT;
+ m->devinfo.next = AUDIO_MIXER_LAST;
+ m->devinfo.prev = AUDIO_MIXER_LAST;
+ m->nid = 0;
+
+ m = &this->mixers[AZ_CLASS_OUTPUT];
+ m->devinfo.index = AZ_CLASS_OUTPUT;
+ strlcpy(m->devinfo.label.name, AudioCoutputs,
+ sizeof(m->devinfo.label.name));
+ m->devinfo.type = AUDIO_MIXER_CLASS;
+ m->devinfo.mixer_class = AZ_CLASS_OUTPUT;
+ m->devinfo.next = AUDIO_MIXER_LAST;
+ m->devinfo.prev = AUDIO_MIXER_LAST;
+ m->nid = 0;
+
+ m = &this->mixers[AZ_CLASS_RECORD];
+ m->devinfo.index = AZ_CLASS_RECORD;
+ strlcpy(m->devinfo.label.name, AudioCrecord,
+ sizeof(m->devinfo.label.name));
+ m->devinfo.type = AUDIO_MIXER_CLASS;
+ m->devinfo.mixer_class = AZ_CLASS_RECORD;
+ m->devinfo.next = AUDIO_MIXER_LAST;
+ m->devinfo.prev = AUDIO_MIXER_LAST;
+ m->nid = 0;
+
+ this->nmixers = AZ_CLASS_RECORD + 1;
+
+#define MIXER_REG_PROLOG \
+ mixer_devinfo_t *d; \
+ err = azalia_mixer_ensure_capacity(this, this->nmixers + 1); \
+ if (err) \
+ return err; \
+ m = &this->mixers[this->nmixers]; \
+ d = &m->devinfo; \
+ d->index = this->nmixers; \
+ m->nid = i
+
+ FOR_EACH_WIDGET(this, i) {
+ const widget_t *w;
+
+ w = &this->w[i];
+
+ if (w->type == COP_AWTYPE_AUDIO_INPUT)
+ nadcs++;
+
+ /* selector */
+ if (w->type != COP_AWTYPE_AUDIO_MIXER && w->nconnections >= 2) {
+ MIXER_REG_PROLOG;
+ snprintf(d->label.name, sizeof(d->label.name),
+ "%s.source", w->name);
+ d->type = AUDIO_MIXER_ENUM;
+ if (w->type == COP_AWTYPE_AUDIO_MIXER)
+ d->mixer_class = AZ_CLASS_RECORD;
+ else if (w->type == COP_AWTYPE_AUDIO_SELECTOR)
+ d->mixer_class = AZ_CLASS_INPUT;
+ else
+ d->mixer_class = AZ_CLASS_OUTPUT;
+ d->next = AUDIO_MIXER_LAST;
+ d->prev = AUDIO_MIXER_LAST;
+ m->target = MI_TARGET_CONNLIST;
+ for (j = 0; j < w->nconnections && j < 32; j++) {
+ d->un.e.member[j].ord = j;
+ strlcpy(d->un.e.member[j].label.name,
+ this->w[w->connections[j]].name,
+ MAX_AUDIO_DEV_LEN);
+ }
+ d->un.e.num_mem = j;
+ this->nmixers++;
+ }
+
+ /* output mute */
+ if (w->widgetcap & COP_AWCAP_OUTAMP &&
+ w->outamp_cap & COP_AMPCAP_MUTE) {
+ MIXER_REG_PROLOG;
+ snprintf(d->label.name, sizeof(d->label.name),
+ "%s.mute", w->name);
+ d->type = AUDIO_MIXER_ENUM;
+ if (w->type == COP_AWTYPE_AUDIO_MIXER)
+ d->mixer_class = AZ_CLASS_OUTPUT;
+ else if (w->type == COP_AWTYPE_AUDIO_SELECTOR)
+ d->mixer_class = AZ_CLASS_OUTPUT;
+ else if (w->type == COP_AWTYPE_PIN_COMPLEX)
+ d->mixer_class = AZ_CLASS_OUTPUT;
+ else
+ d->mixer_class = AZ_CLASS_INPUT;
+ d->next = AUDIO_MIXER_LAST;
+ d->prev = AUDIO_MIXER_LAST;
+ m->target = MI_TARGET_OUTAMP;
+ d->un.e.num_mem = 2;
+ d->un.e.member[0].ord = 0;
+ strlcpy(d->un.e.member[0].label.name, AudioNoff,
+ MAX_AUDIO_DEV_LEN);
+ d->un.e.member[1].ord = 1;
+ strlcpy(d->un.e.member[1].label.name, AudioNon,
+ MAX_AUDIO_DEV_LEN);
+ this->nmixers++;
+ }
+
+ /* output gain */
+ if (w->widgetcap & COP_AWCAP_OUTAMP
+ && COP_AMPCAP_NUMSTEPS(w->outamp_cap)) {
+ MIXER_REG_PROLOG;
+ snprintf(d->label.name, sizeof(d->label.name),
+ "%s", w->name);
+ d->type = AUDIO_MIXER_VALUE;
+ if (w->type == COP_AWTYPE_AUDIO_MIXER)
+ d->mixer_class = AZ_CLASS_OUTPUT;
+ else if (w->type == COP_AWTYPE_AUDIO_SELECTOR)
+ d->mixer_class = AZ_CLASS_OUTPUT;
+ else if (w->type == COP_AWTYPE_PIN_COMPLEX)
+ d->mixer_class = AZ_CLASS_OUTPUT;
+ else
+ d->mixer_class = AZ_CLASS_INPUT;
+ d->next = AUDIO_MIXER_LAST;
+ d->prev = AUDIO_MIXER_LAST;
+ m->target = MI_TARGET_OUTAMP;
+ d->un.v.num_channels = w->widgetcap & COP_AWCAP_STEREO
+ ? 2 : 1;
+#ifdef MAX_VOLUME_255
+ d->un.v.units.name[0] = 0;
+ d->un.v.delta = AUDIO_MAX_GAIN /
+ COP_AMPCAP_NUMSTEPS(w->outamp_cap);
+#else
+ snprintf(d->un.v.units.name, sizeof(d->un.v.units.name),
+ "0.25x%ddB", COP_AMPCAP_STEPSIZE(w->outamp_cap)+1);
+ d->un.v.delta = 1;
+#endif
+ this->nmixers++;
+ }
+
+ /* input mute */
+ if (w->widgetcap & COP_AWCAP_INAMP &&
+ w->inamp_cap & COP_AMPCAP_MUTE) {
+ for (j = 0; j < w->nconnections; j++) {
+ MIXER_REG_PROLOG;
+ snprintf(d->label.name, sizeof(d->label.name),
+ "%s.%s.mute", w->name,
+ this->w[w->connections[j]].name);
+ d->type = AUDIO_MIXER_ENUM;
+ if (w->type == COP_AWTYPE_PIN_COMPLEX)
+ d->mixer_class = AZ_CLASS_OUTPUT;
+ else if (w->type == COP_AWTYPE_AUDIO_INPUT)
+ d->mixer_class = AZ_CLASS_RECORD;
+ else
+ d->mixer_class = AZ_CLASS_INPUT;
+ d->next = AUDIO_MIXER_LAST;
+ d->prev = AUDIO_MIXER_LAST;
+ m->target = j;
+ d->un.e.num_mem = 2;
+ d->un.e.member[0].ord = 0;
+ strlcpy(d->un.e.member[0].label.name,
+ AudioNoff, MAX_AUDIO_DEV_LEN);
+ d->un.e.member[1].ord = 1;
+ strlcpy(d->un.e.member[1].label.name,
+ AudioNon, MAX_AUDIO_DEV_LEN);
+ this->nmixers++;
+ }
+ }
+
+ /* input gain */
+ if (w->widgetcap & COP_AWCAP_INAMP
+ && COP_AMPCAP_NUMSTEPS(w->inamp_cap)) {
+ for (j = 0; j < w->nconnections; j++) {
+ MIXER_REG_PROLOG;
+ snprintf(d->label.name, sizeof(d->label.name),
+ "%s.%s", w->name,
+ this->w[w->connections[j]].name);
+ d->type = AUDIO_MIXER_VALUE;
+ if (w->type == COP_AWTYPE_PIN_COMPLEX)
+ d->mixer_class = AZ_CLASS_OUTPUT;
+ else if (w->type == COP_AWTYPE_AUDIO_INPUT)
+ d->mixer_class = AZ_CLASS_RECORD;
+ else
+ d->mixer_class = AZ_CLASS_INPUT;
+ d->next = AUDIO_MIXER_LAST;
+ d->prev = AUDIO_MIXER_LAST;
+ m->target = j;
+ d->un.v.num_channels =
+ w->widgetcap & COP_AWCAP_STEREO ? 2 : 1;
+#ifdef MAX_VOLUME_255
+ d->un.v.units.name[0] = 0;
+ d->un.v.delta = AUDIO_MAX_GAIN /
+ COP_AMPCAP_NUMSTEPS(w->inamp_cap);
+#else
+ snprintf(d->un.v.units.name,
+ sizeof(d->un.v.units.name), "0.25x%ddB",
+ COP_AMPCAP_STEPSIZE(w->inamp_cap)+1);
+ d->un.v.delta = 1;
+#endif
+ this->nmixers++;
+ }
+ }
+
+ /* pin direction */
+ if (w->type == COP_AWTYPE_PIN_COMPLEX &&
+ w->d.pin.cap & COP_PINCAP_OUTPUT &&
+ w->d.pin.cap & COP_PINCAP_INPUT) {
+ MIXER_REG_PROLOG;
+ snprintf(d->label.name, sizeof(d->label.name),
+ "%s.dir", w->name);
+ d->type = AUDIO_MIXER_ENUM;
+ d->mixer_class = AZ_CLASS_OUTPUT;
+ d->next = AUDIO_MIXER_LAST;
+ d->prev = AUDIO_MIXER_LAST;
+ m->target = MI_TARGET_PINDIR;
+ d->un.e.num_mem = 2;
+ d->un.e.member[0].ord = 0;
+ strlcpy(d->un.e.member[0].label.name, AudioNinput,
+ MAX_AUDIO_DEV_LEN);
+ d->un.e.member[1].ord = 1;
+ strlcpy(d->un.e.member[1].label.name, AudioNoutput,
+ MAX_AUDIO_DEV_LEN);
+ this->nmixers++;
+ }
+
+ /* pin headphone-boost */
+ if (w->type == COP_AWTYPE_PIN_COMPLEX &&
+ w->d.pin.cap & COP_PINCAP_HEADPHONE) {
+ MIXER_REG_PROLOG;
+ snprintf(d->label.name, sizeof(d->label.name),
+ "%s.boost", w->name);
+ d->type = AUDIO_MIXER_ENUM;
+ d->mixer_class = AZ_CLASS_OUTPUT;
+ d->next = AUDIO_MIXER_LAST;
+ d->prev = AUDIO_MIXER_LAST;
+ m->target = MI_TARGET_PINBOOST;
+ d->un.e.num_mem = 2;
+ d->un.e.member[0].ord = 0;
+ strlcpy(d->un.e.member[0].label.name, AudioNoff,
+ MAX_AUDIO_DEV_LEN);
+ d->un.e.member[1].ord = 1;
+ strlcpy(d->un.e.member[1].label.name, AudioNon,
+ MAX_AUDIO_DEV_LEN);
+ this->nmixers++;
+ }
+
+ /* volume knob */
+ if (w->type == COP_AWTYPE_VOLUME_KNOB &&
+ w->d.volume.cap & COP_VKCAP_DELTA) {
+ MIXER_REG_PROLOG;
+ strlcpy(d->label.name, w->name, sizeof(d->label.name));
+ d->type = AUDIO_MIXER_VALUE;
+ d->mixer_class = AZ_CLASS_OUTPUT;
+ d->next = AUDIO_MIXER_LAST;
+ d->prev = AUDIO_MIXER_LAST;
+ m->target = MI_TARGET_VOLUME;
+ d->un.v.num_channels = 1;
+ d->un.v.units.name[0] = 0;
+#ifdef MAX_VOLUME_255
+ d->un.v.delta = AUDIO_MAX_GAIN /
+ COP_VKCAP_NUMSTEPS(w->d.volume.cap);
+#else
+ d->un.v.delta = 1;
+#endif
+ this->nmixers++;
+ }
+ }
+
+ /* if the codec has multiple DAC groups, create "inputs.usingdac" */
+ if (this->ndacgroups > 1) {
+ MIXER_REG_PROLOG;
+ strlcpy(d->label.name, "usingdac", sizeof(d->label.name));
+ d->type = AUDIO_MIXER_ENUM;
+ d->mixer_class = AZ_CLASS_INPUT;
+ d->next = AUDIO_MIXER_LAST;
+ d->prev = AUDIO_MIXER_LAST;
+ m->target = MI_TARGET_DAC;
+ for (i = 0; i < this->ndacgroups && i < 32; i++) {
+ d->un.e.member[i].ord = i;
+ for (j = 0; j < this->dacgroups[i].nconv; j++) {
+ if (j * 2 >= MAX_AUDIO_DEV_LEN)
+ break;
+ snprintf(d->un.e.member[i].label.name + j*2,
+ MAX_AUDIO_DEV_LEN - j*2, "%2.2x",
+ this->dacgroups[i].conv[j]);
+ }
+ }
+ d->un.e.num_mem = i;
+ this->nmixers++;
+ }
+
+ /* if the codec has multiple ADCs, create "record.usingadc" */
+ if (this->nadcs > 1) {
+ MIXER_REG_PROLOG;
+ strlcpy(d->label.name, "usingadc", sizeof(d->label.name));
+ d->type = AUDIO_MIXER_ENUM;
+ d->mixer_class = AZ_CLASS_RECORD;
+ d->next = AUDIO_MIXER_LAST;
+ d->prev = AUDIO_MIXER_LAST;
+ m->target = MI_TARGET_ADC;
+ for (i = 0; i < this->nadcs && i < 32; i++) {
+ d->un.e.member[i].ord = i;
+ strlcpy(d->un.e.member[i].label.name,
+ this->w[this->adcs[i]].name, MAX_AUDIO_DEV_LEN);
+ }
+ d->un.e.num_mem = i;
+ this->nmixers++;
+ }
+
+ /* unmute all */
+ for (i = 0; i < this->nmixers; i++) {
+ mixer_ctrl_t mc;
+
+ if (!IS_MI_TARGET_INAMP(this->mixers[i].target) &&
+ this->mixers[i].target != MI_TARGET_OUTAMP)
+ continue;
+ if (this->mixers[i].devinfo.type != AUDIO_MIXER_ENUM)
+ continue;
+ mc.dev = i;
+ mc.type = AUDIO_MIXER_ENUM;
+ mc.un.ord = 0;
+ azalia_mixer_set(this, &mc);
+ }
+
+ /*
+ * for bidirectional pins,
+ * green=front, orange=surround, gray=c/lfe, black=side --> output
+ * blue=line-in, pink=mic-in --> input
+ */
+ for (i = 0; i < this->nmixers; i++) {
+ mixer_ctrl_t mc;
+
+ if (this->mixers[i].target != MI_TARGET_PINDIR)
+ continue;
+ mc.dev = i;
+ mc.type = AUDIO_MIXER_ENUM;
+ switch (this->w[this->mixers[i].nid].d.pin.color) {
+ case CORB_CD_GREEN:
+ case CORB_CD_ORANGE:
+ case CORB_CD_GRAY:
+ case CORB_CD_BLACK:
+ mc.un.ord = 1;
+ break;
+ default:
+ mc.un.ord = 0;
+ }
+ azalia_mixer_set(this, &mc);
+ }
+
+ /* set unextreme volume */
+ for (i = 0; i < this->nmixers; i++) {
+ mixer_ctrl_t mc;
+
+ if (!IS_MI_TARGET_INAMP(this->mixers[i].target) &&
+ this->mixers[i].target != MI_TARGET_OUTAMP &&
+ this->mixers[i].target != MI_TARGET_VOLUME)
+ continue;
+ if (this->mixers[i].devinfo.type != AUDIO_MIXER_VALUE)
+ continue;
+ mc.dev = i;
+ mc.type = AUDIO_MIXER_VALUE;
+ mc.un.value.num_channels = 1;
+ mc.un.value.level[0] = AUDIO_MAX_GAIN / 2;
+ if (this->mixers[i].target != MI_TARGET_VOLUME &&
+ this->w[this->mixers[i].nid].widgetcap & COP_AWCAP_STEREO) {
+ mc.un.value.num_channels = 2;
+ mc.un.value.level[1] = AUDIO_MAX_GAIN / 2;
+ }
+ azalia_mixer_set(this, &mc);
+ }
+
+ return 0;
+}
+
+int
+azalia_mixer_delete(codec_t *this)
+{
+ if (this->mixers == NULL)
+ return 0;
+ free(this->mixers, M_DEVBUF);
+ this->mixers = NULL;
+ return 0;
+}
+
+int
+azalia_mixer_get(const codec_t *this, mixer_ctrl_t *mc)
+{
+ const mixer_item_t *m;
+ uint32_t result;
+ int err;
+
+ if (mc->dev >= this->nmixers)
+ return ENXIO;
+ m = &this->mixers[mc->dev];
+ mc->type = m->devinfo.type;
+ if (mc->type == AUDIO_MIXER_CLASS)
+ return 0; /* nothing to do */
+
+ /* inamp mute */
+ if (IS_MI_TARGET_INAMP(m->target) && m->devinfo.type == AUDIO_MIXER_ENUM) {
+ err = this->comresp(this, m->nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
+ CORB_GAGM_INPUT | CORB_GAGM_LEFT |
+ MI_TARGET_INAMP(m->target), &result);
+ if (err)
+ return err;
+ mc->un.ord = result & CORB_GAGM_MUTE ? 1 : 0;
+ }
+
+ /* inamp gain */
+ else if (IS_MI_TARGET_INAMP(m->target) && m->devinfo.type == AUDIO_MIXER_VALUE) {
+ err = this->comresp(this, m->nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
+ CORB_GAGM_INPUT | CORB_GAGM_LEFT |
+ MI_TARGET_INAMP(m->target), &result);
+ if (err)
+ return err;
+ mc->un.value.level[0] = azalia_mixer_from_device_value(this, m,
+ CORB_GAGM_GAIN(result));
+ if (this->w[m->nid].widgetcap & COP_AWCAP_STEREO) {
+ err = this->comresp(this, m->nid,
+ CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
+ CORB_GAGM_RIGHT | MI_TARGET_INAMP(m->target),
+ &result);
+ if (err)
+ return err;
+ mc->un.value.level[1] = azalia_mixer_from_device_value
+ (this, m, CORB_GAGM_GAIN(result));
+ mc->un.value.num_channels = 2;
+ } else {
+ mc->un.value.num_channels = 1;
+ }
+ }
+
+ /* outamp mute */
+ else if (m->target == MI_TARGET_OUTAMP && m->devinfo.type == AUDIO_MIXER_ENUM) {
+ err = this->comresp(this, m->nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
+ CORB_GAGM_OUTPUT | CORB_GAGM_LEFT | 0, &result);
+ if (err)
+ return err;
+ mc->un.ord = result & CORB_GAGM_MUTE ? 1 : 0;
+ }
+
+ /* outamp gain */
+ else if (m->target == MI_TARGET_OUTAMP && m->devinfo.type == AUDIO_MIXER_VALUE) {
+ err = this->comresp(this, m->nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
+ CORB_GAGM_OUTPUT | CORB_GAGM_LEFT | 0, &result);
+ if (err)
+ return err;
+ mc->un.value.level[0] = azalia_mixer_from_device_value(this, m,
+ CORB_GAGM_GAIN(result));
+ if (this->w[m->nid].widgetcap & COP_AWCAP_STEREO) {
+ err = this->comresp(this, m->nid,
+ CORB_GET_AMPLIFIER_GAIN_MUTE,
+ CORB_GAGM_OUTPUT | CORB_GAGM_RIGHT | 0, &result);
+ if (err)
+ return err;
+ mc->un.value.level[1] = azalia_mixer_from_device_value
+ (this, m, CORB_GAGM_GAIN(result));
+ mc->un.value.num_channels = 2;
+ } else {
+ mc->un.value.num_channels = 1;
+ }
+ }
+
+ /* selection */
+ else if (m->target == MI_TARGET_CONNLIST) {
+ err = this->comresp(this, m->nid,
+ CORB_GET_CONNECTION_SELECT_CONTROL, 0, &result);
+ if (err)
+ return err;
+ mc->un.ord = CORB_CSC_INDEX(result);
+ }
+
+ /* pin I/O */
+ else if (m->target == MI_TARGET_PINDIR) {
+ err = this->comresp(this, m->nid,
+ CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
+ if (err)
+ return err;
+ mc->un.ord = result & CORB_PWC_OUTPUT ? 1 : 0;
+ }
+
+ /* pin headphone-boost */
+ else if (m->target == MI_TARGET_PINBOOST) {
+ err = this->comresp(this, m->nid,
+ CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
+ if (err)
+ return err;
+ mc->un.ord = result & CORB_PWC_HEADPHONE ? 1 : 0;
+ }
+
+ /* DAC group selection */
+ else if (m->target == MI_TARGET_DAC) {
+ mc->un.ord = this->cur_dac;
+ }
+
+ /* ADC selection */
+ else if (m->target == MI_TARGET_ADC) {
+ mc->un.ord = this->cur_adc;
+ }
+
+ /* Volume knob */
+ else if (m->target == MI_TARGET_VOLUME) {
+ err = this->comresp(this, m->nid, CORB_GET_VOLUME_KNOB,
+ 0, &result);
+ if (err)
+ return err;
+ mc->un.value.level[0] = azalia_mixer_from_device_value(this, m,
+ CORB_VKNOB_VOLUME(result));
+ mc->un.value.num_channels = 1;
+ }
+
+ else {
+ printf("%s: internal error in %s: %x\n", XNAME(this->az),
+ __func__, m->target);
+ return -1;
+ }
+ return 0;
+}
+
+int
+azalia_mixer_set(codec_t *this, const mixer_ctrl_t *mc)
+{
+ const mixer_item_t *m;
+ uint32_t result, value;
+ int err;
+
+ if (mc->dev >= this->nmixers)
+ return ENXIO;
+ m = &this->mixers[mc->dev];
+ if (mc->type != m->devinfo.type)
+ return EINVAL;
+ if (mc->type == AUDIO_MIXER_CLASS)
+ return 0; /* nothing to do */
+
+ /* inamp mute */
+ if (IS_MI_TARGET_INAMP(m->target) && m->devinfo.type == AUDIO_MIXER_ENUM) {
+ /* We have to set stereo mute separately to keep each gain value. */
+ err = this->comresp(this, m->nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
+ CORB_GAGM_INPUT | CORB_GAGM_LEFT |
+ MI_TARGET_INAMP(m->target), &result);
+ if (err)
+ return err;
+ value = CORB_AGM_INPUT | CORB_AGM_LEFT |
+ (m->target << CORB_AGM_INDEX_SHIFT) |
+ CORB_GAGM_GAIN(result);
+ if (mc->un.ord)
+ value |= CORB_AGM_MUTE;
+ err = this->comresp(this, m->nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
+ value, &result);
+ if (err)
+ return err;
+ if (this->w[m->nid].widgetcap & COP_AWCAP_STEREO) {
+ err = this->comresp(this, m->nid,
+ CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
+ CORB_GAGM_RIGHT | MI_TARGET_INAMP(m->target),
+ &result);
+ if (err)
+ return err;
+ value = CORB_AGM_INPUT | CORB_AGM_RIGHT |
+ (m->target << CORB_AGM_INDEX_SHIFT) |
+ CORB_GAGM_GAIN(result);
+ if (mc->un.ord)
+ value |= CORB_AGM_MUTE;
+ err = this->comresp(this, m->nid,
+ CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
+ if (err)
+ return err;
+ }
+ }
+
+ /* inamp gain */
+ else if (IS_MI_TARGET_INAMP(m->target) && m->devinfo.type == AUDIO_MIXER_VALUE) {
+ if (mc->un.value.num_channels < 1)
+ return EINVAL;
+ if (!azalia_mixer_validate_value(this, m, mc->un.value.level[0]))
+ return EINVAL;
+ err = this->comresp(this, m->nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
+ CORB_GAGM_INPUT | CORB_GAGM_LEFT |
+ MI_TARGET_INAMP(m->target), &result);
+ if (err)
+ return err;
+ value = azalia_mixer_to_device_value(this, m,
+ mc->un.value.level[0]);
+ value = CORB_AGM_INPUT | CORB_AGM_LEFT |
+ (m->target << CORB_AGM_INDEX_SHIFT) |
+ (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
+ (value & CORB_AGM_GAIN_MASK);
+ err = this->comresp(this, m->nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
+ value, &result);
+ if (err)
+ return err;
+ if (mc->un.value.num_channels >= 2 &&
+ this->w[m->nid].widgetcap & COP_AWCAP_STEREO) {
+ if (!azalia_mixer_validate_value(this, m,
+ mc->un.value.level[1]))
+ return EINVAL;
+ err = this->comresp(this, m->nid,
+ CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT |
+ CORB_GAGM_RIGHT | MI_TARGET_INAMP(m->target),
+ &result);
+ if (err)
+ return err;
+ value = azalia_mixer_to_device_value(this, m,
+ mc->un.value.level[1]);
+ value = CORB_AGM_INPUT | CORB_AGM_RIGHT |
+ (m->target << CORB_AGM_INDEX_SHIFT) |
+ (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
+ (value & CORB_AGM_GAIN_MASK);
+ err = this->comresp(this, m->nid,
+ CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
+ if (err)
+ return err;
+ }
+ }
+
+ /* outamp mute */
+ else if (m->target == MI_TARGET_OUTAMP && m->devinfo.type == AUDIO_MIXER_ENUM) {
+ err = this->comresp(this, m->nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
+ CORB_GAGM_OUTPUT | CORB_GAGM_LEFT, &result);
+ if (err)
+ return err;
+ value = CORB_AGM_OUTPUT | CORB_AGM_LEFT | CORB_GAGM_GAIN(result);
+ if (mc->un.ord)
+ value |= CORB_AGM_MUTE;
+ err = this->comresp(this, m->nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
+ value, &result);
+ if (err)
+ return err;
+ if (this->w[m->nid].widgetcap & COP_AWCAP_STEREO) {
+ err = this->comresp(this, m->nid,
+ CORB_GET_AMPLIFIER_GAIN_MUTE,
+ CORB_GAGM_OUTPUT | CORB_GAGM_RIGHT, &result);
+ if (err)
+ return err;
+ value = CORB_AGM_OUTPUT | CORB_AGM_RIGHT |
+ CORB_GAGM_GAIN(result);
+ if (mc->un.ord)
+ value |= CORB_AGM_MUTE;
+ err = this->comresp(this, m->nid,
+ CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
+ if (err)
+ return err;
+ }
+ }
+
+ /* outamp gain */
+ else if (m->target == MI_TARGET_OUTAMP && m->devinfo.type == AUDIO_MIXER_VALUE) {
+ if (mc->un.value.num_channels < 1)
+ return EINVAL;
+ if (!azalia_mixer_validate_value(this, m, mc->un.value.level[0]))
+ return EINVAL;
+ err = this->comresp(this, m->nid, CORB_GET_AMPLIFIER_GAIN_MUTE,
+ CORB_GAGM_OUTPUT | CORB_GAGM_LEFT, &result);
+ if (err)
+ return err;
+ value = azalia_mixer_to_device_value(this, m,
+ mc->un.value.level[0]);
+ value = CORB_AGM_OUTPUT | CORB_AGM_LEFT |
+ (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
+ (value & CORB_AGM_GAIN_MASK);
+ err = this->comresp(this, m->nid, CORB_SET_AMPLIFIER_GAIN_MUTE,
+ value, &result);
+ if (err)
+ return err;
+ if (mc->un.value.num_channels >= 2 &&
+ this->w[m->nid].widgetcap & COP_AWCAP_STEREO) {
+ if (!azalia_mixer_validate_value(this, m,
+ mc->un.value.level[1]))
+ return EINVAL;
+ err = this->comresp(this, m->nid,
+ CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_OUTPUT |
+ CORB_GAGM_RIGHT, &result);
+ if (err)
+ return err;
+ value = azalia_mixer_to_device_value(this, m,
+ mc->un.value.level[1]);
+ value = CORB_AGM_OUTPUT | CORB_AGM_RIGHT |
+ (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) |
+ (value & CORB_AGM_GAIN_MASK);
+ err = this->comresp(this, m->nid,
+ CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result);
+ if (err)
+ return err;
+ }
+ }
+
+ /* selection */
+ else if (m->target == MI_TARGET_CONNLIST) {
+ if (mc->un.ord >= this->w[m->nid].nconnections)
+ return EINVAL;
+ err = this->comresp(this, m->nid,
+ CORB_SET_CONNECTION_SELECT_CONTROL, mc->un.ord, &result);
+ if (err)
+ return err;
+ }
+
+ /* pin I/O */
+ else if (m->target == MI_TARGET_PINDIR) {
+ if (mc->un.ord >= 2)
+ return EINVAL;
+ err = this->comresp(this, m->nid,
+ CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
+ if (err)
+ return err;
+ if (mc->un.ord == 0) {
+ result &= ~CORB_PWC_OUTPUT;
+ result |= CORB_PWC_INPUT;
+ } else {
+ result &= ~CORB_PWC_INPUT;
+ result |= CORB_PWC_OUTPUT;
+ }
+ err = this->comresp(this, m->nid,
+ CORB_SET_PIN_WIDGET_CONTROL, result, &result);
+ if (err)
+ return err;
+ }
+
+ /* pin headphone-boost */
+ else if (m->target == MI_TARGET_PINBOOST) {
+ if (mc->un.ord >= 2)
+ return EINVAL;
+ err = this->comresp(this, m->nid,
+ CORB_GET_PIN_WIDGET_CONTROL, 0, &result);
+ if (err)
+ return err;
+ if (mc->un.ord == 0) {
+ result &= ~CORB_PWC_HEADPHONE;
+ } else {
+ result |= CORB_PWC_HEADPHONE;
+ }
+ err = this->comresp(this, m->nid,
+ CORB_SET_PIN_WIDGET_CONTROL, result, &result);
+ if (err)
+ return err;
+ }
+
+ /* DAC group selection */
+ else if (m->target == MI_TARGET_DAC) {
+ if (this->az->running)
+ return EBUSY;
+ if (mc->un.ord >= this->ndacgroups)
+ return EINVAL;
+ this->cur_dac = mc->un.ord;
+ return azalia_codec_construct_format(this);
+ }
+
+ /* ADC selection */
+ else if (m->target == MI_TARGET_ADC) {
+ if (this->az->running)
+ return EBUSY;
+ if (mc->un.ord >= this->nadcs)
+ return EINVAL;
+ this->cur_adc = mc->un.ord;
+ /* use this->adcs[this->cur_adc] */
+ return azalia_codec_construct_format(this);
+ }
+
+ /* Volume knob */
+ else if (m->target == MI_TARGET_VOLUME) {
+ if (mc->un.value.num_channels != 1)
+ return EINVAL;
+ if (!azalia_mixer_validate_value(this, m, mc->un.value.level[0]))
+ return EINVAL;
+ value = azalia_mixer_to_device_value(this, m,
+ mc->un.value.level[0]) | CORB_VKNOB_DIRECT;
+ err = this->comresp(this, m->nid, CORB_SET_VOLUME_KNOB,
+ value, &result);
+ if (err)
+ return err;
+ }
+
+ else {
+ printf("%s: internal error in %s: %x\n", XNAME(this->az),
+ __func__, m->target);
+ return -1;
+ }
+ return 0;
+}
+
+int
+azalia_mixer_ensure_capacity(codec_t *this, size_t newsize)
+{
+ size_t newmax;
+ void *newbuf;
+
+ if (this->maxmixers >= newsize)
+ return 0;
+ newmax = this->maxmixers + 10;
+ if (newmax < newsize)
+ newmax = newsize;
+ newbuf = malloc(sizeof(mixer_item_t) * newmax, M_DEVBUF, M_NOWAIT);
+ if (newbuf == NULL) {
+ printf("%s: out of memory in %s\n", XNAME(this->az),
+ __func__);
+ return ENOMEM;
+ }
+ bzero(newbuf, sizeof(mixer_item_t) * newmax);
+ bcopy(this->mixers, newbuf, this->maxmixers * sizeof(mixer_item_t));
+ free(this->mixers, M_DEVBUF);
+ this->mixers = newbuf;
+ this->maxmixers = newmax;
+ return 0;
+}
+
+u_char
+azalia_mixer_from_device_value(const codec_t *this, const mixer_item_t *m,
+ uint32_t dv)
+{
+#ifdef MAX_VOLUME_255
+ uint32_t dmax;
+
+ if (IS_MI_TARGET_INAMP(m->target))
+ dmax = COP_AMPCAP_NUMSTEPS(this->w[m->nid].inamp_cap);
+ else if (m->target == MI_TARGET_OUTAMP)
+ dmax = COP_AMPCAP_NUMSTEPS(this->w[m->nid].outamp_cap);
+ else if (m->target == MI_TARGET_VOLUME)
+ dmax = COP_VKCAP_NUMSTEPS(this->w[m->nid].d.volume.cap);
+ else {
+ printf("unknown target: %d\n", m->target);
+ dmax = 255;
+ }
+ return dv * AUDIO_MAX_GAIN / dmax;
+#else
+ return dv;
+#endif
+}
+
+uint32_t
+azalia_mixer_to_device_value(const codec_t *this, const mixer_item_t *m,
+ u_char uv)
+{
+#ifdef MAX_VOLUME_255
+ uint32_t dmax;
+
+ if (IS_MI_TARGET_INAMP(m->target))
+ dmax = COP_AMPCAP_NUMSTEPS(this->w[m->nid].inamp_cap);
+ else if (m->target == MI_TARGET_OUTAMP)
+ dmax = COP_AMPCAP_NUMSTEPS(this->w[m->nid].outamp_cap);
+ else if (m->target == MI_TARGET_VOLUME)
+ dmax = COP_VKCAP_NUMSTEPS(this->w[m->nid].d.volume.cap);
+ else {
+ printf("unknown target: %d\n", m->target);
+ dmax = 255;
+ }
+ return uv * dmax / AUDIO_MAX_GAIN;
+#else
+ return uv;
+#endif
+}
+
+boolean_t
+azalia_mixer_validate_value(const codec_t *this, const mixer_item_t *m,
+ u_char uv)
+{
+#ifdef MAX_VOLUME_255
+ return TRUE;
+#else
+ uint32_t dmax;
+
+ if (IS_MI_TARGET_INAMP(m->target))
+ dmax = COP_AMPCAP_NUMSTEPS(this->w[m->nid].inamp_cap);
+ else if (m->target == MI_TARGET_OUTAMP)
+ dmax = COP_AMPCAP_NUMSTEPS(this->w[m->nid].outamp_cap);
+ else if (m->target == MI_TARGET_VOLUME)
+ dmax = COP_VKCAP_NUMSTEPS(this->w[m->nid].d.volume.cap);
+ return uv <= dmax;
+#endif
+}
+
+/* ================================================================
+ * HDA widget functions
+ * ================================================================ */
+
+#define WIDGETCAP_BITS \
+ "\20\014LRSWAP\013POWER\012DIGITAL" \
+ "\011CONNLIST\010UNSOL\07PROC\06STRIPE\05FORMATOV\04AMPOV\03OUTAMP" \
+ "\02INAMP\01STEREO"
+
+int
+azalia_widget_init(widget_t *this, const codec_t *codec, nid_t nid)
+{
+ uint32_t result;
+ int err;
+
+ err = codec->comresp(codec, nid, CORB_GET_PARAMETER,
+ COP_AUDIO_WIDGET_CAP, &result);
+ if (err)
+ return err;
+ this->nid = nid;
+ this->widgetcap = result;
+ this->type = COP_AWCAP_TYPE(result);
+ DPRINTF(("%s: ", XNAME(codec->az)));
+ if (this->widgetcap & COP_AWCAP_POWER) {
+ codec->comresp(codec, nid, CORB_SET_POWER_STATE, CORB_PS_D0, &result);
+ DELAY(100);
+ }
+ switch (this->type) {
+ case COP_AWTYPE_AUDIO_OUTPUT:
+ snprintf(this->name, sizeof(this->name), "dac%2.2x", nid);
+ DPRINTF(("%s wcap=%b\n", this->name,
+ this->widgetcap, WIDGETCAP_BITS));
+ azalia_widget_init_audio(this, codec);
+ break;
+ case COP_AWTYPE_AUDIO_INPUT:
+ snprintf(this->name, sizeof(this->name), "adc%2.2x", nid);
+ DPRINTF(("%s wcap=%b\n", this->name,
+ this->widgetcap, WIDGETCAP_BITS));
+ azalia_widget_init_audio(this, codec);
+ break;
+ case COP_AWTYPE_AUDIO_MIXER:
+ snprintf(this->name, sizeof(this->name), "mix%2.2x", nid);
+ DPRINTF(("%s wcap=%b\n", this->name,
+ this->widgetcap, WIDGETCAP_BITS));
+ break;
+ case COP_AWTYPE_AUDIO_SELECTOR:
+ snprintf(this->name, sizeof(this->name), "sel%2.2x", nid);
+ DPRINTF(("%s wcap=%b\n", this->name,
+ this->widgetcap, WIDGETCAP_BITS));
+ break;
+ case COP_AWTYPE_PIN_COMPLEX:
+ azalia_widget_init_pin(this, codec);
+ snprintf(this->name, sizeof(this->name), "%s%2.2x",
+ pin_colors[this->d.pin.color], nid);
+ DPRINTF(("%s wcap=%b\n", this->name,
+ this->widgetcap, WIDGETCAP_BITS));
+ azalia_widget_print_pin(this);
+ break;
+ case COP_AWTYPE_POWER:
+ snprintf(this->name, sizeof(this->name), "pow%2.2x", nid);
+ DPRINTF(("%s wcap=%b\n", this->name,
+ this->widgetcap, WIDGETCAP_BITS));
+ break;
+ case COP_AWTYPE_VOLUME_KNOB:
+ snprintf(this->name, sizeof(this->name), "volume%2.2x", nid);
+ DPRINTF(("%s wcap=%b\n", this->name,
+ this->widgetcap, WIDGETCAP_BITS));
+ err = codec->comresp(codec, nid, CORB_GET_PARAMETER,
+ COP_VOLUME_KNOB_CAPABILITIES, &result);
+ if (!err) {
+ this->d.volume.cap = result;
+ DPRINTF(("\tdelta=%d steps=%d\n",
+ !!(result & COP_VKCAP_DELTA),
+ COP_VKCAP_NUMSTEPS(result)));
+ }
+ break;
+ case COP_AWTYPE_BEEP_GENERATOR:
+ snprintf(this->name, sizeof(this->name), "beep%2.2x", nid);
+ DPRINTF(("%s wcap=%b\n", this->name,
+ this->widgetcap, WIDGETCAP_BITS));
+ break;
+ default:
+ snprintf(this->name, sizeof(this->name), "widget%2.2x", nid);
+ DPRINTF(("%s wcap=%b\n", this->name,
+ this->widgetcap, WIDGETCAP_BITS));
+ break;
+ }
+ azalia_widget_init_connection(this, codec);
+
+ /* amplifier information */
+ if (this->widgetcap & COP_AWCAP_INAMP) {
+ if (this->widgetcap & COP_AWCAP_AMPOV)
+ codec->comresp(codec, nid, CORB_GET_PARAMETER,
+ COP_INPUT_AMPCAP, &this->inamp_cap);
+ else
+ this->inamp_cap = codec->w[codec->audiofunc].inamp_cap;
+ DPRINTF(("\tinamp: mute=%u size=%u steps=%u offset=%u\n",
+ (this->inamp_cap & COP_AMPCAP_MUTE) != 0,
+ COP_AMPCAP_STEPSIZE(this->inamp_cap),
+ COP_AMPCAP_NUMSTEPS(this->inamp_cap),
+ COP_AMPCAP_OFFSET(this->inamp_cap)));
+ }
+ if (this->widgetcap & COP_AWCAP_OUTAMP) {
+ if (this->widgetcap & COP_AWCAP_AMPOV)
+ codec->comresp(codec, nid, CORB_GET_PARAMETER,
+ COP_OUTPUT_AMPCAP, &this->outamp_cap);
+ else
+ this->outamp_cap = codec->w[codec->audiofunc].outamp_cap;
+ DPRINTF(("\toutamp: mute=%u size=%u steps=%u offset=%u\n",
+ (this->outamp_cap & COP_AMPCAP_MUTE) != 0,
+ COP_AMPCAP_STEPSIZE(this->outamp_cap),
+ COP_AMPCAP_NUMSTEPS(this->outamp_cap),
+ COP_AMPCAP_OFFSET(this->outamp_cap)));
+ }
+ if (codec->init_widget != NULL)
+ codec->init_widget(codec, this, nid);
+ return 0;
+}
+
+int
+azalia_widget_init_audio(widget_t *this, const codec_t *codec)
+{
+ uint32_t result;
+ int err;
+
+ /* check audio format */
+ if (this->widgetcap & COP_AWCAP_FORMATOV) {
+ err = codec->comresp(codec, this->nid,
+ CORB_GET_PARAMETER, COP_STREAM_FORMATS, &result);
+ if (err)
+ return err;
+ this->d.audio.encodings = result;
+ if ((result & COP_STREAM_FORMAT_PCM) == 0) {
+ printf("%s: %s: No PCM support: %x\n",
+ XNAME(codec->az), this->name, result);
+ return -1;
+ }
+ err = codec->comresp(codec, this->nid, CORB_GET_PARAMETER,
+ COP_PCM, &result);
+ if (err)
+ return err;
+ this->d.audio.bits_rates = result;
+ } else {
+ this->d.audio.encodings =
+ codec->w[codec->audiofunc].d.audio.encodings;
+ this->d.audio.bits_rates =
+ codec->w[codec->audiofunc].d.audio.bits_rates;
+ }
+#ifdef AZALIA_DEBUG
+ azalia_widget_print_audio(this, "\t");
+#endif
+ return 0;
+}
+
+#define ENCODING_BITS "\20\3AC3\2FLOAT32\1PCM"
+#define BITSRATES_BITS "\20\x15""32bit\x14""24bit\x13""20bit" \
+ "\x12""16bit\x11""8bit""\x0c""384kHz\x0b""192kHz\x0a""176.4kHz" \
+ "\x09""96kHz\x08""88.2kHz\x07""48kHz\x06""44.1kHz\x05""32kHz\x04" \
+ "22.05kHz\x03""16kHz\x02""11.025kHz\x01""8kHz"
+
+int
+azalia_widget_print_audio(const widget_t *this, const char *lead)
+{
+ printf("%sencodings=%b\n", lead, this->d.audio.encodings,
+ ENCODING_BITS);
+ printf("%sPCM formats=%b\n", lead, this->d.audio.bits_rates,
+ BITSRATES_BITS);
+ return 0;
+}
+
+int
+azalia_widget_init_pin(widget_t *this, const codec_t *codec)
+{
+ uint32_t result;
+ int err;
+
+ err = codec->comresp(codec, this->nid, CORB_GET_CONFIGURATION_DEFAULT,
+ 0, &result);
+ if (err)
+ return err;
+ this->d.pin.config = result;
+ this->d.pin.sequence = CORB_CD_SEQUENCE(result);
+ this->d.pin.association = CORB_CD_ASSOCIATION(result);
+ this->d.pin.color = CORB_CD_COLOR(result);
+ this->d.pin.device = CORB_CD_DEVICE(result);
+
+ err = codec->comresp(codec, this->nid, CORB_GET_PARAMETER,
+ COP_PINCAP, &result);
+ if (err)
+ return err;
+ this->d.pin.cap = result;
+ return 0;
+}
+
+#define PINCAP_BITS "\20\021EAPD\07BALANCE\06INPUT" \
+ "\05OUTPUT\04HEADPHONE\03PRESENCE\02TRIGGER\01IMPEDANCE"
+
+int
+azalia_widget_print_pin(const widget_t *this)
+{
+ DPRINTF(("\tpin config; device=%s color=%s assoc=%d seq=%d",
+ pin_devices[this->d.pin.device], pin_colors[this->d.pin.color],
+ this->d.pin.association, this->d.pin.sequence));
+ DPRINTF((" cap=%b\n", this->d.pin.cap, PINCAP_BITS));
+ return 0;
+}
+
+int
+azalia_widget_init_connection(widget_t *this, const codec_t *codec)
+{
+ uint32_t result;
+ int err;
+ boolean_t longform;
+ int length, i;
+
+ this->selected = -1;
+ if ((this->widgetcap & COP_AWCAP_CONNLIST) == 0)
+ return 0;
+
+ err = codec->comresp(codec, this->nid, CORB_GET_PARAMETER,
+ COP_CONNECTION_LIST_LENGTH, &result);
+ if (err)
+ return err;
+ longform = (result & COP_CLL_LONG) != 0;
+ length = COP_CLL_LENGTH(result);
+ if (length == 0)
+ return 0;
+ this->nconnections = length;
+ this->connections = malloc(sizeof(nid_t) * length, M_DEVBUF, M_NOWAIT);
+ if (this->connections == NULL) {
+ printf("%s: out of memory\n", XNAME(codec->az));
+ return ENOMEM;
+ }
+ if (longform) {
+ for (i = 0; i < length; i += 2) {
+ err = codec->comresp(codec, this->nid,
+ CORB_GET_CONNECTION_LIST_ENTRY, i, &result);
+ if (err)
+ return err;
+ this->connections[i] = CORB_CLE_LONG_0(result);
+ this->connections[i+1] = CORB_CLE_LONG_1(result);
+ }
+ } else {
+ for (i = 0; i < length; i += 4) {
+ err = codec->comresp(codec, this->nid,
+ CORB_GET_CONNECTION_LIST_ENTRY, i, &result);
+ if (err)
+ return err;
+ this->connections[i] = CORB_CLE_SHORT_0(result);
+ this->connections[i+1] = CORB_CLE_SHORT_1(result);
+ this->connections[i+2] = CORB_CLE_SHORT_2(result);
+ this->connections[i+3] = CORB_CLE_SHORT_3(result);
+ }
+ }
+ if (length > 0) {
+ DPRINTF(("\tconnections=0x%x", this->connections[0]));
+ for (i = 1; i < length; i++) {
+ DPRINTF((",0x%x", this->connections[i]));
+ }
+
+ err = codec->comresp(codec, this->nid,
+ CORB_GET_CONNECTION_SELECT_CONTROL, 0, &result);
+ if (err)
+ return err;
+ this->selected = CORB_CSC_INDEX(result);
+ DPRINTF(("; selected=0x%x\n", this->connections[result]));
+
+ }
+ return 0;
+}
+
+/* ================================================================
+ * Stream functions
+ * ================================================================ */
+
+int
+azalia_stream_init(stream_t *this, azalia_t *az, int regindex, int strnum, int dir)
+{
+ int err;
+
+ this->az = az;
+ this->regbase = HDA_SD_BASE + regindex * HDA_SD_SIZE;
+ this->intr_bit = 1 << regindex;
+ this->number = strnum;
+ this->dir = dir;
+
+ /* setup BDL buffers */
+ err = azalia_alloc_dmamem(az, sizeof(bdlist_entry_t) * HDA_BDL_MAX,
+ 128, &this->bdlist);
+ if (err) {
+ printf("%s: can't allocate a BDL buffer\n", XNAME(az));
+ return err;
+ }
+ return 0;
+}
+
+int
+azalia_stream_delete(stream_t *this, azalia_t *az)
+{
+ if (this->bdlist.addr == NULL)
+ return 0;
+ azalia_free_dmamem(az, &this->bdlist);
+ return 0;
+}
+
+int
+azalia_stream_reset(stream_t *this)
+{
+ int i;
+ uint16_t ctl;
+
+ ctl = STR_READ_2(this, CTL);
+ STR_WRITE_2(this, CTL, ctl | HDA_SD_CTL_SRST);
+ for (i = 5000; i >= 0; i--) {
+ DELAY(10);
+ ctl = STR_READ_2(this, CTL);
+ if (ctl & HDA_SD_CTL_SRST)
+ break;
+ }
+ if (i <= 0) {
+ printf("%s: stream reset failure 1\n", XNAME(this->az));
+ return -1;
+ }
+ STR_WRITE_2(this, CTL, ctl & ~HDA_SD_CTL_SRST);
+ for (i = 5000; i >= 0; i--) {
+ DELAY(10);
+ ctl = STR_READ_2(this, CTL);
+ if ((ctl & HDA_SD_CTL_SRST) == 0)
+ break;
+ }
+ if (i <= 0) {
+ printf("%s: stream reset failure 2\n", XNAME(this->az));
+ return -1;
+ }
+ return 0;
+}
+
+int
+azalia_stream_start(stream_t *this, void *start, void *end, int blk,
+ void (*intr)(void *), void *arg, uint16_t fmt)
+{
+ bdlist_entry_t *bdlist;
+ bus_addr_t dmaaddr;
+ int err, index;
+ uint16_t ctl;
+ uint8_t ctl2, intctl;
+
+ this->intr = intr;
+ this->intr_arg = arg;
+
+ err = azalia_stream_reset(this);
+ if (err)
+ return err;
+
+ /* setup BDL */
+ dmaaddr = AZALIA_DMA_DMAADDR(&this->buffer);
+ this->dmaend = dmaaddr + ((caddr_t)end - (caddr_t)start);
+ bdlist = (bdlist_entry_t*)this->bdlist.addr;
+ for (index = 0; index < HDA_BDL_MAX; index++) {
+ bdlist[index].low = dmaaddr;
+ bdlist[index].high = PTR_UPPER32(dmaaddr);
+ bdlist[index].length = blk;
+ bdlist[index].flags = BDLIST_ENTRY_IOC;
+ printf("bdlist[%d]: addr 0x%08x%08x len %x flags %x\n", index,
+ bdlist[index].high, bdlist[index].low,
+ bdlist[index].length, bdlist[index].flags);
+ dmaaddr += blk;
+ if (dmaaddr >= this->dmaend) {
+ index++;
+ break;
+ }
+ }
+ /* The BDL covers the whole of the buffer. */
+ this->dmanext = AZALIA_DMA_DMAADDR(&this->buffer);
+
+ dmaaddr = AZALIA_DMA_DMAADDR(&this->bdlist);
+ STR_WRITE_4(this, BDPL, dmaaddr);
+ STR_WRITE_4(this, BDPU, PTR_UPPER32(dmaaddr));
+ STR_WRITE_2(this, LVI, (index - 1) & HDA_SD_LVI_LVI);
+ ctl2 = STR_READ_1(this, CTL2);
+ STR_WRITE_1(this, CTL2,
+ (ctl2 & ~HDA_SD_CTL2_STRM) | (this->number << HDA_SD_CTL2_STRM_SHIFT));
+ STR_WRITE_4(this, CBL, ((caddr_t)end - (caddr_t)start));
+
+ STR_WRITE_2(this, FMT, fmt);
+
+ err = azalia_codec_connect_stream(&this->az->codecs[this->az->codecno],
+ this->dir, fmt, this->number);
+ if (err)
+ return EINVAL;
+
+ intctl = AZ_READ_1(this->az, INTCTL);
+ intctl |= this->intr_bit;
+ AZ_WRITE_1(this->az, INTCTL, intctl);
+
+ ctl = STR_READ_2(this, CTL);
+ ctl |= ctl | HDA_SD_CTL_DEIE | HDA_SD_CTL_FEIE | HDA_SD_CTL_IOCE | HDA_SD_CTL_RUN;
+ STR_WRITE_2(this, CTL, ctl);
+ return 0;
+}
+
+int
+azalia_stream_halt(stream_t *this)
+{
+ uint16_t ctl;
+
+ ctl = STR_READ_2(this, CTL);
+ ctl &= ~(HDA_SD_CTL_DEIE | HDA_SD_CTL_FEIE | HDA_SD_CTL_IOCE | HDA_SD_CTL_RUN);
+ STR_WRITE_2(this, CTL, ctl);
+ AZ_WRITE_1(this->az, INTCTL, AZ_READ_1(this->az, INTCTL) & ~this->intr_bit);
+ return 0;
+}
+
+int
+azalia_stream_intr(stream_t *this, uint32_t intsts)
+{
+ if ((intsts & this->intr_bit) == 0)
+ return 0;
+ STR_WRITE_1(this, STS, HDA_SD_STS_DESE
+ | HDA_SD_STS_FIFOE | HDA_SD_STS_BCIS);
+ this->intr(this->intr_arg);
+ return 1;
+}
+
+/* ================================================================
+ * MI audio entries
+ * ================================================================ */
+
+int
+azalia_open(void *v, int flags)
+{
+ azalia_t *az;
+
+ DPRINTF(("%s: flags=0x%x\n", __func__, flags));
+ az = v;
+ az->running++;
+ return 0;
+}
+
+void
+azalia_close(void *v)
+{
+ azalia_t *az;
+
+ DPRINTF(("%s\n", __func__));
+ az = v;
+ az->running--;
+}
+
+int
+azalia_query_encoding(void *v, audio_encoding_t *enc)
+{
+ azalia_t *az;
+ codec_t *codec;
+ int i, j;
+
+ az = v;
+ codec = &az->codecs[az->codecno];
+ for (j = 0, i = 0; j < codec->nformats; j++) {
+ if (codec->formats[j].validbits !=
+ codec->formats[j].precision)
+ continue;
+ if (i == enc->index) {
+ enc->encoding = codec->formats[j].encoding;
+ enc->precision = codec->formats[j].precision;
+ switch (enc->encoding) {
+ case AUDIO_ENCODING_SLINEAR_LE:
+ strlcpy(enc->name, enc->precision == 8 ?
+ AudioEslinear : AudioEslinear_le,
+ sizeof enc->name);
+ break;
+ case AUDIO_ENCODING_ULINEAR_LE:
+ strlcpy(enc->name, enc->precision == 8 ?
+ AudioEulinear : AudioEulinear_le,
+ sizeof enc->name);
+ break;
+ default:
+ strlcpy(enc->name, "unknown", sizeof enc->name);
+ break;
+ }
+ return (0);
+ }
+ i++;
+ }
+ return (EINVAL);
+}
+
+int
+azalia_set_params(void *v, int smode, int umode, audio_params_t *p,
+ audio_params_t *r)
+{
+ azalia_t *az;
+ codec_t *codec;
+ void (*pswcode)(void *, u_char *, int) = NULL;
+ void (*rswcode)(void *, u_char *, int) = NULL;
+ int i, j;
+
+ az = v;
+ codec = &az->codecs[az->codecno];
+ if (smode & AUMODE_RECORD && r != NULL) {
+ if (r->encoding == AUDIO_ENCODING_ULAW) { /*XXX*/
+ r->encoding = AUDIO_ENCODING_SLINEAR_LE;
+ r->precision = 16;
+ r->channels = 2;
+ r->sample_rate = 44100;
+ }
+ for (i = 0; i < codec->nformats; i++) {
+ if (r->encoding != codec->formats[i].encoding)
+ continue;
+ if (r->precision != codec->formats[i].precision)
+ continue;
+ if (r->channels != codec->formats[i].channels)
+ continue;
+ break;
+ }
+ if (i == codec->nformats) {
+ printf("didn't find Record format %u/%u/%u\n",
+ r->encoding, r->precision, r->channels);
+ return (EINVAL);
+ }
+ for (j = 0; j < codec->formats[i].frequency_type; j++) {
+ if (r->sample_rate != codec->formats[i].frequency[j])
+ continue;
+ break;
+ }
+ if (j == codec->formats[i].frequency_type) {
+ printf("didn't find Record rate\n",
+ r->sample_rate);
+ return (EINVAL);
+ }
+ r->sw_code = rswcode;
+ }
+ if (smode & AUMODE_PLAY && p != NULL) {
+ if (p->encoding == AUDIO_ENCODING_ULAW) { /*XXX*/
+ p->encoding = AUDIO_ENCODING_SLINEAR_LE;
+ p->precision = 16;
+ p->channels = 2;
+ p->sample_rate = 44100;
+ }
+ for (i = 0; i < codec->nformats; i++) {
+ if (p->encoding != codec->formats[i].encoding)
+ continue;
+ if (p->precision != codec->formats[i].precision)
+ continue;
+ if (p->channels != codec->formats[i].channels)
+ continue;
+ break;
+ }
+ if (i == codec->nformats) {
+ printf("can't find playback format\n",
+ r->encoding, r->precision, r->channels);
+ return (EINVAL);
+ }
+ for (j = 0; j < codec->formats[i].frequency_type; j++) {
+ if (p->sample_rate != codec->formats[i].frequency[j])
+ continue;
+ break;
+ }
+ if (j == codec->formats[i].frequency_type) {
+ printf("can't find playback rate %u\n",
+ p->sample_rate);
+ return (EINVAL);
+ }
+ p->sw_code = pswcode;
+ }
+
+ return (0);
+}
+
+int
+azalia_round_blocksize(void *v, int blk)
+{
+ azalia_t *az;
+ size_t size;
+
+ blk &= ~0x7f; /* must be multiple of 128 */
+ if (blk <= 0)
+ blk = 128;
+ /* number of blocks must be <= HDA_BDL_MAX */
+ az = v;
+ size = az->pstream.buffer.size;
+#ifdef DIAGNOSTIC
+ if (size <= 0) {
+ printf("%s: size is 0", __func__);
+ return 256;
+ }
+#endif
+ if (size > HDA_BDL_MAX * blk) {
+ blk = size / HDA_BDL_MAX;
+ if (blk & 0x7f)
+ blk = (blk + 0x7f) & ~0x7f;
+ }
+ DPRINTF(("%s: resultant block size = %d\n", __func__, blk));
+ return blk;
+}
+
+int
+azalia_halt_output(void *v)
+{
+ azalia_t *az;
+
+ DPRINTF(("%s\n", __func__));
+ az = v;
+ return azalia_stream_halt(&az->pstream);
+}
+
+int
+azalia_halt_input(void *v)
+{
+ azalia_t *az;
+
+ DPRINTF(("%s\n", __func__));
+ az = v;
+ return azalia_stream_halt(&az->rstream);
+}
+
+int
+azalia_getdev(void *v, struct audio_device *dev)
+{
+ azalia_t *az;
+
+ az = v;
+ strlcpy(dev->name, "HD-Audio", MAX_AUDIO_DEV_LEN);
+ snprintf(dev->version, MAX_AUDIO_DEV_LEN,
+ "%d.%d", AZ_READ_1(az, VMAJ), AZ_READ_1(az, VMIN));
+ strlcpy(dev->config, XNAME(az), MAX_AUDIO_DEV_LEN);
+ return 0;
+}
+
+int
+azalia_set_port(void *v, mixer_ctrl_t *mc)
+{
+ azalia_t *az;
+ codec_t *co;
+
+ az = v;
+ co = &az->codecs[az->codecno];
+ return azalia_mixer_set(co, mc);
+}
+
+int
+azalia_get_port(void *v, mixer_ctrl_t *mc)
+{
+ azalia_t *az;
+ codec_t *co;
+
+ az = v;
+ co = &az->codecs[az->codecno];
+ return azalia_mixer_get(co, mc);
+}
+
+int
+azalia_query_devinfo(void *v, mixer_devinfo_t *mdev)
+{
+ azalia_t *az;
+ codec_t *co;
+
+ az = v;
+ co = &az->codecs[az->codecno];
+ if (mdev->index >= co->nmixers)
+ return ENXIO;
+ *mdev = co->mixers[mdev->index].devinfo;
+ return 0;
+}
+
+void *
+azalia_allocm(void *v, int dir, size_t size, int pool, int flags)
+{
+ azalia_t *az;
+ stream_t *stream;
+ int err;
+
+ az = v;
+ stream = dir == AUMODE_PLAY ? &az->pstream : &az->rstream;
+ err = azalia_alloc_dmamem(az, size, 128, &stream->buffer);
+ if (err)
+ return NULL;
+ return stream->buffer.addr;
+}
+
+void
+azalia_freem(void *v, void *addr, int pool)
+{
+ azalia_t *az;
+ stream_t *stream;
+
+ az = v;
+ if (addr == az->pstream.buffer.addr) {
+ stream = &az->pstream;
+ } else if (addr == az->rstream.buffer.addr) {
+ stream = &az->rstream;
+ } else {
+ return;
+ }
+ azalia_free_dmamem(az, &stream->buffer);
+}
+
+size_t
+azalia_round_buffersize(void *v, int dir, size_t size)
+{
+ size &= ~0x7f; /* must be multiple of 128 */
+ if (size <= 0)
+ size = 128;
+ return size;
+}
+
+int
+azalia_get_props(void *v)
+{
+ return AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX;
+}
+
+int
+azalia_trigger_output(void *v, void *start, void *end, int blk,
+ void (*intr)(void *), void *arg, audio_params_t *param)
+{
+ azalia_t *az;
+ int err;
+ uint16_t fmt;
+
+ DPRINTF(("%s: this=%p start=%p end=%p blk=%d {enc=%u %uch %u/%ubit %uHz}\n",
+ __func__, v, start, end, blk, param->encoding, param->channels,
+ param->precision, param->precision, param->sample_rate));
+
+ err = azalia_params2fmt(param, &fmt);
+ if (err)
+ return EINVAL;
+
+ az = v;
+ return azalia_stream_start(&az->pstream, start, end, blk, intr, arg, fmt);
+}
+
+int
+azalia_trigger_input(void *v, void *start, void *end, int blk,
+ void (*intr)(void *), void *arg, audio_params_t *param)
+{
+ azalia_t *az;
+ int err;
+ uint16_t fmt;
+
+ DPRINTF(("%s: this=%p start=%p end=%p blk=%d {enc=%u %uch %u/%ubit %uHz}\n",
+ __func__, v, start, end, blk, param->encoding, param->channels,
+ param->precision, param->precision, param->sample_rate));
+
+ err = azalia_params2fmt(param, &fmt);
+ if (err)
+ return EINVAL;
+
+ az = v;
+ return azalia_stream_start(&az->rstream, start, end, blk, intr, arg, fmt);
+}
+
+/* --------------------------------
+ * helpers for MI audio functions
+ * -------------------------------- */
+int
+azalia_params2fmt(const audio_params_t *param, uint16_t *fmt)
+{
+ uint16_t ret;
+
+ ret = 0;
+#ifdef DIAGNOSTIC
+ if (param->channels > HDA_MAX_CHANNELS) {
+ printf("%s: too many channels: %u\n", __func__,
+ param->channels);
+ return EINVAL;
+ }
+#endif
+ ret |= param->channels - 1;
+
+ switch (param->precision) {
+ case 8:
+ ret |= HDA_SD_FMT_BITS_8_16;
+ break;
+ case 16:
+ ret |= HDA_SD_FMT_BITS_16_16;
+ break;
+ case 32:
+ ret |= HDA_SD_FMT_BITS_32_32;
+ break;
+ }
+
+#if 0
+ switch (param->validbits) {
+ case 8:
+ ret |= HDA_SD_FMT_BITS_8_16;
+ break;
+ case 16:
+ ret |= HDA_SD_FMT_BITS_16_16;
+ break;
+ case 20:
+ ret |= HDA_SD_FMT_BITS_20_32;
+ break;
+ case 24:
+ ret |= HDA_SD_FMT_BITS_24_32;
+ break;
+ case 32:
+ ret |= HDA_SD_FMT_BITS_32_32;
+ break;
+ default:
+ printf("%s: invalid validbits: %u\n", __func__,
+ param->validbits);
+ }
+#endif
+
+ if (param->sample_rate == 384000) {
+ printf("%s: invalid sample_rate: %u\n", __func__,
+ param->sample_rate);
+ return EINVAL;
+ } else if (param->sample_rate == 192000) {
+ ret |= HDA_SD_FMT_BASE_48 | HDA_SD_FMT_MULT_X4 | HDA_SD_FMT_DIV_BY1;
+ } else if (param->sample_rate == 176400) {
+ ret |= HDA_SD_FMT_BASE_44 | HDA_SD_FMT_MULT_X4 | HDA_SD_FMT_DIV_BY1;
+ } else if (param->sample_rate == 96000) {
+ ret |= HDA_SD_FMT_BASE_48 | HDA_SD_FMT_MULT_X2 | HDA_SD_FMT_DIV_BY1;
+ } else if (param->sample_rate == 88200) {
+ ret |= HDA_SD_FMT_BASE_44 | HDA_SD_FMT_MULT_X2 | HDA_SD_FMT_DIV_BY1;
+ } else if (param->sample_rate == 48000) {
+ ret |= HDA_SD_FMT_BASE_48 | HDA_SD_FMT_MULT_X1 | HDA_SD_FMT_DIV_BY1;
+ } else if (param->sample_rate == 44100) {
+ ret |= HDA_SD_FMT_BASE_44 | HDA_SD_FMT_MULT_X1 | HDA_SD_FMT_DIV_BY1;
+ } else if (param->sample_rate == 32000) {
+ ret |= HDA_SD_FMT_BASE_48 | HDA_SD_FMT_MULT_X2 | HDA_SD_FMT_DIV_BY3;
+ } else if (param->sample_rate == 22050) {
+ ret |= HDA_SD_FMT_BASE_44 | HDA_SD_FMT_MULT_X1 | HDA_SD_FMT_DIV_BY2;
+ } else if (param->sample_rate == 16000) {
+ ret |= HDA_SD_FMT_BASE_48 | HDA_SD_FMT_MULT_X1 | HDA_SD_FMT_DIV_BY3;
+ } else if (param->sample_rate == 11025) {
+ ret |= HDA_SD_FMT_BASE_44 | HDA_SD_FMT_MULT_X1 | HDA_SD_FMT_DIV_BY4;
+ } else if (param->sample_rate == 8000) {
+ ret |= HDA_SD_FMT_BASE_48 | HDA_SD_FMT_MULT_X1 | HDA_SD_FMT_DIV_BY6;
+ } else {
+ printf("%s: invalid sample_rate: %u\n", __func__,
+ param->sample_rate);
+ return EINVAL;
+ }
+ *fmt = ret;
+ return 0;
+}
+
+int
+azalia_create_encodings(struct audio_format *formats, int nformats,
+ struct audio_encoding_set **encodings)
+{
+ int i;
+ u_int j;
+
+ for (i = 0; i < nformats; i++) {
+ printf("format(%d): encoding %u vbits %u prec %u chans %u cmask 0x%x\n",
+ i, formats[i].encoding, formats[i].validbits,
+ formats[i].precision, formats[i].channels,
+ formats[i].channel_mask);
+ printf("format(%d) rates:", i);
+ for (j = 0; j < formats[i].frequency_type; j++) {
+ printf(" %u", formats[i].frequency[j]);
+ }
+ printf("\n");
+ }
+ return (0);
+}
diff --git a/sys/dev/pci/azalia.h b/sys/dev/pci/azalia.h
new file mode 100644
index 00000000000..945383dc9a7
--- /dev/null
+++ b/sys/dev/pci/azalia.h
@@ -0,0 +1,546 @@
+/* $NetBSD: azalia.h,v 1.3 2005/09/29 04:14:03 kent Exp $ */
+
+/*-
+ * Copyright (c) 2005 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by TAMURA Kent
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/types.h>
+#include <sys/audioio.h>
+
+/* ----------------------------------------------------------------
+ * High Definition Audio constant values
+ * ---------------------------------------------------------------- */
+
+/* High Definition Audio registers */
+#define HDA_GCAP 0x000 /* 2 */
+#define HDA_GCAP_OSS(x) ((x & 0xf000) >> 12)
+#define HDA_GCAP_ISS(x) ((x & 0x0f00) >> 8)
+#define HDA_GCAP_BSS(x) ((x & 0x00f8) >> 3)
+#define HDA_GCAP_NSDO_MASK 0x0006
+#define HDA_GCAP_NSDO_1 0x0000
+#define HDA_GCAP_NSDO_2 0x0002
+#define HDA_GCAP_NSDO_4 0x0004
+#define HDA_GCAP_NSDO_RESERVED 0x0006
+#define HDA_GCAP_64OK 0x0001
+#define HDA_VMIN 0x002 /* 1 */
+#define HDA_VMAJ 0x003 /* 1 */
+#define HDA_OUTPAY 0x004 /* 2 */
+#define HDA_INPAY 0x006 /* 2 */
+#define HDA_GCTL 0x008 /* 4 */
+#define HDA_GCTL_UNSOL 0x00000080
+#define HDA_GCTL_FCNTRL 0x00000002
+#define HDA_GCTL_CRST 0x00000001
+#define HDA_WAKEEN 0x00c /* 2 */
+#define HDA_WAKEEN_SDIWEN 0x7fff
+#define HDA_STATESTS 0x00e /* 2 */
+#define HDA_STATESTS_SDIWAKE 0x7fff
+#define HDA_GSTS 0x010 /* 2 */
+#define HDA_GSTS_FSTS 0x0002
+#define HDA_OUTSTRMPAY 0x018 /* 2 */
+#define HDA_INSTRMPAY 0x01a /* 2 */
+#define HDA_INTCTL 0x020 /* 4 */
+#define HDA_INTCTL_GIE 0x80000000
+#define HDA_INTCTL_CIE 0x40000000
+#define HDA_INTCTL_SIE 0x3fffffff
+#define HDA_INTSTS 0x024 /* 4 */
+#define HDA_INTSTS_GIS 0x80000000
+#define HDA_INTSTS_CIS 0x40000000
+#define HDA_INTSTS_SIS 0x3fffffff
+#define HDA_WALCLK 0x030 /* 4 */
+#define HDA_SSYNC 0x034 /* 4 */
+#define HDA_SSYNC_SSYNC 0x3fffffff
+#define HDA_CORBLBASE 0x040 /* 4 */
+#define HDA_CORBUBASE 0x044 /* 4 */
+#define HDA_CORBWP 0x048 /* 2 */
+#define HDA_CORBWP_CORBWP 0x00ff
+#define HDA_CORBRP 0x04a /* 2 */
+#define HDA_CORBRP_CORBRPRST 0x8000
+#define HDA_CORBRP_CORBRP 0x00ff
+#define HDA_CORBCTL 0x04c /* 1 */
+#define HDA_CORBCTL_CORBRUN 0x02
+#define HDA_CORBCTL_CMEIE 0x01
+#define HDA_CORBSTS 0x04d /* 1 */
+#define HDA_CORBSTS_CMEI 0x01
+#define HDA_CORBSIZE 0x04e /* 1 */
+#define HDA_CORBSIZE_CORBSZCAP_MASK 0xf0
+#define HDA_CORBSIZE_CORBSZCAP_2 0x10
+#define HDA_CORBSIZE_CORBSZCAP_16 0x20
+#define HDA_CORBSIZE_CORBSZCAP_256 0x40
+#define HDA_CORBSIZE_CORBSIZE_MASK 0x03
+#define HDA_CORBSIZE_CORBSIZE_2 0x00
+#define HDA_CORBSIZE_CORBSIZE_16 0x01
+#define HDA_CORBSIZE_CORBSIZE_256 0x02
+#define HDA_RIRBLBASE 0x050 /* 4 */
+#define HDA_RIRBUBASE 0x054 /* 4 */
+#define HDA_RIRBWP 0x058 /* 2 */
+#define HDA_RIRBWP_RIRBWPRST 0x8000
+#define HDA_RIRBWP_RIRBWP 0x00ff
+#define HDA_RINTCNT 0x05a /* 2 */
+#define HDA_RINTCNT_RINTCNT 0x00ff
+#define HDA_RIRBCTL 0x05c /* 1 */
+#define HDA_RIRBCTL_RIRBOIC 0x04
+#define HDA_RIRBCTL_RIRBDMAEN 0x02
+#define HDA_RIRBCTL_RINTCTL 0x01
+#define HDA_RIRBSTS 0x05d /* 1 */
+#define HDA_RIRBSTS_RIRBOIS 0x04
+#define HDA_RIRBSTS_RINTFL 0x01
+#define HDA_RIRBSIZE 0x05e /* 1 */
+#define HDA_RIRBSIZE_RIRBSZCAP_MASK 0xf0
+#define HDA_RIRBSIZE_RIRBSZCAP_2 0x10
+#define HDA_RIRBSIZE_RIRBSZCAP_16 0x20
+#define HDA_RIRBSIZE_RIRBSZCAP_256 0x40
+#define HDA_RIRBSIZE_RIRBSIZE_MASK 0x03
+#define HDA_RIRBSIZE_RIRBSIZE_2 0x00
+#define HDA_RIRBSIZE_RIRBSIZE_16 0x01
+#define HDA_RIRBSIZE_RIRBSIZE_256 0x02
+#define HDA_IC 0x060 /* 4 */
+#define HDA_IR 0x064 /* 4 */
+#define HDA_IRS 0x068 /* 2 */
+#define HDA_IRS_IRRADD 0x00f0
+#define HDA_IRS_IRRUNSOL 0x0008
+#define HDA_IRS_IRV 0x0002
+#define HDA_IRS_ICB 0x0001
+#define HDA_DPLBASE 0x070 /* 4 */
+#define HDA_DPLBASE_DPLBASE 0xffffff80
+#define HDA_DPLBASE_ENABLE 0x00000001
+#define HDA_DPUBASE 0x074
+
+#define HDA_SD_BASE 0x080
+#define HDA_SD_CTL 0x00 /* 2 */
+#define HDA_SD_CTL_DEIE 0x0010
+#define HDA_SD_CTL_FEIE 0x0008
+#define HDA_SD_CTL_IOCE 0x0004
+#define HDA_SD_CTL_RUN 0x0002
+#define HDA_SD_CTL_SRST 0x0001
+#define HDA_SD_CTL2 0x02 /* 1 */
+#define HDA_SD_CTL2_STRM 0xf0
+#define HDA_SD_CTL2_STRM_SHIFT 4
+#define HDA_SD_CTL2_DIR 0x08
+#define HDA_SD_CTL2_TP 0x04
+#define HDA_SD_CTL2_STRIPE 0x03
+#define HDA_SD_STS 0x03 /* 1 */
+#define HDA_SD_STS_FIFORDY 0x20
+#define HDA_SD_STS_DESE 0x10
+#define HDA_SD_STS_FIFOE 0x08
+#define HDA_SD_STS_BCIS 0x04
+#define HDA_SD_LPIB 0x04 /* 4 */
+#define HDA_SD_CBL 0x08 /* 4 */
+#define HDA_SD_LVI 0x0c /* 2 */
+#define HDA_SD_LVI_LVI 0x00ff
+#define HDA_SD_FIFOW 0x0e /* 2 */
+#define HDA_SD_FIFOS 0x10 /* 2 */
+#define HDA_SD_FMT 0x12 /* 2 */
+#define HDA_SD_FMT_BASE 0x4000
+#define HDA_SD_FMT_BASE_48 0x0000
+#define HDA_SD_FMT_BASE_44 0x4000
+#define HDA_SD_FMT_MULT 0x3800
+#define HDA_SD_FMT_MULT_X1 0x0000
+#define HDA_SD_FMT_MULT_X2 0x0800
+#define HDA_SD_FMT_MULT_X3 0x1000
+#define HDA_SD_FMT_MULT_X4 0x1800
+#define HDA_SD_FMT_DIV 0x0700
+#define HDA_SD_FMT_DIV_BY1 0x0000
+#define HDA_SD_FMT_DIV_BY2 0x0100
+#define HDA_SD_FMT_DIV_BY3 0x0200
+#define HDA_SD_FMT_DIV_BY4 0x0300
+#define HDA_SD_FMT_DIV_BY5 0x0400
+#define HDA_SD_FMT_DIV_BY6 0x0500
+#define HDA_SD_FMT_DIV_BY7 0x0600
+#define HDA_SD_FMT_DIV_BY8 0x0700
+#define HDA_SD_FMT_BITS 0x0070
+#define HDA_SD_FMT_BITS_8_16 0x0000
+#define HDA_SD_FMT_BITS_16_16 0x0010
+#define HDA_SD_FMT_BITS_20_32 0x0020
+#define HDA_SD_FMT_BITS_24_32 0x0030
+#define HDA_SD_FMT_BITS_32_32 0x0040
+#define HDA_SD_FMT_CHAN 0x000f
+#define HDA_SD_BDPL 0x18 /* 4 */
+#define HDA_SD_BDPU 0x1c /* 4 */
+#define HDA_SD_SIZE 0x20
+
+/* CORB commands */
+#define CORB_GET_PARAMETER 0xf00
+#define COP_VENDOR_ID 0x00
+#define COP_VID_VENDOR(x) (x >> 16)
+#define COP_VID_DEVICE(x) (x & 0xffff)
+#define COP_REVISION_ID 0x02
+#define COP_RID_MAJ(x) ((x >> 20) & 0x0f)
+#define COP_RID_MIN(x) ((x >> 16) & 0x0f)
+#define COP_RID_REVISION(x) ((x >> 8) & 0xff)
+#define COP_RID_STEPPING(x) (x & 0xff)
+#define COP_SUBORDINATE_NODE_COUNT 0x04
+#define COP_START_NID(x) ((x & 0x00ff0000) >> 16)
+#define COP_NSUBNODES(x) (x & 0x000000ff)
+#define COP_FUNCTION_GROUP_TYPE 0x05
+#define COP_FTYPE(x) (x & 0x000000ff)
+#define COP_FTYPE_RESERVED 0x01
+#define COP_FTYPE_AUDIO 0x01
+#define COP_FTYPE_MODEM 0x02
+#define COP_AUDIO_FUNCTION_GROUP_CAPABILITY 0x08
+#define COP_AUDIO_WIDGET_CAP 0x09
+#define COP_AWCAP_TYPE(x) ((x >> 20) & 0xf)
+#define COP_AWTYPE_AUDIO_OUTPUT 0x0
+#define COP_AWTYPE_AUDIO_INPUT 0x1
+#define COP_AWTYPE_AUDIO_MIXER 0x2
+#define COP_AWTYPE_AUDIO_SELECTOR 0x3
+#define COP_AWTYPE_PIN_COMPLEX 0x4
+#define COP_AWTYPE_POWER 0x5
+#define COP_AWTYPE_VOLUME_KNOB 0x6
+#define COP_AWTYPE_BEEP_GENERATOR 0x7
+#define COP_AWTYPE_VENDOR_DEFINED 0xf
+#define COP_AWCAP_STEREO 0x001
+#define COP_AWCAP_INAMP 0x002
+#define COP_AWCAP_OUTAMP 0x004
+#define COP_AWCAP_AMPOV 0x008
+#define COP_AWCAP_FORMATOV 0x010
+#define COP_AWCAP_STRIPE 0x020
+#define COP_AWCAP_PROC 0x040
+#define COP_AWCAP_UNSOL 0x080
+#define COP_AWCAP_CONNLIST 0x100
+#define COP_AWCAP_DIGITAL 0x200
+#define COP_AWCAP_POWER 0x400
+#define COP_AWCAP_LRSWAP 0x800
+#define COP_AWCAP_DELAY(x) ((x >> 16) & 0xf)
+#define COP_PCM 0x0a
+#define COP_PCM_B32 0x00100000
+#define COP_PCM_B24 0x00080000
+#define COP_PCM_B20 0x00040000
+#define COP_PCM_B16 0x00020000
+#define COP_PCM_B8 0x00010000
+#define COP_PCM_R3840 0x00000800
+#define COP_PCM_R1920 0x00000400
+#define COP_PCM_R1764 0x00000200
+#define COP_PCM_R960 0x00000100
+#define COP_PCM_R882 0x00000080
+#define COP_PCM_R480 0x00000040
+#define COP_PCM_R441 0x00000020
+#define COP_PCM_R320 0x00000010
+#define COP_PCM_R220 0x00000008
+#define COP_PCM_R160 0x00000004
+#define COP_PCM_R110 0x00000002
+#define COP_PCM_R80 0x00000001
+#define COP_STREAM_FORMATS 0x0b
+#define COP_STREAM_FORMAT_PCM 0x00000001
+#define COP_STREAM_FORMAT_FLOAT32 0x00000002
+#define COP_STREAM_FORMAT_AC3 0x00000003
+#define COP_PINCAP 0x0c
+#define COP_PINCAP_IMPEDANCE 0x00000001
+#define COP_PINCAP_TRIGGER 0x00000002
+#define COP_PINCAP_PRESENCE 0x00000004
+#define COP_PINCAP_HEADPHONE 0x00000008
+#define COP_PINCAP_OUTPUT 0x00000010
+#define COP_PINCAP_INPUT 0x00000020
+#define COP_PINCAP_BALANCE 0x00000040
+#define COP_PINCAP_VREF(x) ((x >> 8) & 0xff)
+#define COP_PINCAP_EAPD 0x00010000
+#define COP_INPUT_AMPCAP 0x0d
+#define COP_AMPCAP_OFFSET(x) (x & 0x0000007f)
+#define COP_AMPCAP_NUMSTEPS(x) ((x >> 8) & 0x7f)
+#define COP_AMPCAP_STEPSIZE(x) ((x >> 16) & 0x7f)
+#define COP_AMPCAP_MUTE 0x80000000
+#define COP_CONNECTION_LIST_LENGTH 0x0e
+#define COP_CLL_LONG 0x00000080
+#define COP_CLL_LENGTH(x) (x & 0x0000007f)
+#define COP_SUPPORTED_POWER_STATES 0x0f
+#define COP_PROCESSING_CAPABILITIES 0x10
+#define COP_GPIO_COUNT 0x11
+#define COP_OUTPUT_AMPCAP 0x12
+#define COP_VOLUME_KNOB_CAPABILITIES 0x13
+#define COP_VKCAP_DELTA 0x00000080
+#define COP_VKCAP_NUMSTEPS(x) (x & 0x7f)
+#define CORB_GET_CONNECTION_SELECT_CONTROL 0xf01
+#define CORB_CSC_INDEX(x) (x & 0xff)
+#define CORB_SET_CONNECTION_SELECT_CONTROL 0x701
+#define CORB_GET_CONNECTION_LIST_ENTRY 0xf02
+#define CORB_CLE_LONG_0(x) (x & 0x0000ffff)
+#define CORB_CLE_LONG_1(x) ((x & 0xffff0000) >> 16)
+#define CORB_CLE_SHORT_0(x) (x & 0xff)
+#define CORB_CLE_SHORT_1(x) ((x >> 8) & 0xff)
+#define CORB_CLE_SHORT_2(x) ((x >> 16) & 0xff)
+#define CORB_CLE_SHORT_3(x) ((x >> 24) & 0xff)
+#define CORB_GET_PROCESSING_STATE 0xf03
+#define CORB_SET_PROCESSING_STATE 0x703
+#define CORB_GET_COEFFICIENT_INDEX 0xd00
+#define CORB_SET_COEFFICIENT_INDEX 0x500
+#define CORB_GET_PROCESSING_COEFFICIENT 0xc00
+#define CORB_SET_PROCESSING_COEFFICIENT 0x400
+#define CORB_GET_AMPLIFIER_GAIN_MUTE 0xb00
+#define CORB_GAGM_INPUT 0x0000
+#define CORB_GAGM_OUTPUT 0x8000
+#define CORB_GAGM_RIGHT 0x0000
+#define CORB_GAGM_LEFT 0x2000
+#define CORB_GAGM_MUTE 0x00000080
+#define CORB_GAGM_GAIN(x) (x & 0x0000007f)
+#define CORB_SET_AMPLIFIER_GAIN_MUTE 0x300
+#define CORB_AGM_GAIN_MASK 0x007f
+#define CORB_AGM_MUTE 0x0080
+#define CORB_AGM_INDEX_SHIFT 8
+#define CORB_AGM_RIGHT 0x1000
+#define CORB_AGM_LEFT 0x2000
+#define CORB_AGM_INPUT 0x4000
+#define CORB_AGM_OUTPUT 0x8000
+#define CORB_GET_CONVERTER_FORMAT 0xa00
+#define CORB_SET_CONVERTER_FORMAT 0x200
+#define CORB_GET_DIGITAL_CONVERTER_CONTROL 0xf0d
+#define CORB_SET_DIGITAL_CONVERTER_CONTROL_L 0x70d
+#define CORB_SET_DIGITAL_CONVERTER_CONTROL_H 0x70e
+#define CORB_GET_POWER_STATE 0xf05
+#define CORB_SET_POWER_STATE 0x705
+#define CORB_PS_D0 0x0
+#define CORB_PS_D1 0x1
+#define CORB_PS_D2 0x2
+#define CORB_PS_D3 0x3
+#define CORB_GET_CONVERTER_STREAM_CHANNEL 0xf06
+#define CORB_SET_CONVERTER_STREAM_CHANNEL 0x706
+#define CORB_GET_INPUT_CONVERTER_SDI_SELECT 0xf04
+#define CORB_SET_INPUT_CONVERTER_SDI_SELECT 0x704
+#define CORB_GET_PIN_WIDGET_CONTROL 0xf07
+#define CORB_SET_PIN_WIDGET_CONTROL 0x707
+#define CORB_PWC_HEADPHONE 0x80
+#define CORB_PWC_OUTPUT 0x40
+#define CORB_PWC_INPUT 0x20
+#define CORB_PWC_VREF_HIZ 0x00
+#define CORB_PWC_VREF_50 0x01
+#define CORB_PWC_VREF_GND 0x02
+#define CORB_PWC_VREF_80 0x04
+#define CORB_PWC_VREF_100 0x05
+#define CORB_GET_UNSOLICITED_RESPONSE 0xf08
+#define CORB_SET_UNSOLICITED_RESPONSE 0x708
+#define CORB_GET_PIN_SENSE 0xf09
+#define CORB_EXECUTE_PIN_SENSE 0x709
+#define CORB_GET_EAPD_BTL_ENABLE 0xf0c
+#define CORB_SET_EAPD_BTL_ENABLE 0x70c
+#define CORB_GET_GPI_DATA 0xf10
+#define CORB_SET_GPI_DATA 0x710
+#define CORB_GET_GPI_WAKE_ENABLE_MASK 0xf11
+#define CORB_SET_GPI_WAKE_ENABLE_MASK 0x711
+#define CORB_GET_GPI_UNSOLICITED_ENABLE_MASK 0xf12
+#define CORB_SET_GPI_UNSOLICITED_ENABLE_MASK 0x712
+#define CORB_GET_GPI_STICKY_MASK 0xf13
+#define CORB_SET_GPI_STICKY_MASK 0x713
+#define CORB_GET_GPO_DATA 0xf14
+#define CORB_SET_GPO_DATA 0x714
+#define CORB_GET_GPIO_DATA 0xf15
+#define CORB_SET_GPIO_DATA 0x715
+#define CORB_GET_GPIO_ENABLE_MASK 0xf16
+#define CORB_SET_GPIO_ENABLE_MASK 0x716
+#define CORB_GET_GPIO_DIRECTION 0xf17
+#define CORB_SET_GPIO_DIRECTION 0x717
+#define CORB_GET_GPIO_WAKE_ENABLE_MASK 0xf18
+#define CORB_SET_GPIO_WAKE_ENABLE_MASK 0x718
+#define CORB_GET_GPIO_UNSOLICITED_ENABLE_MASK 0xf19
+#define CORB_SET_GPIO_UNSOLICITED_ENABLE_MASK 0x719
+#define CORB_GET_GPIO_STICKY_MASK 0xf1a
+#define CORB_SET_GPIO_STICKY_MASK 0x71a
+#define CORB_GET_BEEP_GENERATION 0xf0a
+#define CORB_SET_BEEP_GENERATION 0x70a
+#define CORB_GET_VOLUME_KNOB 0xf0f
+#define CORB_SET_VOLUME_KNOB 0x70f
+#define CORB_VKNOB_DIRECT 0x80
+#define CORB_VKNOB_VOLUME(x) (x & 0x7f)
+#define CORB_GET_SUBSYSTEM_ID 0xf20
+#define CORB_SET_SUBSYSTEM_ID_1 0x720
+#define CORB_SET_SUBSYSTEM_ID_2 0x721
+#define CORB_SET_SUBSYSTEM_ID_3 0x722
+#define CORB_SET_SUBSYSTEM_ID_4 0x723
+#define CORB_GET_CONFIGURATION_DEFAULT 0xf1c
+#define CORB_SET_CONFIGURATION_DEFAULT_1 0x71c
+#define CORB_SET_CONFIGURATION_DEFAULT_2 0x71d
+#define CORB_SET_CONFIGURATION_DEFAULT_3 0x71e
+#define CORB_SET_CONFIGURATION_DEFAULT_4 0x71f
+#define CORB_CD_SEQUENCE(x) (x & 0x0000000f)
+#define CORB_CD_SEQUENCE_MAX 0x0f
+#define CORB_CD_ASSOCIATION(x) ((x >> 4) & 0xf)
+#define CORB_CD_ASSOCIATION_MAX 0x0f
+#define CORB_CD_MISC_MASK 0x00000f00
+#define CORB_CD_COLOR(x) ((x >> 12) & 0xf)
+#define CORB_CD_COLOR_UNKNOWN 0x0
+#define CORB_CD_BLACK 0x1
+#define CORB_CD_GRAY 0x2
+#define CORB_CD_BLUE 0x3
+#define CORB_CD_GREEN 0x4
+#define CORB_CD_RED 0x5
+#define CORB_CD_ORANGE 0x6
+#define CORB_CD_YELLOW 0x7
+#define CORB_CD_PURPLE 0x8
+#define CORB_CD_PINK 0x9
+#define CORB_CD_WHITE 0xe
+#define CORB_CD_COLOR_OTHER 0xf
+#define CORB_CD_CONNECTION_MASK 0x000f0000
+#define CORB_CD_DEVICE(x) ((x >> 20) & 0xf)
+#define CORB_CD_LINEOUT 0x0
+#define CORB_CD_SPEAKER 0x1
+#define CORB_CD_HEADPHONE 0x2
+#define CORB_CD_CD 0x3
+#define CORB_CD_SPDIFOUT 0x4
+#define CORB_CD_DIGITALOUT 0x5
+#define CORB_CD_MODEMLINE 0x6
+#define CORB_CD_MODEMHANDSET 0x7
+#define CORB_CD_LINEIN 0x8
+#define CORB_CD_AUX 0x9
+#define CORB_CD_MICIN 0xa
+#define CORB_CD_TELEPHONY 0xb
+#define CORB_CD_SPDIFIN 0xc
+#define CORB_CD_DIGITALIN 0xd
+#define CORB_CD_DEVICE_OTHER 0xf
+#define CORB_CD_LOCATION_MASK 0x3f000000
+#define CORB_CD_PORT_MASK 0xc0000000
+#define CORB_GET_STRIPE_CONTROL 0xf24
+#define CORB_SET_STRIPE_CONTROL 0x720 /* XXX typo in the spec? */
+#define CORB_EXECUTE_FUNCTION_RESET 0x7ff
+
+#define CORB_NID_ROOT 0
+#define HDA_MAX_CHANNELS 16
+
+
+#define PCI_SUBCLASS_HDAUDIO 0x03
+
+/* memory-mapped types */
+typedef struct {
+ uint32_t low;
+ uint32_t high;
+ uint32_t length;
+ uint32_t flags;
+#define BDLIST_ENTRY_IOC 0x00000001
+} __packed bdlist_entry_t;
+#define HDA_BDL_MAX 256
+
+typedef struct {
+ uint32_t position;
+ uint32_t reserved;
+} __packed dmaposition_t;
+
+typedef uint32_t corb_entry_t;
+typedef struct {
+ uint32_t resp;
+ uint32_t resp_ex;
+#define RIRB_UNSOLICITED_RESPONSE (1 << 4)
+} __packed rirb_entry_t;
+
+
+/* #define AZALIA_DEBUG */
+#ifdef AZALIA_DEBUG
+# define DPRINTF(x) do { printf x; } while (0/*CONSTCOND*/)
+#else
+# define DPRINTF(x) do {} while (0/*CONSTCOND*/)
+#endif
+#define PTR_UPPER32(x) ((uint64_t)(x) >> 32)
+#define FLAGBUFLEN 256
+#define MAX_VOLUME_255 1
+
+typedef int nid_t;
+
+typedef struct {
+ nid_t nid;
+ uint32_t widgetcap;
+ int type; /* = bit20-24 of widgetcap */
+ int nconnections;
+ nid_t *connections;
+ int selected;
+ uint32_t inamp_cap;
+ uint32_t outamp_cap;
+ char name[MAX_AUDIO_DEV_LEN];
+ union {
+ struct { /* for AUDIO_INPUT/OUTPUT */
+ uint32_t encodings;
+ uint32_t bits_rates;
+ } audio;
+ struct { /* for PIN */
+ uint32_t cap;
+ uint32_t config;
+ int sequence;
+ int association;
+ int color;
+ int device;
+ } pin;
+ struct { /* for VOLUME_KNOB */
+ uint32_t cap;
+ } volume;
+ } d;
+} widget_t;
+
+typedef struct {
+ mixer_devinfo_t devinfo;
+ nid_t nid; /* target NID; 0 is invalid. */
+ int target; /* 0-15: inamp index, 0x100: outamp, ... */
+#define IS_MI_TARGET_INAMP(x) ((x) <= 15)
+#define MI_TARGET_INAMP(x) (x)
+#define MI_TARGET_OUTAMP 0x100
+#define MI_TARGET_CONNLIST 0x101
+#define MI_TARGET_PINDIR 0x102 /* for bidirectional pin */
+#define MI_TARGET_PINBOOST 0x103 /* for headphone pin */
+#define MI_TARGET_DAC 0x104
+#define MI_TARGET_ADC 0x105
+#define MI_TARGET_VOLUME 0x106
+} mixer_item_t;
+
+typedef struct {
+ int nconv;
+ nid_t conv[HDA_MAX_CHANNELS];
+} convgroup_t;
+
+typedef struct codec_t {
+ int (*comresp)(const struct codec_t *, nid_t, uint32_t, uint32_t, uint32_t *);
+ int (*init_dacgroup)(struct codec_t *);
+ int (*init_widget)(const struct codec_t *, widget_t *, nid_t);
+
+ struct azalia_t *az;
+ const char *name;
+ int address;
+ int nfunctions;
+ nid_t audiofunc; /* NID of an audio function node */
+ nid_t wstart; /* start NID of audio widgets */
+ nid_t wend; /* the last NID of audio widgets + 1 */
+ widget_t *w; /* widgets in the audio function.
+ * w[0] to w[wstart-1] are unused. */
+#define FOR_EACH_WIDGET(this, i) for (i = (this)->wstart; i < (this)->wend; i++)
+
+ int ndacgroups;
+ convgroup_t dacgroups[32];
+ int cur_dac; /* currently selected DAC group index */
+ int nadcs;
+ nid_t adcs[32];
+ int cur_adc; /* currently selected ADC index */
+
+ int nmixers, maxmixers;
+ mixer_item_t *mixers;
+
+ struct audio_format* formats;
+ int nformats;
+ struct audio_encoding_set *encodings;
+} codec_t;
+
+
+int azalia_codec_init_vtbl(codec_t *, uint32_t);
diff --git a/sys/dev/pci/azalia_codec.c b/sys/dev/pci/azalia_codec.c
new file mode 100644
index 00000000000..61b51d004db
--- /dev/null
+++ b/sys/dev/pci/azalia_codec.c
@@ -0,0 +1,383 @@
+/* $NetBSD: azalia_codec.c,v 1.3 2005/09/29 04:14:03 kent Exp $ */
+
+/*-
+ * Copyright (c) 2005 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by TAMURA Kent
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#include <sys/cdefs.h>
+#ifdef NETBSD_GOOP
+__KERNEL_RCSID(0, "$NetBSD: azalia_codec.c,v 1.3 2005/09/29 04:14:03 kent Exp $");
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <uvm/uvm_param.h>
+#include <dev/pci/azalia.h>
+
+
+static int azalia_codec_init_dacgroup(codec_t *);
+static int azalia_codec_add_dacgroup(codec_t *, int, uint32_t);
+static int azalia_codec_find_pin(const codec_t *, int, int, uint32_t);
+static int azalia_codec_find_dac(const codec_t *, int, int);
+static int alc260_init_dacgroup(codec_t *);
+static int alc880_init_dacgroup(codec_t *);
+static int alc882_init_dacgroup(codec_t *);
+static int alc882_init_widget(const codec_t *, widget_t *, nid_t);
+static int stac9221_init_dacgroup(codec_t *);
+
+
+int
+azalia_codec_init_vtbl(codec_t *this, uint32_t vid)
+{
+ switch (vid) {
+ case 0x10ec0260:
+ this->name = "Realtek ALC260";
+ this->init_dacgroup = alc260_init_dacgroup;
+ break;
+ case 0x10ec0880:
+ this->name = "Realtek ALC880";
+ this->init_dacgroup = alc880_init_dacgroup;
+ break;
+ case 0x10ec0882:
+ this->name = "Realtek ALC882";
+ this->init_dacgroup = alc882_init_dacgroup;
+ this->init_widget = alc882_init_widget;
+ break;
+ case 0x83847680:
+ this->name = "Sigmatel STAC9221";
+ this->init_dacgroup = stac9221_init_dacgroup;
+ default:
+ this->name = NULL;
+ this->init_dacgroup = azalia_codec_init_dacgroup;
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------
+ * functions for generic codecs
+ * ---------------------------------------------------------------- */
+
+static int
+azalia_codec_init_dacgroup(codec_t *this)
+{
+ int i, j, assoc, group;
+
+ /*
+ * grouping DACs
+ * [0] the lowest assoc DACs
+ * [1] the lowest assoc digital outputs
+ * [2] the 2nd assoc DACs
+ * :
+ */
+ this->ndacgroups = 0;
+ for (assoc = 0; assoc < CORB_CD_ASSOCIATION_MAX; assoc++) {
+ azalia_codec_add_dacgroup(this, assoc, 0);
+ azalia_codec_add_dacgroup(this, assoc, COP_AWCAP_DIGITAL);
+ }
+
+ /* find DACs which do not connect with any pins by default */
+ DPRINTF(("%s: find non-connected DACs\n", __func__));
+ FOR_EACH_WIDGET(this, i) {
+ boolean_t found;
+
+ if (this->w[i].type != COP_AWTYPE_AUDIO_OUTPUT)
+ continue;
+ found = FALSE;
+ for (group = 0; group < this->ndacgroups; group++) {
+ for (j = 0; j < this->dacgroups[group].nconv; j++) {
+ if (i == this->dacgroups[group].conv[j]) {
+ found = TRUE;
+ group = this->ndacgroups;
+ break;
+ }
+ }
+ }
+ if (found)
+ continue;
+ if (this->ndacgroups >= 32)
+ break;
+ this->dacgroups[this->ndacgroups].nconv = 1;
+ this->dacgroups[this->ndacgroups].conv[0] = i;
+ this->ndacgroups++;
+ }
+ this->cur_dac = 0;
+
+ /* enumerate ADCs */
+ this->nadcs = 0;
+ FOR_EACH_WIDGET(this, i) {
+ if (this->w[i].type != COP_AWTYPE_AUDIO_INPUT)
+ continue;
+ this->adcs[this->nadcs++] = i;
+ if (this->nadcs >= 32)
+ break;
+ }
+ this->cur_adc = 0;
+ return 0;
+}
+
+static int
+azalia_codec_add_dacgroup(codec_t *this, int assoc, uint32_t digital)
+{
+ int i, j, n, dac, seq;
+
+ n = 0;
+ for (seq = 0 ; seq < CORB_CD_SEQUENCE_MAX; seq++) {
+ i = azalia_codec_find_pin(this, assoc, seq, digital);
+ if (i < 0)
+ continue;
+ dac = azalia_codec_find_dac(this, i, 0);
+ if (dac < 0)
+ continue;
+ /* duplication check */
+ for (j = 0; j < n; j++) {
+ if (this->dacgroups[this->ndacgroups].conv[j] == dac)
+ break;
+ }
+ if (j < n) /* this group already has <dac> */
+ continue;
+ this->dacgroups[this->ndacgroups].conv[n++] = dac;
+ DPRINTF(("%s: assoc=%d seq=%d ==> g=%d n=%d\n",
+ __func__, assoc, seq, this->ndacgroups, n-1));
+ }
+ if (n <= 0) /* no such DACs */
+ return 0;
+ this->dacgroups[this->ndacgroups].nconv = n;
+
+ /* check if the same combination is already registered */
+ for (i = 0; i < this->ndacgroups; i++) {
+ if (n != this->dacgroups[i].nconv)
+ continue;
+ for (j = 0; j < n; j++) {
+ if (this->dacgroups[this->ndacgroups].conv[j] !=
+ this->dacgroups[i].conv[j])
+ break;
+ }
+ if (j >= n) /* matched */
+ return 0;
+ }
+ /* found no equivalent group */
+ this->ndacgroups++;
+ return 0;
+}
+
+static int
+azalia_codec_find_pin(const codec_t *this, int assoc, int seq, uint32_t digital)
+{
+ int i;
+
+ FOR_EACH_WIDGET(this, i) {
+ if (this->w[i].type != COP_AWTYPE_PIN_COMPLEX)
+ continue;
+ if ((this->w[i].d.pin.cap & COP_PINCAP_OUTPUT) == 0)
+ continue;
+ if ((this->w[i].widgetcap & COP_AWCAP_DIGITAL) != digital)
+ continue;
+ if (this->w[i].d.pin.association != assoc)
+ continue;
+ if (this->w[i].d.pin.sequence == seq) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+static int
+azalia_codec_find_dac(const codec_t *this, int index, int depth)
+{
+ const widget_t *w;
+ int i, j, ret;
+
+ w = &this->w[index];
+ if (w->type == COP_AWTYPE_AUDIO_OUTPUT) {
+ DPRINTF(("%s: DAC: nid=0x%x index=%d\n",
+ __func__, w->nid, index));
+ return index;
+ }
+ if (++depth > 50) {
+ return -1;
+ }
+ if (w->selected >= 0) {
+ j = w->connections[w->selected];
+ ret = azalia_codec_find_dac(this, j, depth);
+ if (ret >= 0) {
+ DPRINTF(("%s: DAC path: nid=0x%x index=%d\n",
+ __func__, w->nid, index));
+ return ret;
+ }
+ }
+ for (i = 0; i < w->nconnections; i++) {
+ j = w->connections[i];
+ ret = azalia_codec_find_dac(this, j, depth);
+ if (ret >= 0) {
+ DPRINTF(("%s: DAC path: nid=0x%x index=%d\n",
+ __func__, w->nid, index));
+ return ret;
+ }
+ }
+ return -1;
+}
+
+/* ----------------------------------------------------------------
+ * Realtek ALC260
+ * ---------------------------------------------------------------- */
+
+static int
+alc260_init_dacgroup(codec_t *this)
+{
+ static const convgroup_t dacs[2] = {
+ {1, {0x02}}, /* analog 2ch */
+ {1, {0x03}}}; /* digital */
+
+ this->ndacgroups = 2;
+ this->dacgroups[0] = dacs[0];
+ this->dacgroups[1] = dacs[1];
+
+ this->nadcs = 3;
+ this->adcs[0] = 0x04;
+ this->adcs[1] = 0x05;
+ this->adcs[2] = 0x06; /* digital */
+ return 0;
+}
+
+/* ----------------------------------------------------------------
+ * Realtek ALC880
+ * ---------------------------------------------------------------- */
+
+static int
+alc880_init_dacgroup(codec_t *this)
+{
+ static const convgroup_t dacs[2] = {
+ {4, {0x02, 0x04, 0x03, 0x05}}, /* analog 8ch */
+ {1, {0x06}}}; /* digital */
+
+ this->ndacgroups = 2;
+ this->dacgroups[0] = dacs[0];
+ this->dacgroups[1] = dacs[1];
+
+ this->nadcs = 4;
+ this->adcs[0] = 0x07;
+ this->adcs[1] = 0x08;
+ this->adcs[2] = 0x09;
+ this->adcs[3] = 0x0a; /* digital */
+ return 0;
+}
+
+/* ----------------------------------------------------------------
+ * Realtek ALC882
+ * ---------------------------------------------------------------- */
+
+static int
+alc882_init_dacgroup(codec_t *this)
+{
+ static const convgroup_t dacs[3] = {
+ {4, {0x02, 0x04, 0x03, 0x05}}, /* analog 8ch */
+ {1, {0x06}}, /* digital */
+ {1, {0x25}}}; /* another analog */
+
+ this->ndacgroups = 3;
+ this->dacgroups[0] = dacs[0];
+ this->dacgroups[1] = dacs[1];
+ this->dacgroups[2] = dacs[2];
+
+ this->nadcs = 4;
+ this->adcs[0] = 0x07;
+ this->adcs[1] = 0x08;
+ this->adcs[2] = 0x09;
+ this->adcs[3] = 0x0a; /* digital */
+ return 0;
+}
+
+static int
+alc882_init_widget(const codec_t *this, widget_t *w, nid_t nid)
+{
+ switch (nid) {
+ case 0x14:
+ strlcpy(w->name, "green", sizeof(w->name));
+ break;
+ case 0x15:
+ strlcpy(w->name, "gray", sizeof(w->name));
+ break;
+ case 0x16:
+ strlcpy(w->name, "orange", sizeof(w->name));
+ break;
+ case 0x17:
+ strlcpy(w->name, "black", sizeof(w->name));
+ break;
+ case 0x18:
+ strlcpy(w->name, "mic1", sizeof(w->name));
+ break;
+ case 0x19:
+ strlcpy(w->name, "mic2", sizeof(w->name));
+ break;
+ case 0x1a:
+ strlcpy(w->name, AudioNline, sizeof(w->name));
+ break;
+ case 0x1b:
+ /* AudioNheadphone is too long */
+ strlcpy(w->name, "hp", sizeof(w->name));
+ break;
+ case 0x1c:
+ strlcpy(w->name, AudioNcd, sizeof(w->name));
+ break;
+ case 0x1d:
+ strlcpy(w->name, AudioNspeaker, sizeof(w->name));
+ break;
+ }
+ return 0;
+}
+
+/* ----------------------------------------------------------------
+ * Sigmatel STAC9221
+ * ---------------------------------------------------------------- */
+
+static int
+stac9221_init_dacgroup(codec_t *this)
+{
+ static const convgroup_t dacs[3] = {
+ {4, {0x02, 0x03, 0x05, 0x04}}, /* analog 8ch */
+ {1, {0x08}}, /* digital */
+ {1, {0x1a}}}; /* another digital? */
+
+ this->ndacgroups = 3;
+ this->dacgroups[0] = dacs[0];
+ this->dacgroups[1] = dacs[1];
+ this->dacgroups[2] = dacs[2];
+
+ this->nadcs = 3;
+ this->adcs[0] = 6; /* XXX four channel recording */
+ this->adcs[1] = 7;
+ this->adcs[2] = 9; /* digital */
+ return 0;
+}
diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci
index 025f7df6d9b..0bfbdd3797a 100644
--- a/sys/dev/pci/files.pci
+++ b/sys/dev/pci/files.pci
@@ -1,4 +1,4 @@
-# $OpenBSD: files.pci,v 1.202 2006/04/06 20:25:14 marco Exp $
+# $OpenBSD: files.pci,v 1.203 2006/04/26 15:53:08 jason Exp $
# $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $
#
# Config file and device description for machine-independent PCI code.
@@ -104,6 +104,12 @@ device auich: audio, auconv, mulaw, ac97
attach auich at pci
file dev/pci/auich.c auich
+# Intel 82801 HDA
+device azalia: audio, auconv, mulaw, ac97
+attach azalia at pci
+file dev/pci/azalia.c azalia
+file dev/pci/azalia_codec.c azalia
+
# Creative Labs EMU10k1 (SBLive! series and PCI512)
device emu: audio, auconv, mulaw, ac97
attach emu at pci