diff options
| author | jmcneill <jmcneill@NetBSD.org> | 2020-02-09 16:06:17 +0000 |
|---|---|---|
| committer | jmcneill <jmcneill@NetBSD.org> | 2020-02-09 16:06:17 +0000 |
| commit | 41eed51bcd4f205bc6fc7926b3380043ffd78b4f (patch) | |
| tree | 65f5b9e00630a32783690334551c5db14a8bf8a7 /sys/dev | |
| parent | 80f479d86218ae5e2acf27865a7860eb18c4417a (diff) | |
Retire azalia(4).
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/DEVNAMES | 3 | ||||
| -rw-r--r-- | sys/dev/pci/azalia.c | 2436 | ||||
| -rw-r--r-- | sys/dev/pci/azalia.h | 594 | ||||
| -rw-r--r-- | sys/dev/pci/azalia_codec.c | 4278 | ||||
| -rw-r--r-- | sys/dev/pci/files.pci | 8 |
5 files changed, 2 insertions, 7317 deletions
diff --git a/sys/dev/DEVNAMES b/sys/dev/DEVNAMES index b1c753bb950..48b620e312c 100644 --- a/sys/dev/DEVNAMES +++ b/sys/dev/DEVNAMES @@ -1,4 +1,4 @@ -# $NetBSD: DEVNAMES,v 1.326 2020/01/17 15:00:20 maya Exp $ +# $NetBSD: DEVNAMES,v 1.327 2020/02/09 16:06:18 jmcneill Exp $ # # This file contains all used device names and defined attributes in # alphabetical order. New devices added to the system somewhere should first @@ -150,7 +150,6 @@ avmebus atari awi MI axe MI az MI -azalia MI bah MI bandit macppc bba MI diff --git a/sys/dev/pci/azalia.c b/sys/dev/pci/azalia.c deleted file mode 100644 index 48ba674171c..00000000000 --- a/sys/dev/pci/azalia.c +++ /dev/null @@ -1,2436 +0,0 @@ -/* $NetBSD: azalia.c,v 1.88 2019/06/08 08:02:38 isaki Exp $ */ - -/*- - * Copyright (c) 2005, 2008 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. - * - * 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. - */ - -/* - * High Definition Audio Specification - * ftp://download.intel.com/standards/hdaudio/pdf/HDAudio_03.pdf - * - * - * TO DO: - * - power hook - * - multiple codecs (needed?) - * - multiple streams (needed?) - */ - -#include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: azalia.c,v 1.88 2019/06/08 08:02:38 isaki Exp $"); - -#include <sys/param.h> -#include <sys/device.h> -#include <sys/fcntl.h> -#include <sys/kmem.h> -#include <sys/systm.h> -#include <sys/module.h> - -#include <dev/audio/audio_if.h> - -#include <dev/pci/pcidevs.h> -#include <dev/pci/pcivar.h> -#include <dev/pci/azalia.h> - -/* ---------------------------------------------------------------- - * 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; - void *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; - -/* XXXfreza use bus_space_subregion() instead of adding 'regbase' offset */ -#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 { - device_t dev; - device_t audiodev; - kmutex_t lock; - kmutex_t intr_lock; - - pci_chipset_tag_t pc; - pcitag_t tag; - void *ih; - bus_space_tag_t iot; - bus_space_handle_t ioh; - bus_size_t map_size; - bus_dma_tag_t dmat; - pcireg_t pciid; - uint32_t subid; - - 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; -#define UNSOLQ_SIZE 256 - rirb_entry_t *unsolq; - int unsolq_wp; - int unsolq_rp; - bool unsolq_kick; - - bool ok64; - int nistreams, nostreams, nbstreams; - stream_t pstream; - stream_t rstream; - - int mode_cap; -} azalia_t; - -#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 */ -static int azalia_pci_match(device_t, cfdata_t, void *); -static void azalia_pci_attach(device_t, device_t, void *); -static int azalia_pci_detach(device_t, int); -static bool azalia_pci_resume(device_t, const pmf_qual_t *); -static void azalia_childdet(device_t, device_t); -static int azalia_intr(void *); -static int azalia_attach(azalia_t *); -static void azalia_attach_intr(device_t); -static int azalia_init_corb(azalia_t *, int); -static int azalia_delete_corb(azalia_t *); -static int azalia_init_rirb(azalia_t *, int); -static int azalia_delete_rirb(azalia_t *); -static int azalia_set_command(const azalia_t *, nid_t, int, uint32_t, - uint32_t); -static int azalia_get_response(azalia_t *, uint32_t *); -static void azalia_rirb_kick_unsol_events(azalia_t *); -static void azalia_rirb_intr(azalia_t *); -static int azalia_alloc_dmamem(azalia_t *, size_t, size_t, azalia_dma_t *); -static int azalia_free_dmamem(const azalia_t *, azalia_dma_t*); - -static int azalia_codec_init(codec_t *, int, uint32_t); -static int azalia_codec_delete(codec_t *); -static void azalia_codec_add_bits(codec_t *, int, uint32_t, int); -static void azalia_codec_add_format(codec_t *, int, int, int, uint32_t, - int32_t); -static int azalia_codec_comresp(const codec_t *, nid_t, uint32_t, - uint32_t, uint32_t *); -static int azalia_codec_connect_stream(codec_t *, int, uint16_t, int); -static int azalia_codec_disconnect_stream(codec_t *, int); - -static int azalia_widget_init(widget_t *, const codec_t *, int, const char *); -static int azalia_widget_init_audio(widget_t *, const codec_t *, const char *); -#ifdef AZALIA_DEBUG -static int azalia_widget_print_audio(const widget_t *, const char *, int); -#endif -static int azalia_widget_init_pin(widget_t *, const codec_t *); -static int azalia_widget_print_pin(const widget_t *, const char *); -static int azalia_widget_init_connection(widget_t *, const codec_t *, const char *); - -static int azalia_stream_init(stream_t *, azalia_t *, int, int, int); -static int azalia_stream_delete(stream_t *, azalia_t *); -static int azalia_stream_reset(stream_t *); -static int azalia_stream_start(stream_t *, void *, void *, int, - void (*)(void *), void *, uint16_t); -static int azalia_stream_halt(stream_t *); -static int azalia_stream_intr(stream_t *, uint32_t); - -static int azalia_open(void *, int); -static void azalia_close(void *); -static int azalia_query_format(void *, audio_format_query_t *); -static int azalia_set_format(void *, int, - const audio_params_t *, const audio_params_t *, - audio_filter_reg_t *, audio_filter_reg_t *); -static int azalia_round_blocksize(void *, int, int, const audio_params_t *); -static int azalia_halt_output(void *); -static int azalia_halt_input(void *); -static int azalia_getdev(void *, struct audio_device *); -static int azalia_set_port(void *, mixer_ctrl_t *); -static int azalia_get_port(void *, mixer_ctrl_t *); -static int azalia_query_devinfo(void *, mixer_devinfo_t *); -static void *azalia_allocm(void *, int, size_t); -static void azalia_freem(void *, void *, size_t); -static size_t azalia_round_buffersize(void *, int, size_t); -static int azalia_get_props(void *); -static int azalia_trigger_output(void *, void *, void *, int, - void (*)(void *), void *, const audio_params_t *); -static int azalia_trigger_input(void *, void *, void *, int, - void (*)(void *), void *, const audio_params_t *); -static void azalia_get_locks(void *, kmutex_t **, kmutex_t **); - -static int azalia_params2fmt(const audio_params_t *, uint16_t *); - -/* variables */ -CFATTACH_DECL2_NEW(azalia, sizeof(azalia_t), - azalia_pci_match, azalia_pci_attach, azalia_pci_detach, NULL, - NULL, azalia_childdet); - -static const struct audio_hw_if azalia_hw_if = { - .open = azalia_open, - .close = azalia_close, - .query_format = azalia_query_format, - .set_format = azalia_set_format, - .round_blocksize = azalia_round_blocksize, - .halt_output = azalia_halt_output, - .halt_input = azalia_halt_input, - .getdev = azalia_getdev, - .set_port = azalia_set_port, - .get_port = azalia_get_port, - .query_devinfo = azalia_query_devinfo, - .allocm = azalia_allocm, - .freem = azalia_freem, - .round_buffersize = azalia_round_buffersize, - .get_props = azalia_get_props, - .trigger_output = azalia_trigger_output, - .trigger_input = azalia_trigger_input, - .get_locks = azalia_get_locks, -}; - -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 - * ================================================================ */ - -static int -azalia_pci_match(device_t parent, cfdata_t 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_MULTIMEDIA_HDAUDIO) - return 1; - return 0; -} - -static void -azalia_pci_attach(device_t parent, device_t self, void *aux) -{ - azalia_t *sc = device_private(self); - struct pci_attach_args *pa = aux; - pcireg_t v; - pci_intr_handle_t ih; - const char *intrrupt_str; - char vendor[PCI_VENDORSTR_LEN]; - char product[PCI_PRODUCTSTR_LEN]; - char intrbuf[PCI_INTRSTR_LEN]; - - sc->dev = self; - sc->dmat = pa->pa_dmat; - - aprint_normal(": Generic High Definition Audio Controller\n"); - - v = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_PCI_HDBARL); - v &= PCI_MAPREG_TYPE_MASK | PCI_MAPREG_MEM_TYPE_MASK; - if (pci_mapreg_map(pa, ICH_PCI_HDBARL, v, 0, - &sc->iot, &sc->ioh, NULL, &sc->map_size)) { - aprint_error_dev(self, "can't map device i/o space\n"); - 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)) { - aprint_error_dev(self, "can't map interrupt\n"); - return; - } - - mutex_init(&sc->lock, MUTEX_DEFAULT, IPL_NONE); - mutex_init(&sc->intr_lock, MUTEX_DEFAULT, IPL_AUDIO); - - sc->pc = pa->pa_pc; - sc->tag = pa->pa_tag; - intrrupt_str = pci_intr_string(pa->pa_pc, ih, intrbuf, sizeof(intrbuf)); - sc->ih = pci_intr_establish_xname(pa->pa_pc, ih, IPL_AUDIO, azalia_intr, - sc, device_xname(self)); - if (sc->ih == NULL) { - aprint_error_dev(self, "can't establish interrupt"); - if (intrrupt_str != NULL) - aprint_error(" at %s", intrrupt_str); - aprint_error("\n"); - mutex_destroy(&sc->lock); - mutex_destroy(&sc->intr_lock); - return; - } - aprint_normal_dev(self, "interrupting at %s\n", intrrupt_str); - - if (!pmf_device_register(self, NULL, azalia_pci_resume)) - aprint_error_dev(self, "couldn't establish power handler\n"); - - sc->pciid = pa->pa_id; - pci_findvendor(vendor, sizeof(vendor), PCI_VENDOR(pa->pa_id)); - pci_findproduct(product, sizeof(product), PCI_VENDOR(pa->pa_id), - PCI_PRODUCT(pa->pa_id)); - aprint_normal_dev(self, "host: %s %s (rev. %d)", - vendor, product, PCI_REVISION(pa->pa_class)); - - if (azalia_attach(sc)) { - aprint_error_dev(self, "initialization failure\n"); - azalia_pci_detach(self, 0); - mutex_destroy(&sc->lock); - mutex_destroy(&sc->intr_lock); - return; - } - sc->subid = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG); - - config_interrupts(self, azalia_attach_intr); -} - -static void -azalia_childdet(device_t self, device_t child) -{ - azalia_t *az = device_private(self); - - KASSERT(az->audiodev == child); - az->audiodev = NULL; -} - -static int -azalia_pci_detach(device_t self, int flags) -{ - azalia_t *az; - int i; - - DPRINTF(("%s\n", __func__)); - az = device_private(self); - if (az->audiodev != NULL) - config_detach(az->audiodev, flags); - - mutex_enter(&az->lock); - -#if notyet - DPRINTF(("%s: halt streams\n", __func__)); - azalia_stream_halt(&az->rstream); - azalia_stream_halt(&az->pstream); -#endif - - - DPRINTF(("%s: delete streams\n", __func__)); - azalia_stream_delete(&az->rstream, az); - azalia_stream_delete(&az->pstream, az); - - DPRINTF(("%s: delete codecs\n", __func__)); - for (i = 0; i < az->ncodecs; i++) { - azalia_codec_delete(&az->codecs[i]); - } - az->ncodecs = 0; - - DPRINTF(("%s: delete CORB and RIRB\n", __func__)); - azalia_delete_corb(az); - azalia_delete_rirb(az); - - mutex_exit(&az->lock); - mutex_destroy(&az->lock); - mutex_destroy(&az->intr_lock); - - DPRINTF(("%s: delete PCI resources\n", __func__)); - 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; -} - -static bool -azalia_pci_resume(device_t dv, const pmf_qual_t *qual) -{ - azalia_t *az = device_private(dv); - - mutex_enter(&az->lock); - mutex_spin_enter(&az->intr_lock); - azalia_attach(az); - azalia_attach_intr(az->dev); - mutex_spin_exit(&az->intr_lock); - mutex_exit(&az->lock); - - return true; -} - -static int -azalia_intr(void *v) -{ - azalia_t *az; - int ret; - uint32_t intsts; - uint8_t rirbsts; - - az = v; - - if (!device_has_power(az->dev)) - return 0; - - mutex_spin_enter(&az->intr_lock); - - intsts = AZ_READ_4(az, INTSTS); - if (intsts == 0) { - mutex_spin_exit(&az->intr_lock); - return 0; - } - - ret = azalia_stream_intr(&az->pstream, intsts) + - azalia_stream_intr(&az->rstream, intsts); - - rirbsts = AZ_READ_1(az, RIRBSTS); - if (rirbsts & (HDA_RIRBSTS_RIRBOIS | HDA_RIRBSTS_RINTFL)) { - if (rirbsts & HDA_RIRBSTS_RINTFL) { - azalia_rirb_intr(az); - } else { - /*printf("[Overflow!]");*/ - } - AZ_WRITE_1(az, RIRBSTS, - rirbsts | HDA_RIRBSTS_RIRBOIS | HDA_RIRBSTS_RINTFL); - ret++; - } - - mutex_spin_exit(&az->intr_lock); - return ret; -} - -/* ================================================================ - * HDA controller functions - * ================================================================ */ - -static int -azalia_attach(azalia_t *az) -{ - int i, n; - uint32_t gctl; - uint16_t gcap; - uint16_t statests; - - if (az->audiodev == NULL) - aprint_normal(", HDA rev. %d.%d\n", - 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", - device_xname(az->dev), az->nostreams, az->nistreams, az->nbstreams)); - if (az->nistreams > 0) - az->mode_cap |= AUMODE_RECORD; - if (az->nostreams > 0) - az->mode_cap |= AUMODE_PLAY; - - /* 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) { - aprint_error_dev(az->dev, "reset failure\n"); - 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) { - aprint_error_dev(az->dev, "reset-exit failure\n"); - return ETIMEDOUT; - } - - /* enable unsolicited response */ - gctl = AZ_READ_4(az, GCTL); - AZ_WRITE_4(az, GCTL, gctl | HDA_GCTL_UNSOL); - - /* 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", - device_xname(az->dev), i)); - az->codecs[n].address = i; - az->codecs[n++].dev = az->dev; - } - } - az->ncodecs = n; - if (az->ncodecs < 1) { - aprint_error_dev(az->dev, "No HD-Audio codecs\n"); - return -1; - } - return 0; -} - -static void -azalia_attach_intr(device_t self) -{ - azalia_t *az; - int err, i, c, reinit; - - az = device_private(self); - reinit = az->audiodev == NULL ? 0 : 1; - - 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 */ - if (azalia_init_corb(az, reinit)) - goto err_exit; - /* 4.4.2 Response Inbound Ring Buffer */ - if (azalia_init_rirb(az, reinit)) - goto err_exit; - - 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], reinit, az->subid); - 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", - device_xname(az->dev), az->codecno)); - if (az->codecs[c].dacs.ngroups <= 0) - az->mode_cap &= ~AUMODE_PLAY; - if (az->codecs[c].adcs.ngroups <= 0) - az->mode_cap &= ~AUMODE_RECORD; - - /* Use stream#1 and #2. Don't use stream#0. */ - if (reinit == 0) { - 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; -} - -static int -azalia_init_corb(azalia_t *az, int reinit) -{ - 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) { - aprint_error_dev(az->dev, "CORB is running\n"); - 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 { - aprint_error_dev(az->dev, "Invalid CORBSZCAP: 0x%2x\n", cap); - return -1; - } - - if (reinit == 0) { - err = azalia_alloc_dmamem(az, entries * sizeof(corb_entry_t), - 128, &az->corb_dma); - if (err) { - aprint_error_dev(az->dev, "can't allocate CORB buffer\n"); - 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); - 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) { - aprint_error_dev(az->dev, "CORBRP reset failure\n"); - 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; -} - -static 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; -} - -static int -azalia_init_rirb(azalia_t *az, int reinit) -{ - 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) { - aprint_error_dev(az->dev, "RIRB is running\n"); - 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 { - aprint_error_dev(az->dev, "Invalid RIRBSZCAP: 0x%2x\n", cap); - return -1; - } - - if (reinit == 0) { - err = azalia_alloc_dmamem(az, entries * sizeof(rirb_entry_t), - 128, &az->rirb_dma); - if (err) { - aprint_error_dev(az->dev, "can't allocate RIRB buffer\n"); - 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__)); - - /* setup the unsolicited response queue */ - az->unsolq_rp = 0; - az->unsolq_wp = 0; - az->unsolq_kick = FALSE; - if (reinit == 0) { - az->unsolq = kmem_zalloc(sizeof(rirb_entry_t) * UNSOLQ_SIZE, - KM_SLEEP); - } else { - memset(az->unsolq, 0, sizeof(rirb_entry_t) * UNSOLQ_SIZE); - } - if (az->unsolq == NULL) { - aprint_error_dev(az->dev, "can't allocate unsolicited response queue.\n"); - azalia_free_dmamem(az, &az->rirb_dma); - return ENOMEM; - } - -#if notyet - rirbctl = AZ_READ_1(az, RIRBCTL); - AZ_WRITE_1(az, RIRBCTL, rirbctl & ~HDA_RIRBCTL_RINTCTL); -#endif - - /* 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); - for (i = 5000; i >= 0; i--) { - DELAY(10); - rirbctl = AZ_READ_1(az, RIRBCTL); - if (rirbctl & HDA_RIRBCTL_RIRBDMAEN) - break; - } - if (i <= 0) { - aprint_error_dev(az->dev, "RIRB is not running\n"); - return EBUSY; - } - - return 0; -} - -static int -azalia_delete_rirb(azalia_t *az) -{ - int i; - uint8_t rirbctl; - - if (az->unsolq != NULL) { - kmem_free(az->unsolq, UNSOLQ_SIZE); - az->unsolq = NULL; - } - 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; -} - -static 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) { - aprint_error_dev(az->dev, "CORB is not running.\n"); - 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; -} - -static 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) { - aprint_error_dev(az->dev, "RIRB is not running.\n"); - 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) { - aprint_error_dev(az->dev, "RIRB time out\n"); - 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_RESP_UNSOL) { - az->unsolq[az->unsolq_wp].resp = rirb[az->rirb_rp].resp; - az->unsolq[az->unsolq_wp++].resp_ex = rirb[az->rirb_rp].resp_ex; - az->unsolq_wp %= UNSOLQ_SIZE; - } else - break; - } - if (result != NULL) - *result = rirb[az->rirb_rp].resp; - azalia_rirb_kick_unsol_events(az); -#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)); - 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; -} - -static void -azalia_rirb_kick_unsol_events(azalia_t *az) -{ - if (az->unsolq_kick) - return; - az->unsolq_kick = TRUE; - while (az->unsolq_rp != az->unsolq_wp) { - int i; - int tag; - codec_t *codec; - i = RIRB_RESP_CODEC(az->unsolq[az->unsolq_rp].resp_ex); - tag = RIRB_UNSOL_TAG(az->unsolq[az->unsolq_rp].resp); - codec = &az->codecs[i]; - DPRINTF(("%s: codec#=%d tag=%d\n", __func__, i, tag)); - az->unsolq_rp++; - az->unsolq_rp %= UNSOLQ_SIZE; - if (codec->unsol_event != NULL) - codec->unsol_event(codec, tag); - } - az->unsolq_kick = FALSE; -} - -static void -azalia_rirb_intr(azalia_t *az) -{ - const rirb_entry_t *rirb; - uint16_t wp, newrp; - - wp = AZ_READ_2(az, RIRBWP) & HDA_RIRBWP_RIRBWP; - if (az->rirb_rp == wp) - return; /* interrupted but no data in RIRB */ - /* Copy the first sequence of unsolicited reponses in the RIRB to - * unsolq. Don't consume non-unsolicited responses. */ - rirb = (rirb_entry_t*)az->rirb_dma.addr; - while (az->rirb_rp != wp) { - newrp = az->rirb_rp + 1; - if (newrp >= az->rirb_size) - newrp = 0; - if (rirb[newrp].resp_ex & RIRB_RESP_UNSOL) { - az->unsolq[az->unsolq_wp].resp = rirb[newrp].resp; - az->unsolq[az->unsolq_wp++].resp_ex = rirb[newrp].resp_ex; - az->unsolq_wp %= UNSOLQ_SIZE; - az->rirb_rp = newrp; - } else { - break; - } - } - azalia_rirb_kick_unsol_events(az); -} - -static 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_WAITOK); - if (err) - return err; - if (nsegs != 1) - goto free; - err = bus_dmamem_map(az->dmat, d->segments, 1, size, - &d->addr, BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_NOCACHE); - if (err) - goto free; - err = bus_dmamap_create(az->dmat, size, 1, size, 0, - BUS_DMA_WAITOK, &d->map); - if (err) - goto unmap; - err = bus_dmamap_load(az->dmat, d->map, d->addr, size, - NULL, BUS_DMA_WAITOK); - 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; -} - -static 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 coodec functions - * ================================================================ */ - -static int -azalia_codec_init(codec_t *this, int reinit, uint32_t subid) -{ -#define LEAD_LEN 100 - char lead[LEAD_LEN]; - uint32_t rev, id, result; - int err, addr, n, i; - - this->comresp = azalia_codec_comresp; - addr = this->address; - DPRINTF(("%s: information of codec[%d] follows:\n", - device_xname(this->dev), 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, &id); - if (err) - return err; - this->vid = id; - this->subid = subid; - - if (!reinit) { - err = azalia_codec_init_vtbl(this); - if (err) - return err; - } - - if (!reinit) { - aprint_normal("%s: codec[%d]: ", device_xname(this->dev), addr); - if (this->name == NULL) { - aprint_normal("0x%4.4x/0x%4.4x (rev. %u.%u)", - id >> 16, id & 0xffff, - COP_RID_REVISION(rev), COP_RID_STEPPING(rev)); - } else { - aprint_normal("%s (rev. %u.%u)", this->name, - COP_RID_REVISION(rev), COP_RID_STEPPING(rev)); - } - aprint_normal(", HDA rev. %u.%u\n", - 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) { - aprint_error("%s: No function groups\n", - device_xname(this->dev)); - 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? */ - } else if (COP_FTYPE(result) == COP_FTYPE_MODEM && !reinit) { - aprint_normal("%s: codec[%d]: No support for modem " - "function groups\n", - device_xname(this->dev), addr); - } - } - if (this->audiofunc < 0 && !reinit) { - aprint_verbose("%s: codec[%d] has no audio function groups\n", - device_xname(this->dev), 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) { - if (!reinit) - aprint_error("%s: invalid node structure\n", - device_xname(this->dev)); - return -1; - } - this->wend = this->wstart + COP_NSUBNODES(result); - if (!reinit) { - this->w = kmem_zalloc(sizeof(widget_t) * this->wend, - KM_SLEEP); - if (this->w == NULL) { - aprint_error("%s: out of memory\n", - device_xname(this->dev)); - return ENOMEM; - } - } else - memset(this->w, 0, 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; - lead[0] = 0; -#ifdef AZALIA_DEBUG - snprintf(lead, LEAD_LEN, "%s: ", device_xname(this->dev)); - azalia_widget_print_audio(&this->w[this->audiofunc], lead, -1); - result = this->w[this->audiofunc].inamp_cap; - DPRINTF(("%sinamp: mute=%u size=%u steps=%u offset=%u\n", lead, - (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(("%soutamp: mute=%u size=%u steps=%u offset=%u\n", lead, - (result & COP_AMPCAP_MUTE) != 0, COP_AMPCAP_STEPSIZE(result), - COP_AMPCAP_NUMSTEPS(result), COP_AMPCAP_OFFSET(result))); -#endif - - strlcpy(this->w[CORB_NID_ROOT].name, "root", - sizeof(this->w[CORB_NID_ROOT].name)); - strlcpy(this->w[this->audiofunc].name, "hdaudio", - sizeof(this->w[this->audiofunc].name)); - FOR_EACH_WIDGET(this, i) { - err = azalia_widget_init(&this->w[i], this, i, lead); - if (err) - return err; - } -#if defined(AZALIA_DEBUG) && defined(AZALIA_DEBUG_DOT) - DPRINTF(("-------- Graphviz DOT starts\n")); - if (this->name == NULL) { - DPRINTF(("digraph \"0x%4.4x/0x%4.4x (rev. %u.%u)\" {\n", - id >> 16, id & 0xffff, - COP_RID_REVISION(rev), COP_RID_STEPPING(rev))); - } else { - DPRINTF(("digraph \"%s (rev. %u.%u)\" {\n", this->name, - COP_RID_REVISION(rev), COP_RID_STEPPING(rev))); - } - FOR_EACH_WIDGET(this, i) { - const widget_t *w; - int j; - w = &this->w[i]; - switch (w->type) { - case COP_AWTYPE_AUDIO_OUTPUT: - DPRINTF((" %s [shape=box,style=filled,fillcolor=\"" - "#88ff88\"];\n", w->name)); - break; - case COP_AWTYPE_AUDIO_INPUT: - DPRINTF((" %s [shape=box,style=filled,fillcolor=\"" - "#ff8888\"];\n", w->name)); - break; - case COP_AWTYPE_AUDIO_MIXER: - DPRINTF((" %s [shape=invhouse];\n", w->name)); - break; - case COP_AWTYPE_AUDIO_SELECTOR: - DPRINTF((" %s [shape=invtrapezium];\n", w->name)); - break; - case COP_AWTYPE_PIN_COMPLEX: - DPRINTF((" %s [label=\"%s\\ndevice=%s\",style=filled", - w->name, w->name, pin_devices[w->d.pin.device])); - if (w->d.pin.cap & COP_PINCAP_OUTPUT && - w->d.pin.cap & COP_PINCAP_INPUT) - DPRINTF((",shape=doublecircle,fillcolor=\"" - "#ffff88\"];\n")); - else if (w->d.pin.cap & COP_PINCAP_OUTPUT) - DPRINTF((",shape=circle,fillcolor=\"#88ff88\"];\n")); - else - DPRINTF((",shape=circle,fillcolor=\"#ff8888\"];\n")); - break; - } - if ((w->widgetcap & COP_AWCAP_CONNLIST) == 0) - continue; - for (j = 0; j < w->nconnections; j++) { - int src = w->connections[j]; - if (!VALID_WIDGET_NID(src, this)) - continue; - DPRINTF((" %s -> %s [sametail=%s];\n", - this->w[src].name, w->name, this->w[src].name)); - } - } - - DPRINTF((" {rank=min;")); - FOR_EACH_WIDGET(this, i) { - const widget_t *w; - w = &this->w[i]; - switch (w->type) { - case COP_AWTYPE_AUDIO_OUTPUT: - case COP_AWTYPE_AUDIO_INPUT: - DPRINTF((" %s;", w->name)); - break; - } - } - DPRINTF(("}\n")); - - DPRINTF((" {rank=max;")); - FOR_EACH_WIDGET(this, i) { - const widget_t *w; - w = &this->w[i]; - switch (w->type) { - case COP_AWTYPE_PIN_COMPLEX: - DPRINTF((" %s;", w->name)); - break; - } - } - DPRINTF(("}\n")); - - DPRINTF(("}\n")); - DPRINTF(("-------- Graphviz DOT ends\n")); -#endif /* AZALIA_DEBUG && AZALIA_DEBUG_DOT */ - - err = this->init_dacgroup(this); - if (err) - return err; -#ifdef AZALIA_DEBUG - for (i = 0; i < this->dacs.ngroups; i++) { - DPRINTF(("%s: dacgroup[%d]:", __func__, i)); - for (n = 0; n < this->dacs.groups[i].nconv; n++) { - DPRINTF((" %2.2x", this->dacs.groups[i].conv[n])); - } - DPRINTF(("\n")); - } -#endif - - /* set invalid values for azalia_codec_construct_format() to work */ - this->dacs.cur = -1; - this->adcs.cur = -1; - err = azalia_codec_construct_format(this, - this->dacs.ngroups > 0 ? 0 : -1, - this->adcs.ngroups > 0 ? 0 : -1); - if (err) - return err; - - return this->mixer_init(this); -} - -static int -azalia_codec_delete(codec_t *this) -{ - if (this->mixer_delete != NULL) - this->mixer_delete(this); - if (this->formats != NULL) { - kmem_free(this->formats, this->szformats); - this->formats = NULL; - } - if (this->extra != NULL) { - kmem_free(this->extra, this->szextra); - this->extra = NULL; - } - return 0; -} - -int -azalia_codec_construct_format(codec_t *this, int newdac, int newadc) -{ -#ifdef AZALIA_DEBUG - char flagbuf[FLAGBUFLEN]; - int prev_dac = this->dacs.cur; - int prev_adc = this->adcs.cur; -#endif - const convgroup_t *group; - uint32_t bits_rates; - int variation; - int nbits, c, chan, i; - nid_t nid; - - variation = 0; - chan = 0; - - if (newdac >= 0 && newdac < this->dacs.ngroups) { - this->dacs.cur = newdac; - group = &this->dacs.groups[this->dacs.cur]; - 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) { - aprint_error("%s: invalid PCM format: 0x%8.8x\n", - device_xname(this->dev), bits_rates); - return -1; - } - variation = group->nconv * nbits; - } - - if (newadc >= 0 && newadc < this->adcs.ngroups) { - this->adcs.cur = newadc; - group = &this->adcs.groups[this->adcs.cur]; - 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) { - aprint_error("%s: invalid PCM format: 0x%8.8x\n", - device_xname(this->dev), bits_rates); - return -1; - } - variation += group->nconv * nbits; - } - - if (this->formats != NULL) - kmem_free(this->formats, this->szformats); - this->nformats = 0; - this->szformats = sizeof(struct audio_format) * variation; - this->formats = kmem_zalloc(this->szformats, KM_SLEEP); - - /* register formats for playback */ - if (this->dacs.cur >= 0 && this->dacs.cur < this->dacs.ngroups) { - group = &this->dacs.groups[this->dacs.cur]; - for (c = 0; c < group->nconv; c++) { - chan = 0; - bits_rates = ~0; - for (i = 0; i <= c; i++) { - nid = group->conv[i]; - chan += WIDGET_CHANNELS(&this->w[nid]); - bits_rates &= this->w[nid].d.audio.bits_rates; - } - azalia_codec_add_bits(this, chan, - bits_rates, AUMODE_PLAY); - } -#ifdef AZALIA_DEBUG - /* print playback capability */ - if (prev_dac != this->dacs.cur) { - snprintf(flagbuf, FLAGBUFLEN, "%s: playback: ", - device_xname(this->dev)); - azalia_widget_print_audio(&this->w[group->conv[0]], - flagbuf, chan); - } -#endif - } - - /* register formats for recording */ - if (this->adcs.cur >= 0 && this->adcs.cur < this->adcs.ngroups) { - group = &this->adcs.groups[this->adcs.cur]; - for (c = 0; c < group->nconv; c++) { - chan = 0; - bits_rates = ~0; - for (i = 0; i <= c; i++) { - nid = group->conv[i]; - chan += WIDGET_CHANNELS(&this->w[nid]); - bits_rates &= this->w[nid].d.audio.bits_rates; - } - azalia_codec_add_bits(this, chan, - bits_rates, AUMODE_RECORD); - } -#ifdef AZALIA_DEBUG - /* print recording capability */ - if (prev_adc != this->adcs.cur) { - snprintf(flagbuf, FLAGBUFLEN, "%s: recording: ", - device_xname(this->dev)); - azalia_widget_print_audio(&this->w[group->conv[0]], - flagbuf, chan); - } -#endif - } - -#ifdef DIAGNOSTIC - if (this->nformats > variation) { - aprint_error("%s: Internal error: the format buffer is too small: " - "nformats=%d variation=%d\n", device_xname(this->dev), - this->nformats, variation); - return ENOMEM; - } -#endif - - return 0; -} - -static 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); -} - -static 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; - } - f->frequency_type = 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; -} - -static int -azalia_codec_comresp(const codec_t *codec, nid_t nid, uint32_t control, - uint32_t param, uint32_t* result) -{ - azalia_t *az = device_private(codec->dev); - int err; - - err = azalia_set_command(az, codec->address, nid, control, param); - if (err == 0) - err = azalia_get_response(az, result); - return err; -} - -static int -azalia_codec_connect_stream(codec_t *this, int dir, uint16_t fmt, int number) -{ - const convgroup_t *group; - uint32_t v; - int i, err, startchan, nchan; - nid_t nid; - bool flag222; - - DPRINTF(("%s: fmt=0x%4.4x number=%d\n", __func__, fmt, number)); - err = 0; - if (dir == AUMODE_RECORD) - group = &this->adcs.groups[this->adcs.cur]; - else - group = &this->dacs.groups[this->dacs.cur]; - flag222 = group->nconv >= 3 && - (WIDGET_CHANNELS(&this->w[group->conv[0]]) == 2) && - (WIDGET_CHANNELS(&this->w[group->conv[1]]) == 2) && - (WIDGET_CHANNELS(&this->w[group->conv[2]]) == 2); - nchan = (fmt & HDA_SD_FMT_CHAN) + 1; - startchan = 0; - for (i = 0; i < group->nconv; i++) { - uint32_t stream_chan; - 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; - stream_chan = (number << 4) | startchan; - if (startchan >= nchan) - stream_chan = 0; /* stream#0 */ - err = this->comresp(this, nid, CORB_SET_CONVERTER_STREAM_CHANNEL, - stream_chan, NULL); - if (err) - goto exit; - if (this->w[nid].widgetcap & COP_AWCAP_DIGITAL) { - /* enable S/PDIF */ - this->comresp(this, nid, CORB_GET_DIGITAL_CONTROL, - 0, &v); - v = (v & 0xff) | CORB_DCC_DIGEN; - this->comresp(this, nid, CORB_SET_DIGITAL_CONTROL_L, - v, NULL); - } - startchan += WIDGET_CHANNELS(&this->w[nid]); - } - -exit: - DPRINTF(("%s: leave with %d\n", __func__, err)); - return err; -} - -static int -azalia_codec_disconnect_stream(codec_t *this, int dir) -{ - const convgroup_t *group; - uint32_t v; - int i; - nid_t nid; - - if (dir == AUMODE_RECORD) - group = &this->adcs.groups[this->adcs.cur]; - else - group = &this->dacs.groups[this->dacs.cur]; - for (i = 0; i < group->nconv; i++) { - nid = group->conv[i]; - this->comresp(this, nid, - CORB_SET_CONVERTER_STREAM_CHANNEL, 0, NULL);/* stream#0 */ - if (this->w[nid].widgetcap & COP_AWCAP_DIGITAL) { - /* disable S/PDIF */ - this->comresp(this, nid, - CORB_GET_DIGITAL_CONTROL, 0, &v); - v = (v & ~CORB_DCC_DIGEN) & 0xff; - this->comresp(this, nid, - CORB_SET_DIGITAL_CONTROL_L, v, NULL); - } - } - return 0; -} - -/* ================================================================ - * HDA widget functions - * ================================================================ */ - -static int -azalia_widget_init(widget_t *this, const codec_t *codec, - nid_t nid, const char *lead) -{ - char flagbuf[FLAGBUFLEN]; - 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); - snprintb(flagbuf, sizeof(flagbuf), - "\20\014LRSWAP\013POWER\012DIGITAL" - "\011CONNLIST\010UNSOL\07PROC\06STRIPE\05FORMATOV\04AMPOV\03OUTAMP" - "\02INAMP\01STEREO", this->widgetcap); - DPRINTF(("%s: ", device_xname(codec->dev))); - 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=%s\n", this->name, flagbuf)); - azalia_widget_init_audio(this, codec, lead); - break; - case COP_AWTYPE_AUDIO_INPUT: - snprintf(this->name, sizeof(this->name), "adc%2.2x", nid); - DPRINTF(("%s wcap=%s\n", this->name, flagbuf)); - azalia_widget_init_audio(this, codec, lead); - break; - case COP_AWTYPE_AUDIO_MIXER: - snprintf(this->name, sizeof(this->name), "mix%2.2x", nid); - DPRINTF(("%s wcap=%s\n", this->name, flagbuf)); - break; - case COP_AWTYPE_AUDIO_SELECTOR: - snprintf(this->name, sizeof(this->name), "sel%2.2x", nid); - DPRINTF(("%s wcap=%s\n", this->name, flagbuf)); - 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=%s\n", this->name, flagbuf)); - azalia_widget_print_pin(this, lead); - break; - case COP_AWTYPE_POWER: - snprintf(this->name, sizeof(this->name), "pow%2.2x", nid); - DPRINTF(("%s wcap=%s\n", this->name, flagbuf)); - break; - case COP_AWTYPE_VOLUME_KNOB: - snprintf(this->name, sizeof(this->name), "volume%2.2x", nid); - DPRINTF(("%s wcap=%s\n", this->name, flagbuf)); - err = codec->comresp(codec, nid, CORB_GET_PARAMETER, - COP_VOLUME_KNOB_CAPABILITIES, &result); - if (!err) { - this->d.volume.cap = result; - DPRINTF(("%sdelta=%d steps=%d\n", lead, - !!(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=%s\n", this->name, flagbuf)); - break; - default: - snprintf(this->name, sizeof(this->name), "widget%2.2x", nid); - DPRINTF(("%s wcap=%s\n", this->name, flagbuf)); - break; - } - azalia_widget_init_connection(this, codec, lead); - - /* 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(("%sinamp: mute=%u size=%u steps=%u offset=%u\n", - lead, (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(("%soutamp: mute=%u size=%u steps=%u offset=%u\n", - lead, (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; -} - -static int -azalia_widget_init_audio(widget_t *this, const codec_t *codec, const char *lead) -{ - 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 == 0) { /* quirk for CMI9880. - * This must not occuur usually... */ - this->d.audio.encodings = - codec->w[codec->audiofunc].d.audio.encodings; - this->d.audio.bits_rates = - codec->w[codec->audiofunc].d.audio.bits_rates; - } else { - if ((result & COP_STREAM_FORMAT_PCM) == 0) { - aprint_error("%s: %s: No PCM support: %x\n", - device_xname(codec->dev), 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, lead, -1); -#endif - return 0; -} - -#ifdef AZALIA_DEBUG -static int -azalia_widget_print_audio(const widget_t *this, const char *lead, int channels) -{ - char flagbuf[FLAGBUFLEN]; - - snprintb(flagbuf, sizeof(flagbuf), - "\20\3AC3\2FLOAT32\1PCM", this->d.audio.encodings); - if (channels < 0) { - aprint_normal("%sencodings=%s\n", lead, flagbuf); - } else if (this->widgetcap & COP_AWCAP_DIGITAL) { - aprint_normal("%smax channels=%d, DIGITAL, encodings=%s\n", - lead, channels, flagbuf); - } else { - aprint_normal("%smax channels=%d, encodings=%s\n", - lead, channels, flagbuf); - } - snprintb(flagbuf, sizeof(flagbuf), - "\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", - this->d.audio.bits_rates); - aprint_normal("%sPCM formats=%s\n", lead, flagbuf); - return 0; -} -#endif - -static 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; - - /* input pin */ - if ((this->d.pin.cap & COP_PINCAP_INPUT) && - (this->d.pin.cap & COP_PINCAP_OUTPUT) == 0) { - err = codec->comresp(codec, this->nid, - CORB_GET_PIN_WIDGET_CONTROL, 0, &result); - if (err == 0) { - result &= ~CORB_PWC_OUTPUT; - result |= CORB_PWC_INPUT; - codec->comresp(codec, this->nid, - CORB_SET_PIN_WIDGET_CONTROL, result, NULL); - } - } - /* output pin, or bidirectional pin */ - if (this->d.pin.cap & COP_PINCAP_OUTPUT) { - err = codec->comresp(codec, this->nid, - CORB_GET_PIN_WIDGET_CONTROL, 0, &result); - if (err == 0) { - result &= ~CORB_PWC_INPUT; - result |= CORB_PWC_OUTPUT; - codec->comresp(codec, this->nid, - CORB_SET_PIN_WIDGET_CONTROL, result, NULL); - } - } - return 0; -} - -static int -azalia_widget_print_pin(const widget_t *this, const char *lead) -{ - char flagbuf[FLAGBUFLEN]; - - DPRINTF(("%spin config; device=%s color=%s assoc=%d seq=%d", lead, - pin_devices[this->d.pin.device], pin_colors[this->d.pin.color], - this->d.pin.association, this->d.pin.sequence)); - snprintb(flagbuf, sizeof(flagbuf), - "\20\021EAPD\07BALANCE\06INPUT" - "\05OUTPUT\04HEADPHONE\03PRESENCE\02TRIGGER\01IMPEDANCE", - this->d.pin.cap); - DPRINTF((" cap=%s\n", flagbuf)); - return 0; -} - -static int -azalia_widget_init_connection(widget_t *this, const codec_t *codec, - const char *lead) -{ - uint32_t result; - int err; - bool 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 = kmem_alloc(sizeof(nid_t) * (length + 3), KM_SLEEP); - if (longform) { - for (i = 0; i < length;) { - 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++] = CORB_CLE_LONG_1(result); - } - } else { - for (i = 0; i < length;) { - 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++] = CORB_CLE_SHORT_1(result); - this->connections[i++] = CORB_CLE_SHORT_2(result); - this->connections[i++] = CORB_CLE_SHORT_3(result); - } - } - if (length > 0) { - DPRINTF(("%sconnections=0x%x", lead, 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 - * ================================================================ */ - -static 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) { - aprint_error_dev(az->dev, "can't allocate a BDL buffer\n"); - return err; - } - return 0; -} - -static 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; -} - -static int -azalia_stream_reset(stream_t *this) -{ - int i; - uint16_t ctl; - - if (this->bdlist.addr == NULL) - return EINVAL; - 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) { - aprint_error_dev(this->az->dev, "stream reset failure 1\n"); - 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) { - aprint_error_dev(this->az->dev, "stream reset failure 2\n"); - return -1; - } - return 0; -} - -static 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; - uint32_t intctl; - uint8_t ctl, ctl2; - - DPRINTF(("%s: start=%p end=%p\n", __func__, start, end)); - if (this->bdlist.addr == NULL) - return EINVAL; - 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 + ((char *)end - (char *)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; - 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, ((char *)end - (char *)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_4(this->az, INTCTL); - intctl |= this->intr_bit; - AZ_WRITE_4(this->az, INTCTL, intctl); - - ctl = STR_READ_1(this, CTL); - ctl |= ctl | HDA_SD_CTL_DEIE | HDA_SD_CTL_FEIE | HDA_SD_CTL_IOCE | HDA_SD_CTL_RUN; - STR_WRITE_1(this, CTL, ctl); - return 0; -} - -static int -azalia_stream_halt(stream_t *this) -{ - uint16_t ctl; - - if (this->bdlist.addr == NULL) - return EINVAL; - this->intr = this->intr_arg = NULL; - 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_4(this->az, INTCTL, AZ_READ_4(this->az, INTCTL) & ~this->intr_bit); - azalia_codec_disconnect_stream - (&this->az->codecs[this->az->codecno], this->dir); - return 0; -} - -static int -azalia_stream_intr(stream_t *this, uint32_t intsts) -{ - if (this->bdlist.addr == NULL) - return 0; - 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); - - if (this->intr != NULL) - this->intr(this->intr_arg); - return 1; -} - -/* ================================================================ - * MI audio entries - * ================================================================ */ - -static int -azalia_open(void *v, int flags) -{ - azalia_t *az; - codec_t *codec; - - DPRINTF(("%s: flags=0x%x\n", __func__, flags)); - az = v; - codec = &az->codecs[az->codecno]; - if (flags & FWRITE && (az->mode_cap & AUMODE_PLAY) == 0) - return EACCES; - if (flags & FREAD && (az->mode_cap & AUMODE_RECORD) == 0) - return EACCES; - codec->running++; - return 0; -} - -static void -azalia_close(void *v) -{ - azalia_t *az; - codec_t *codec; - - DPRINTF(("%s\n", __func__)); - az = v; - codec = &az->codecs[az->codecno]; - codec->running--; -} - -static int -azalia_query_format(void *v, audio_format_query_t *afp) -{ - azalia_t *az; - codec_t *codec; - - az = v; - codec = &az->codecs[az->codecno]; - return audio_query_format(codec->formats, codec->nformats, afp); -} - -static int -azalia_set_format(void *v, int setmode, - const audio_params_t *p, const audio_params_t *r, - audio_filter_reg_t *pfil, audio_filter_reg_t *rfil) -{ - - return 0; -} - -static int -azalia_round_blocksize(void *v, int blk, int mode, - const audio_params_t *param) -{ - 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 = mode == AUMODE_PLAY ? az->pstream.buffer.size : az->rstream.buffer.size; -#ifdef DIAGNOSTIC - if (size <= 0) { - aprint_error("%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; -} - -static int -azalia_halt_output(void *v) -{ - azalia_t *az; - - DPRINTF(("%s\n", __func__)); - az = v; - return azalia_stream_halt(&az->pstream); -} - -static int -azalia_halt_input(void *v) -{ - azalia_t *az; - - DPRINTF(("%s\n", __func__)); - az = v; - return azalia_stream_halt(&az->rstream); -} - -static 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, device_xname(az->dev), MAX_AUDIO_DEV_LEN); - return 0; -} - -static int -azalia_set_port(void *v, mixer_ctrl_t *mc) -{ - azalia_t *az; - codec_t *co; - - az = v; - co = &az->codecs[az->codecno]; - return co->set_port(co, mc); -} - -static int -azalia_get_port(void *v, mixer_ctrl_t *mc) -{ - azalia_t *az; - codec_t *co; - - az = v; - co = &az->codecs[az->codecno]; - return co->get_port(co, mc); -} - -static int -azalia_query_devinfo(void *v, mixer_devinfo_t *mdev) -{ - azalia_t *az; - const codec_t *co; - - az = v; - co = &az->codecs[az->codecno]; - if (mdev->index < 0 || mdev->index >= co->nmixers) - return ENXIO; - *mdev = co->mixers[mdev->index].devinfo; - return 0; -} - -static void * -azalia_allocm(void *v, int dir, size_t size) -{ - 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; -} - -static void -azalia_freem(void *v, void *addr, size_t size) -{ - 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); -} - -static 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; -} - -static int -azalia_get_props(void *v) -{ - - return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE | - AUDIO_PROP_INDEPENDENT | AUDIO_PROP_FULLDUPLEX; -} - -static int -azalia_trigger_output(void *v, void *start, void *end, int blk, - void (*intr)(void *), void *arg, const 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->validbits, 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); -} - -static int -azalia_trigger_input(void *v, void *start, void *end, int blk, - void (*intr)(void *), void *arg, const 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->validbits, 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 - * -------------------------------- */ -static int -azalia_params2fmt(const audio_params_t *param, uint16_t *fmt) -{ - uint16_t ret; - - ret = 0; -#ifdef DIAGNOSTIC - if (param->channels > HDA_MAX_CHANNELS) { - aprint_error("%s: too many channels: %u\n", __func__, - param->channels); - return EINVAL; - } -#endif - ret |= param->channels - 1; - - 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: - aprint_error("%s: invalid validbits: %u\n", __func__, - param->validbits); - } - - if (param->sample_rate == 384000) { - aprint_error("%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 { - aprint_error("%s: invalid sample_rate: %u\n", __func__, - param->sample_rate); - return EINVAL; - } - *fmt = ret; - return 0; -} - -MODULE(MODULE_CLASS_DRIVER, azalia, "pci"); - -static void -azalia_get_locks(void *addr, kmutex_t **intr, kmutex_t **thread) -{ - azalia_t *az; - - az = addr; - *intr = &az->intr_lock; - *thread = &az->lock; -} - -#ifdef _MODULE -#include "ioconf.c" -#endif - -static int -azalia_modcmd(modcmd_t cmd, void *arg) -{ - int error = 0; - - switch (cmd) { - case MODULE_CMD_INIT: -#ifdef _MODULE - error = config_init_component(cfdriver_ioconf_azalia, - cfattach_ioconf_azalia, cfdata_ioconf_azalia); -#endif - break; - case MODULE_CMD_FINI: -#ifdef _MODULE - error = config_fini_component(cfdriver_ioconf_azalia, - cfattach_ioconf_azalia, cfdata_ioconf_azalia); -#endif - break; - default: - return ENOTTY; - } - - return error; -} diff --git a/sys/dev/pci/azalia.h b/sys/dev/pci/azalia.h deleted file mode 100644 index 2b89c31b4ad..00000000000 --- a/sys/dev/pci/azalia.h +++ /dev/null @@ -1,594 +0,0 @@ -/* $NetBSD: azalia.h,v 1.22 2019/05/08 13:40:18 isaki Exp $ */ - -/*- - * Copyright (c) 2005, 2008 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. - * - * 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 0x00000100 -#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_CONTROL 0xf0d -#define CORB_SET_DIGITAL_CONTROL_L 0x70d -#define CORB_SET_DIGITAL_CONTROL_H 0x70e -#define CORB_DCC_DIGEN 0x01 -#define CORB_DCC_V 0x02 -#define CORB_DCC_VCFG 0x04 -#define CORB_DCC_PRE 0x08 -#define CORB_DCC_COPY 0x10 -#define CORB_DCC_NAUDIO 0x20 -#define CORB_DCC_PRO 0x40 -#define CORB_DCC_L 0x80 -#define CORB_DCC_CC(x) ((x >> 8) & 0x7f) -#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_UNSOL_ENABLE 0x80 -#define CORB_UNSOL_TAG(x) (x & 0x3f) -#define CORB_GET_PIN_SENSE 0xf09 -#define CORB_PS_PRESENCE 0x80000000 -#define CORB_PS_IMPEDANCE(x) (x & 0x7fffffff) -#define CORB_EXECUTE_PIN_SENSE 0x709 -#define CORB_PS_RIGHT 0x1 -#define CORB_GET_EAPD_BTL_ENABLE 0xf0c -#define CORB_SET_EAPD_BTL_ENABLE 0x70c -#define CORB_EAPD_BTL 0x01 -#define CORB_EAPD_EAPD 0x02 -#define CORB_EAPD_LRSWAP 0x04 -#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 - - -#ifndef PCI_SUBCLASS_MULTIMEDIA_HDAUDIO -#define PCI_SUBCLASS_MULTIMEDIA_HDAUDIO 0x03 -#endif - -/* 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_UNSOL_TAG(resp) ((resp) >> 26) -#define RIRB_RESP_UNSOL (1 << 4) -#define RIRB_RESP_CODEC(ex) ((ex) & 0xf) -} __packed rirb_entry_t; - - -/* #define AZALIA_DEBUG */ -/* #define AZALIA_DEBUG_DOT */ -#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)(uintptr_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; -#define WIDGET_CHANNELS(w) ((w)->widgetcap & COP_AWCAP_STEREO ? 2 : 1) - -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 -#define MI_TARGET_SPDIF 0x107 -#define MI_TARGET_SPDIF_CC 0x108 -#define MI_TARGET_EAPD 0x109 -#define MI_TARGET_BALANCE 0x10a -#define MI_TARGET_LRSWAP 0x10b -} mixer_item_t; - -#define VALID_WIDGET_NID(nid, codec) (nid == (codec)->audiofunc || \ - (nid >= (codec)->wstart && \ - nid < (codec)->wend)) - -#define PIN_STATUS(wid, conn) \ - do { \ - if ((wid)->type != COP_AWTYPE_PIN_COMPLEX) \ - (conn) = 0; \ - else \ - (conn) = \ - ((wid)->d.pin.config & CORB_CD_PORT_MASK) >> 30; \ - } while (0) - -typedef struct { - int nconv; - nid_t conv[HDA_MAX_CHANNELS]; /* front, surround, clfe, side, ... */ -} convgroup_t; -typedef struct { - int cur; - int ngroups; - convgroup_t groups[32]; -} convgroupset_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); - int (*mixer_init)(struct codec_t *); - int (*mixer_delete)(struct codec_t *); - int (*set_port)(struct codec_t *, mixer_ctrl_t *); - int (*get_port)(struct codec_t *, mixer_ctrl_t *); - int (*unsol_event)(struct codec_t *, int); - - device_t dev; /* parent azalia(4) instance */ - uint32_t vid; /* codec vendor/device ID */ - uint32_t subid; /* PCI subvendor/device ID */ - 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++) - - convgroupset_t dacs; - convgroupset_t adcs; - int running; - - int nmixers, maxmixers; - size_t szmixers; - mixer_item_t *mixers; - - struct audio_format *formats; - int nformats; - size_t szformats; - - uint32_t *extra; - size_t szextra; -} codec_t; - - -int azalia_codec_init_vtbl(codec_t *); -int azalia_codec_construct_format(codec_t *, int, int); diff --git a/sys/dev/pci/azalia_codec.c b/sys/dev/pci/azalia_codec.c deleted file mode 100644 index 5dad5e644c6..00000000000 --- a/sys/dev/pci/azalia_codec.c +++ /dev/null @@ -1,4278 +0,0 @@ -/* $NetBSD: azalia_codec.c,v 1.81 2017/07/28 01:36:41 nat Exp $ */ - -/*- - * Copyright (c) 2005, 2008 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. - * - * 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> -__KERNEL_RCSID(0, "$NetBSD: azalia_codec.c,v 1.81 2017/07/28 01:36:41 nat Exp $"); - -#include <sys/param.h> -#include <sys/device.h> -#include <sys/kmem.h> -#include <sys/null.h> -#include <sys/systm.h> - -#include <dev/pci/azalia.h> - -#ifdef MAX_VOLUME_255 -# define MIXER_DELTA(n) (AUDIO_MAX_GAIN / (n)) -#else -# define MIXER_DELTA(n) (1) -#endif -#define AZ_CLASS_INPUT 0 -#define AZ_CLASS_OUTPUT 1 -#define AZ_CLASS_RECORD 2 -#define AZ_CLASS_PLAYBACK 3 -#define AZ_CLASS_MIXER 4 -#define AzaliaCplayback "playback" -#define AzaliaCmixer "mix" -#define AzaliaNfront "front" -#define AzaliaNclfe "clfe" -#define AzaliaNside "side" -#define AzaliaNdigital "spdif" -#define ENUM_OFFON .un.e={2, {{{AudioNoff, 0}, 0}, {{AudioNon, 0}, 1}}} -#define ENUM_IO .un.e={2, {{{AudioNinput, 0}, 0}, {{AudioNoutput, 0}, 1}}} -#define ENUM_V .un.v={{ "", 0 }, 0, 0 } -#define AZ_MIXER_CLASSES \ - {{AZ_CLASS_INPUT, {AudioCinputs, 0}, AUDIO_MIXER_CLASS, \ - AZ_CLASS_INPUT, 0, 0, ENUM_V }, 0, 0, }, \ - {{AZ_CLASS_OUTPUT, {AudioCoutputs, 0}, AUDIO_MIXER_CLASS, \ - AZ_CLASS_OUTPUT, 0, 0, ENUM_V }, 0, 0, }, \ - {{AZ_CLASS_RECORD, {AudioCrecord, 0}, AUDIO_MIXER_CLASS, \ - AZ_CLASS_RECORD, 0, 0, ENUM_V }, 0, 0, }, \ - {{AZ_CLASS_PLAYBACK, {AzaliaCplayback, 0}, AUDIO_MIXER_CLASS, \ - AZ_CLASS_PLAYBACK, 0, 0, ENUM_V }, 0, 0, }, \ - {{AZ_CLASS_MIXER, {AzaliaCmixer, 0}, AUDIO_MIXER_CLASS, \ - AZ_CLASS_MIXER, 0, 0, ENUM_V }, 0, 0, } -#define AZ_MIXER_SPDIF(cl, nid) \ - {{0, {AzaliaNdigital, 0}, AUDIO_MIXER_SET, cl, 0, 0, \ - .un.s={6, {{{"v", 0}, CORB_DCC_V}, {{"vcfg", 0}, CORB_DCC_VCFG}, \ - {{"pre", 0}, CORB_DCC_PRE}, {{"copy", 0}, CORB_DCC_COPY}, \ - {{"pro", 0}, CORB_DCC_PRO}, {{"l", 0}, CORB_DCC_L}}}}, \ - nid, MI_TARGET_SPDIF}, \ - {{0, {AzaliaNdigital".cc", 0}, AUDIO_MIXER_VALUE, cl, 0, 0, \ - .un.v={{"", 0}, 1, 1}}, nid, MI_TARGET_SPDIF_CC} - - -static int generic_codec_init_dacgroup(codec_t *); -static int generic_codec_add_dacgroup(codec_t *, int, uint32_t); -static int generic_codec_find_pin(const codec_t *, int, int, uint32_t); -static int generic_codec_find_dac(const codec_t *, int, int); - -static int generic_mixer_init(codec_t *); -static int generic_mixer_autoinit(codec_t *); -static int generic_mixer_init_widget(const codec_t *, widget_t *, nid_t); - -static int generic_mixer_fix_indexes(codec_t *); -static int generic_mixer_default(codec_t *); -static int generic_mixer_pin_sense(codec_t *); -static int generic_mixer_widget_name(const codec_t *, widget_t *); -static int generic_mixer_create_virtual(codec_t *); -static int generic_mixer_delete(codec_t *); -static void generic_mixer_cat_names - (char *, size_t, const char *, const char *, const char *); -static int generic_mixer_ensure_capacity(codec_t *, size_t); -static int generic_mixer_get(const codec_t *, nid_t, int, mixer_ctrl_t *); -static int generic_mixer_set(codec_t *, nid_t, int, const mixer_ctrl_t *); -static int generic_mixer_pinctrl(codec_t *, nid_t, uint32_t); -static u_char generic_mixer_from_device_value - (const codec_t *, nid_t, int, uint32_t ); -static uint32_t generic_mixer_to_device_value - (const codec_t *, nid_t, int, u_char); -static uint32_t generic_mixer_max(const codec_t *, nid_t, int); -static bool generic_mixer_validate_value - (const codec_t *, nid_t, int, u_char); -static int generic_set_port(codec_t *, mixer_ctrl_t *); -static int generic_get_port(codec_t *, mixer_ctrl_t *); - -static int alc260_init_dacgroup(codec_t *); -static int alc260_mixer_init(codec_t *); -static int alc260_set_port(codec_t *, mixer_ctrl_t *); -static int alc260_get_port(codec_t *, mixer_ctrl_t *); -static int alc260_unsol_event(codec_t *, int); -static int alc262_init_widget(const codec_t *, widget_t *, nid_t); -static int alc268_init_dacgroup(codec_t *); -static int alc662_init_dacgroup(codec_t *); -static int alc861_init_dacgroup(codec_t *); -static int alc861vdgr_init_dacgroup(codec_t *); -static int alc880_init_dacgroup(codec_t *); -static int alc880_mixer_init(codec_t *); -static int alc882_init_dacgroup(codec_t *); -static int alc882_mixer_init(codec_t *); -static int alc882_set_port(codec_t *, mixer_ctrl_t *); -static int alc882_get_port(codec_t *, mixer_ctrl_t *); -static int alc883_init_dacgroup(codec_t *); -static int alc883_mixer_init(codec_t *); -static int alc885_init_dacgroup(codec_t *); -static int alc888_init_dacgroup(codec_t *); -static int alc888_init_widget(const codec_t *, widget_t *, nid_t); -static int alc888_mixer_init(codec_t *); -static int ad1981hd_init_widget(const codec_t *, widget_t *, nid_t); -static int ad1981hd_mixer_init(codec_t *); -static int ad1983_mixer_init(codec_t *); -static int ad1983_unsol_event(codec_t *, int); -static int ad1984_init_dacgroup(codec_t *); -static int ad1984_init_widget(const codec_t *, widget_t *, nid_t); -static int ad1984_mixer_init(codec_t *); -static int ad1984_unsol_event(codec_t *, int); -static int ad1986a_init_dacgroup(codec_t *); -static int ad1986a_mixer_init(codec_t *); -static int ad1988_init_dacgroup(codec_t *); -static int cmi9880_init_dacgroup(codec_t *); -static int cmi9880_mixer_init(codec_t *); -static int stac9221_init_dacgroup(codec_t *); -static int stac9221_mixer_init(codec_t *); -static int stac9221_gpio_unmute(codec_t *, int); -static int stac9200_mixer_init(codec_t *); -static int stac9200_unsol_event(codec_t *, int); -static int atihdmi_init_dacgroup(codec_t *); - - -int -azalia_codec_init_vtbl(codec_t *this) -{ - size_t extra_size; - - /** - * We can refer this->vid and this->subid. - */ - DPRINTF(("%s: vid=%08x subid=%08x\n", __func__, this->vid, this->subid)); - extra_size = 0; - this->name = NULL; - this->init_dacgroup = generic_codec_init_dacgroup; - this->mixer_init = generic_mixer_init; - this->mixer_delete = generic_mixer_delete; - this->set_port = generic_set_port; - this->get_port = generic_get_port; - this->unsol_event = NULL; - switch (this->vid) { - case 0x10027919: - case 0x1002793c: - this->name = "ATI RS600 HDMI"; - this->init_dacgroup = atihdmi_init_dacgroup; - break; - case 0x1002791a: - this->name = "ATI RS690/780 HDMI"; - this->init_dacgroup = atihdmi_init_dacgroup; - break; - case 0x1002aa01: - this->name = "ATI R600 HDMI"; - this->init_dacgroup = atihdmi_init_dacgroup; - break; - case 0x10ec0260: - this->name = "Realtek ALC260"; - this->mixer_init = alc260_mixer_init; - this->init_dacgroup = alc260_init_dacgroup; - this->set_port = alc260_set_port; - this->unsol_event = alc260_unsol_event; - extra_size = 1; - break; - case 0x10ec0262: - this->name = "Realtek ALC262"; - this->init_widget = alc262_init_widget; - break; - case 0x10ec0268: - this->name = "Realtek ALC268"; - this->init_dacgroup = alc268_init_dacgroup; - this->mixer_init = generic_mixer_autoinit; - this->init_widget = generic_mixer_init_widget; - break; - case 0x10ec0269: - this->name = "Realtek ALC269"; - this->mixer_init = generic_mixer_autoinit; - this->init_widget = generic_mixer_init_widget; - break; - case 0x10ec0662: - this->name = "Realtek ALC662-GR"; - this->init_dacgroup = alc662_init_dacgroup; - this->mixer_init = generic_mixer_autoinit; - this->init_widget = generic_mixer_init_widget; - break; - case 0x10ec0663: - this->name = "Realtek ALC663"; - this->init_dacgroup = alc662_init_dacgroup; - this->mixer_init = generic_mixer_autoinit; - this->init_widget = generic_mixer_init_widget; - break; - case 0x10ec0861: - this->name = "Realtek ALC861"; - this->init_dacgroup = alc861_init_dacgroup; - break; - case 0x10ec0862: - this->name = "Realtek ALC861-VD-GR"; - this->init_dacgroup = alc861vdgr_init_dacgroup; - break; - case 0x10ec0880: - this->name = "Realtek ALC880"; - this->init_dacgroup = alc880_init_dacgroup; - this->mixer_init = alc880_mixer_init; - break; - case 0x10ec0882: - this->name = "Realtek ALC882"; - this->init_dacgroup = alc882_init_dacgroup; - this->mixer_init = alc882_mixer_init; - this->get_port = alc882_get_port; - this->set_port = alc882_set_port; - break; - case 0x10ec0883: - /* ftp://209.216.61.149/pc/audio/ALC883_DataSheet_1.3.pdf */ - this->name = "Realtek ALC883"; - this->init_dacgroup = alc883_init_dacgroup; - this->mixer_init = alc883_mixer_init; - this->get_port = alc882_get_port; - this->set_port = alc882_set_port; - break; - case 0x10ec0885: - this->name = "Realtek ALC885"; - this->init_dacgroup = alc885_init_dacgroup; - this->mixer_init = generic_mixer_autoinit; - this->init_widget = generic_mixer_init_widget; - break; - case 0x10ec0888: - this->name = "Realtek ALC888"; - this->init_dacgroup = alc888_init_dacgroup; - this->init_widget = alc888_init_widget; - this->mixer_init = alc888_mixer_init; - break; - case 0x11d41981: - /* http://www.analog.com/en/prod/0,2877,AD1981HD,00.html */ - this->name = "Analog Devices AD1981HD"; - this->init_widget = ad1981hd_init_widget; - this->mixer_init = ad1981hd_mixer_init; - break; - case 0x11d41983: - /* http://www.analog.com/en/prod/0,2877,AD1983,00.html */ - this->name = "Analog Devices AD1983"; - this->mixer_init = ad1983_mixer_init; - this->unsol_event = ad1983_unsol_event; - break; - case 0x11d41984: - /* http://www.analog.com/en/prod/0,2877,AD1984,00.html */ - this->name = "Analog Devices AD1984"; - this->init_dacgroup = ad1984_init_dacgroup; - this->init_widget = ad1984_init_widget; - this->mixer_init = ad1984_mixer_init; - this->unsol_event = ad1984_unsol_event; - break; - case 0x11d4194a: - /* http://www.analog.com/static/imported-files/data_sheets/AD1984A.pdf */ - this->name = "Analog Devices AD1984A"; - this->init_dacgroup = ad1984_init_dacgroup; - this->init_widget = ad1984_init_widget; - this->mixer_init = ad1984_mixer_init; - this->unsol_event = ad1984_unsol_event; - break; - case 0x11d41986: - /* http://www.analog.com/en/prod/0,2877,AD1986A,00.html */ - this->name = "Analog Devices AD1986A"; - this->init_dacgroup = ad1986a_init_dacgroup; - this->mixer_init = ad1986a_mixer_init; - break; - case 0x11d41988: - /* http://www.analog.com/en/prod/0,2877,AD1988A,00.html */ - this->name = "Analog Devices AD1988A"; - this->init_dacgroup = ad1988_init_dacgroup; - break; - case 0x11d4198b: - /* http://www.analog.com/en/prod/0,2877,AD1988B,00.html */ - this->name = "Analog Devices AD1988B"; - this->init_dacgroup = ad1988_init_dacgroup; - break; - case 0x434d4980: - this->name = "CMedia CMI9880"; - this->init_dacgroup = cmi9880_init_dacgroup; - this->mixer_init = cmi9880_mixer_init; - break; - case 0x83847612: - /* http://www.idt.com/products/getDoc.cfm?docID=17122893 */ - this->name = "Sigmatel STAC9230X"; - break; - case 0x83847613: - /* http://www.idt.com/products/getDoc.cfm?docID=17122893 */ - this->name = "Sigmatel STAC9230D"; - break; - case 0x83847614: - /* http://www.idt.com/products/getDoc.cfm?docID=17122893 */ - this->name = "Sigmatel STAC9229X"; - break; - case 0x83847615: - /* http://www.idt.com/products/getDoc.cfm?docID=17122893 */ - this->name = "Sigmatel STAC9229D"; - break; - case 0x83847616: - /* http://www.idt.com/products/getDoc.cfm?docID=17122893 */ - this->name = "Sigmatel STAC9228X"; - break; - case 0x83847617: - /* http://www.idt.com/products/getDoc.cfm?docID=17122893 */ - this->name = "Sigmatel STAC9228D"; - break; - case 0x83847618: - /* http://www.idt.com/products/getDoc.cfm?docID=17122893 */ - this->name = "Sigmatel STAC9227X"; - break; - case 0x83847619: - /* http://www.idt.com/products/getDoc.cfm?docID=17122893 */ - this->name = "Sigmatel STAC9227D"; - break; - case 0x83847680: - this->name = "Sigmatel STAC9221"; - this->init_dacgroup = stac9221_init_dacgroup; - this->mixer_init = stac9221_mixer_init; - break; - case 0x83847683: - this->name = "Sigmatel STAC9221D"; - this->init_dacgroup = stac9221_init_dacgroup; - break; - case 0x83847690: - /* http://www.idt.com/products/getDoc.cfm?docID=17812077 */ - this->name = "Sigmatel STAC9200"; - this->mixer_init = stac9200_mixer_init; - this->unsol_event = stac9200_unsol_event; - break; - case 0x83847691: - /* http://www.idt.com/products/getDoc.cfm?docID=17812077 */ - this->name = "Sigmatel STAC9200D"; - this->mixer_init = stac9200_mixer_init; - this->unsol_event = stac9200_unsol_event; - break; - } - if (extra_size > 0) { - this->szextra = sizeof(uint32_t) * extra_size; - this->extra = kmem_zalloc(this->szextra, KM_SLEEP); - } - return 0; -} - -/* ---------------------------------------------------------------- - * functions for generic codecs - * ---------------------------------------------------------------- */ - -static int -generic_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->dacs.ngroups = 0; - for (assoc = 0; assoc < CORB_CD_ASSOCIATION_MAX; assoc++) { - generic_codec_add_dacgroup(this, assoc, 0); - generic_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) { - bool found; - - if (this->w[i].type != COP_AWTYPE_AUDIO_OUTPUT) - continue; - found = FALSE; - for (group = 0; group < this->dacs.ngroups; group++) { - for (j = 0; j < this->dacs.groups[group].nconv; j++) { - if (i == this->dacs.groups[group].conv[j]) { - found = TRUE; - group = this->dacs.ngroups; - break; - } - } - } - if (found) - continue; - if (this->dacs.ngroups >= 32) - break; - this->dacs.groups[this->dacs.ngroups].nconv = 1; - this->dacs.groups[this->dacs.ngroups].conv[0] = i; - this->dacs.ngroups++; - } - this->dacs.cur = 0; - - /* enumerate ADCs */ - this->adcs.ngroups = 0; - FOR_EACH_WIDGET(this, i) { - if (this->w[i].type != COP_AWTYPE_AUDIO_INPUT) - continue; - this->adcs.groups[this->adcs.ngroups].nconv = 1; - this->adcs.groups[this->adcs.ngroups].conv[0] = i; - this->adcs.ngroups++; - if (this->adcs.ngroups >= 32) - break; - } - this->adcs.cur = 0; - return 0; -} - -static int -generic_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 = generic_codec_find_pin(this, assoc, seq, digital); - if (i < 0) - continue; - dac = generic_codec_find_dac(this, i, 0); - if (dac < 0) - continue; - /* duplication check */ - for (j = 0; j < n; j++) { - if (this->dacs.groups[this->dacs.ngroups].conv[j] == dac) - break; - } - if (j < n) /* this group already has <dac> */ - continue; - this->dacs.groups[this->dacs.ngroups].conv[n++] = dac; - DPRINTF(("%s: assoc=%d seq=%d ==> g=%d n=%d\n", - __func__, assoc, seq, this->dacs.ngroups, n-1)); - } - if (n <= 0) /* no such DACs */ - return 0; - this->dacs.groups[this->dacs.ngroups].nconv = n; - - /* check if the same combination is already registered */ - for (i = 0; i < this->dacs.ngroups; i++) { - if (n != this->dacs.groups[i].nconv) - continue; - for (j = 0; j < n; j++) { - if (this->dacs.groups[this->dacs.ngroups].conv[j] != - this->dacs.groups[i].conv[j]) - break; - } - if (j >= n) /* matched */ - return 0; - } - /* found no equivalent group */ - this->dacs.ngroups++; - return 0; -} - -static int -generic_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 -generic_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]; - if (VALID_WIDGET_NID(j, this)) { - ret = generic_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]; - if (!VALID_WIDGET_NID(j, this)) - continue; - ret = generic_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; -} - -/* ---------------------------------------------------------------- - * Generic mixer functions - * ---------------------------------------------------------------- */ - -#define GMIDPRINTF(x) do {} while (0/*CONSTCOND*/) - -static int -generic_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 err, i, j, k; - - this->maxmixers = 10; - this->nmixers = 0; - this->szmixers = sizeof(mixer_item_t) * this->maxmixers; - this->mixers = kmem_zalloc(this->szmixers, KM_SLEEP); - - /* register classes */ - DPRINTF(("%s: register classes\n", __func__)); - 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; - - m = &this->mixers[AZ_CLASS_PLAYBACK]; - m->devinfo.index = AZ_CLASS_PLAYBACK; - strlcpy(m->devinfo.label.name, AzaliaCplayback, - sizeof(m->devinfo.label.name)); - m->devinfo.type = AUDIO_MIXER_CLASS; - m->devinfo.mixer_class = AZ_CLASS_PLAYBACK; - m->devinfo.next = AUDIO_MIXER_LAST; - m->devinfo.prev = AUDIO_MIXER_LAST; - m->nid = 0; - - m = &this->mixers[AZ_CLASS_MIXER]; - m->devinfo.index = AZ_CLASS_MIXER; - strlcpy(m->devinfo.label.name, AzaliaCmixer, - sizeof(m->devinfo.label.name)); - m->devinfo.type = AUDIO_MIXER_CLASS; - m->devinfo.mixer_class = AZ_CLASS_MIXER; - m->devinfo.next = AUDIO_MIXER_LAST; - m->devinfo.prev = AUDIO_MIXER_LAST; - m->nid = 0; - - this->nmixers = AZ_CLASS_MIXER + 1; - -#define MIXER_REG_PROLOG \ - mixer_devinfo_t *d; \ - err = generic_mixer_ensure_capacity(this, this->nmixers + 1); \ - if (err) \ - return err; \ - m = &this->mixers[this->nmixers]; \ - d = &m->devinfo; \ - m->nid = i - - FOR_EACH_WIDGET(this, i) { - const widget_t *w; - - w = &this->w[i]; - - /* skip unconnected pins */ - if (w->type == COP_AWTYPE_PIN_COMPLEX) { - uint8_t conn = - (w->d.pin.config & CORB_CD_PORT_MASK) >> 30; - if (conn == 1) /* no physical connection */ - continue; - } - - /* selector */ - if (w->type != COP_AWTYPE_AUDIO_MIXER && - w->type != COP_AWTYPE_POWER && w->nconnections >= 2) { - MIXER_REG_PROLOG; - GMIDPRINTF(("%s: selector %s\n", __func__, w->name)); - 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; - m->target = MI_TARGET_CONNLIST; - for (j = 0, k = 0; j < w->nconnections && k < 32; j++) { - uint8_t conn; - - if (!VALID_WIDGET_NID(w->connections[j], this)) - continue; - /* skip unconnected pins */ - PIN_STATUS(&this->w[w->connections[j]], - conn); - if (conn == 1) - continue; - GMIDPRINTF(("%s: selector %d=%s\n", __func__, j, - this->w[w->connections[j]].name)); - d->un.e.member[k].ord = j; - strlcpy(d->un.e.member[k].label.name, - this->w[w->connections[j]].name, - MAX_AUDIO_DEV_LEN); - k++; - } - d->un.e.num_mem = k; - this->nmixers++; - } - - /* output mute */ - if (w->widgetcap & COP_AWCAP_OUTAMP && - w->outamp_cap & COP_AMPCAP_MUTE) { - MIXER_REG_PROLOG; - GMIDPRINTF(("%s: output mute %s\n", __func__, w->name)); - 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_MIXER; - 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; - 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; - GMIDPRINTF(("%s: output gain %s\n", __func__, w->name)); - 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_MIXER; - 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; - m->target = MI_TARGET_OUTAMP; - d->un.v.num_channels = WIDGET_CHANNELS(w); -#ifdef MAX_VOLUME_255 - d->un.v.units.name[0] = 0; -#else - snprintf(d->un.v.units.name, sizeof(d->un.v.units.name), - "0.25x%ddB", COP_AMPCAP_STEPSIZE(w->outamp_cap)+1); -#endif - d->un.v.delta = - MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->outamp_cap)); - this->nmixers++; - } - - /* input mute */ - if (w->widgetcap & COP_AWCAP_INAMP && - w->inamp_cap & COP_AMPCAP_MUTE) { - GMIDPRINTF(("%s: input mute %s\n", __func__, w->name)); - if (w->type != COP_AWTYPE_AUDIO_SELECTOR && - w->type != COP_AWTYPE_AUDIO_MIXER) { - 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_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; - m->target = 0; - 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++; - } else { - uint8_t conn; - - for (j = 0; j < w->nconnections; j++) { - MIXER_REG_PROLOG; - if (!VALID_WIDGET_NID(w->connections[j], this)) - continue; - - /* skip unconnected pins */ - PIN_STATUS(&this->w[w->connections[j]], - conn); - if (conn == 1) - continue; - - GMIDPRINTF(("%s: input mute %s.%s\n", __func__, - w->name, this->w[w->connections[j]].name)); - generic_mixer_cat_names( - d->label.name, - sizeof(d->label.name), - w->name, this->w[w->connections[j]].name, - "mute"); - 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 if (w->type == COP_AWTYPE_AUDIO_MIXER) - d->mixer_class = AZ_CLASS_MIXER; - else - d->mixer_class = AZ_CLASS_INPUT; - 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)) { - GMIDPRINTF(("%s: input gain %s\n", __func__, w->name)); - if (w->type != COP_AWTYPE_AUDIO_SELECTOR && - w->type != COP_AWTYPE_AUDIO_MIXER) { - MIXER_REG_PROLOG; - snprintf(d->label.name, sizeof(d->label.name), - "%s", w->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; - m->target = 0; - d->un.v.num_channels = WIDGET_CHANNELS(w); -#ifdef MAX_VOLUME_255 - d->un.v.units.name[0] = 0; -#else - snprintf(d->un.v.units.name, - sizeof(d->un.v.units.name), "0.25x%ddB", - COP_AMPCAP_STEPSIZE(w->inamp_cap)+1); -#endif - d->un.v.delta = - MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->inamp_cap)); - this->nmixers++; - } else { - uint8_t conn; - - for (j = 0; j < w->nconnections; j++) { - MIXER_REG_PROLOG; - if (!VALID_WIDGET_NID(w->connections[j], this)) - continue; - /* skip unconnected pins */ - PIN_STATUS(&this->w[w->connections[j]], - conn); - if (conn == 1) - continue; - GMIDPRINTF(("%s: input gain %s.%s\n", __func__, - w->name, this->w[w->connections[j]].name)); - 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 if (w->type == COP_AWTYPE_AUDIO_MIXER) - d->mixer_class = AZ_CLASS_MIXER; - else - d->mixer_class = AZ_CLASS_INPUT; - m->target = j; - d->un.v.num_channels = WIDGET_CHANNELS(w); -#ifdef MAX_VOLUME_255 - d->un.v.units.name[0] = 0; -#else - snprintf(d->un.v.units.name, - sizeof(d->un.v.units.name), "0.25x%ddB", - COP_AMPCAP_STEPSIZE(w->inamp_cap)+1); -#endif - d->un.v.delta = - MIXER_DELTA(COP_AMPCAP_NUMSTEPS(w->inamp_cap)); - 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; - GMIDPRINTF(("%s: pin dir %s\n", __func__, w->name)); - snprintf(d->label.name, sizeof(d->label.name), - "%s.dir", w->name); - d->type = AUDIO_MIXER_ENUM; - d->mixer_class = AZ_CLASS_OUTPUT; - 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; - GMIDPRINTF(("%s: hpboost %s\n", __func__, w->name)); - snprintf(d->label.name, sizeof(d->label.name), - "%s.boost", w->name); - d->type = AUDIO_MIXER_ENUM; - d->mixer_class = AZ_CLASS_OUTPUT; - 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++; - } - - if (w->type == COP_AWTYPE_PIN_COMPLEX && - w->d.pin.cap & COP_PINCAP_EAPD) { - MIXER_REG_PROLOG; - GMIDPRINTF(("%s: eapd %s\n", __func__, w->name)); - snprintf(d->label.name, sizeof(d->label.name), - "%s.eapd", w->name); - d->type = AUDIO_MIXER_ENUM; - d->mixer_class = AZ_CLASS_OUTPUT; - m->target = MI_TARGET_EAPD; - 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++; - } - - if (w->type == COP_AWTYPE_PIN_COMPLEX && - w->d.pin.cap & COP_PINCAP_BALANCE) { - MIXER_REG_PROLOG; - GMIDPRINTF(("%s: balance %s\n", __func__, w->name)); - snprintf(d->label.name, sizeof(d->label.name), - "%s.balance", w->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; - m->target = MI_TARGET_BALANCE; - 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++; - } - - if (w->widgetcap & COP_AWCAP_LRSWAP) { - MIXER_REG_PROLOG; - GMIDPRINTF(("%s: lrswap %s\n", __func__, w->name)); - snprintf(d->label.name, sizeof(d->label.name), - "%s.lrswap", w->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; - m->target = MI_TARGET_LRSWAP; - 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; - GMIDPRINTF(("%s: volume knob %s\n", __func__, w->name)); - strlcpy(d->label.name, w->name, sizeof(d->label.name)); - d->type = AUDIO_MIXER_VALUE; - d->mixer_class = AZ_CLASS_OUTPUT; - m->target = MI_TARGET_VOLUME; - d->un.v.num_channels = 1; - d->un.v.units.name[0] = 0; - d->un.v.delta = - MIXER_DELTA(COP_VKCAP_NUMSTEPS(w->d.volume.cap)); - this->nmixers++; - } - } - - /* if the codec has multiple DAC groups, create "playback.mode" */ - if (this->dacs.ngroups > 1) { - MIXER_REG_PROLOG; - GMIDPRINTF(("%s: create playback.mode\n", __func__)); - strlcpy(d->label.name, AudioNmode, sizeof(d->label.name)); - d->type = AUDIO_MIXER_ENUM; - d->mixer_class = AZ_CLASS_PLAYBACK; - m->target = MI_TARGET_DAC; - for (i = 0; i < this->dacs.ngroups && i < 32; i++) { - d->un.e.member[i].ord = i; - for (j = 0; j < this->dacs.groups[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->dacs.groups[i].conv[j]); - } - } - d->un.e.num_mem = i; - this->nmixers++; - } - - /* if the codec has multiple ADC groups, create "record.mode" */ - if (this->adcs.ngroups > 1) { - MIXER_REG_PROLOG; - GMIDPRINTF(("%s: create record.mode\n", __func__)); - strlcpy(d->label.name, AudioNmode, sizeof(d->label.name)); - d->type = AUDIO_MIXER_ENUM; - d->mixer_class = AZ_CLASS_RECORD; - m->target = MI_TARGET_ADC; - for (i = 0; i < this->adcs.ngroups && i < 32; i++) { - d->un.e.member[i].ord = i; - for (j = 0; j < this->adcs.groups[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->adcs.groups[i].conv[j]); - } - } - d->un.e.num_mem = i; - this->nmixers++; - } - - generic_mixer_fix_indexes(this); - generic_mixer_default(this); - return 0; -} - -static void -generic_mixer_cat_names(char *dst, size_t dstsize, - const char *str1, const char *str2, const char *str3) -{ - const char *last2; - size_t len1, len2, len3, total; - - len1 = strlen(str1); - len2 = strlen(str2); - len3 = strlen(str3); - total = len1 + 1 + len2 + 1 + len3 + 1; - if (total - (len3 - 1) <= dstsize) { - snprintf(dst, dstsize, "%s.%s.%s", str1, str2, str3); - return; - } - last2 = len2 > 2 ? str2 + len2 - 2 : str2; - if (len2 > 4) { - snprintf(dst, dstsize, "%s.%.2s%s.%s", str1, str2, last2, str3); - return; - } - snprintf(dst, dstsize, "%s.%s.%s", str1, last2, str3); -} - -static int -generic_mixer_ensure_capacity(codec_t *this, size_t newsize) -{ - size_t newmax; - size_t newsz; - void *newbuf; - - if (this->maxmixers >= newsize) - return 0; - newmax = this->maxmixers + 10; - if (newmax < newsize) - newmax = newsize; - newsz = sizeof(mixer_item_t) * newmax; - newbuf = kmem_zalloc(newsz, KM_SLEEP); - memcpy(newbuf, this->mixers, this->szmixers); - kmem_free(this->mixers, this->szmixers); - this->mixers = newbuf; - this->szmixers = newsize; - this->maxmixers = newmax; - return 0; -} - -static int -generic_mixer_fix_indexes(codec_t *this) -{ - int i; - mixer_devinfo_t *d; - - for (i = 0; i < this->nmixers; i++) { - d = &this->mixers[i].devinfo; -#ifdef DIAGNOSTIC - if (d->index != 0 && d->index != i) - aprint_error("%s: index mismatch %d %d\n", __func__, - d->index, i); -#endif - d->index = i; - if (d->prev == 0) - d->prev = AUDIO_MIXER_LAST; - if (d->next == 0) - d->next = AUDIO_MIXER_LAST; - } - return 0; -} - -static int -generic_mixer_default(codec_t *this) -{ - int i; - mixer_item_t *m; - /* unmute all */ - DPRINTF(("%s: unmute\n", __func__)); - for (i = 0; i < this->nmixers; i++) { - mixer_ctrl_t mc; - - m = &this->mixers[i]; - if (!IS_MI_TARGET_INAMP(m->target) && - m->target != MI_TARGET_OUTAMP) - continue; - if (m->devinfo.type != AUDIO_MIXER_ENUM) - continue; - mc.dev = i; - mc.type = AUDIO_MIXER_ENUM; - mc.un.ord = 0; - generic_mixer_set(this, m->nid, m->target, &mc); - } - - /* - * For bidirectional pins, make the default `output' - */ - DPRINTF(("%s: process bidirectional pins\n", __func__)); - for (i = 0; i < this->nmixers; i++) { - mixer_ctrl_t mc; - - m = &this->mixers[i]; - if (m->target != MI_TARGET_PINDIR) - continue; - mc.dev = i; - mc.type = AUDIO_MIXER_ENUM; - mc.un.ord = 1; /* output */ - generic_mixer_set(this, m->nid, m->target, &mc); - } - - /* set unextreme volume */ - DPRINTF(("%s: set volume\n", __func__)); - for (i = 0; i < this->nmixers; i++) { - mixer_ctrl_t mc; - - m = &this->mixers[i]; - if (!IS_MI_TARGET_INAMP(m->target) && - m->target != MI_TARGET_OUTAMP && - m->target != MI_TARGET_VOLUME) - continue; - if (m->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 (m->target != MI_TARGET_VOLUME && - WIDGET_CHANNELS(&this->w[m->nid]) == 2) { - mc.un.value.num_channels = 2; - mc.un.value.level[1] = AUDIO_MAX_GAIN / 2; - } - generic_mixer_set(this, m->nid, m->target, &mc); - } - - return 0; -} - -static int -generic_mixer_pin_sense(codec_t *this) -{ - typedef enum { - PIN_DIR_IN, - PIN_DIR_OUT, - PIN_DIR_MIC - } pintype_t; - const widget_t *w; - int i; - - FOR_EACH_WIDGET(this, i) { - pintype_t pintype = PIN_DIR_IN; - - w = &this->w[i]; - if (w->type != COP_AWTYPE_PIN_COMPLEX) - continue; - if (!(w->d.pin.cap & COP_PINCAP_INPUT)) - pintype = PIN_DIR_OUT; - if (!(w->d.pin.cap & COP_PINCAP_OUTPUT)) - pintype = PIN_DIR_IN; - - switch (w->d.pin.device) { - case CORB_CD_LINEOUT: - case CORB_CD_SPEAKER: - case CORB_CD_HEADPHONE: - case CORB_CD_SPDIFOUT: - case CORB_CD_DIGITALOUT: - pintype = PIN_DIR_OUT; - break; - case CORB_CD_CD: - case CORB_CD_LINEIN: - pintype = PIN_DIR_IN; - break; - case CORB_CD_MICIN: - pintype = PIN_DIR_MIC; - break; - } - - switch (pintype) { - case PIN_DIR_IN: - this->comresp(this, w->nid, - CORB_SET_PIN_WIDGET_CONTROL, - CORB_PWC_INPUT, NULL); - break; - case PIN_DIR_OUT: - this->comresp(this, w->nid, - CORB_SET_PIN_WIDGET_CONTROL, - CORB_PWC_OUTPUT, NULL); - break; - case PIN_DIR_MIC: - this->comresp(this, w->nid, - CORB_SET_PIN_WIDGET_CONTROL, - CORB_PWC_INPUT|CORB_PWC_VREF_80, NULL); - break; - } - - if (w->d.pin.cap & COP_PINCAP_EAPD) { - uint32_t result; - int err; - - err = this->comresp(this, w->nid, - CORB_GET_EAPD_BTL_ENABLE, 0, &result); - if (err) - continue; - result &= 0xff; - result |= CORB_EAPD_EAPD; - err = this->comresp(this, w->nid, - CORB_SET_EAPD_BTL_ENABLE, result, &result); - if (err) - continue; - } - } - - return 0; -} - -static int -generic_mixer_widget_name(const codec_t *this, widget_t *w) -{ - const char *name = NULL, *grossloc = "", *geoloc = ""; - uint8_t grosslocval, geolocval; - - if (w->type != COP_AWTYPE_PIN_COMPLEX) - return 0; - - switch (w->d.pin.device) { - case CORB_CD_LINEOUT: name = "lineout"; break; - case CORB_CD_SPEAKER: name = "spkr"; break; - case CORB_CD_HEADPHONE: name = "hp"; break; - case CORB_CD_CD: name = AudioNcd; break; - case CORB_CD_SPDIFOUT: name = "spdifout"; break; - case CORB_CD_DIGITALOUT: name = "digout"; break; - case CORB_CD_MODEMLINE: name = "modemline"; break; - case CORB_CD_MODEMHANDSET: name = "modemhset"; break; - case CORB_CD_LINEIN: name = "linein"; break; - case CORB_CD_AUX: name = AudioNaux; break; - case CORB_CD_MICIN: name = AudioNmicrophone; break; - case CORB_CD_TELEPHONY: name = "telephony"; break; - case CORB_CD_SPDIFIN: name = "spdifin"; break; - case CORB_CD_DIGITALIN: name = "digin"; break; - case CORB_CD_DEVICE_OTHER: name = "reserved"; break; - default: name = "unused"; break; - } - - grosslocval = ((w->d.pin.config & CORB_CD_LOCATION_MASK) >> 24) & 0xf; - geolocval = (w->d.pin.config & CORB_CD_LOCATION_MASK) >> 28; - - switch (geolocval) { - case 0x00: /* external on primary chassis */ - case 0x10: /* external on separate chassis */ - geoloc = (geolocval == 0x00 ? "" : "d"); - switch (grosslocval) { - case 0x00: grossloc = ""; break; /* N/A */ - case 0x01: grossloc = ""; break; /* rear */ - case 0x02: grossloc = ".front"; break; /* front */ - case 0x03: grossloc = ".left"; break; /* left */ - case 0x04: grossloc = ".right"; break; /* right */ - case 0x05: grossloc = ".top"; break; /* top */ - case 0x06: grossloc = ".bottom"; break; /* bottom */ - case 0x07: grossloc = ".rearpnl"; break; /* rear panel */ - case 0x08: grossloc = ".drivebay"; break; /* drive bay */ - default: grossloc = ""; break; - } - break; - case 0x01: - geoloc = "i"; - switch (grosslocval) { - case 0x00: grossloc = ""; break; /* N/A */ - case 0x07: grossloc = ".riser"; break; /* riser */ - case 0x08: grossloc = ".hdmi"; break; /* hdmi */ - default: grossloc = ""; break; - } - break; - default: - geoloc = "o"; - switch (grosslocval) { - case 0x00: grossloc = ""; break; /* N/A */ - case 0x06: grossloc = ".bottom"; break; /* bottom */ - case 0x07: grossloc = ".lidin"; break; /* lid inside */ - case 0x08: grossloc = ".lidout"; break; /* lid outside */ - default: grossloc = ""; break; - } - break; - } - - snprintf(w->name, sizeof(w->name), "%s%s%s", geoloc, name, grossloc); - return 0; -} - -static int -generic_mixer_create_virtual(codec_t *this) -{ - mixer_item_t *m; - mixer_devinfo_t *d; - convgroup_t *cgdac = &this->dacs.groups[0]; - convgroup_t *cgadc = &this->adcs.groups[0]; - int i, err, mdac, madc, mmaster; - - /* Clear mixer indexes, to make generic_mixer_fix_index happy */ - for (i = 0; i < this->nmixers; i++) { - d = &this->mixers[i].devinfo; - d->index = d->prev = d->next = 0; - } - - mdac = madc = mmaster = -1; - for (i = 0; i < this->nmixers; i++) { - if (this->mixers[i].devinfo.type != AUDIO_MIXER_VALUE) - continue; - if (mdac < 0 && this->dacs.ngroups > 0 && cgdac->nconv > 0) { - if (this->mixers[i].nid == cgdac->conv[0]) - mdac = mmaster = i; - } - if (madc < 0 && this->adcs.ngroups > 0 && cgadc->nconv > 0) { - if (this->mixers[i].nid == cgadc->conv[0]) - madc = i; - } - } - - if (mdac == -1) { - /* - * no volume mixer found on the DAC; enumerate peer widgets - * and try to find a volume mixer on them - */ - widget_t *w; - int j; - FOR_EACH_WIDGET(this, i) { - w = &this->w[i]; - for (j = 0; j < w->nconnections; j++) - if (w->connections[j] == cgdac->conv[0]) - break; - - if (j == w->nconnections) - continue; - - for (j = 0; j < this->nmixers; j++) { - if (this->mixers[j].devinfo.type != - AUDIO_MIXER_VALUE) - continue; - if (this->mixers[j].nid == w->nid) { - mdac = mmaster = j; - break; - } - } - - if (mdac == -1) - break; - } - } - - if (mdac >= 0) { - err = generic_mixer_ensure_capacity(this, this->nmixers + 1); - if (err) - return err; - m = &this->mixers[this->nmixers]; - d = &m->devinfo; - memcpy(m, &this->mixers[mmaster], sizeof(*m)); - d->mixer_class = AZ_CLASS_OUTPUT; - snprintf(d->label.name, sizeof(d->label.name), AudioNmaster); - this->nmixers++; - - err = generic_mixer_ensure_capacity(this, this->nmixers + 1); - if (err) - return err; - m = &this->mixers[this->nmixers]; - d = &m->devinfo; - memcpy(m, &this->mixers[mdac], sizeof(*m)); - d->mixer_class = AZ_CLASS_INPUT; - snprintf(d->label.name, sizeof(d->label.name), AudioNdac); - this->nmixers++; - } - - if (madc >= 0) { - err = generic_mixer_ensure_capacity(this, this->nmixers + 1); - if (err) - return err; - m = &this->mixers[this->nmixers]; - d = &m->devinfo; - memcpy(m, &this->mixers[madc], sizeof(*m)); - d->mixer_class = AZ_CLASS_RECORD; - snprintf(d->label.name, sizeof(d->label.name), AudioNvolume); - this->nmixers++; - } - - generic_mixer_fix_indexes(this); - - return 0; -} - -static int -generic_mixer_autoinit(codec_t *this) -{ - generic_mixer_init(this); - generic_mixer_create_virtual(this); - generic_mixer_pin_sense(this); - - return 0; -} - -static int -generic_mixer_init_widget(const codec_t *this, widget_t *w, nid_t nid) -{ - return generic_mixer_widget_name(this, w); -} - -static int -generic_mixer_delete(codec_t *this) -{ - if (this->mixers == NULL) - return 0; - kmem_free(this->mixers, this->szmixers); - this->mixers = NULL; - return 0; -} - -/** - * @param mc mc->type must be set by the caller before the call - */ -static int -generic_mixer_get(const codec_t *this, nid_t nid, int target, mixer_ctrl_t *mc) -{ - uint32_t result; - nid_t n; - int err; - - /* inamp mute */ - if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_ENUM) { - err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE, - CORB_GAGM_INPUT | CORB_GAGM_LEFT | - MI_TARGET_INAMP(target), &result); - if (err) - return err; - mc->un.ord = result & CORB_GAGM_MUTE ? 1 : 0; - } - - /* inamp gain */ - else if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_VALUE) { - err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE, - CORB_GAGM_INPUT | CORB_GAGM_LEFT | - MI_TARGET_INAMP(target), &result); - if (err) - return err; - mc->un.value.level[0] = generic_mixer_from_device_value(this, - nid, target, CORB_GAGM_GAIN(result)); - if (this->w[nid].type == COP_AWTYPE_AUDIO_SELECTOR || - this->w[nid].type == COP_AWTYPE_AUDIO_MIXER) { - n = this->w[nid].connections[MI_TARGET_INAMP(target)]; -#ifdef AZALIA_DEBUG - if (!VALID_WIDGET_NID(n, this)) { - DPRINTF(("%s: invalid target: nid=%d nconn=%d index=%d\n", - __func__, nid, this->w[nid].nconnections, - MI_TARGET_INAMP(target))); - return EINVAL; - } -#endif - } else { - n = nid; - } - mc->un.value.num_channels = WIDGET_CHANNELS(&this->w[n]); - if (mc->un.value.num_channels == 2) { - err = this->comresp(this, nid, - CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT | - CORB_GAGM_RIGHT | MI_TARGET_INAMP(target), - &result); - if (err) - return err; - mc->un.value.level[1] = generic_mixer_from_device_value - (this, nid, target, CORB_GAGM_GAIN(result)); - } - } - - /* outamp mute */ - else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_ENUM) { - err = this->comresp(this, 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 (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_VALUE) { - err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE, - CORB_GAGM_OUTPUT | CORB_GAGM_LEFT | 0, &result); - if (err) - return err; - mc->un.value.level[0] = generic_mixer_from_device_value(this, - nid, target, CORB_GAGM_GAIN(result)); - mc->un.value.num_channels = WIDGET_CHANNELS(&this->w[nid]); - if (mc->un.value.num_channels == 2) { - err = this->comresp(this, nid, - CORB_GET_AMPLIFIER_GAIN_MUTE, - CORB_GAGM_OUTPUT | CORB_GAGM_RIGHT | 0, &result); - if (err) - return err; - mc->un.value.level[1] = generic_mixer_from_device_value - (this, nid, target, CORB_GAGM_GAIN(result)); - } - } - - /* selection */ - else if (target == MI_TARGET_CONNLIST) { - err = this->comresp(this, nid, - CORB_GET_CONNECTION_SELECT_CONTROL, 0, &result); - if (err) - return err; - result = CORB_CSC_INDEX(result); - if (!VALID_WIDGET_NID(this->w[nid].connections[result], this)) - mc->un.ord = -1; - else - mc->un.ord = result; - } - - /* pin I/O */ - else if (target == MI_TARGET_PINDIR) { - err = this->comresp(this, 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 (target == MI_TARGET_PINBOOST) { - err = this->comresp(this, 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 (target == MI_TARGET_DAC) { - mc->un.ord = this->dacs.cur; - } - - /* ADC selection */ - else if (target == MI_TARGET_ADC) { - mc->un.ord = this->adcs.cur; - } - - /* Volume knob */ - else if (target == MI_TARGET_VOLUME) { - err = this->comresp(this, nid, CORB_GET_VOLUME_KNOB, - 0, &result); - if (err) - return err; - mc->un.value.level[0] = generic_mixer_from_device_value(this, - nid, target, CORB_VKNOB_VOLUME(result)); - mc->un.value.num_channels = 1; - } - - /* S/PDIF */ - else if (target == MI_TARGET_SPDIF) { - err = this->comresp(this, nid, CORB_GET_DIGITAL_CONTROL, - 0, &result); - if (err) - return err; - mc->un.mask = result & 0xff & ~(CORB_DCC_DIGEN | CORB_DCC_NAUDIO); - } else if (target == MI_TARGET_SPDIF_CC) { - err = this->comresp(this, nid, CORB_GET_DIGITAL_CONTROL, - 0, &result); - if (err) - return err; - mc->un.value.num_channels = 1; - mc->un.value.level[0] = CORB_DCC_CC(result); - } - - /* EAPD */ - else if (target == MI_TARGET_EAPD) { - err = this->comresp(this, nid, - CORB_GET_EAPD_BTL_ENABLE, 0, &result); - if (err) - return err; - mc->un.ord = result & CORB_EAPD_EAPD ? 1 : 0; - } - - /* Balanced I/O */ - else if (target == MI_TARGET_BALANCE) { - err = this->comresp(this, nid, - CORB_GET_EAPD_BTL_ENABLE, 0, &result); - if (err) - return err; - mc->un.ord = result & CORB_EAPD_BTL ? 1 : 0; - } - - /* LR-Swap */ - else if (target == MI_TARGET_LRSWAP) { - err = this->comresp(this, nid, - CORB_GET_EAPD_BTL_ENABLE, 0, &result); - if (err) - return err; - mc->un.ord = result & CORB_EAPD_LRSWAP ? 1 : 0; - } - - else { - aprint_error_dev(this->dev, "internal error in %s: target=%x\n", - __func__, target); - return -1; - } - return 0; -} - -static int -generic_mixer_set(codec_t *this, nid_t nid, int target, const mixer_ctrl_t *mc) -{ - uint32_t result, value; - int err; - - /* inamp mute */ - if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_ENUM) { - /* We have to set stereo mute separately to keep each gain value. */ - err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE, - CORB_GAGM_INPUT | CORB_GAGM_LEFT | - MI_TARGET_INAMP(target), &result); - if (err) - return err; - value = CORB_AGM_INPUT | CORB_AGM_LEFT | - (target << CORB_AGM_INDEX_SHIFT) | - CORB_GAGM_GAIN(result); - if (mc->un.ord) - value |= CORB_AGM_MUTE; - err = this->comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE, - value, &result); - if (err) - return err; - if (WIDGET_CHANNELS(&this->w[nid]) == 2) { - err = this->comresp(this, nid, - CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT | - CORB_GAGM_RIGHT | MI_TARGET_INAMP(target), - &result); - if (err) - return err; - value = CORB_AGM_INPUT | CORB_AGM_RIGHT | - (target << CORB_AGM_INDEX_SHIFT) | - CORB_GAGM_GAIN(result); - if (mc->un.ord) - value |= CORB_AGM_MUTE; - err = this->comresp(this, nid, - CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result); - if (err) - return err; - } - } - - /* inamp gain */ - else if (IS_MI_TARGET_INAMP(target) && mc->type == AUDIO_MIXER_VALUE) { - if (mc->un.value.num_channels < 1) - return EINVAL; - if (!generic_mixer_validate_value(this, nid, target, - mc->un.value.level[0])) - return EINVAL; - err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE, - CORB_GAGM_INPUT | CORB_GAGM_LEFT | - MI_TARGET_INAMP(target), &result); - if (err) - return err; - value = generic_mixer_to_device_value(this, nid, target, - mc->un.value.level[0]); - value = CORB_AGM_INPUT | CORB_AGM_LEFT | - (target << CORB_AGM_INDEX_SHIFT) | - (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) | - (value & CORB_AGM_GAIN_MASK); - err = this->comresp(this, nid, CORB_SET_AMPLIFIER_GAIN_MUTE, - value, &result); - if (err) - return err; - if (mc->un.value.num_channels >= 2 && - WIDGET_CHANNELS(&this->w[nid]) == 2) { - if (!generic_mixer_validate_value(this, nid, target, - mc->un.value.level[1])) - return EINVAL; - err = this->comresp(this, nid, - CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_INPUT | - CORB_GAGM_RIGHT | MI_TARGET_INAMP(target), - &result); - if (err) - return err; - value = generic_mixer_to_device_value(this, nid, target, - mc->un.value.level[1]); - value = CORB_AGM_INPUT | CORB_AGM_RIGHT | - (target << CORB_AGM_INDEX_SHIFT) | - (result & CORB_GAGM_MUTE ? CORB_AGM_MUTE : 0) | - (value & CORB_AGM_GAIN_MASK); - err = this->comresp(this, nid, - CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result); - if (err) - return err; - } - } - - /* outamp mute */ - else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_ENUM) { - err = this->comresp(this, 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, nid, CORB_SET_AMPLIFIER_GAIN_MUTE, - value, &result); - if (err) - return err; - if (WIDGET_CHANNELS(&this->w[nid]) == 2) { - err = this->comresp(this, 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, nid, - CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result); - if (err) - return err; - } - } - - /* outamp gain */ - else if (target == MI_TARGET_OUTAMP && mc->type == AUDIO_MIXER_VALUE) { - if (mc->un.value.num_channels < 1) - return EINVAL; - if (!generic_mixer_validate_value(this, nid, target, - mc->un.value.level[0])) - return EINVAL; - err = this->comresp(this, nid, CORB_GET_AMPLIFIER_GAIN_MUTE, - CORB_GAGM_OUTPUT | CORB_GAGM_LEFT, &result); - if (err) - return err; - value = generic_mixer_to_device_value(this, nid, target, - 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, nid, CORB_SET_AMPLIFIER_GAIN_MUTE, - value, &result); - if (err) - return err; - if (mc->un.value.num_channels >= 2 && - WIDGET_CHANNELS(&this->w[nid]) == 2) { - if (!generic_mixer_validate_value(this, nid, target, - mc->un.value.level[1])) - return EINVAL; - err = this->comresp(this, nid, - CORB_GET_AMPLIFIER_GAIN_MUTE, CORB_GAGM_OUTPUT | - CORB_GAGM_RIGHT, &result); - if (err) - return err; - value = generic_mixer_to_device_value(this, nid, target, - 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, nid, - CORB_SET_AMPLIFIER_GAIN_MUTE, value, &result); - if (err) - return err; - } - } - - /* selection */ - else if (target == MI_TARGET_CONNLIST) { - if (mc->un.ord < 0 || - mc->un.ord >= this->w[nid].nconnections || - !VALID_WIDGET_NID(this->w[nid].connections[mc->un.ord], this)) - return EINVAL; - err = this->comresp(this, nid, - CORB_SET_CONNECTION_SELECT_CONTROL, mc->un.ord, &result); - if (err) - return err; - } - - /* pin I/O */ - else if (target == MI_TARGET_PINDIR) { - if (mc->un.ord >= 2) - return EINVAL; - if (mc->un.ord == 0) { - return generic_mixer_pinctrl(this, nid, CORB_PWC_INPUT); - } else { - return generic_mixer_pinctrl( - this, nid, CORB_PWC_OUTPUT); - } - } - - /* pin headphone-boost */ - else if (target == MI_TARGET_PINBOOST) { - if (mc->un.ord >= 2) - return EINVAL; - err = this->comresp(this, 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, nid, - CORB_SET_PIN_WIDGET_CONTROL, result, &result); - if (err) - return err; - } - - /* DAC group selection */ - else if (target == MI_TARGET_DAC) { - if (this->running) - return EBUSY; - if (mc->un.ord >= this->dacs.ngroups) - return EINVAL; - return azalia_codec_construct_format(this, - mc->un.ord, this->adcs.cur); - } - - /* ADC selection */ - else if (target == MI_TARGET_ADC) { - if (this->running) - return EBUSY; - if (mc->un.ord >= this->adcs.ngroups) - return EINVAL; - return azalia_codec_construct_format(this, - this->dacs.cur, mc->un.ord); - } - - /* Volume knob */ - else if (target == MI_TARGET_VOLUME) { - if (mc->un.value.num_channels != 1) - return EINVAL; - if (!generic_mixer_validate_value(this, nid, - target, mc->un.value.level[0])) - return EINVAL; - value = generic_mixer_to_device_value(this, nid, target, - mc->un.value.level[0]) | CORB_VKNOB_DIRECT; - err = this->comresp(this, nid, CORB_SET_VOLUME_KNOB, - value, &result); - if (err) - return err; - } - - /* S/PDIF */ - else if (target == MI_TARGET_SPDIF) { - err = this->comresp(this, nid, CORB_GET_DIGITAL_CONTROL, - 0, &result); - result &= CORB_DCC_DIGEN | CORB_DCC_NAUDIO; - result |= mc->un.mask & 0xff & ~CORB_DCC_DIGEN; - err = this->comresp(this, nid, CORB_SET_DIGITAL_CONTROL_L, - result, NULL); - if (err) - return err; - } else if (target == MI_TARGET_SPDIF_CC) { - if (mc->un.value.num_channels != 1) - return EINVAL; - if (mc->un.value.level[0] > 127) - return EINVAL; - err = this->comresp(this, nid, CORB_SET_DIGITAL_CONTROL_H, - mc->un.value.level[0], NULL); - if (err) - return err; - } - - /* EAPD */ - else if (target == MI_TARGET_EAPD) { - if (mc->un.ord >= 2) - return EINVAL; - err = this->comresp(this, nid, - CORB_GET_EAPD_BTL_ENABLE, 0, &result); - if (err) - return err; - result &= 0xff; - if (mc->un.ord == 0) { - result &= ~CORB_EAPD_EAPD; - } else { - result |= CORB_EAPD_EAPD; - } - err = this->comresp(this, nid, - CORB_SET_EAPD_BTL_ENABLE, result, &result); - if (err) - return err; - } - - /* Balanced I/O */ - else if (target == MI_TARGET_BALANCE) { - if (mc->un.ord >= 2) - return EINVAL; - err = this->comresp(this, nid, - CORB_GET_EAPD_BTL_ENABLE, 0, &result); - if (err) - return err; - result &= 0xff; - if (mc->un.ord == 0) { - result &= ~CORB_EAPD_BTL; - } else { - result |= CORB_EAPD_BTL; - } - err = this->comresp(this, nid, - CORB_SET_EAPD_BTL_ENABLE, result, &result); - if (err) - return err; - } - - /* LR-Swap */ - else if (target == MI_TARGET_LRSWAP) { - if (mc->un.ord >= 2) - return EINVAL; - err = this->comresp(this, nid, - CORB_GET_EAPD_BTL_ENABLE, 0, &result); - if (err) - return err; - result &= 0xff; - if (mc->un.ord == 0) { - result &= ~CORB_EAPD_LRSWAP; - } else { - result |= CORB_EAPD_LRSWAP; - } - err = this->comresp(this, nid, - CORB_SET_EAPD_BTL_ENABLE, result, &result); - if (err) - return err; - } - - else { - aprint_error_dev(this->dev, "internal error in %s: target=%x\n", - __func__, target); - return -1; - } - return 0; -} - -static int -generic_mixer_pinctrl(codec_t *this, nid_t nid, uint32_t value) -{ - int err; - uint32_t result; - - err = this->comresp(this, nid, CORB_GET_PIN_WIDGET_CONTROL, 0, &result); - if (err) - return err; - result &= ~(CORB_PWC_OUTPUT | CORB_PWC_INPUT); - result |= value & (CORB_PWC_OUTPUT | CORB_PWC_INPUT); - return this->comresp(this, nid, - CORB_SET_PIN_WIDGET_CONTROL, result, NULL); -} - -static u_char -generic_mixer_from_device_value(const codec_t *this, nid_t nid, int target, - uint32_t dv) -{ -#ifdef MAX_VOLUME_255 - uint32_t dmax; - - if (IS_MI_TARGET_INAMP(target)) - dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap); - else if (target == MI_TARGET_OUTAMP) - dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap); - else if (target == MI_TARGET_VOLUME) - dmax = COP_VKCAP_NUMSTEPS(this->w[nid].d.volume.cap); - else { - printf("unknown target: %d\n", target); - dmax = 127; - } - return dv * MIXER_DELTA(dmax); -#else - return dv; -#endif -} - -static uint32_t -generic_mixer_to_device_value(const codec_t *this, nid_t nid, int target, - u_char uv) -{ -#ifdef MAX_VOLUME_255 - uint32_t dmax; - - if (IS_MI_TARGET_INAMP(target)) - dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap); - else if (target == MI_TARGET_OUTAMP) - dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap); - else if (target == MI_TARGET_VOLUME) - dmax = COP_VKCAP_NUMSTEPS(this->w[nid].d.volume.cap); - else { - printf("unknown target: %d\n", target); - dmax = 127; - } - return uv / MIXER_DELTA(dmax); -#else - return uv; -#endif -} - -static uint32_t -generic_mixer_max(const codec_t *this, nid_t nid, - int target) -{ -#ifdef MAX_VOLUME_255 - return AUDIO_MAX_GAIN; -#else - uint32_t dmax; - - if (IS_MI_TARGET_INAMP(target)) - dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].inamp_cap); - else if (target == MI_TARGET_OUTAMP) - dmax = COP_AMPCAP_NUMSTEPS(this->w[nid].outamp_cap); - else if (target == MI_TARGET_VOLUME) - dmax = COP_VKCAP_NUMSTEPS(this->w[nid].d.volume.cap); - return dmax; -#endif -} - -static bool -generic_mixer_validate_value(const codec_t *this, nid_t nid, - int target, u_char uv) -{ -#ifdef MAX_VOLUME_255 - return TRUE; -#else - return uv <= generic_mixer_max(this, nid, target); -#endif -} - -static int -generic_set_port(codec_t *this, mixer_ctrl_t *mc) -{ - const mixer_item_t *m; - - if (mc->dev < 0 || 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 */ - return generic_mixer_set(this, m->nid, m->target, mc); -} - -static int -generic_get_port(codec_t *this, mixer_ctrl_t *mc) -{ - const mixer_item_t *m; - - if (mc->dev < 0 || 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 */ - return generic_mixer_get(this, m->nid, m->target, mc); -} - - -/* ---------------------------------------------------------------- - * Realtek ALC260 - * - * Fujitsu LOOX T70M/T - * Internal Speaker: 0x10 - * Front Headphone: 0x14 - * Front mic: 0x12 - * ---------------------------------------------------------------- */ - -#define ALC260_FUJITSU_ID 0x132610cf -#define ALC260_EVENT_HP 0 -#define ALC260_EXTRA_MASTER 0 -static const mixer_item_t alc260_mixer_items[] = { - AZ_MIXER_CLASSES, - {{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, 3}}, 0x08, MI_TARGET_OUTAMP}, /* and 0x09, 0x0a(mono) */ - {{0, {AudioNmaster".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_OUTAMP}, - {{0, {AudioNheadphone".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x10, MI_TARGET_OUTAMP}, - {{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x10, MI_TARGET_PINBOOST}, - {{0, {AudioNmono".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x11, MI_TARGET_OUTAMP}, - {{0, {"mic1.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x12, MI_TARGET_OUTAMP}, - {{0, {"mic1", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_IO}, 0x12, MI_TARGET_PINDIR}, - {{0, {"mic2.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x13, MI_TARGET_OUTAMP}, - {{0, {"mic2", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_IO}, 0x13, MI_TARGET_PINDIR}, - {{0, {"line1.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP}, - {{0, {"line1", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_IO}, 0x14, MI_TARGET_PINDIR}, - {{0, {"line2.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP}, - {{0, {"line2", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_IO}, 0x15, MI_TARGET_PINDIR}, - - {{0, {AudioNdac".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)}, /* and 0x09, 0x0a(mono) */ - {{0, {"mic1.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(0)}, - {{0, {"mic1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(0)}, - {{0, {"mic2.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(1)}, - {{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(1)}, - {{0, {"line1.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(2)}, - {{0, {"line1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(2)}, - {{0, {"line2.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(3)}, - {{0, {"line2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(3)}, - {{0, {AudioNcd".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(4)}, - {{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(4)}, - {{0, {AudioNspeaker".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(5)}, - {{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(5)}, - - {{0, {"adc04.source", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0, - .un.e={5, {{{"mic1", 0}, 0}, {{"mic2", 0}, 1}, {{"line1", 0}, 2}, - {{"line2", 0}, 3}, {{AudioNcd, 0}, 4}}}}, - 0x04, MI_TARGET_CONNLIST}, - {{0, {"adc04.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0, - ENUM_OFFON}, 0x04, MI_TARGET_INAMP(0)}, - {{0, {"adc04", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0, - .un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x04, MI_TARGET_INAMP(0)}, - {{0, {"adc05.source", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0, - .un.e={6, {{{"mic1", 0}, 0}, {{"mic2", 0}, 1}, {{"line1", 0}, 2}, - {{"line2", 0}, 3}, {{AudioNcd, 0}, 4}, {{AudioNmixerout, 0}, 5}}}}, - 0x05, MI_TARGET_CONNLIST}, - {{0, {"adc05.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0, - ENUM_OFFON}, 0x05, MI_TARGET_INAMP(0)}, - {{0, {"adc05", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0, - .un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x05, MI_TARGET_INAMP(0)}, - - {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0, - .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC}, - {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0, - .un.e={3, {{{"adc04", 0}, 0}, {{"adc05", 0}, 1}, {{AzaliaNdigital, 0}, 2}}}}, - 0, MI_TARGET_ADC}, -}; - -static const mixer_item_t alc260_loox_mixer_items[] = { - AZ_MIXER_CLASSES, - - {{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, 3}}, 0x08, MI_TARGET_OUTAMP}, /* and 0x09, 0x0a(mono) */ - {{0, {AudioNmaster".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x10, MI_TARGET_OUTAMP}, - {{0, {AudioNmaster".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x10, MI_TARGET_PINBOOST}, - {{0, {AudioNheadphone".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP}, - {{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST}, - - {{0, {AudioNdac".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)}, /* and 0x09, 0x0a(mono) */ - {{0, {AudioNmicrophone".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(0)}, - {{0, {AudioNmicrophone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(0)}, - {{0, {AudioNcd".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(4)}, - {{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(4)}, - {{0, {AudioNspeaker".mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(5)}, - {{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x07, MI_TARGET_INAMP(5)}, - - {{0, {"adc04.source", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0, - .un.e={2, {{{AudioNmicrophone, 0}, 0}, {{AudioNcd, 0}, 4}}}}, 0x04, MI_TARGET_CONNLIST}, - {{0, {"adc04.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0, - ENUM_OFFON}, 0x04, MI_TARGET_INAMP(0)}, - {{0, {"adc04", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0, - .un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x04, MI_TARGET_INAMP(0)}, - {{0, {"adc05.source", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0, - .un.e={3, {{{AudioNmicrophone, 0}, 0}, {{AudioNcd, 0}, 4}, {{AudioNmixerout, 0}, 5}}}}, - 0x05, MI_TARGET_CONNLIST}, - {{0, {"adc05.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0, - ENUM_OFFON}, 0x05, MI_TARGET_INAMP(0)}, - {{0, {"adc05", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, 0, 0, - .un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x05, MI_TARGET_INAMP(0)}, - - {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0, - .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC}, - {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0, - .un.e={3, {{{"adc04", 0}, 0}, {{"adc05", 0}, 1}, {{AzaliaNdigital, 0}, 2}}}}, - 0, MI_TARGET_ADC}, -}; - -static int -alc260_mixer_init(codec_t *this) -{ - const mixer_item_t *mi; - mixer_ctrl_t mc; - uint32_t value; - - switch (this->subid) { - case ALC260_FUJITSU_ID: - this->nmixers = __arraycount(alc260_loox_mixer_items); - mi = alc260_loox_mixer_items; - break; - default: - this->nmixers = __arraycount(alc260_mixer_items); - mi = alc260_mixer_items; - } - this->szmixers = sizeof(mixer_item_t) * this->nmixers; - this->mixers = kmem_alloc(this->szmixers, KM_SLEEP); - memcpy(this->mixers, mi, sizeof(mixer_item_t) * this->nmixers); - generic_mixer_fix_indexes(this); - generic_mixer_default(this); - - mc.dev = -1; /* no need for generic_mixer_set() */ - mc.type = AUDIO_MIXER_ENUM; - mc.un.ord = 1; /* pindir: output */ - generic_mixer_set(this, 0x0f, MI_TARGET_PINDIR, &mc); /* lineout */ - generic_mixer_set(this, 0x10, MI_TARGET_PINDIR, &mc); /* headphones */ - mc.un.ord = 0; /* pindir: input */ - generic_mixer_set(this, 0x12, MI_TARGET_PINDIR, &mc); /* mic1 */ - generic_mixer_set(this, 0x13, MI_TARGET_PINDIR, &mc); /* mic2 */ - generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc); /* line1 */ - generic_mixer_set(this, 0x15, MI_TARGET_PINDIR, &mc); /* line2 */ - mc.un.ord = 0; /* mute: off */ - generic_mixer_set(this, 0x08, MI_TARGET_INAMP(0), &mc); - generic_mixer_set(this, 0x08, MI_TARGET_INAMP(1), &mc); - generic_mixer_set(this, 0x09, MI_TARGET_INAMP(0), &mc); - generic_mixer_set(this, 0x09, MI_TARGET_INAMP(1), &mc); - generic_mixer_set(this, 0x0a, MI_TARGET_INAMP(0), &mc); - generic_mixer_set(this, 0x0a, MI_TARGET_INAMP(1), &mc); - if (this->subid == ALC260_FUJITSU_ID) { - mc.un.ord = 1; /* pindir: output */ - generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc); /* line1 */ - mc.un.ord = 4; /* connlist: cd */ - generic_mixer_set(this, 0x05, MI_TARGET_CONNLIST, &mc); - /* setup a unsolicited event for the headphones */ - this->comresp(this, 0x14, CORB_SET_UNSOLICITED_RESPONSE, - CORB_UNSOL_ENABLE | ALC260_EVENT_HP, NULL); - this->extra[ALC260_EXTRA_MASTER] = 0; /* unmute */ - /* If the headphone presents, mute the internal speaker */ - this->comresp(this, 0x14, CORB_GET_PIN_SENSE, 0, &value); - mc.un.ord = value & CORB_PS_PRESENCE ? 1 : 0; - generic_mixer_set(this, 0x10, MI_TARGET_OUTAMP, &mc); - this->get_port = alc260_get_port; - } - return 0; -} - -static int -alc260_init_dacgroup(codec_t *this) -{ - static const convgroupset_t dacs = { - -1, 2, - {{1, {0x02}}, /* analog 2ch */ - {1, {0x03}}}}; /* digital */ - static const convgroupset_t adcs = { - -1, 3, - {{1, {0x04}}, /* analog 2ch */ - {1, {0x05}}, /* analog 2ch */ - {1, {0x06}}}}; /* digital */ - - this->dacs = dacs; - this->adcs = adcs; - return 0; -} - -static int -alc260_set_port(codec_t *this, mixer_ctrl_t *mc) -{ - const mixer_item_t *m; - mixer_ctrl_t mc2; - uint32_t value; - int err; - - if (mc->dev < 0 || 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; - if (m->nid == 0x08 && m->target == MI_TARGET_OUTAMP) { - DPRINTF(("%s: hook for outputs.master\n", __func__)); - err = generic_mixer_set(this, m->nid, m->target, mc); - if (!err) { - generic_mixer_set(this, 0x09, m->target, mc); - mc2 = *mc; - mc2.un.value.num_channels = 1; - mc2.un.value.level[0] = (mc2.un.value.level[0] - + mc2.un.value.level[1]) / 2; - generic_mixer_set(this, 0x0a, m->target, &mc2); - } - return err; - } else if (m->nid == 0x08 && m->target == MI_TARGET_INAMP(0)) { - DPRINTF(("%s: hook for inputs.dac.mute\n", __func__)); - err = generic_mixer_set(this, m->nid, m->target, mc); - if (!err) { - generic_mixer_set(this, 0x09, m->target, mc); - generic_mixer_set(this, 0x0a, m->target, mc); - } - return err; - } else if (m->nid == 0x04 && - m->target == MI_TARGET_CONNLIST && - m->devinfo.un.e.num_mem == 2) { - if (1 <= mc->un.ord && mc->un.ord <= 3) - return EINVAL; - } else if (m->nid == 0x05 && - m->target == MI_TARGET_CONNLIST && - m->devinfo.un.e.num_mem == 3) { - if (1 <= mc->un.ord && mc->un.ord <= 3) - return EINVAL; - } else if (this->subid == ALC260_FUJITSU_ID && m->nid == 0x10 && - m->target == MI_TARGET_OUTAMP) { - if (mc->un.ord != 0 && mc->un.ord != 1) - return EINVAL; - this->extra[ALC260_EXTRA_MASTER] = mc->un.ord; - err = this->comresp(this, 0x14, CORB_GET_PIN_SENSE, 0, &value); - if (err) - return err; - if (!(value & CORB_PS_PRESENCE)) { - return generic_mixer_set(this, m->nid, m->target, mc); - } - return 0; - } - return generic_mixer_set(this, m->nid, m->target, mc); -} - -static int -alc260_get_port(codec_t *this, mixer_ctrl_t *mc) -{ - const mixer_item_t *m; - - if (mc->dev < 0 || mc->dev >= this->nmixers) - return ENXIO; - m = &this->mixers[mc->dev]; - mc->type = m->devinfo.type; - if (mc->type == AUDIO_MIXER_CLASS) - return 0; - if (this->subid == ALC260_FUJITSU_ID && m->nid == 0x10 && - m->target == MI_TARGET_OUTAMP) { - mc->un.ord = this->extra[ALC260_EXTRA_MASTER]; - return 0; - } - return generic_mixer_get(this, m->nid, m->target, mc); -} - -static int -alc260_unsol_event(codec_t *this, int tag) -{ - int err; - uint32_t value; - mixer_ctrl_t mc; - - switch (tag) { - case ALC260_EVENT_HP: - err = this->comresp(this, 0x14, CORB_GET_PIN_SENSE, 0, &value); - if (err) - break; - mc.dev = -1; - mc.type = AUDIO_MIXER_ENUM; - if (value & CORB_PS_PRESENCE) { - DPRINTF(("%s: headphone has been inserted.\n", __func__)); - mc.un.ord = 1; /* mute */ - generic_mixer_set(this, 0x10, MI_TARGET_OUTAMP, &mc); - } else { - DPRINTF(("%s: headphone has been pulled out.\n", __func__)); - mc.un.ord = this->extra[ALC260_EXTRA_MASTER]; - generic_mixer_set(this, 0x10, MI_TARGET_OUTAMP, &mc); - } - break; - default: - printf("%s: unknown tag: %d\n", __func__, tag); - } - return 0; -} - -/* ---------------------------------------------------------------- - * Realtek ALC262 - * ---------------------------------------------------------------- */ - -static int -alc262_init_widget(const codec_t *this, widget_t *w, nid_t nid) -{ - switch (nid) { - case 0x0c: - strlcpy(w->name, AudioNmaster, sizeof(w->name)); - break; - } - - return 0; -} - -/* ---------------------------------------------------------------- - * Realtek ALC268 - * ---------------------------------------------------------------- */ - -static int -alc268_init_dacgroup(codec_t *this) -{ - static const convgroupset_t dacs = { - -1, 1, - {{2, {0x02, 0x03}}}}; /* analog 4ch */ - static const convgroupset_t adcs = { - -1, 1, - {{2, {0x08, 0x07}}}}; /* analog 4ch */ - - this->dacs = dacs; - this->adcs = adcs; - return 0; -} - -/* ---------------------------------------------------------------- - * Realtek ALC662-GR - * ---------------------------------------------------------------- */ - -static int -alc662_init_dacgroup(codec_t *this) -{ - static const convgroupset_t dacs = { - -1, 1, - {{3, {0x02, 0x03, 0x04}}}}; /* analog 6ch */ - static const convgroupset_t adcs = { - -1, 1, - {{2, {0x09, 0x08}}}}; /* analog 4ch */ - - this->dacs = dacs; - this->adcs = adcs; - return 0; -} - -/* ---------------------------------------------------------------- - * Realtek ALC861 - * ---------------------------------------------------------------- */ - -static int -alc861_init_dacgroup(codec_t *this) -{ - static const convgroupset_t dacs = { - -1, 2, - {{4, {0x03, 0x04, 0x05, 0x06}}, /* analog 8ch */ - {1, {0x07}}}}; /* digital */ - static const convgroupset_t adcs = { - -1, 1, - {{1, {0x08}}}}; /* analog 2ch */ - - this->dacs = dacs; - this->adcs = adcs; - return 0; -} - -/* ---------------------------------------------------------------- - * Realtek ALC861-VD-GR - * ---------------------------------------------------------------- */ - -static int -alc861vdgr_init_dacgroup(codec_t *this) -{ - static const convgroupset_t dacs = { - -1, 2, - {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */ - {1, {0x06}}}}; /* digital */ - static const convgroupset_t adcs = { - -1, 1, - {{1, {0x09}}}}; /* analog 2ch */ - - this->dacs = dacs; - this->adcs = adcs; - return 0; -} - -/* ---------------------------------------------------------------- - * Realtek ALC880 - * ---------------------------------------------------------------- */ - -static const mixer_item_t alc880_mixer_items[] = { - AZ_MIXER_CLASSES, - - {{0, {AudioNsurround"."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, .un.e={5, {{{"mic1", 0}, 0}, {{"mic2", 0}, 1}, {{"line1", 0}, 2}, - {{"line2", 0}, 3}, {{AudioNcd, 0}, 4}}}}, 0x08, MI_TARGET_CONNLIST}, - {{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)}, - {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x08, MI_TARGET_INAMP(0)}, - - {{0, {AzaliaNfront"."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, .un.e={5, {{{"mic1", 0}, 0}, {{"mic2", 0}, 1}, {{"line1", 0}, 2}, - {{"line2", 0}, 3}, {{AudioNcd, 0}, 4}, - {{AudioNmixerout, 0}, 5}}}}, 0x09, MI_TARGET_CONNLIST}, - {{0, {AzaliaNfront"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)}, - {{0, {AzaliaNfront, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(35)}}, 0x09, MI_TARGET_INAMP(0)}, - - {{0, {"mic1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(0)}, - {{0, {"mic1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(0)}, - {{0, {"mic2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(1)}, - {{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(1)}, - {{0, {"line1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(2)}, - {{0, {"line1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(2)}, - {{0, {"line2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(3)}, - {{0, {"line2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(3)}, - {{0, {AudioNcd"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(4)}, - {{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(4)}, - {{0, {AudioNspeaker"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(5)}, - {{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(65)}}, 0x0b, MI_TARGET_INAMP(5)}, - - {{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x0c, MI_TARGET_OUTAMP}, - {{0, {AzaliaNfront".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(0)}, - {{0, {AzaliaNfront".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(1)}, - {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x0d, MI_TARGET_OUTAMP}, - {{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x0e, MI_TARGET_OUTAMP}, - {{0, {AzaliaNside, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x0f, MI_TARGET_OUTAMP}, -#if 0 /* The followings are useless. */ - {{0, {AudioNsurround".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(0)}, - {{0, {AudioNsurround".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(1)}, - {{0, {AzaliaNclfe".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(0)}, - {{0, {AzaliaNclfe".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(1)}, - {{0, {AzaliaNside".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(0)}, - {{0, {AzaliaNside".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(1)}, -#endif - - {{0, {AudioNmaster"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP}, - {{0, {AudioNmaster".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST}, - {{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP}, - {{0, {AudioNsurround".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_PINBOOST}, - {{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_OUTAMP}, - {{0, {AzaliaNclfe".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_PINBOOST}, - {{0, {AzaliaNside"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_OUTAMP}, - {{0, {AzaliaNside".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_PINBOOST}, - - {{0, {"mic2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x19, MI_TARGET_OUTAMP}, - {{0, {"mic2.boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x19, MI_TARGET_PINBOOST}, - {{0, {"mic2.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_IO}, 0x19, MI_TARGET_PINDIR}, - {{0, {"line1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x1a, MI_TARGET_OUTAMP}, - {{0, {"line1.boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x1a, MI_TARGET_PINBOOST}, - {{0, {"line1.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_IO}, 0x1a, MI_TARGET_PINDIR}, - {{0, {"line2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP}, - {{0, {"line2.boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_PINBOOST}, - {{0, {"line2.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_IO}, 0x1b, MI_TARGET_PINDIR}, - - {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0, - .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC}, - {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0, - .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_ADC}, -}; - -static int -alc880_mixer_init(codec_t *this) -{ - mixer_ctrl_t mc; - - this->nmixers = __arraycount(alc880_mixer_items); - this->szmixers = sizeof(alc880_mixer_items); - this->mixers = kmem_alloc(this->szmixers, KM_SLEEP); - memcpy(this->mixers, alc880_mixer_items, sizeof(alc880_mixer_items)); - generic_mixer_fix_indexes(this); - generic_mixer_default(this); - - mc.dev = -1; - mc.type = AUDIO_MIXER_ENUM; - mc.un.ord = 0; /* mute: off */ - generic_mixer_set(this, 0x0c, MI_TARGET_INAMP(0), &mc); /* dac->front */ - generic_mixer_set(this, 0x0c, MI_TARGET_INAMP(1), &mc); /* mixer->front */ - generic_mixer_set(this, 0x0d, MI_TARGET_INAMP(0), &mc); /* dac->surround */ - generic_mixer_set(this, 0x0e, MI_TARGET_INAMP(0), &mc); /* dac->clfe */ - generic_mixer_set(this, 0x0f, MI_TARGET_INAMP(0), &mc); /* dac->side */ - mc.un.ord = 1; /* mute: on */ - generic_mixer_set(this, 0x0d, MI_TARGET_INAMP(1), &mc); /* mixer->surround */ - generic_mixer_set(this, 0x0e, MI_TARGET_INAMP(1), &mc); /* mixer->clfe */ - generic_mixer_set(this, 0x0f, MI_TARGET_INAMP(1), &mc); /* mixer->side */ - mc.un.ord = 1; /* pindir: output */ - generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc); /* front */ - generic_mixer_set(this, 0x15, MI_TARGET_PINDIR, &mc); /* surround */ - generic_mixer_set(this, 0x16, MI_TARGET_PINDIR, &mc); /* clfe */ - generic_mixer_set(this, 0x17, MI_TARGET_PINDIR, &mc); /* side */ - generic_mixer_set(this, 0x19, MI_TARGET_PINDIR, &mc); /* mic2 */ - generic_mixer_set(this, 0x1b, MI_TARGET_PINDIR, &mc); /* line2 */ - mc.un.ord = 0; /* pindir: input */ - generic_mixer_set(this, 0x18, MI_TARGET_PINDIR, &mc); /* mic1 */ - generic_mixer_set(this, 0x1a, MI_TARGET_PINDIR, &mc); /* line1 */ - return 0; -} - -static int -alc880_init_dacgroup(codec_t *this) -{ - static const convgroupset_t dacs = { - -1, 2, - {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */ - {1, {0x06}}}}; /* digital */ - static const convgroupset_t adcs = { - -1, 2, - {{2, {0x08, 0x09}}, /* analog 4ch */ - {1, {0x0a}}}}; /* digital */ - - this->dacs = dacs; - this->adcs = adcs; - return 0; -} - -/* ---------------------------------------------------------------- - * Realtek ALC882 - * ---------------------------------------------------------------- */ - -static const mixer_item_t alc882_mixer_items[] = { - AZ_MIXER_CLASSES, - - /* 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x14,0x15,0x16,0x17 */ - {{0, {"mic1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(0)}, - {{0, {"mic1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(0)}, - {{0, {"mic2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(1)}, - {{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(1)}, - {{0, {AudioNline"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(2)}, - {{0, {AudioNline, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(2)}, - {{0, {AudioNcd"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(4)}, - {{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(4)}, - {{0, {AudioNspeaker"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(5)}, - {{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(5)}, - - {{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0c, MI_TARGET_OUTAMP}, - {{0, {AudioNmaster"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP}, - {{0, {AudioNmaster".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST}, - {{0, {AudioNheadphone"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP}, - {{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_PINBOOST}, - {{0, {AzaliaNfront".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(0)}, - {{0, {AzaliaNfront".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(1)}, - - {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0d, MI_TARGET_OUTAMP}, - {{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP}, - {{0, {AudioNsurround".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_PINBOOST}, - - {{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0e, MI_TARGET_OUTAMP}, - {{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_OUTAMP}, - {{0, {AzaliaNclfe".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_PINBOOST}, - - {{0, {AzaliaNside, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0f, MI_TARGET_OUTAMP}, - {{0, {AzaliaNside"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_OUTAMP}, - {{0, {AzaliaNside".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_PINBOOST}, - - /* 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x14,0x15,0x16,0x17,0xb */ -#define ALC882_MIC1 0x001 -#define ALC882_MIC2 0x002 -#define ALC882_LINE 0x004 -#define ALC882_CD 0x010 -#define ALC882_BEEP 0x020 -#define ALC882_MIX 0x400 -#define ALC882_MASK 0x437 - {{0, {AzaliaNfront"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_INAMP(0)}, - {{0, {AzaliaNfront, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x07, MI_TARGET_INAMP(0)}, - {{0, {AzaliaNfront"."AudioNsource, 0}, AUDIO_MIXER_SET, AZ_CLASS_RECORD, - 0, 0, .un.s={6, {{{"mic1", 0}, ALC882_MIC1}, {{"mic2", 0}, ALC882_MIC2}, - {{AudioNline, 0}, ALC882_LINE}, {{AudioNcd, 0}, ALC882_CD}, - {{AudioNspeaker, 0}, ALC882_BEEP}, - {{AudioNmixerout, 0}, ALC882_MIX}}}}, 0x24, -1}, - {{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)}, - {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x08, MI_TARGET_INAMP(0)}, - {{0, {AudioNsurround"."AudioNsource, 0}, AUDIO_MIXER_SET, AZ_CLASS_RECORD, - 0, 0, .un.s={6, {{{"mic1", 0}, ALC882_MIC1}, {{"mic2", 0}, ALC882_MIC2}, - {{AudioNline, 0}, ALC882_LINE}, {{AudioNcd, 0}, ALC882_CD}, - {{AudioNspeaker, 0}, ALC882_BEEP}, - {{AudioNmixerout, 0}, ALC882_MIX}}}}, 0x23, -1}, - {{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, ENUM_OFFON}, 0x09, MI_TARGET_INAMP(0)}, - {{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x09, MI_TARGET_INAMP(0)}, - {{0, {AzaliaNclfe"."AudioNsource, 0}, AUDIO_MIXER_SET, AZ_CLASS_RECORD, - 0, 0, .un.s={6, {{{"mic1", 0}, ALC882_MIC1}, {{"mic2", 0}, ALC882_MIC2}, - {{AudioNline, 0}, ALC882_LINE}, {{AudioNcd, 0}, ALC882_CD}, - {{AudioNspeaker, 0}, ALC882_BEEP}, - {{AudioNmixerout, 0}, ALC882_MIX}}}}, 0x22, -1}, - - {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0, - .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC}, - {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0, - .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_ADC}, -/* AZ_MIXER_SPDIF(AZ_CLASS_PLAYBACK, 0x06),*/ -}; - -static int -alc882_mixer_init(codec_t *this) -{ - mixer_ctrl_t mc; - - this->nmixers = __arraycount(alc882_mixer_items); - this->szmixers = sizeof(alc882_mixer_items); - this->mixers = kmem_alloc(this->szmixers, KM_SLEEP); - memcpy(this->mixers, alc882_mixer_items, sizeof(alc882_mixer_items)); - generic_mixer_fix_indexes(this); - generic_mixer_default(this); - - mc.dev = -1; - mc.type = AUDIO_MIXER_ENUM; - mc.un.ord = 1; /* pindir: output */ - generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc); - generic_mixer_set(this, 0x1b, MI_TARGET_PINDIR, &mc); - generic_mixer_set(this, 0x15, MI_TARGET_PINDIR, &mc); - generic_mixer_set(this, 0x16, MI_TARGET_PINDIR, &mc); - generic_mixer_set(this, 0x17, MI_TARGET_PINDIR, &mc); - mc.un.ord = 0; /* [0] 0x0c */ - generic_mixer_set(this, 0x14, MI_TARGET_CONNLIST, &mc); - generic_mixer_set(this, 0x1b, MI_TARGET_CONNLIST, &mc); - mc.un.ord = 1; /* [1] 0x0d */ - generic_mixer_set(this, 0x15, MI_TARGET_CONNLIST, &mc); - mc.un.ord = 2; /* [2] 0x0e */ - generic_mixer_set(this, 0x16, MI_TARGET_CONNLIST, &mc); - mc.un.ord = 2; /* [3] 0x0fb */ - generic_mixer_set(this, 0x17, MI_TARGET_CONNLIST, &mc); - - mc.un.ord = 0; /* pindir: input */ - generic_mixer_set(this, 0x18, MI_TARGET_PINDIR, &mc); - generic_mixer_set(this, 0x19, MI_TARGET_PINDIR, &mc); - generic_mixer_set(this, 0x1a, MI_TARGET_PINDIR, &mc); - /* XXX: inamp for 18/19/1a */ - - mc.un.ord = 0; /* unmute */ - generic_mixer_set(this, 0x24, MI_TARGET_INAMP(0), &mc); - generic_mixer_set(this, 0x23, MI_TARGET_INAMP(1), &mc); - generic_mixer_set(this, 0x22, MI_TARGET_INAMP(2), &mc); - generic_mixer_set(this, 0x0d, MI_TARGET_INAMP(0), &mc); - generic_mixer_set(this, 0x0e, MI_TARGET_INAMP(0), &mc); - generic_mixer_set(this, 0x0f, MI_TARGET_INAMP(0), &mc); - mc.un.ord = 1; /* mute */ - generic_mixer_set(this, 0x0d, MI_TARGET_INAMP(1), &mc); - generic_mixer_set(this, 0x0e, MI_TARGET_INAMP(1), &mc); - generic_mixer_set(this, 0x0f, MI_TARGET_INAMP(1), &mc); - return 0; -} - -static int -alc882_init_dacgroup(codec_t *this) -{ -#if 0 /* makes no sense to support for 0x25 node */ - static const convgroupset_t dacs = { - -1, 3, - {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */ - {1, {0x06}}, /* digital */ - {1, {0x25}}}}; /* another analog */ -#else - static const convgroupset_t dacs = { - -1, 2, - {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */ - {1, {0x06}}}}; /* digital */ -#endif - static const convgroupset_t adcs = { - -1, 2, - {{3, {0x07, 0x08, 0x09}}, /* analog 6ch */ - {1, {0x0a}}}}; /* digital */ - - this->dacs = dacs; - this->adcs = adcs; - return 0; -} - -static int -alc882_set_port(codec_t *this, mixer_ctrl_t *mc) -{ - const mixer_item_t *m; - mixer_ctrl_t mc2; - uint32_t mask, bit; - int i, err; - - if (mc->dev < 0 || 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; - if ((m->nid == 0x22 || m->nid == 0x23 || m->nid == 0x24) - && m->target == -1) { - DPRINTF(("%s: hook for record.*.source\n", __func__)); - mc2.dev = -1; - mc2.type = AUDIO_MIXER_ENUM; - bit = 1; - mask = mc->un.mask & ALC882_MASK; - for (i = 0; i < this->w[m->nid].nconnections && i < 32; i++) { - mc2.un.ord = (mask & bit) ? 0 : 1; - err = generic_mixer_set(this, m->nid, - MI_TARGET_INAMP(i), &mc2); - if (err) - return err; - bit = bit << 1; - } - return 0; - } - return generic_mixer_set(this, m->nid, m->target, mc); -} - -static int -alc882_get_port(codec_t *this, mixer_ctrl_t *mc) -{ - const mixer_item_t *m; - uint32_t mask, bit, result; - int i, err; - - if (mc->dev < 0 || mc->dev >= this->nmixers) - return ENXIO; - m = &this->mixers[mc->dev]; - mc->type = m->devinfo.type; - if (mc->type == AUDIO_MIXER_CLASS) - return 0; - if ((m->nid == 0x22 || m->nid == 0x23 || m->nid == 0x24) - && m->target == -1) { - DPRINTF(("%s: hook for record.*.source\n", __func__)); - mask = 0; - bit = 1; - for (i = 0; i < this->w[m->nid].nconnections && i < 32; i++) { - err = this->comresp(this, m->nid, CORB_GET_AMPLIFIER_GAIN_MUTE, - CORB_GAGM_INPUT | CORB_GAGM_LEFT | - i, &result); - if (err) - return err; - if ((result & CORB_GAGM_MUTE) == 0) - mask |= bit; - bit = bit << 1; - } - mc->un.mask = mask & ALC882_MASK; - return 0; - } - return generic_mixer_get(this, m->nid, m->target, mc); -} - -/* ---------------------------------------------------------------- - * Realtek ALC883 - * ALC882 without adc07 and mix24. - * ---------------------------------------------------------------- */ - -static int -alc883_init_dacgroup(codec_t *this) -{ - static const convgroupset_t dacs = { - -1, 2, - {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */ - {1, {0x06}}}}; /* digital */ - /* don't support for 0x25 dac */ - static const convgroupset_t adcs = { - -1, 2, - {{2, {0x08, 0x09}}, /* analog 4ch */ - {1, {0x0a}}}}; /* digital */ - - this->dacs = dacs; - this->adcs = adcs; - return 0; -} - -static const mixer_item_t alc883_mixer_items[] = { - AZ_MIXER_CLASSES, - - /* 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x14,0x15,0x16,0x17 */ - {{0, {"mic1."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(0)}, - {{0, {"mic1", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(0)}, - {{0, {"mic2."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(1)}, - {{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(1)}, - {{0, {AudioNline"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(2)}, - {{0, {AudioNline, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(2)}, - {{0, {AudioNcd"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(4)}, - {{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(4)}, - {{0, {AudioNspeaker"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_INAMP(5)}, - {{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_INAMP(5)}, - - {{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0c, MI_TARGET_OUTAMP}, - {{0, {AudioNmaster"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP}, - {{0, {AudioNmaster".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_PINBOOST}, - {{0, {AudioNheadphone"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP}, - {{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_PINBOOST}, - {{0, {AzaliaNfront".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(0)}, - {{0, {AzaliaNfront".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_INAMP(1)}, - {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0d, MI_TARGET_OUTAMP}, - {{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP}, - {{0, {AudioNsurround".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_PINBOOST}, - {{0, {AudioNsurround".dac.mut", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(0)}, - {{0, {AudioNsurround".mixer.m", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_INAMP(1)}, - - {{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0e, MI_TARGET_OUTAMP}, - {{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_OUTAMP}, - {{0, {AzaliaNclfe".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_PINBOOST}, - {{0, {AzaliaNclfe".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(0)}, - {{0, {AzaliaNclfe".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_INAMP(1)}, - - {{0, {AzaliaNside, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0f, MI_TARGET_OUTAMP}, - {{0, {AzaliaNside"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_OUTAMP}, - {{0, {AzaliaNside".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_PINBOOST}, - {{0, {AzaliaNside".dac.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(0)}, - {{0, {AzaliaNside".mixer.mute", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0f, MI_TARGET_INAMP(1)}, - - {{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)}, - {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x08, MI_TARGET_INAMP(0)}, - {{0, {AudioNsurround"."AudioNsource, 0}, AUDIO_MIXER_SET, AZ_CLASS_RECORD, - 0, 0, .un.s={6, {{{"mic1", 0}, ALC882_MIC1}, {{"mic2", 0}, ALC882_MIC2}, - {{AudioNline, 0}, ALC882_LINE}, {{AudioNcd, 0}, ALC882_CD}, - {{AudioNspeaker, 0}, ALC882_BEEP}, - {{AudioNmixerout, 0}, ALC882_MIX}}}}, 0x23, -1}, - - {{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, ENUM_OFFON}, 0x09, MI_TARGET_INAMP(0)}, - {{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x09, MI_TARGET_INAMP(0)}, - {{0, {AzaliaNclfe"."AudioNsource, 0}, AUDIO_MIXER_SET, AZ_CLASS_RECORD, - 0, 0, .un.s={6, {{{"mic1", 0}, ALC882_MIC1}, {{"mic2", 0}, ALC882_MIC2}, - {{AudioNline, 0}, ALC882_LINE}, {{AudioNcd, 0}, ALC882_CD}, - {{AudioNspeaker, 0}, ALC882_BEEP}, - {{AudioNmixerout, 0}, ALC882_MIX}}}}, 0x22, -1}, - - {{0, {"usingdac", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, 0, 0, - .un.e={2, {{{"analog", 0}, 0}, {{"digital", 0}, 1}}}}, 0, MI_TARGET_DAC}, - {{0, {"usingadc", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, 0, 0, - .un.e={2, {{{"analog", 0}, 0}, {{"digital", 0}, 1}}}}, 0, MI_TARGET_ADC}, -}; - -int -alc883_mixer_init(codec_t *this) -{ - mixer_ctrl_t mc; - - this->nmixers = __arraycount(alc883_mixer_items); - this->szmixers = sizeof(alc883_mixer_items); - this->mixers = kmem_alloc(this->szmixers, KM_SLEEP); - memcpy(this->mixers, alc883_mixer_items, sizeof(alc883_mixer_items)); - generic_mixer_fix_indexes(this); - generic_mixer_default(this); - - mc.dev = -1; - mc.type = AUDIO_MIXER_ENUM; - mc.un.ord = 1; /* pindir: output */ - generic_mixer_set(this, 0x14, MI_TARGET_PINDIR, &mc); - generic_mixer_set(this, 0x1b, MI_TARGET_PINDIR, &mc); - generic_mixer_set(this, 0x15, MI_TARGET_PINDIR, &mc); - generic_mixer_set(this, 0x16, MI_TARGET_PINDIR, &mc); - generic_mixer_set(this, 0x17, MI_TARGET_PINDIR, &mc); - mc.un.ord = 0; /* [0] 0x0c */ - generic_mixer_set(this, 0x14, MI_TARGET_CONNLIST, &mc); - generic_mixer_set(this, 0x1b, MI_TARGET_CONNLIST, &mc); - mc.un.ord = 1; /* [1] 0x0d */ - generic_mixer_set(this, 0x15, MI_TARGET_CONNLIST, &mc); - mc.un.ord = 2; /* [2] 0x0e */ - generic_mixer_set(this, 0x16, MI_TARGET_CONNLIST, &mc); - mc.un.ord = 2; /* [3] 0x0fb */ - generic_mixer_set(this, 0x17, MI_TARGET_CONNLIST, &mc); - - mc.un.ord = 0; /* pindir: input */ - generic_mixer_set(this, 0x18, MI_TARGET_PINDIR, &mc); - generic_mixer_set(this, 0x19, MI_TARGET_PINDIR, &mc); - generic_mixer_set(this, 0x1a, MI_TARGET_PINDIR, &mc); - /* XXX: inamp for 18/19/1a */ - - mc.un.ord = 0; /* unmute */ - generic_mixer_set(this, 0x23, MI_TARGET_INAMP(1), &mc); - generic_mixer_set(this, 0x22, MI_TARGET_INAMP(2), &mc); - return 0; -} -/* ---------------------------------------------------------------- - * Realtek ALC885 - * ---------------------------------------------------------------- */ - -static int -alc885_init_dacgroup(codec_t *this) -{ - static const convgroupset_t dacs = { - -1, 2, - {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */ - {1, {0x06}}}}; /* digital */ - /* don't support for 0x25 dac */ - static const convgroupset_t adcs = { - -1, 2, - {{3, {0x07, 0x08, 0x09}}, /* analog 6ch */ - {1, {0x0a}}}}; /* digital */ - - this->dacs = dacs; - this->adcs = adcs; - return 0; -} - -/* ---------------------------------------------------------------- - * Realtek ALC888 - * ---------------------------------------------------------------- */ - -static int -alc888_init_dacgroup(codec_t *this) -{ - static const convgroupset_t dacs = { - -1, 2, - {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */ - {1, {0x06}}}}; /* digital */ - /* don't support for 0x25 dac */ - /* ALC888S has another SPDIF-out 0x10 */ - static const convgroupset_t adcs = { - -1, 2, - {{2, {0x08, 0x09}}, /* analog 4ch */ - {1, {0x0a}}}}; /* digital */ - - this->dacs = dacs; - this->adcs = adcs; - return 0; -} - -static int -alc888_init_widget(const codec_t *this, widget_t *w, nid_t nid) -{ - switch (nid) { - case 0x0c: - strlcpy(w->name, AudioNmaster, sizeof(w->name)); - break; - } - return 0; -} - -static int -alc888_mixer_init(codec_t *this) -{ - mixer_item_t *m = NULL; - mixer_devinfo_t *d; - int err, i, mdac_index = -1; - - err = generic_mixer_init(this); - if (err) - return err; - - /* Clear mixer indexes, to make generic_mixer_fix_indexes happy */ - for (i = 0; i < this->nmixers; i++) { - d = &this->mixers[i].devinfo; - d->index = d->prev = d->next = 0; - } - - /* We're looking for front l/r mixer, which we know is nid 0x0c */ - for (i = 0; i < this->nmixers; i++) - if (this->mixers[i].nid == 0x0c) { - mdac_index = i; - break; - } - if (mdac_index >= 0) { - /* - * ALC888 doesn't have a master mixer, so create a fake - * inputs.dac that mirrors outputs.master - */ - err = generic_mixer_ensure_capacity(this, this->nmixers + 1); - if (err) - return err; - - m = &this->mixers[this->nmixers]; - d = &m->devinfo; - memcpy(m, &this->mixers[mdac_index], sizeof(*m)); - d->mixer_class = AZ_CLASS_INPUT; - snprintf(d->label.name, sizeof(d->label.name), AudioNdac); - this->nmixers++; - } - - /* Recreate mixer indexes and defaults after making a mess of things */ - generic_mixer_fix_indexes(this); - generic_mixer_default(this); - - return 0; -} - -/* ---------------------------------------------------------------- - * Analog Devices AD1981HD - * ---------------------------------------------------------------- */ - -#define AD1981HD_THINKPAD 0x201017aa - -static const mixer_item_t ad1981hd_mixer_items[] = { - AZ_MIXER_CLASSES, - - {{0, {AzaliaNdigital "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={2, {{{"os", 0}, 0}, {{"adc", 0}, 1}}}}, 0x02, MI_TARGET_CONNLIST}, - - {{0, {"lineout." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}}, - 0x05, MI_TARGET_CONNLIST}, - {{0, {"lineout." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x05, MI_TARGET_OUTAMP}, - {{0, {"lineout", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(63)}}, 0x05, MI_TARGET_OUTAMP}, - {{0, {"lineout", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(3)}}, 0x05, MI_TARGET_INAMP(0)}, - {{0, {"lineout.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_IO}, 0x05, MI_TARGET_PINDIR}, - {{0, {"lineout.boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x05, MI_TARGET_PINBOOST}, - {{0, {"lineout.eapd", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x05, MI_TARGET_EAPD}, - - {{0, {AudioNheadphone ".src", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}}, - 0x06, MI_TARGET_CONNLIST}, - {{0, {AudioNheadphone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x06, MI_TARGET_OUTAMP}, - {{0, {AudioNheadphone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(63)}}, 0x06, MI_TARGET_OUTAMP}, - {{0, {AudioNheadphone ".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x06, MI_TARGET_PINBOOST}, - - {{0, {AudioNmono "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_OUTAMP}, - {{0, {AudioNmono, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(63)}}, 0x07, MI_TARGET_OUTAMP}, - - {{0, {AudioNmicrophone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(3)}}, 0x08, MI_TARGET_INAMP(0)}, - - {{0, {"linein." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}}, - 0x09, MI_TARGET_CONNLIST}, - {{0, {"linein." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x09, MI_TARGET_OUTAMP}, - {{0, {"linein", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(63)}}, 0x09, MI_TARGET_OUTAMP}, - {{0, {"linein", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(3)}}, 0x09, MI_TARGET_INAMP(0)}, - {{0, {"linein.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_IO}, 0x09, MI_TARGET_PINDIR}, - - {{0, {AudioNmono "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={6, {{{AudioNdac, 0}, 0}, {{"mixedmic", 0}, 1}, - {{"linein", 0}, 2}, {{AudioNmixerout, 0}, 3}, - {{"lineout", 0}, 4}, {{"mic2", 0}, 5}}}}, - 0x0b, MI_TARGET_CONNLIST}, - - {{0, {"beep." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, .un.e={2, {{{"digitalbeep", 0}, 0}, {{"beep", 0}, 1}}}}, - 0x0d, MI_TARGET_CONNLIST}, - {{0, {"beep." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_OUTAMP}, - {{0, {"beep", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(15)}}, 0x0d, MI_TARGET_OUTAMP}, - - {{0, {AudioNdac "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x11, MI_TARGET_OUTAMP}, - {{0, {AudioNdac, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x11, MI_TARGET_OUTAMP}, - - {{0, {AudioNmicrophone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x12, MI_TARGET_OUTAMP}, - {{0, {AudioNmicrophone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x12, MI_TARGET_OUTAMP}, - - {{0, {"linein." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x13, MI_TARGET_OUTAMP}, - {{0, {"linein", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x13, MI_TARGET_OUTAMP}, - - {{0, {AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, .un.e={8, {{{"mixedmic", 0}, 0}, {{"linein", 0}, 1}, - {{AudioNmixerout, 0}, 2}, {{AudioNmono, 0}, 3}, - {{AudioNcd, 0}, 4}, {{"lineout", 0}, 5}, - {{"mic2", 0}, 6}, {{AudioNaux, 0}, 7}}}}, - 0x15, MI_TARGET_CONNLIST}, - {{0, {AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP}, - {{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(15)}}, 0x15, MI_TARGET_OUTAMP}, - - {{0, {"mic2." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}}, - 0x18, MI_TARGET_CONNLIST}, - {{0, {"mic2." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x18, MI_TARGET_OUTAMP}, - {{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(63)}}, 0x18, MI_TARGET_OUTAMP}, - {{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(3)}}, 0x18, MI_TARGET_INAMP(0)}, - {{0, {"mic2.dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_IO}, 0x18, MI_TARGET_PINDIR}, - - {{0, {"lineout." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x1a, MI_TARGET_OUTAMP}, - {{0, {"lineout", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1a, MI_TARGET_OUTAMP}, - - {{0, {AudioNaux "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP}, - {{0, {AudioNaux, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1b, MI_TARGET_OUTAMP}, - - {{0, {"mic2." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x1c, MI_TARGET_OUTAMP}, - {{0, {"mic2", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1c, MI_TARGET_OUTAMP}, - - {{0, {AudioNcd "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x1d, MI_TARGET_OUTAMP}, - {{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1d, MI_TARGET_OUTAMP}, - - {{0, {"mixedmic.mute1", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, ENUM_OFFON}, 0x1e, MI_TARGET_OUTAMP}, - {{0, {"mixedmic.mute2", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, ENUM_OFFON}, 0x1f, MI_TARGET_OUTAMP}, - - {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0, - .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC}, -}; - -static int -ad1981hd_init_widget(const codec_t *this, widget_t *w, nid_t nid) -{ - switch (nid) { - case 0x05: - strlcpy(w->name, AudioNline "out", sizeof(w->name)); - break; - case 0x06: - strlcpy(w->name, "hp", sizeof(w->name)); - break; - case 0x07: - strlcpy(w->name, AudioNmono, sizeof(w->name)); - break; - case 0x08: - strlcpy(w->name, AudioNmicrophone, sizeof(w->name)); - break; - case 0x09: - strlcpy(w->name, AudioNline "in", sizeof(w->name)); - break; - case 0x0d: - strlcpy(w->name, "beep", sizeof(w->name)); - break; - case 0x17: - strlcpy(w->name, AudioNaux, sizeof(w->name)); - break; - case 0x18: - strlcpy(w->name, AudioNmicrophone "2", sizeof(w->name)); - break; - case 0x19: - strlcpy(w->name, AudioNcd, sizeof(w->name)); - break; - case 0x1d: - strlcpy(w->name, AudioNcd, sizeof(w->name)); - break; - } - return 0; -} - -static int -ad1981hd_mixer_init(codec_t *this) -{ - mixer_ctrl_t mc; - - this->nmixers = __arraycount(ad1981hd_mixer_items); - this->szmixers = sizeof(ad1981hd_mixer_items); - this->mixers = kmem_alloc(this->szmixers, KM_SLEEP); - memcpy(this->mixers, ad1981hd_mixer_items, sizeof(ad1981hd_mixer_items)); - generic_mixer_fix_indexes(this); - generic_mixer_default(this); - - if (this->subid == AD1981HD_THINKPAD) { - mc.dev = -1; - mc.type = AUDIO_MIXER_ENUM; - mc.un.ord = 1; - generic_mixer_set(this, 0x09, MI_TARGET_PINDIR, &mc); - generic_mixer_set(this, 0x05, MI_TARGET_EAPD, &mc); - mc.type = AUDIO_MIXER_VALUE; - mc.un.value.num_channels = 2; - mc.un.value.level[0] = AUDIO_MAX_GAIN; - mc.un.value.level[1] = AUDIO_MAX_GAIN; - generic_mixer_set(this, 0x1a, MI_TARGET_VOLUME, &mc); - } - return 0; -} - -/* ---------------------------------------------------------------- - * Analog Devices AD1983 - * ---------------------------------------------------------------- */ - -static const mixer_item_t ad1983_mixer_items[] = { - AZ_MIXER_CLASSES, - - {{0, {AzaliaNdigital "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={2, {{{"os", 0}, 0}, {{"adc", 0}, 1}}}}, 0x02, MI_TARGET_CONNLIST}, - - {{0, {AudioNspeaker "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}}, - 0x05, MI_TARGET_CONNLIST}, - {{0, {AudioNspeaker "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x05, MI_TARGET_OUTAMP}, - {{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x05, MI_TARGET_OUTAMP}, - - {{0, {AudioNheadphone ".src", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{AudioNmixerout, 0}, 1}}}}, - 0x06, MI_TARGET_CONNLIST}, - {{0, {AudioNheadphone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x06, MI_TARGET_OUTAMP}, - {{0, {AudioNheadphone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(64)}}, 0x06, MI_TARGET_OUTAMP}, - {{0, {AudioNheadphone ".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x06, MI_TARGET_PINBOOST}, - - {{0, {AudioNmono "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_OUTAMP}, - {{0, {AudioNmono, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(64)}}, 0x07, MI_TARGET_OUTAMP}, - - {{0, {AudioNmono "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={4, {{{AudioNdac, 0}, 0}, {{AudioNmicrophone, 0}, 1}, - {{AudioNline, 0}, 2}, {{AudioNmixerout, 0}, 3}}}}, - 0x0b, MI_TARGET_CONNLIST}, - - {{0, {AudioNmicrophone "." AudioNpreamp "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x0c, MI_TARGET_OUTAMP}, - {{0, {AudioNmicrophone "." AudioNpreamp, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(4)}}, 0x0c, MI_TARGET_OUTAMP}, - {{0, {AudioNmicrophone "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, .un.e={2, {{{AudioNmicrophone, 0}, 0}, {{AudioNline, 0}, 1}}}}, - 0x0c, MI_TARGET_CONNLIST}, - - {{0, {AudioNline "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, .un.e={2, {{{AudioNline, 0}, 0}, {{AudioNmicrophone, 0}, 1}}}}, - 0x0d, MI_TARGET_CONNLIST}, - - {{0, {"beep." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x10, MI_TARGET_OUTAMP}, - {{0, {"beep", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(16)}}, 0x10, MI_TARGET_OUTAMP}, - - {{0, {AudioNdac "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x11, MI_TARGET_OUTAMP}, - {{0, {AudioNdac, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(32)}}, 0x11, MI_TARGET_OUTAMP}, - - {{0, {AudioNmicrophone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x12, MI_TARGET_OUTAMP}, - {{0, {AudioNmicrophone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(32)}}, 0x12, MI_TARGET_OUTAMP}, - - {{0, {AudioNline "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x13, MI_TARGET_OUTAMP}, - {{0, {AudioNline, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(32)}}, 0x13, MI_TARGET_OUTAMP}, - - {{0, {AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, .un.e={4, {{{AudioNmicrophone, 0}, 0}, {{AudioNline, 0}, 1}, - {{AudioNmixerout, 0}, 2}, {{AudioNmono, 0}, 3}}}}, - 0x14, MI_TARGET_CONNLIST}, - {{0, {AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP}, - {{0, {AudioNvolume, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(16)}}, 0x14, MI_TARGET_OUTAMP}, - - {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0, - .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC}, -}; - -static int -ad1983_mixer_init(codec_t *this) -{ - mixer_ctrl_t mc; - - this->nmixers = __arraycount(ad1983_mixer_items); - this->szmixers = sizeof(ad1983_mixer_items); - this->mixers = kmem_alloc(this->szmixers, KM_SLEEP); - memcpy(this->mixers, ad1983_mixer_items, sizeof(ad1983_mixer_items)); - generic_mixer_fix_indexes(this); - generic_mixer_default(this); - -#define AD198X_EVENT_HP 1 -#define AD198X_EVENT_SPEAKER 2 - - mc.dev = -1; /* no need for generic_mixer_set() */ - mc.type = AUDIO_MIXER_ENUM; - mc.un.ord = 1; /* connlist: mixerout */ - generic_mixer_set(this, 0x05, MI_TARGET_CONNLIST, &mc); - generic_mixer_set(this, 0x06, MI_TARGET_CONNLIST, &mc); - mc.un.ord = 3; /* connlist: mixerout */ - generic_mixer_set(this, 0x0b, MI_TARGET_CONNLIST, &mc); - - /* setup a unsolicited event for the headphones and speaker */ - this->comresp(this, 0x05, CORB_SET_UNSOLICITED_RESPONSE, - CORB_UNSOL_ENABLE | AD198X_EVENT_SPEAKER, NULL); - this->comresp(this, 0x06, CORB_SET_UNSOLICITED_RESPONSE, - CORB_UNSOL_ENABLE | AD198X_EVENT_HP, NULL); - ad1983_unsol_event(this, AD198X_EVENT_SPEAKER); - ad1983_unsol_event(this, AD198X_EVENT_HP); - return 0; -} - -static int -ad1983_unsol_event(codec_t *this, int tag) -{ - int err; - uint32_t value; - mixer_ctrl_t mc; - - mc.dev = -1; - mc.type = AUDIO_MIXER_ENUM; - - switch (tag) { - case AD198X_EVENT_HP: - err = this->comresp(this, 0x06, CORB_GET_PIN_SENSE, 0, &value); - if (err) - break; - if (value & CORB_PS_PRESENCE) { - DPRINTF(("%s: headphone has been inserted.\n", __func__)); - mc.un.ord = 1; /* mute */ - generic_mixer_set(this, 0x05, MI_TARGET_OUTAMP, &mc); - generic_mixer_set(this, 0x07, MI_TARGET_OUTAMP, &mc); - } else { - DPRINTF(("%s: headphone has been pulled out.\n", __func__)); - mc.un.ord = 0; /* unmute */ - generic_mixer_set(this, 0x05, MI_TARGET_OUTAMP, &mc); - /* if no speaker unmute internal mono */ - err = this->comresp(this, 0x05, CORB_GET_PIN_SENSE, 0, &value); - if (err) - break; - if (!(value & CORB_PS_PRESENCE)) - generic_mixer_set(this, 0x07, MI_TARGET_OUTAMP, &mc); - } - break; - case AD198X_EVENT_SPEAKER: - err = this->comresp(this, 0x05, CORB_GET_PIN_SENSE, 0, &value); - if (err) - break; - if (value & CORB_PS_PRESENCE) { - DPRINTF(("%s: speaker has been inserted.\n", __func__)); - mc.un.ord = 1; /* mute */ - generic_mixer_set(this, 0x07, MI_TARGET_OUTAMP, &mc); - } else { - DPRINTF(("%s: speaker has been pulled out.\n", __func__)); - /* if no headphones unmute internal mono */ - err = this->comresp(this, 0x06, CORB_GET_PIN_SENSE, 0, &value); - if (err) - break; - if (!(value & CORB_PS_PRESENCE)) { - mc.un.ord = 0; /* unmute */ - generic_mixer_set(this, 0x07, MI_TARGET_OUTAMP, &mc); - } - } - break; - default: - printf("%s: unknown tag: %d\n", __func__, tag); - } - return 0; -} - -/* ---------------------------------------------------------------- - * Analog Devices AD1984 - * ---------------------------------------------------------------- */ - -#define AD1984_THINKPAD 0x20ac17aa -#define AD1984_DELL_OPTIPLEX_755 0x02111028 -#define AD1984A_DELL_OPTIPLEX_760 0x027f1028 - -static int -ad1984_init_dacgroup(codec_t *this) -{ - static const convgroupset_t dacs = { - -1, 2, - {{2, {0x04, 0x03}}, /* analog 4ch */ - {1, {0x02}}}}; /* digital */ - static const convgroupset_t adcs = { - -1, 3, - {{2, {0x08, 0x09}}, /* analog 4ch */ - {1, {0x06}}, /* digital */ - {1, {0x05}}}}; /* digital */ - this->dacs = dacs; - this->adcs = adcs; - return 0; -} - -static int -ad1984_mixer_init(codec_t *this) -{ - int err; - - err = generic_mixer_autoinit(this); - if (err) - return err; - - if (this->subid == AD1984_DELL_OPTIPLEX_755 || - this->subid == AD1984A_DELL_OPTIPLEX_760) { - /* setup a unsolicited event for the headphones and speaker */ - this->comresp(this, 0x12, CORB_SET_UNSOLICITED_RESPONSE, - CORB_UNSOL_ENABLE | AD198X_EVENT_SPEAKER, NULL); - this->comresp(this, 0x11, CORB_SET_UNSOLICITED_RESPONSE, - CORB_UNSOL_ENABLE | AD198X_EVENT_HP, NULL); - ad1984_unsol_event(this, AD198X_EVENT_SPEAKER); - ad1984_unsol_event(this, AD198X_EVENT_HP); - } - - return 0; -} - -static int -ad1984_init_widget(const codec_t *this, widget_t *w, nid_t nid) -{ - switch (nid) { - case 0x07: - strlcpy(w->name, "hp", sizeof(w->name)); - break; - case 0x0a: - strlcpy(w->name, "spkr", sizeof(w->name)); - break; - case 0x0b: - strlcpy(w->name, AudioNaux, sizeof(w->name)); - break; - case 0x0c: - strlcpy(w->name, "adc08", sizeof(w->name)); - break; - case 0x0d: - strlcpy(w->name, "adc09", sizeof(w->name)); - break; - case 0x0e: - strlcpy(w->name, AudioNmono "sel", sizeof(w->name)); - break; - case 0x0f: - strlcpy(w->name, AudioNaux "sel", sizeof(w->name)); - break; - case 0x10: - strlcpy(w->name, "beep", sizeof(w->name)); - break; - case 0x1e: - strlcpy(w->name, AudioNmono, sizeof(w->name)); - break; - case 0x22: - strlcpy(w->name, "hp" "sel", sizeof(w->name)); - break; - case 0x23: - strlcpy(w->name, "dock" "sel", sizeof(w->name)); - break; - case 0x24: - strlcpy(w->name, "dock", sizeof(w->name)); - break; - case 0x25: - strlcpy(w->name, "dock.pre", sizeof(w->name)); - break; - default: - return generic_mixer_init_widget(this, w, nid); - } - - return 0; -} - -static int -ad1984_unsol_event(codec_t *this, int tag) -{ - int err; - uint32_t value; - mixer_ctrl_t mc; - - mc.dev = -1; - mc.type = AUDIO_MIXER_ENUM; - - switch (tag) { - case AD198X_EVENT_HP: - err = this->comresp(this, 0x11, CORB_GET_PIN_SENSE, 0, &value); - if (err) - break; - if (value & CORB_PS_PRESENCE) { - DPRINTF(("%s: headphone has been inserted.\n", __func__)); - mc.un.ord = 1; /* mute */ - generic_mixer_set(this, 0x12, MI_TARGET_OUTAMP, &mc); - generic_mixer_set(this, 0x13, MI_TARGET_OUTAMP, &mc); - } else { - DPRINTF(("%s: headphone has been pulled out.\n", __func__)); - mc.un.ord = 0; /* unmute */ - generic_mixer_set(this, 0x12, MI_TARGET_OUTAMP, &mc); - /* if no speaker unmute internal mono */ - err = this->comresp(this, 0x12, CORB_GET_PIN_SENSE, 0, &value); - if (err) - break; - if (!(value & CORB_PS_PRESENCE)) - generic_mixer_set(this, 0x13, MI_TARGET_OUTAMP, &mc); - } - break; - case AD198X_EVENT_SPEAKER: - err = this->comresp(this, 0x12, CORB_GET_PIN_SENSE, 0, &value); - if (err) - break; - if (value & CORB_PS_PRESENCE) { - DPRINTF(("%s: speaker has been inserted.\n", __func__)); - mc.un.ord = 1; /* mute */ - generic_mixer_set(this, 0x13, MI_TARGET_OUTAMP, &mc); - } else { - DPRINTF(("%s: speaker has been pulled out.\n", __func__)); - /* if no headphones unmute internal mono */ - err = this->comresp(this, 0x11, CORB_GET_PIN_SENSE, 0, &value); - if (err) - break; - if (!(value & CORB_PS_PRESENCE)) { - mc.un.ord = 0; /* unmute */ - generic_mixer_set(this, 0x13, MI_TARGET_OUTAMP, &mc); - } - } - break; - default: - printf("%s: unknown tag: %d\n", __func__, tag); - } - return 0; -} - -/* ---------------------------------------------------------------- - * Analog Devices AD1986A - * ---------------------------------------------------------------- */ - -static const mixer_item_t ad1986a_mixer_items[] = { - AZ_MIXER_CLASSES, - - {{0, {AzaliaNdigital "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={2, {{{"hdaudio", 0}, 0}, {{"recordout", 0}, 1}}}}, 0x02, MI_TARGET_CONNLIST}, - -#if 0 - /* fix to mixerout (default) */ - {{0, {AudioNheadphone ".src", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={3, {{{AudioNmixerout, 0}, 0}, {{"surrounddac", 0}, 1}, - {{"clfedac", 0}, 2}}}}, 0x0a, MI_TARGET_CONNLIST}, -#endif - {{0, {AudioNheadphone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x1a, MI_TARGET_OUTAMP}, - {{0, {AudioNheadphone "." "boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x1a, MI_TARGET_PINBOOST}, - {{0, {AudioNheadphone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1a, MI_TARGET_OUTAMP}, - -#if 0 - /* fix to mixerout (default) */ - {{0, {AudioNline "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={2, {{{AudioNmixerout, 0}, 0}, {{"surrounddac", 0}, 1}}}}, - 0x0b, MI_TARGET_CONNLIST}, -#endif - {{0, {AudioNline "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_OUTAMP}, - {{0, {AudioNline "." "boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_PINBOOST}, - {{0, {AudioNline "." "eapd", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x1b, MI_TARGET_EAPD}, - {{0, {AudioNline, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1b, MI_TARGET_OUTAMP}, - -#if 0 - /* fix to the surround DAC (default) */ - {{0, {AudioNsurround "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={2, {{{"surrounddac", 0}, 0}, {{AudioNmixerout, 0}, 1}}}}, - 0x0c, MI_TARGET_CONNLIST}, - /* unmute */ - {{0, {AudioNsurround "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x1c, MI_TARGET_OUTAMP}, - /* fix to output */ - {{0, {AudioNsurround "." "dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_IO}, 0x1c, MI_TARGET_PINDIR}, - /* fix to maximum */ - {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1c, MI_TARGET_OUTAMP}, -#endif - {{0, {AudioNsurround "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x04, MI_TARGET_OUTAMP}, - {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x04, MI_TARGET_OUTAMP}, - -#if 0 - /* fix to the clfe DAC (default) */ - {{0, {AzaliaNclfe "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={2, {{{"clfedac", 0}, 0}, {{"mixeroutmono", 0}, 1}}}}, - 0x0d, MI_TARGET_CONNLIST}, - /* unmute */ - {{0, {AzaliaNclfe "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x1d, MI_TARGET_OUTAMP}, - /* fix to output */ - {{0, {AzaliaNclfe "." "dir", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_IO}, 0x1d, MI_TARGET_PINDIR}, - /* fix to maximum */ - {{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1d, MI_TARGET_OUTAMP}, -#endif - {{0, {AzaliaNclfe "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x05, MI_TARGET_OUTAMP}, - {{0, {AzaliaNclfe, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x05, MI_TARGET_OUTAMP}, - {{0, {AzaliaNclfe "." "lrswap", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x1d, MI_TARGET_LRSWAP}, - - {{0, {AudioNmono "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={2, {{{AudioNmixerout, 0}, 0}, {{AudioNmicrophone, 0}, 1}}}}, - 0x0e, MI_TARGET_CONNLIST}, - {{0, {AudioNmono "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x1e, MI_TARGET_OUTAMP}, - {{0, {AudioNmono, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x1e, MI_TARGET_OUTAMP}, - - /* Front DAC */ - {{0, {AudioNdac "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x03, MI_TARGET_OUTAMP}, - {{0, {AudioNdac, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x03, MI_TARGET_OUTAMP}, - -#if 0 - /* 0x09: 5.1 -> Stereo Downmix */ - {{0, {"downmix" "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x09, MI_TARGET_OUTAMP}, -#endif - -#if 0 - /* mic source is mic jack (default) */ - {{0, {AudioNmicrophone "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, .un.e={8, {{{AudioNmicrophone, 0}, 0}, {{AudioNline, 0}, 1}, - {{AzaliaNclfe, 0}, 2}, {{AzaliaNclfe "2", 0}, 3}, - {{"micclfe", 0}, 4}, {{"micline", 0}, 5}, - {{"clfeline", 0}, 6}, {{"miclineclfe", 0}, 7}}}}, - 0x0f, MI_TARGET_CONNLIST}, -#endif - {{0, {AudioNmicrophone "." "gain", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(3)}}, 0x0f, MI_TARGET_OUTAMP}, - {{0, {AudioNmicrophone "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, ENUM_OFFON}, 0x13, MI_TARGET_OUTAMP}, - {{0, {AudioNmicrophone, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x13, MI_TARGET_OUTAMP}, - -#if 0 - /* line source is line jack (default) */ - {{0, {AudioNline "." AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, .un.e={3, {{{AudioNline, 0}, 0}, {{AudioNsurround, 0}, 1}, - {{AudioNmicrophone, 0}, 2}}}}, 0x10, MI_TARGET_CONNLIST}, -#endif - {{0, {AudioNline "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x17, MI_TARGET_OUTAMP}, - {{0, {AudioNline, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x17, MI_TARGET_OUTAMP}, - - {{0, {"phone" "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP}, - {{0, {"phone", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x14, MI_TARGET_OUTAMP}, - - {{0, {AudioNcd "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x15, MI_TARGET_OUTAMP}, - {{0, {AudioNcd, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x15, MI_TARGET_OUTAMP}, - - {{0, {AudioNaux "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x16, MI_TARGET_OUTAMP}, - {{0, {AudioNaux, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x16, MI_TARGET_OUTAMP}, - - {{0, {AudioNspeaker "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x18, MI_TARGET_OUTAMP}, - {{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(15)}}, 0x18, MI_TARGET_OUTAMP}, - - - /* 0x11: inputs.sel11.source=sel0f [ sel0f mix2b ] XXXX - inputs.sel11.lrswap=off [ off on ] XXXX */ - - {{0, {AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, .un.e={7, {{{AudioNmicrophone, 0}, 0}, {{AudioNcd, 0}, 1}, - {{AudioNaux, 0}, 2}, {{AudioNline, 0}, 3}, - {{AudioNmixerout, 0}, 4}, {{"mixeroutmono", 0}, 5}, - {{"phone", 0}, 6}}}}, - 0x12, MI_TARGET_CONNLIST}, - {{0, {AudioNvolume "." AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, ENUM_OFFON}, 0x12, MI_TARGET_OUTAMP}, - {{0, {AudioNvolume, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(15)}}, 0x12, MI_TARGET_OUTAMP}, - - {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, 0, 0, - .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, 0, MI_TARGET_DAC}, -}; - -static int -ad1986a_mixer_init(codec_t *this) -{ - mixer_ctrl_t mc; - - this->nmixers = __arraycount(ad1986a_mixer_items); - this->szmixers = sizeof(ad1986a_mixer_items); - this->mixers = kmem_alloc(this->szmixers, KM_SLEEP); - memcpy(this->mixers, ad1986a_mixer_items, sizeof(ad1986a_mixer_items)); - generic_mixer_fix_indexes(this); - generic_mixer_default(this); - - mc.dev = -1; - mc.type = AUDIO_MIXER_ENUM; - mc.un.ord = 0; /* unmute */ - generic_mixer_set(this, 0x1c, MI_TARGET_OUTAMP, &mc); - mc.un.ord = 1; /* dir: output */ - generic_mixer_set(this, 0x1c, MI_TARGET_PINDIR, &mc); - mc.type = AUDIO_MIXER_VALUE; - mc.un.value.num_channels = 2; - mc.un.value.level[0] = AUDIO_MAX_GAIN; - mc.un.value.level[1] = AUDIO_MAX_GAIN; - generic_mixer_set(this, 0x1c, MI_TARGET_VOLUME, &mc); - mc.type = AUDIO_MIXER_ENUM; - mc.un.ord = 0; /* unmute */ - generic_mixer_set(this, 0x1d, MI_TARGET_OUTAMP, &mc); - mc.un.ord = 1; /* dir: output */ - generic_mixer_set(this, 0x1d, MI_TARGET_PINDIR, &mc); - mc.type = AUDIO_MIXER_VALUE; - mc.un.value.num_channels = 2; - mc.un.value.level[0] = AUDIO_MAX_GAIN; - mc.un.value.level[1] = AUDIO_MAX_GAIN; - generic_mixer_set(this, 0x1d, MI_TARGET_VOLUME, &mc); - return 0; -} - -static int -ad1986a_init_dacgroup(codec_t *this) -{ - static const convgroupset_t dacs = { - -1, 2, - {{6, {0x03, 0x04, 0x05}}, /* analog 6ch */ - {1, {0x02}}}}; /* digital */ - static const convgroupset_t adcs = { - -1, 1, - {{1, {0x06}}}}; /* analog 2ch */ - - this->dacs = dacs; - this->adcs = adcs; - return 0; -} - -/* ---------------------------------------------------------------- - * Analog Devices AD1988A/AD1988B - * ---------------------------------------------------------------- */ - -static int -ad1988_init_dacgroup(codec_t *this) -{ - static const convgroupset_t dacs = { - -1, 3, - {{4, {0x04, 0x05, 0x06, 0x0a}}, /* analog 8ch */ - {1, {0x02}}, /* digital */ - {1, {0x03}}}}; /* another analog */ - static const convgroupset_t adcs = { - -1, 2, - {{2, {0x08, 0x09, 0x0f}}, /* analog 6ch */ - {1, {0x07}}}}; /* digital */ - - this->dacs = dacs; - this->adcs = adcs; - return 0; -} - -/* ---------------------------------------------------------------- - * CMedia CMI9880 - * ---------------------------------------------------------------- */ - -static const mixer_item_t cmi9880_mixer_items[] = { - AZ_MIXER_CLASSES, - - {{0, {AudioNmaster"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x03, MI_TARGET_OUTAMP}, - {{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x04, MI_TARGET_OUTAMP}, - {{0, {AzaliaNclfe"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x05, MI_TARGET_OUTAMP}, - {{0, {AzaliaNside"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x06, MI_TARGET_OUTAMP}, - {{0, {AzaliaNdigital"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x07, MI_TARGET_OUTAMP}, - - {{0, {AzaliaNfront"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, ENUM_OFFON}, 0x08, MI_TARGET_INAMP(0)}, - {{0, {AzaliaNfront, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(30)}}, 0x08, MI_TARGET_INAMP(0)}, - {{0, {AzaliaNfront"."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, .un.e={4, {{{AudioNmicrophone, 0}, 5}, {{AudioNcd, 0}, 6}, - {{"line1", 0}, 7}, {{"line2", 0}, 8}}}}, - 0x08, MI_TARGET_CONNLIST}, - {{0, {AudioNsurround"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, ENUM_OFFON}, 0x09, MI_TARGET_INAMP(0)}, - {{0, {AudioNsurround, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_RECORD, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(30)}}, 0x09, MI_TARGET_INAMP(0)}, - {{0, {AudioNsurround"."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, .un.e={4, {{{AudioNmicrophone, 0}, 5}, {{AudioNcd, 0}, 6}, - {{"line1", 0}, 7}, {{"line2", 0}, 8}}}}, - 0x09, MI_TARGET_CONNLIST}, - - {{0, {AudioNspeaker"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, ENUM_OFFON}, 0x23, MI_TARGET_OUTAMP}, - {{0, {AudioNspeaker, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_INPUT, - 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(15)}}, 0x23, MI_TARGET_OUTAMP} -}; - -static int -cmi9880_mixer_init(codec_t *this) -{ - mixer_ctrl_t mc; - - this->nmixers = __arraycount(cmi9880_mixer_items); - this->szmixers = sizeof(cmi9880_mixer_items); - this->mixers = kmem_alloc(this->szmixers, KM_SLEEP); - memcpy(this->mixers, cmi9880_mixer_items, sizeof(cmi9880_mixer_items)); - generic_mixer_fix_indexes(this); - generic_mixer_default(this); - - mc.dev = -1; - mc.type = AUDIO_MIXER_ENUM; - mc.un.ord = 5; /* record.front.source=mic */ - generic_mixer_set(this, 0x08, MI_TARGET_CONNLIST, &mc); - mc.un.ord = 7; /* record.surround.source=line1 */ - generic_mixer_set(this, 0x09, MI_TARGET_CONNLIST, &mc); - mc.un.ord = 1; /* pindir: output */ - generic_mixer_set(this, 0x0b, MI_TARGET_PINDIR, &mc); - generic_mixer_set(this, 0x0c, MI_TARGET_PINDIR, &mc); - generic_mixer_set(this, 0x0d, MI_TARGET_PINDIR, &mc); - generic_mixer_set(this, 0x0e, MI_TARGET_PINDIR, &mc); - generic_mixer_set(this, 0x0f, MI_TARGET_PINDIR, &mc); - mc.un.ord = 0; /* front DAC -> headphones */ - generic_mixer_set(this, 0x0f, MI_TARGET_CONNLIST, &mc); - mc.un.ord = 0; /* pindir: input */ - generic_mixer_set(this, 0x10, MI_TARGET_PINDIR, &mc); /* mic */ - generic_mixer_set(this, 0x13, MI_TARGET_PINDIR, &mc); /* SPDIF-in */ - generic_mixer_set(this, 0x1f, MI_TARGET_PINDIR, &mc); /* line1 */ - generic_mixer_set(this, 0x20, MI_TARGET_PINDIR, &mc); /* line2 */ - return 0; -} - -static int -cmi9880_init_dacgroup(codec_t *this) -{ - static const convgroupset_t dacs = { - -1, 2, - {{4, {0x03, 0x04, 0x05, 0x06}}, /* analog 8ch */ - {1, {0x07}}}}; /* digital */ - static const convgroupset_t adcs = { - -1, 2, - {{2, {0x08, 0x09}}, /* analog 4ch */ - {1, {0x0a}}}}; /* digital */ - - this->dacs = dacs; - this->adcs = adcs; - return 0; -} - -/* ---------------------------------------------------------------- - * Sigmatel STAC9221 and STAC9221D - * ---------------------------------------------------------------- */ - -#define STAC9221_MAC 0x76808384 - -static int -stac9221_init_dacgroup(codec_t *this) -{ - static const convgroupset_t dacs = { - -1, 3, - {{4, {0x02, 0x03, 0x04, 0x05}}, /* analog 8ch */ - {1, {0x08}}, /* digital */ - {1, {0x1a}}}}; /* another digital? */ - static const convgroupset_t adcs = { - -1, 2, - {{2, {0x06, 0x07}}, /* analog 4ch */ - {1, {0x09}}}}; /* digital */ - - this->dacs = dacs; - this->adcs = adcs; - return 0; -} - -static int -stac9221_mixer_init(codec_t *this) -{ - int err; - - err = generic_mixer_init(this); - if (err) - return err; - if (this->subid == STAC9221_MAC) { - stac9221_gpio_unmute(this, 0); - stac9221_gpio_unmute(this, 1); - } - return 0; -} - -static int -stac9221_gpio_unmute(codec_t *this, int pin) -{ - uint32_t data, mask, dir; - - this->comresp(this, this->audiofunc, CORB_GET_GPIO_DATA, 0, &data); - this->comresp(this, this->audiofunc, - CORB_GET_GPIO_ENABLE_MASK, 0, &mask); - this->comresp(this, this->audiofunc, CORB_GET_GPIO_DIRECTION, 0, &dir); - data &= ~(1 << pin); - mask |= 1 << pin; - dir |= 1 << pin; - this->comresp(this, this->audiofunc, 0x7e7, 0, NULL); - this->comresp(this, this->audiofunc, - CORB_SET_GPIO_ENABLE_MASK, mask, NULL); - this->comresp(this, this->audiofunc, - CORB_SET_GPIO_DIRECTION, dir, NULL); - DELAY(1000); - this->comresp(this, this->audiofunc, CORB_SET_GPIO_DATA, data, NULL); - return 0; -} - -/* ---------------------------------------------------------------- - * Sigmatel STAC9200 and STAC9200D - * ---------------------------------------------------------------- */ - -static const mixer_item_t stac9200_mixer_items[] = { - AZ_MIXER_CLASSES, - - {{0, {AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={3, {{{AudioNdac, 0}, 0}, {{AzaliaNdigital"-in", 0}, 1}, - {{"selector", 0}, 2}}}}, 0x07, MI_TARGET_CONNLIST}, - {{0, {AzaliaNdigital"."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, .un.e={2, {{{AudioNdac, 0}, 0}, {{"selector", 0}, 1}}}}, - 0x09, MI_TARGET_CONNLIST}, /* AudioNdac is not accurate name */ - {{0, {"selector."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x0a, MI_TARGET_OUTAMP}, - {{0, {"selector", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(15)}}, 0x0a, MI_TARGET_OUTAMP}, - {{0, {AudioNmaster"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x0b, MI_TARGET_OUTAMP}, - {{0, {AudioNmaster, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 2, MIXER_DELTA(31)}}, 0x0b, MI_TARGET_OUTAMP}, - {{0, {"selector."AudioNsource, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_INPUT, - 0, 0, .un.e={3, {{{"mic1", 0}, 0}, {{"mic2", 0}, 1}, {{AudioNcd, 0}, 4}}}}, - 0x0c, MI_TARGET_CONNLIST}, - {{0, {AudioNheadphone".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x0d, MI_TARGET_PINBOOST}, - {{0, {AudioNspeaker".boost", 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x0e, MI_TARGET_PINBOOST}, - {{0, {AudioNmono"."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x11, MI_TARGET_OUTAMP}, - {{0, {AudioNmono, 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(31)}}, 0x11, MI_TARGET_OUTAMP}, - {{0, {"beep."AudioNmute, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_OUTPUT, - 0, 0, ENUM_OFFON}, 0x14, MI_TARGET_OUTAMP}, - {{0, {"beep", 0}, AUDIO_MIXER_VALUE, AZ_CLASS_OUTPUT, - 0, 0, .un.v={{"", 0}, 1, MIXER_DELTA(3)}}, 0x14, MI_TARGET_OUTAMP}, - {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_PLAYBACK, - 0, 0, .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, - 0, MI_TARGET_DAC}, - {{0, {AudioNmode, 0}, AUDIO_MIXER_ENUM, AZ_CLASS_RECORD, - 0, 0, .un.e={2, {{{"analog", 0}, 0}, {{AzaliaNdigital, 0}, 1}}}}, - 0, MI_TARGET_ADC}, -}; - -static int -stac9200_mixer_init(codec_t *this) -{ - mixer_ctrl_t mc; - uint32_t value; - - this->nmixers = __arraycount(stac9200_mixer_items); - this->szmixers = sizeof(stac9200_mixer_items); - this->mixers = kmem_alloc(this->szmixers, KM_SLEEP); - memcpy(this->mixers, stac9200_mixer_items, sizeof(stac9200_mixer_items)); - generic_mixer_fix_indexes(this); - generic_mixer_default(this); - - mc.dev = -1; /* no need for generic_mixer_set() */ - mc.type = AUDIO_MIXER_ENUM; - mc.un.ord = 1; /* pindir: output */ - generic_mixer_set(this, 0x0d, MI_TARGET_PINDIR, &mc); /* headphones */ - generic_mixer_set(this, 0x0e, MI_TARGET_PINDIR, &mc); /* speaker */ - mc.un.ord = 0; /* pindir: input */ - generic_mixer_set(this, 0x0f, MI_TARGET_PINDIR, &mc); /* mic2 */ - generic_mixer_set(this, 0x10, MI_TARGET_PINDIR, &mc); /* mic1 */ - mc.type = AUDIO_MIXER_VALUE; - mc.un.value.num_channels = 2; - mc.un.value.level[0] = generic_mixer_max(this, 0x0c, MI_TARGET_OUTAMP); - mc.un.value.level[1] = mc.un.value.level[0]; - generic_mixer_set(this, 0x0c, MI_TARGET_OUTAMP, &mc); - -#define STAC9200_DELL_INSPIRON6400_ID 0x01bd1028 -#define STAC9200_DELL_INSPIRON9400_ID 0x01cd1028 -#define STAC9200_DELL_640M_ID 0x01d81028 -#define STAC9200_DELL_LATITUDE_D420_ID 0x01d61028 -#define STAC9200_DELL_LATITUDE_D430_ID 0x02011028 - -#define STAC9200_EVENT_HP 0 -#define STAC9200_NID_HP 0x0d -#define STAC9200_NID_SPEAKER 0x0e - - switch (this->subid) { - case STAC9200_DELL_INSPIRON6400_ID: - case STAC9200_DELL_INSPIRON9400_ID: - case STAC9200_DELL_640M_ID: - case STAC9200_DELL_LATITUDE_D420_ID: - case STAC9200_DELL_LATITUDE_D430_ID: - /* Does every DELL model have the same pin configuration? - * I'm not sure. */ - - /* setup a unsolicited event for the headphones */ - this->comresp(this, STAC9200_NID_HP, CORB_SET_UNSOLICITED_RESPONSE, - CORB_UNSOL_ENABLE | STAC9200_EVENT_HP, NULL); - /* If the headphone presents, mute the internal speaker */ - this->comresp(this, STAC9200_NID_HP, CORB_GET_PIN_SENSE, 0, &value); - if (value & CORB_PS_PRESENCE) { - generic_mixer_pinctrl(this, STAC9200_NID_SPEAKER, 0); - } else { - generic_mixer_pinctrl(this, - STAC9200_NID_SPEAKER, CORB_PWC_OUTPUT); - } - break; - default: - break; - } - return 0; -} - -static int -stac9200_unsol_event(codec_t *this, int tag) -{ - int err; - uint32_t value; - - switch (tag) { - case STAC9200_EVENT_HP: - err = this->comresp(this, STAC9200_NID_HP, - CORB_GET_PIN_SENSE, 0, &value); - if (err) - break; - if (value & CORB_PS_PRESENCE) { - DPRINTF(("%s: headphone has been inserted.\n", __func__)); - generic_mixer_pinctrl(this, STAC9200_NID_SPEAKER, 0); - } else { - DPRINTF(("%s: headphone has been pulled out.\n", __func__)); - generic_mixer_pinctrl(this, STAC9200_NID_SPEAKER, CORB_PWC_OUTPUT); - } - break; - default: - printf("%s: unknown tag: %d\n", __func__, tag); - } - return 0; -} - -/* ---------------------------------------------------------------- - * ATI HDMI - * ---------------------------------------------------------------- */ - -static int -atihdmi_init_dacgroup(codec_t *this) -{ - static const convgroupset_t dacs = { - -1, 1, - {{1, {0x02}}}}; /* digital */ - static const convgroupset_t adcs = { - -1, 0, - {{0, {0x00}}}}; /* no recording */ - - this->dacs = dacs; - this->adcs = adcs; - return 0; -} diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index b7c2ba4c7ca..63d088a5474 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1,4 +1,4 @@ -# $NetBSD: files.pci,v 1.425 2020/01/25 18:59:43 thorpej Exp $ +# $NetBSD: files.pci,v 1.426 2020/02/09 16:06:18 jmcneill Exp $ # # Config file and device description for machine-independent PCI code. # Included by ports that need it. Requires that the SCSI files be @@ -500,12 +500,6 @@ device auixp: audiobus, ac97, aurateconv attach auixp at pci file dev/pci/auixp.c auixp -# High Definition Audio -device azalia: audiobus, ac97, aurateconv -attach azalia at pci -file dev/pci/azalia.c azalia -file dev/pci/azalia_codec.c azalia - # AMD Geode CS5536 Companion Audio device gcscaudio: audiobus, ac97, aurateconv attach gcscaudio at pci |
