diff options
author | mav <mav@FreeBSD.org> | 2012-01-15 13:21:36 +0000 |
---|---|---|
committer | mav <mav@FreeBSD.org> | 2012-01-15 13:21:36 +0000 |
commit | a84af1b28cf33f23a875d32dc0f9a4ce3069588f (patch) | |
tree | 8289c967044f52cc3a8f60637b0eb5a3620165b2 | |
parent | 9f44ed5ca827811dd8729bb0ab4397c71161fa9c (diff) | |
download | FreeBSD-src-a84af1b28cf33f23a875d32dc0f9a4ce3069588f.zip FreeBSD-src-a84af1b28cf33f23a875d32dc0f9a4ce3069588f.tar.gz |
Major snd_hda driver rewrite:
- Huge old hdac driver was split into three independent pieces: HDA
controller driver (hdac), HDA CODEC driver (hdacc) and HDA sudio function
driver (hdaa).
- Support for multichannel recording was added. Now, as specification
defines, driver checks input associations for pins with sequence numbers
14 and 15, and if found (usually) -- works as before, mixing signals
together. If it doesn't, it configures input association as multichannel.
- Signal tracer was improved to look for cases where several DACs/ADCs in
CODEC can work with the same audio signal. If such case found, driver
registers additional playback/record stream (channel) for the pcm device.
- New controller streams reservation mechanism was implemented. That
allows to have more pcm devices then streams supported by the controller
(usually 4 in each direction). Now it limits only number of simultaneously
transferred audio streams, that is rarely reachable and properly reported
if happens.
- Codec pins and GPIO signals configuration was exported via set of
writable sysctls. Another sysctl dev.hdaa.X.reconfig allows to trigger
driver reconfiguration in run-time.
- Driver now decodes pins location and connector type names. In some cases
it allows to hint user where on the system case connectors, related to the
pcm device, are located. Number of channels supported by pcm device,
reported now (if it is not 2), should also make search easier.
- Added workaround for digital mic on some Asus laptops/netbooks.
MFC after: 2 months
Sponsored by: iXsystems, Inc.
-rw-r--r-- | share/man/man4/snd_hda.4 | 343 | ||||
-rw-r--r-- | sys/conf/files | 4 | ||||
-rw-r--r-- | sys/conf/kmod.mk | 1 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hda_reg.h | 8 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdaa.c | 5901 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdaa.h | 230 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdaa_patches.c | 638 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdac.c | 7818 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdac.h | 542 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdac_if.m | 114 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdac_private.h | 220 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdac_reg.h | 8 | ||||
-rw-r--r-- | sys/dev/sound/pci/hda/hdacc.c | 662 | ||||
-rw-r--r-- | sys/modules/sound/driver/hda/Makefile | 5 |
14 files changed, 8973 insertions, 7521 deletions
diff --git a/share/man/man4/snd_hda.4 b/share/man/man4/snd_hda.4 index 6dedb02..270a9f5 100644 --- a/share/man/man4/snd_hda.4 +++ b/share/man/man4/snd_hda.4 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd January 22, 2010 +.Dd January 11, 2012 .Dt SND_HDA 4 .Os .Sh NAME @@ -53,8 +53,9 @@ support for several logical audio devices, and general purpose DMA channels. .Pp The .Nm -driver is a HDA bus controller driver and HDA codecs audio functions bridge -driver that allows the generic audio driver, +driver includes HDA bus controller driver (hdac), HDA codec driver (hdacc) +and HDA codecs audio functions bridge driver (hdaa) that allows +the generic audio driver, .Xr sound 4 , to be used with this hardware. Only audio functions are supported by @@ -77,7 +78,9 @@ For example, one device for main rear 7.1 output and inputs, one device for independent headset connectors at front and one device for SPDIF or HDMI audio input/output. The assignment of audio inputs and outputs may be tuned with -.Xr device.hints 5 . +.Xr device.hints 5 +or +.Xr sysctl 8 . The driver's verbose boot messages provide a lot of information about the operation of the driver and present audio setup. .Pp @@ -92,19 +95,26 @@ The following variables are available at boot-time through the file: .Bl -tag -width ".Va hint.hdac.%d.config"-offset indent .It Va hint.hdac.%d.config -Configures a range of possible options. +Configures a range of possible controller options. Possible values are: +.Dq Li 64bit , .Dq Li dmapos , +.Dq Li msi . +An option prefixed with +.Dq Li no , +such as +.Dq Li nomsi , +will do the opposite and takes precedence. +Options can be separated by whitespace and commas. +.It Va hint.hdac.%d.msi +Controls MSI (Message Signaled Interrupts) support. +.It Va hint.hdac.%d.cad%d.nid%d.config +Same as +.Va hint.hdaa.%d.nid%d.config +.It Va hint.hdaa.%d.config +Configures a range of possible audio function options. +Possible values are: .Dq Li eapdinv , -.Dq Li gpio0 , -.Dq Li gpio1 , -.Dq Li gpio2 , -.Dq Li gpio3 , -.Dq Li gpio4 , -.Dq Li gpio5 , -.Dq Li gpio6 , -.Dq Li gpio7 , -.Dq Li gpioflush , .Dq Li ivref , .Dq Li ivref50 , .Dq Li ivref80 , @@ -126,22 +136,47 @@ such as will do the opposite and takes precedence. Options can be separated by whitespace and commas. .Pp +The +.Dq Li eapdinv +option inverts External Amplifier Power Down signal. +The +.Dq Li fixedrate +denies all sampling rates except 48KHz. +The +.Dq Li forcestereo +denies mono playback/recording. +The +.Dq Li senseinv +option inverts jack sensing logic. +The +.Dq Li ivref Ns Ar X +and +.Dq Li ovref Ns Ar X +options control the voltage used to power external microphones. +.It Va hint.hdaa.%d.gpio_config +Overrides audio function GPIO pins configuration set by BIOS. +May be specified as a set of space-separated +.Dq Ar num Ns = Ns Ar value +pairs, where +.Ar num +is GPIO line number, and +.Ar value +is one of: +.Dq Li keep , +.Dq Li set , +.Dq Li clear , +.Dq Li disable +and +.Dq Li input . +.Pp .Dq Li GPIO Ns s are a codec's General Purpose I/O pins which system integrators sometimes use to control external muters, amplifiers and so on. If you have no sound, or sound volume is not adequate, you may have to experiment a bit with the GPIO setup to find the optimal setup for your system. -.Pp -The -.Dq Li ivref Ns Ar X -and -.Dq Li ovref Ns Ar X -options control the voltage used to power external microphones. -.It Va hint.hdac.%d.msi -Controls MSI (Message Signaled Interrupts) support. -.It Va hint.hdac.%d.cad%d.nid%d.config -Overrides codec pin configuration set by BIOS. +.It Va hint.hdaa.%d.nid%d.config +Overrides audio function pin configuration set by BIOS. May be specified as a 32-bit hexadecimal value with a leading .Dq 0x , or as a set of space-separated @@ -165,7 +200,7 @@ The following options are supported: Association number. Associations are used to group individual pins to form a complex multi-pin device. -For example, to group 4 connectors for 7.1 output, or to treat several +For example, to group 4 connectors for 7.1 input/output, or to treat several input connectors as sources for the same input device. Association numbers can be specified as numeric values from 0 to 15. A value of 0 means disabled pin. @@ -180,16 +215,22 @@ A unique, per-association number used to order pins inside the particular association. Sequence numbers can be specified as numeric values from 0 to 15. .Pp -For output assotiations sequence numbers encode speaker pairs positions: -0 - Front, 1 - Center/LFE, 2 - Back, 3 - Front Wide Center, 4 - Side. -Standard combinations are: (0) - Stereo; (0, 2), (0, 4) - Quadro; -(0, 1, 2), (0, 1, 4) - 5.1; (0, 1, 2, 4) - 7.1. -.Pp The sequence number 15 has a special meaning for output associations. Output pins with this number and device type .Dq Ar Headphones will duplicate (with automatic mute if jack detection is supported) the first pin in that association. +.Pp +The sequence numbers 14 and 15 has a special meaning for input associations. +Their presence in association defines it as multiplexed or mixed respectively. +If none of them present and there are more then one pin in association, +the association will provide multichannel input. +.Pp +For multichannel input/output assotiations sequence numbers encode +channel pairs positions: +0 - Front, 1 - Center/LFE, 2 - Back, 3 - Front Wide Center, 4 - Side. +Standard combinations are: (0) - Stereo; (0, 2), (0, 4) - Quadro; +(0, 1, 2), (0, 1, 4) - 5.1; (0, 1, 2, 4) - 7.1. .It Va device Device type. Can be specified as a number from 0 to 15 or as a name: @@ -278,7 +319,11 @@ The following variables are available in addition to those available to all .Xr sound 4 devices: -.Bl -tag -width ".Va dev.hdac.%d.polling" -offset indent +.Bl -tag -width ".Va dev.hdaa.%d.nid%d_original" -offset indent +.It Va dev.hdac.%d.pindump +Setting this to a non-zero value dumps the current pin configuration, main +capabilities and jack sense status of all audio functions on the controller +to console and syslog. .It Va dev.hdac.%d.polling Enables polling mode. In this mode the driver operates by querying the device state on timer @@ -288,11 +333,30 @@ instead of interrupts. Polling is disabled by default. Do not enable it unless you are facing weird interrupt problems or if the device cannot generate interrupts at all. -.It Va dev.hdac.%d.polling_interval -Controller/Jack Sense polling interval (1-1000 ms) -.It Va dev.hdac.%d.pindump -Setting this to a non-zero value dumps the current pin configuration, main -capabilities and jack sense status to console and syslog. +.It Va dev.hdaa.%d.config +Run-time equivalent of the +.Va hint.hdaa.%d.config +tunable. +.It Va dev.hdaa.%d.gpi_state +Current state of GPI lines. +.It Va dev.hdaa.%d.gpio_state +Current state of GPIO lines. +.It Va dev.hdaa.%d.gpio_config +Run-time equivalent of the +.Va hint.hdaa.%d.gpio.config +tunable. +.It Va dev.hdaa.%d.gpo_state +Current state of GPO lines. +.It Va dev.hdaa.%d.nid%d_config +Run-time equivalent of the +.Va hint.hdaa.%d.nid%d.config +tunable. +.It Va dev.hdaa.%d.nid%d_original +Original pin configuration written by BIOS. +.It Va dev.hdaa.%d.reconfig +Setting this to a non-zero value makes driver to destroy existing pcm devices +and process new pins configuration set via +.Va dev.hdaa.%d.nid%d_config. .El .Sh EXAMPLES Taking HP Compaq DX2300 with Realtek ALC888 HDA codec for example. @@ -307,22 +371,23 @@ So high codec uniformity and flexibility allow driver to configure it in many different ways, depending on requested pins usage described by pins configuration. The driver reports such default pin configuration when verbose messages enabled: .Bd -literal -hdac0: nid 20 0x01014020 as 2 seq 0 Line-out Jack jack 1 loc 1 color Green misc 0 -hdac0: nid 21 0x99130110 as 1 seq 0 Speaker Fixed jack 3 loc 25 color Unknown misc 1 -hdac0: nid 22 0x411111f0 as 15 seq 0 Speaker None jack 1 loc 1 color Black misc 1 -hdac0: nid 23 0x411111f0 as 15 seq 0 Speaker None jack 1 loc 1 color Black misc 1 -hdac0: nid 24 0x01a19830 as 3 seq 0 Mic Jack jack 1 loc 1 color Pink misc 8 -hdac0: nid 25 0x02a1983f as 3 seq 15 Mic Jack jack 1 loc 2 color Pink misc 8 -hdac0: nid 26 0x01813031 as 3 seq 1 Line-in Jack jack 1 loc 1 color Blue misc 0 -hdac0: nid 27 0x0221401f as 1 seq 15 Headphones Jack jack 1 loc 2 color Green misc 0 -hdac0: nid 28 0x411111f0 as 15 seq 0 Speaker None jack 1 loc 1 color Black misc 1 -hdac0: nid 30 0x411111f0 as 15 seq 0 Speaker None jack 1 loc 1 color Black misc 1 -hdac0: nid 31 0x411111f0 as 15 seq 0 Speaker None jack 1 loc 1 color Black misc 1 +hdaa0: nid 0x as seq device conn jack loc color misc +hdaa0: 20 01014020 2 0 Line-out Jack 1/8 Rear Green 0 +hdaa0: 21 99130110 1 0 Speaker Fixed ATAPI Onboard Unknown 1 +hdaa0: 22 411111f0 15 0 Speaker None 1/8 Rear Black 1 DISA +hdaa0: 23 411111f0 15 0 Speaker None 1/8 Rear Black 1 DISA +hdaa0: 24 01a19830 3 0 Mic Jack 1/8 Rear Pink 8 +hdaa0: 25 02a1983f 3 15 Mic Jack 1/8 Front Pink 8 +hdaa0: 26 01813031 3 1 Line-in Jack 1/8 Rear Blue 0 +hdaa0: 27 0221401f 1 15 Headphones Jack 1/8 Front Green 0 +hdaa0: 28 411111f0 15 0 Speaker None 1/8 Rear Black 1 DISA +hdaa0: 30 411111f0 15 0 Speaker None 1/8 Rear Black 1 DISA +hdaa0: 31 411111f0 15 0 Speaker None 1/8 Rear Black 1 DISA .Ed .Pp Here we can see, that the nodes with ID (nid) 25 and 27 are front panel -connectors (Jack, loc 2), nids 20, 24 and 26 are rear panel connectors -(Jack, loc 1) and nid 21 is a built-in speaker (Fixed, loc 25). +connectors (Jack, Front), nids 20, 24 and 26 are rear panel connectors +(Jack, Rear) and nid 21 is a built-in speaker (Fixed, Onboard). Pins with nids 22, 23, 28, 30 and 31 will be disabled by driver due to "None" connectivity. So the pin count and description matches to connectors that we have. @@ -330,15 +395,15 @@ we have. Using association (as) and sequence (seq) fields values pins are grouped into 3 associations: .Bd -literal -hdac0: Association 0 (1) out: -hdac0: Pin nid=21 seq=0 -hdac0: Pin nid=27 seq=15 -hdac0: Association 1 (2) out: -hdac0: Pin nid=20 seq=0 -hdac0: Association 2 (3) in: -hdac0: Pin nid=24 seq=0 -hdac0: Pin nid=26 seq=1 -hdac0: Pin nid=25 seq=15 +hdaa0: Association 0 (1) out: +hdaa0: Pin nid=21 seq=0 +hdaa0: Pin nid=27 seq=15 +hdaa0: Association 1 (2) out: +hdaa0: Pin nid=20 seq=0 +hdaa0: Association 2 (3) in: +hdaa0: Pin nid=24 seq=0 +hdaa0: Pin nid=26 seq=1 +hdaa0: Pin nid=25 seq=15 .Ed .Pp Each @@ -497,148 +562,14 @@ Most of controls use logarithmic scale. .Sh HARDWARE The .Nm -driver supports many Intel HDA compatible audio chipsets including the -following: -.Pp -.Bl -bullet -compact -.It -ATI SB450 -.It -ATI SB600 -.It -Intel 631x/632xESB -.It -Intel 82801F (ICH6) -.It -Intel 82801G (ICH7) -.It -Intel 82801H (ICH8) -.It -Intel 82801I (ICH9) -.It -Intel 82801J (ICH10) -.It -Intel US15W (SCH) -.It -nVidia MCP51 -.It -nVidia MCP55 -.It -nVidia MCP61A -.It -nVidia MCP61B -.It -nVidia MCP63 -.It -nVidia MCP65A -.It -nVidia MCP65B -.It -nVidia MCP67A -.It -nVidia MCP67B -.It -nVidia MCP68 -.It -nVidia MCP69 -.It -nVidia MCP73 -.It -nVidia MCP78 -.It -nVidia MCP79 -.It -nVidia MCP89 -.It -SiS 966 -.It -VIA VT8251/8237A -.El -.Pp -The following and many other codecs have been verified to work: +driver supports controllers having PCI class 4 (multimedia) and +subclass 3 (HDA), compatible with Intel HDA specification. .Pp -.Bl -bullet -compact -.It -Analog Devices AD1981HD -.It -Analog Devices AD1983 -.It -Analog Devices AD1984 -.It -Analog Devices AD1986A -.It -Analog Devices AD1988 -.It -Analog Devices AD1988B -.It -CMedia CMI9880 -.It -Conexant CX20549 (Venice) -.It -Conexant CX20551 (Waikiki) -.It -Conexant CX20561 (Hermosa) -.It -Realtek ALC260 -.It -Realtek ALC262 -.It -Realtek ALC268 -.It -Realtek ALC660 -.It -Realtek ALC861 -.It -Realtek ALC861VD -.It -Realtek ALC880 -.It -Realtek ALC882 -.It -Realtek ALC883 -.It -Realtek ALC885 -.It -Realtek ALC888 -.It -Realtek ALC889 -.It -Sigmatel STAC9205 -.It -Sigmatel STAC9220 -.It -Sigmatel STAC9220D / 9223D -.It -Sigmatel STAC9221 -.It -Sigmatel STAC9221D -.It -Sigmatel STAC9227D -.It -Sigmatel STAC9227X -.It -Sigmatel STAC9228D -.It -Sigmatel STAC9228X -.It -Sigmatel STAC9229D -.It -Sigmatel STAC9229X -.It -Sigmatel STAC9230D -.It -Sigmatel STAC9230X -.It -Sigmatel STAC9271D -.It -Sigmatel STAC9872AK -.It -VIA VT1708 -.It -VIA VT1708B -.It -VIA VT1709 -.El +The +.Nm +driver supports more then two hundred different controllers and CODECs. +There is no sense to list all of them here, as in most cases specific CODEC +configuration and wiring are more important then type of the CODEC itself. .Sh SEE ALSO .Xr sound 4 , .Xr snd_ich 4 , @@ -665,19 +596,17 @@ This manual page was written by and .An Giorgos Keramidas Aq keramida@FreeBSD.org . .Sh BUGS -A few Hardware/OEM vendors tend to screw up BIOS settings, thus -rendering the -.Nm -driver useless. -This usually results in a state where the +Some Hardware/OEM vendors tend to screw up BIOS settings or use custom +unusual CODEC wiring that create problems to the driver. +This may result in missing pcm devices, or a state where the .Nm driver seems to attach and work, but no sound is played. Some cases can be solved by tuning .Pa loader.conf variables. -Before trying to fix problem that way, make sure that there really is a problem -and that the PCM audio device in use really corresponds to the expected -audio connector. +But before trying to fix problem that way, make sure that there really is +a problem and that the PCM audio device in use really corresponds to the +expected audio connector. .Pp Some vendors use non-standardized General Purpose I/O (GPIO) pins of the codec to control external amplifiers. diff --git a/sys/conf/files b/sys/conf/files index 6d600e0..a2cba01 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1750,7 +1750,11 @@ dev/sound/pci/t4dwave.c optional snd_t4dwave pci dev/sound/pci/via8233.c optional snd_via8233 pci dev/sound/pci/via82c686.c optional snd_via82c686 pci dev/sound/pci/vibes.c optional snd_vibes pci +dev/sound/pci/hda/hdaa.c optional snd_hda pci +dev/sound/pci/hda/hdaa_patches.c optional snd_hda pci dev/sound/pci/hda/hdac.c optional snd_hda pci +dev/sound/pci/hda/hdac_if.m optional snd_hda pci +dev/sound/pci/hda/hdacc.c optional snd_hda pci dev/sound/pcm/ac97.c optional sound dev/sound/pcm/ac97_if.m optional sound dev/sound/pcm/ac97_patch.c optional sound diff --git a/sys/conf/kmod.mk b/sys/conf/kmod.mk index 7bf7643..7c0de71 100644 --- a/sys/conf/kmod.mk +++ b/sys/conf/kmod.mk @@ -345,6 +345,7 @@ MFILES?= dev/acpica/acpi_if.m dev/acpi_support/acpi_wmi_if.m \ dev/mii/miibus_if.m dev/mvs/mvs_if.m dev/ofw/ofw_bus_if.m \ dev/pccard/card_if.m dev/pccard/power_if.m dev/pci/pci_if.m \ dev/pci/pcib_if.m dev/ppbus/ppbus_if.m dev/smbus/smbus_if.m \ + dev/sound/pci/hda/hdac_if.m \ dev/sound/pcm/ac97_if.m dev/sound/pcm/channel_if.m \ dev/sound/pcm/feeder_if.m dev/sound/pcm/mixer_if.m \ dev/sound/midi/mpu_if.m dev/sound/midi/mpufoi_if.m \ diff --git a/sys/dev/sound/pci/hda/hda_reg.h b/sys/dev/sound/pci/hda/hda_reg.h index 0eeea64..847a626 100644 --- a/sys/dev/sound/pci/hda/hda_reg.h +++ b/sys/dev/sound/pci/hda/hda_reg.h @@ -400,7 +400,7 @@ HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG_SHIFT) #define HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE 0x80 -#define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_MASK 0x1f +#define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_MASK 0x3f #define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_SHIFT 0 #define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG(param) \ @@ -418,14 +418,10 @@ (HDA_CMD_12BIT((cad), (nid), \ HDA_CMD_VERB_SET_PIN_SENSE, (payload))) -#define HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT_MASK 0x80000000 -#define HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT_SHIFT 31 +#define HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT 0x80000000 #define HDA_CMD_GET_PIN_SENSE_IMP_SENSE_MASK 0x7fffffff #define HDA_CMD_GET_PIN_SENSE_IMP_SENSE_SHIFT 0 -#define HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT(rsp) \ - (((rsp) & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT_MASK) >> \ - HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT_SHIFT) #define HDA_CMD_GET_PIN_SENSE_IMP_SENSE(rsp) \ (((rsp) & HDA_CMD_GET_PIN_SENSE_IMP_SENSE_MASK) >> \ HDA_CMD_GET_PIN_SENSE_IMP_SENSE_SHIFT) diff --git a/sys/dev/sound/pci/hda/hdaa.c b/sys/dev/sound/pci/hda/hdaa.c new file mode 100644 index 0000000..cee159e --- /dev/null +++ b/sys/dev/sound/pci/hda/hdaa.c @@ -0,0 +1,5901 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca> + * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org> + * Copyright (c) 2008-2012 Alexander Motin <mav@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Intel High Definition Audio (Audio function) driver for FreeBSD. + */ + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + +#include <dev/sound/pcm/sound.h> + +#include <sys/ctype.h> +#include <sys/taskqueue.h> + +#include <dev/sound/pci/hda/hdac.h> +#include <dev/sound/pci/hda/hdaa.h> +#include <dev/sound/pci/hda/hda_reg.h> + +#include "mixer_if.h" + +SND_DECLARE_FILE("$FreeBSD$"); + +#define hdaa_lock(devinfo) snd_mtxlock((devinfo)->lock) +#define hdaa_unlock(devinfo) snd_mtxunlock((devinfo)->lock) +#define hdaa_lockassert(devinfo) snd_mtxassert((devinfo)->lock) +#define hdaa_lockowned(devinfo) mtx_owned((devinfo)->lock) + +static const struct { + char *key; + uint32_t value; +} hdaa_quirks_tab[] = { + { "softpcmvol", HDAA_QUIRK_SOFTPCMVOL }, + { "fixedrate", HDAA_QUIRK_FIXEDRATE }, + { "forcestereo", HDAA_QUIRK_FORCESTEREO }, + { "eapdinv", HDAA_QUIRK_EAPDINV }, + { "senseinv", HDAA_QUIRK_SENSEINV }, + { "ivref50", HDAA_QUIRK_IVREF50 }, + { "ivref80", HDAA_QUIRK_IVREF80 }, + { "ivref100", HDAA_QUIRK_IVREF100 }, + { "ovref50", HDAA_QUIRK_OVREF50 }, + { "ovref80", HDAA_QUIRK_OVREF80 }, + { "ovref100", HDAA_QUIRK_OVREF100 }, + { "ivref", HDAA_QUIRK_IVREF }, + { "ovref", HDAA_QUIRK_OVREF }, + { "vref", HDAA_QUIRK_VREF }, +}; +#define HDAA_QUIRKS_TAB_LEN \ + (sizeof(hdaa_quirks_tab) / sizeof(hdaa_quirks_tab[0])) + +#define HDA_BDL_MIN 2 +#define HDA_BDL_MAX 256 +#define HDA_BDL_DEFAULT HDA_BDL_MIN + +#define HDA_BLK_MIN HDA_DMA_ALIGNMENT +#define HDA_BLK_ALIGN (~(HDA_BLK_MIN - 1)) + +#define HDA_BUFSZ_MIN 4096 +#define HDA_BUFSZ_MAX 65536 +#define HDA_BUFSZ_DEFAULT 16384 + +#define HDA_PARSE_MAXDEPTH 10 + +MALLOC_DEFINE(M_HDAA, "hdaa", "HDA Audio"); + +const char *HDA_COLORS[16] = {"Unknown", "Black", "Grey", "Blue", "Green", "Red", + "Orange", "Yellow", "Purple", "Pink", "Res.A", "Res.B", "Res.C", "Res.D", + "White", "Other"}; + +const char *HDA_DEVS[16] = {"Line-out", "Speaker", "Headphones", "CD", + "SPDIF-out", "Digital-out", "Modem-line", "Modem-handset", "Line-in", + "AUX", "Mic", "Telephony", "SPDIF-in", "Digital-in", "Res.E", "Other"}; + +const char *HDA_CONNS[4] = {"Jack", "None", "Fixed", "Both"}; + +const char *HDA_CONNECTORS[16] = { + "Unknown", "1/8", "1/4", "ATAPI", "RCA", "Optical", "Digital", "Analog", + "DIN", "XLR", "RJ-11", "Combo", "0xc", "0xd", "0xe", "Other" }; + +const char *HDA_LOCS[64] = { + "0x00", "Rear", "Front", "Left", "Right", "Top", "Bottom", "Rear-panel", + "Drive-bay", "0x09", "0x0a", "0x0b", "0x0c", "0x0d", "0x0e", "0x0f", + "Internal", "0x11", "0x12", "0x13", "0x14", "0x15", "0x16", "Riser", + "0x18", "Onboard", "0x1a", "0x1b", "0x1c", "0x1d", "0x1e", "0x1f", + "External", "Ext-Rear", "Ext-Front", "Ext-Left", "Ext-Right", "Ext-Top", "Ext-Bottom", "0x07", + "0x28", "0x29", "0x2a", "0x2b", "0x2c", "0x2d", "0x2e", "0x2f", + "Other", "0x31", "0x32", "0x33", "0x34", "0x35", "Other-Bott", "Lid-In", + "Lid-Out", "0x39", "0x3a", "0x3b", "0x3c", "0x3d", "0x3e", "0x3f" }; + +const char *HDA_GPIO_ACTIONS[8] = { + "keep", "set", "clear", "disable", "input", "0x05", "0x06", "0x07"}; + +/* Default */ +static uint32_t hdaa_fmt[] = { + SND_FORMAT(AFMT_S16_LE, 2, 0), + 0 +}; + +static struct pcmchan_caps hdaa_caps = {48000, 48000, hdaa_fmt, 0}; + +static const struct { + uint32_t rate; + int valid; + uint16_t base; + uint16_t mul; + uint16_t div; +} hda_rate_tab[] = { + { 8000, 1, 0x0000, 0x0000, 0x0500 }, /* (48000 * 1) / 6 */ + { 9600, 0, 0x0000, 0x0000, 0x0400 }, /* (48000 * 1) / 5 */ + { 12000, 0, 0x0000, 0x0000, 0x0300 }, /* (48000 * 1) / 4 */ + { 16000, 1, 0x0000, 0x0000, 0x0200 }, /* (48000 * 1) / 3 */ + { 18000, 0, 0x0000, 0x1000, 0x0700 }, /* (48000 * 3) / 8 */ + { 19200, 0, 0x0000, 0x0800, 0x0400 }, /* (48000 * 2) / 5 */ + { 24000, 0, 0x0000, 0x0000, 0x0100 }, /* (48000 * 1) / 2 */ + { 28800, 0, 0x0000, 0x1000, 0x0400 }, /* (48000 * 3) / 5 */ + { 32000, 1, 0x0000, 0x0800, 0x0200 }, /* (48000 * 2) / 3 */ + { 36000, 0, 0x0000, 0x1000, 0x0300 }, /* (48000 * 3) / 4 */ + { 38400, 0, 0x0000, 0x1800, 0x0400 }, /* (48000 * 4) / 5 */ + { 48000, 1, 0x0000, 0x0000, 0x0000 }, /* (48000 * 1) / 1 */ + { 64000, 0, 0x0000, 0x1800, 0x0200 }, /* (48000 * 4) / 3 */ + { 72000, 0, 0x0000, 0x1000, 0x0100 }, /* (48000 * 3) / 2 */ + { 96000, 1, 0x0000, 0x0800, 0x0000 }, /* (48000 * 2) / 1 */ + { 144000, 0, 0x0000, 0x1000, 0x0000 }, /* (48000 * 3) / 1 */ + { 192000, 1, 0x0000, 0x1800, 0x0000 }, /* (48000 * 4) / 1 */ + { 8820, 0, 0x4000, 0x0000, 0x0400 }, /* (44100 * 1) / 5 */ + { 11025, 1, 0x4000, 0x0000, 0x0300 }, /* (44100 * 1) / 4 */ + { 12600, 0, 0x4000, 0x0800, 0x0600 }, /* (44100 * 2) / 7 */ + { 14700, 0, 0x4000, 0x0000, 0x0200 }, /* (44100 * 1) / 3 */ + { 17640, 0, 0x4000, 0x0800, 0x0400 }, /* (44100 * 2) / 5 */ + { 18900, 0, 0x4000, 0x1000, 0x0600 }, /* (44100 * 3) / 7 */ + { 22050, 1, 0x4000, 0x0000, 0x0100 }, /* (44100 * 1) / 2 */ + { 25200, 0, 0x4000, 0x1800, 0x0600 }, /* (44100 * 4) / 7 */ + { 26460, 0, 0x4000, 0x1000, 0x0400 }, /* (44100 * 3) / 5 */ + { 29400, 0, 0x4000, 0x0800, 0x0200 }, /* (44100 * 2) / 3 */ + { 33075, 0, 0x4000, 0x1000, 0x0300 }, /* (44100 * 3) / 4 */ + { 35280, 0, 0x4000, 0x1800, 0x0400 }, /* (44100 * 4) / 5 */ + { 44100, 1, 0x4000, 0x0000, 0x0000 }, /* (44100 * 1) / 1 */ + { 58800, 0, 0x4000, 0x1800, 0x0200 }, /* (44100 * 4) / 3 */ + { 66150, 0, 0x4000, 0x1000, 0x0100 }, /* (44100 * 3) / 2 */ + { 88200, 1, 0x4000, 0x0800, 0x0000 }, /* (44100 * 2) / 1 */ + { 132300, 0, 0x4000, 0x1000, 0x0000 }, /* (44100 * 3) / 1 */ + { 176400, 1, 0x4000, 0x1800, 0x0000 }, /* (44100 * 4) / 1 */ +}; +#define HDA_RATE_TAB_LEN (sizeof(hda_rate_tab) / sizeof(hda_rate_tab[0])) + +/**************************************************************************** + * Function prototypes + ****************************************************************************/ +static int hdaa_pcmchannel_setup(struct hdaa_chan *); + +static void hdaa_widget_connection_select(struct hdaa_widget *, uint8_t); +static void hdaa_audio_ctl_amp_set(struct hdaa_audio_ctl *, + uint32_t, int, int); +static struct hdaa_audio_ctl *hdaa_audio_ctl_amp_get(struct hdaa_devinfo *, + nid_t, int, int, int); +static void hdaa_audio_ctl_amp_set_internal(struct hdaa_devinfo *, + nid_t, int, int, int, int, int, int); + +static void hdaa_dump_pin_config(struct hdaa_widget *w, uint32_t conf); + +static char * +hdaa_audio_ctl_ossmixer_mask2allname(uint32_t mask, char *buf, size_t len) +{ + static char *ossname[] = SOUND_DEVICE_NAMES; + int i, first = 1; + + bzero(buf, len); + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (mask & (1 << i)) { + if (first == 0) + strlcat(buf, ", ", len); + strlcat(buf, ossname[i], len); + first = 0; + } + } + return (buf); +} + +static struct hdaa_audio_ctl * +hdaa_audio_ctl_each(struct hdaa_devinfo *devinfo, int *index) +{ + if (devinfo == NULL || + index == NULL || devinfo->ctl == NULL || + devinfo->ctlcnt < 1 || + *index < 0 || *index >= devinfo->ctlcnt) + return (NULL); + return (&devinfo->ctl[(*index)++]); +} + +static struct hdaa_audio_ctl * +hdaa_audio_ctl_amp_get(struct hdaa_devinfo *devinfo, nid_t nid, int dir, + int index, int cnt) +{ + struct hdaa_audio_ctl *ctl; + int i, found = 0; + + if (devinfo == NULL || devinfo->ctl == NULL) + return (NULL); + + i = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0) + continue; + if (ctl->widget->nid != nid) + continue; + if (dir && ctl->ndir != dir) + continue; + if (index >= 0 && ctl->ndir == HDAA_CTL_IN && + ctl->dir == ctl->ndir && ctl->index != index) + continue; + found++; + if (found == cnt || cnt <= 0) + return (ctl); + } + + return (NULL); +} + +/* + * Jack detection (Speaker/HP redirection) event handler. + */ +static void +hdaa_hp_switch_handler(struct hdaa_devinfo *devinfo, int asid) +{ + struct hdaa_audio_as *as; + struct hdaa_widget *w; + struct hdaa_audio_ctl *ctl; + uint32_t val, res; + int j; + + as = &devinfo->as[asid]; + if (as->hpredir < 0) + return; + + w = hdaa_widget_get(devinfo, as->pins[15]); + if (w == NULL || w->enable == 0 || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + return; + + res = hda_command(devinfo->dev, + HDA_CMD_GET_PIN_SENSE(0, as->pins[15])); + + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Pin sense: nid=%d sence=0x%08x", + as->pins[15], res); + ); + + res = (res & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT) != 0; + if (devinfo->quirks & HDAA_QUIRK_SENSEINV) + res ^= 1; + + HDA_BOOTVERBOSE( + printf(" %sconnected\n", res == 0 ? "dis" : ""); + ); + + /* (Un)Mute headphone pin. */ + ctl = hdaa_audio_ctl_amp_get(devinfo, + as->pins[15], HDAA_CTL_IN, -1, 1); + if (ctl != NULL && ctl->mute) { + /* If pin has muter - use it. */ + val = (res != 0) ? 0 : 1; + if (val != ctl->forcemute) { + ctl->forcemute = val; + hdaa_audio_ctl_amp_set(ctl, + HDAA_AMP_MUTE_DEFAULT, + HDAA_AMP_VOL_DEFAULT, HDAA_AMP_VOL_DEFAULT); + } + } else { + /* If there is no muter - disable pin output. */ + w = hdaa_widget_get(devinfo, as->pins[15]); + if (w != NULL && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + if (res != 0) + val = w->wclass.pin.ctrl | + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + else + val = w->wclass.pin.ctrl & + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + if (val != w->wclass.pin.ctrl) { + w->wclass.pin.ctrl = val; + hda_command(devinfo->dev, + HDA_CMD_SET_PIN_WIDGET_CTRL(0, + w->nid, w->wclass.pin.ctrl)); + } + } + } + /* (Un)Mute other pins. */ + for (j = 0; j < 15; j++) { + if (as->pins[j] <= 0) + continue; + ctl = hdaa_audio_ctl_amp_get(devinfo, + as->pins[j], HDAA_CTL_IN, -1, 1); + if (ctl != NULL && ctl->mute) { + /* If pin has muter - use it. */ + val = (res != 0) ? 1 : 0; + if (val == ctl->forcemute) + continue; + ctl->forcemute = val; + hdaa_audio_ctl_amp_set(ctl, + HDAA_AMP_MUTE_DEFAULT, + HDAA_AMP_VOL_DEFAULT, HDAA_AMP_VOL_DEFAULT); + continue; + } + /* If there is no muter - disable pin output. */ + w = hdaa_widget_get(devinfo, as->pins[j]); + if (w != NULL && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + if (res != 0) + val = w->wclass.pin.ctrl & + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + else + val = w->wclass.pin.ctrl | + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + if (val != w->wclass.pin.ctrl) { + w->wclass.pin.ctrl = val; + hda_command(devinfo->dev, + HDA_CMD_SET_PIN_WIDGET_CTRL(0, + w->nid, w->wclass.pin.ctrl)); + } + } + } +} + +/* + * Callback for poll based jack detection. + */ +static void +hdaa_jack_poll_callback(void *arg) +{ + struct hdaa_devinfo *devinfo = arg; + int i; + + hdaa_lock(devinfo); + if (devinfo->poll_ival == 0) { + hdaa_unlock(devinfo); + return; + } + for (i = 0; i < devinfo->ascnt; i++) { + if (devinfo->as[i].hpredir < 0) + continue; + hdaa_hp_switch_handler(devinfo, i); + } + callout_reset(&devinfo->poll_jack, devinfo->poll_ival, + hdaa_jack_poll_callback, devinfo); + hdaa_unlock(devinfo); +} + +/* + * Jack detection initializer. + */ +static void +hdaa_hp_switch_init(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w; + int i, poll = 0; + + for (i = 0; i < devinfo->ascnt; i++) { + if (as[i].hpredir < 0) + continue; + + w = hdaa_widget_get(devinfo, as[i].pins[15]); + if (w == NULL || w->enable == 0 || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(w->wclass.pin.cap) == 0 || + (HDA_CONFIG_DEFAULTCONF_MISC(w->wclass.pin.config) & 1) != 0) { + device_printf(devinfo->dev, + "No jack detection support at pin %d\n", + as[i].pins[15]); + continue; + } + if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) { + as[i].unsol = HDAC_UNSOL_ALLOC( + device_get_parent(devinfo->dev), devinfo->dev, + w->nid); + hda_command(devinfo->dev, + HDA_CMD_SET_UNSOLICITED_RESPONSE(0, w->nid, + HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE | + as[i].unsol)); + } else + poll = 1; + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Headphones redirection " + "for as=%d nid=%d using %s.\n", + i, w->nid, + (poll != 0) ? "polling" : "unsolicited responses"); + ); + hdaa_hp_switch_handler(devinfo, i); + } + if (poll) { + callout_reset(&devinfo->poll_jack, 1, + hdaa_jack_poll_callback, devinfo); + } +} + +static void +hdaa_hp_switch_deinit(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w; + int i; + + for (i = 0; i < devinfo->ascnt; i++) { + if (as[i].unsol < 0) + continue; + w = hdaa_widget_get(devinfo, as[i].pins[15]); + if (w == NULL || w->enable == 0 || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + hda_command(devinfo->dev, + HDA_CMD_SET_UNSOLICITED_RESPONSE(0, w->nid, 0)); + HDAC_UNSOL_FREE( + device_get_parent(devinfo->dev), devinfo->dev, + as[i].unsol); + as[i].unsol = -1; + } +} + +uint32_t +hdaa_widget_pin_patch(uint32_t config, const char *str) +{ + char buf[256]; + char *key, *value, *rest, *bad; + int ival, i; + + strlcpy(buf, str, sizeof(buf)); + rest = buf; + while ((key = strsep(&rest, "=")) != NULL) { + value = strsep(&rest, " \t"); + if (value == NULL) + break; + ival = strtol(value, &bad, 10); + if (strcmp(key, "seq") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK; + config |= ((ival << HDA_CONFIG_DEFAULTCONF_SEQUENCE_SHIFT) & + HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK); + } else if (strcmp(key, "as") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK; + config |= ((ival << HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT) & + HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK); + } else if (strcmp(key, "misc") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_MISC_MASK; + config |= ((ival << HDA_CONFIG_DEFAULTCONF_MISC_SHIFT) & + HDA_CONFIG_DEFAULTCONF_MISC_MASK); + } else if (strcmp(key, "color") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_COLOR_MASK; + if (bad[0] == 0) { + config |= ((ival << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT) & + HDA_CONFIG_DEFAULTCONF_COLOR_MASK); + }; + for (i = 0; i < 16; i++) { + if (strcasecmp(HDA_COLORS[i], value) == 0) { + config |= (i << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT); + break; + } + } + } else if (strcmp(key, "ctype") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK; + if (bad[0] == 0) { + config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT) & + HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK); + } + for (i = 0; i < 16; i++) { + if (strcasecmp(HDA_CONNECTORS[i], value) == 0) { + config |= (i << HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT); + break; + } + } + } else if (strcmp(key, "device") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + if (bad[0] == 0) { + config |= ((ival << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT) & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK); + continue; + }; + for (i = 0; i < 16; i++) { + if (strcasecmp(HDA_DEVS[i], value) == 0) { + config |= (i << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT); + break; + } + } + } else if (strcmp(key, "loc") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_LOCATION_MASK; + if (bad[0] == 0) { + config |= ((ival << HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT) & + HDA_CONFIG_DEFAULTCONF_LOCATION_MASK); + continue; + } + for (i = 0; i < 64; i++) { + if (strcasecmp(HDA_LOCS[i], value) == 0) { + config |= (i << HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT); + break; + } + } + } else if (strcmp(key, "conn") == 0) { + config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; + if (bad[0] == 0) { + config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT) & + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + continue; + }; + for (i = 0; i < 4; i++) { + if (strcasecmp(HDA_CONNS[i], value) == 0) { + config |= (i << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT); + break; + } + } + } + } + return (config); +} + +uint32_t +hdaa_gpio_patch(uint32_t gpio, const char *str) +{ + char buf[256]; + char *key, *value, *rest; + int ikey, i; + + strlcpy(buf, str, sizeof(buf)); + rest = buf; + while ((key = strsep(&rest, "=")) != NULL) { + value = strsep(&rest, " \t"); + if (value == NULL) + break; + ikey = strtol(key, NULL, 10); + if (ikey < 0 || ikey > 7) + continue; + for (i = 0; i < 7; i++) { + if (strcasecmp(HDA_GPIO_ACTIONS[i], value) == 0) { + gpio &= ~HDAA_GPIO_MASK(ikey); + gpio |= i << HDAA_GPIO_SHIFT(ikey); + break; + } + } + } + return (gpio); +} + +static void +hdaa_local_patch_pin(struct hdaa_widget *w) +{ + device_t dev = w->devinfo->dev; + const char *res = NULL; + uint32_t config, orig; + char buf[32]; + + config = orig = w->wclass.pin.config; + snprintf(buf, sizeof(buf), "cad%u.nid%u.config", + hda_get_codec_id(dev), w->nid); + if (resource_string_value(device_get_name( + device_get_parent(device_get_parent(dev))), + device_get_unit(device_get_parent(device_get_parent(dev))), + buf, &res) == 0) { + if (strncmp(res, "0x", 2) == 0) { + config = strtol(res + 2, NULL, 16); + } else { + config = hdaa_widget_pin_patch(config, res); + } + } + snprintf(buf, sizeof(buf), "nid%u.config", w->nid); + if (resource_string_value(device_get_name(dev), device_get_unit(dev), + buf, &res) == 0) { + if (strncmp(res, "0x", 2) == 0) { + config = strtol(res + 2, NULL, 16); + } else { + config = hdaa_widget_pin_patch(config, res); + } + } + HDA_BOOTVERBOSE( + if (config != orig) + device_printf(w->devinfo->dev, + "Patching pin config nid=%u 0x%08x -> 0x%08x\n", + w->nid, orig, config); + ); + w->wclass.pin.newconf = w->wclass.pin.config = config; +} + +static int +hdaa_sysctl_config(SYSCTL_HANDLER_ARGS) +{ + char buf[256]; + int error; + uint32_t conf; + + conf = *(uint32_t *)oidp->oid_arg1; + snprintf(buf, sizeof(buf), "0x%08x as=%d seq=%d " + "device=%s conn=%s ctype=%s loc=%s color=%s misc=%d", + conf, + HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf), + HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf), + HDA_DEVS[HDA_CONFIG_DEFAULTCONF_DEVICE(conf)], + HDA_CONNS[HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf)], + HDA_CONNECTORS[HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf)], + HDA_LOCS[HDA_CONFIG_DEFAULTCONF_LOCATION(conf)], + HDA_COLORS[HDA_CONFIG_DEFAULTCONF_COLOR(conf)], + HDA_CONFIG_DEFAULTCONF_MISC(conf)); + error = sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (error != 0 || req->newptr == NULL) + return (error); + if (strncmp(buf, "0x", 2) == 0) + conf = strtol(buf + 2, NULL, 16); + else + conf = hdaa_widget_pin_patch(conf, buf); + *(uint32_t *)oidp->oid_arg1 = conf; + return (0); +} + +static void +hdaa_config_fetch(const char *str, uint32_t *on, uint32_t *off) +{ + int i = 0, j, k, len, inv; + + for (;;) { + while (str[i] != '\0' && + (str[i] == ',' || isspace(str[i]) != 0)) + i++; + if (str[i] == '\0') + return; + j = i; + while (str[j] != '\0' && + !(str[j] == ',' || isspace(str[j]) != 0)) + j++; + len = j - i; + if (len > 2 && strncmp(str + i, "no", 2) == 0) + inv = 2; + else + inv = 0; + for (k = 0; len > inv && k < HDAA_QUIRKS_TAB_LEN; k++) { + if (strncmp(str + i + inv, + hdaa_quirks_tab[k].key, len - inv) != 0) + continue; + if (len - inv != strlen(hdaa_quirks_tab[k].key)) + continue; + if (inv == 0) { + *on |= hdaa_quirks_tab[k].value; + *off &= ~hdaa_quirks_tab[k].value; + } else { + *off |= hdaa_quirks_tab[k].value; + *on &= ~hdaa_quirks_tab[k].value; + } + break; + } + i = j; + } +} + +static int +hdaa_sysctl_quirks(SYSCTL_HANDLER_ARGS) +{ + char buf[256]; + int error, n = 0, i; + uint32_t quirks, quirks_off; + + quirks = *(uint32_t *)oidp->oid_arg1; + buf[0] = 0; + for (i = 0; i < HDAA_QUIRKS_TAB_LEN; i++) { + if ((quirks & hdaa_quirks_tab[i].value) != 0) + n += snprintf(buf + n, sizeof(buf) - n, "%s%s", + n != 0 ? "," : "", hdaa_quirks_tab[i].key); + } + error = sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (error != 0 || req->newptr == NULL) + return (error); + if (strncmp(buf, "0x", 2) == 0) + quirks = strtol(buf + 2, NULL, 16); + else { + quirks = 0; + hdaa_config_fetch(buf, &quirks, &quirks_off); + } + *(uint32_t *)oidp->oid_arg1 = quirks; + return (0); +} + +static void +hdaa_local_patch(struct hdaa_devinfo *devinfo) +{ + struct hdaa_widget *w; + const char *res = NULL; + uint32_t quirks_on = 0, quirks_off = 0, x; + int i; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL) + continue; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + hdaa_local_patch_pin(w); + } + + if (resource_string_value(device_get_name(devinfo->dev), + device_get_unit(devinfo->dev), "config", &res) == 0) { + if (res != NULL && strlen(res) > 0) + hdaa_config_fetch(res, &quirks_on, &quirks_off); + devinfo->quirks |= quirks_on; + devinfo->quirks &= ~quirks_off; + } + if (devinfo->newquirks == -1) + devinfo->newquirks = devinfo->quirks; + else + devinfo->quirks = devinfo->newquirks; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + "Config options: 0x%08x\n", devinfo->quirks); + ); + + if (resource_string_value(device_get_name(devinfo->dev), + device_get_unit(devinfo->dev), "gpio_config", &res) == 0) { + if (strncmp(res, "0x", 2) == 0) { + devinfo->gpio = strtol(res + 2, NULL, 16); + } else { + devinfo->gpio = hdaa_gpio_patch(devinfo->gpio, res); + } + } + if (devinfo->newgpio == -1) + devinfo->newgpio = devinfo->gpio; + else + devinfo->gpio = devinfo->newgpio; + if (devinfo->newgpo == -1) + devinfo->newgpo = devinfo->gpo; + else + devinfo->gpo = devinfo->newgpo; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, "GPIO config options:"); + for (i = 0; i < 7; i++) { + x = (devinfo->gpio & HDAA_GPIO_MASK(i)) >> HDAA_GPIO_SHIFT(i); + if (x != 0) + printf(" %d=%s", i, HDA_GPIO_ACTIONS[x]); + } + printf("\n"); + ); +} + +static void +hdaa_widget_connection_parse(struct hdaa_widget *w) +{ + uint32_t res; + int i, j, max, ents, entnum; + nid_t nid = w->nid; + nid_t cnid, addcnid, prevcnid; + + w->nconns = 0; + + res = hda_command(w->devinfo->dev, + HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_CONN_LIST_LENGTH)); + + ents = HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(res); + + if (ents < 1) + return; + + entnum = HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM(res) ? 2 : 4; + max = (sizeof(w->conns) / sizeof(w->conns[0])) - 1; + prevcnid = 0; + +#define CONN_RMASK(e) (1 << ((32 / (e)) - 1)) +#define CONN_NMASK(e) (CONN_RMASK(e) - 1) +#define CONN_RESVAL(r, e, n) ((r) >> ((32 / (e)) * (n))) +#define CONN_RANGE(r, e, n) (CONN_RESVAL(r, e, n) & CONN_RMASK(e)) +#define CONN_CNID(r, e, n) (CONN_RESVAL(r, e, n) & CONN_NMASK(e)) + + for (i = 0; i < ents; i += entnum) { + res = hda_command(w->devinfo->dev, + HDA_CMD_GET_CONN_LIST_ENTRY(0, nid, i)); + for (j = 0; j < entnum; j++) { + cnid = CONN_CNID(res, entnum, j); + if (cnid == 0) { + if (w->nconns < ents) + device_printf(w->devinfo->dev, + "WARNING: nid=%d has zero cnid " + "entnum=%d j=%d index=%d " + "entries=%d found=%d res=0x%08x\n", + nid, entnum, j, i, + ents, w->nconns, res); + else + goto getconns_out; + } + if (cnid < w->devinfo->startnode || + cnid >= w->devinfo->endnode) { + HDA_BOOTVERBOSE( + device_printf(w->devinfo->dev, + "WARNING: nid=%d has cnid outside " + "of the AFG range j=%d " + "entnum=%d index=%d res=0x%08x\n", + nid, j, entnum, i, res); + ); + } + if (CONN_RANGE(res, entnum, j) == 0) + addcnid = cnid; + else if (prevcnid == 0 || prevcnid >= cnid) { + device_printf(w->devinfo->dev, + "WARNING: Invalid child range " + "nid=%d index=%d j=%d entnum=%d " + "prevcnid=%d cnid=%d res=0x%08x\n", + nid, i, j, entnum, prevcnid, + cnid, res); + addcnid = cnid; + } else + addcnid = prevcnid + 1; + while (addcnid <= cnid) { + if (w->nconns > max) { + device_printf(w->devinfo->dev, + "Adding %d (nid=%d): " + "Max connection reached! max=%d\n", + addcnid, nid, max + 1); + goto getconns_out; + } + w->connsenable[w->nconns] = 1; + w->conns[w->nconns++] = addcnid++; + } + prevcnid = cnid; + } + } + +getconns_out: + return; +} + +static void +hdaa_widget_parse(struct hdaa_widget *w) +{ + device_t dev = w->devinfo->dev; + uint32_t wcap, cap; + nid_t nid = w->nid; + char buf[64]; + + w->param.widget_cap = wcap = hda_command(dev, + HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_AUDIO_WIDGET_CAP)); + w->type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(wcap); + + hdaa_widget_connection_parse(w); + + if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(wcap)) { + if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap)) + w->param.outamp_cap = + hda_command(dev, + HDA_CMD_GET_PARAMETER(0, nid, + HDA_PARAM_OUTPUT_AMP_CAP)); + else + w->param.outamp_cap = + w->devinfo->outamp_cap; + } else + w->param.outamp_cap = 0; + + if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(wcap)) { + if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap)) + w->param.inamp_cap = + hda_command(dev, + HDA_CMD_GET_PARAMETER(0, nid, + HDA_PARAM_INPUT_AMP_CAP)); + else + w->param.inamp_cap = + w->devinfo->inamp_cap; + } else + w->param.inamp_cap = 0; + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { + if (HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR(wcap)) { + cap = hda_command(dev, + HDA_CMD_GET_PARAMETER(0, nid, + HDA_PARAM_SUPP_STREAM_FORMATS)); + w->param.supp_stream_formats = (cap != 0) ? cap : + w->devinfo->supp_stream_formats; + cap = hda_command(dev, + HDA_CMD_GET_PARAMETER(0, nid, + HDA_PARAM_SUPP_PCM_SIZE_RATE)); + w->param.supp_pcm_size_rate = (cap != 0) ? cap : + w->devinfo->supp_pcm_size_rate; + } else { + w->param.supp_stream_formats = + w->devinfo->supp_stream_formats; + w->param.supp_pcm_size_rate = + w->devinfo->supp_pcm_size_rate; + } + } else { + w->param.supp_stream_formats = 0; + w->param.supp_pcm_size_rate = 0; + } + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + w->wclass.pin.original = w->wclass.pin.newconf = + w->wclass.pin.config = hda_command(dev, + HDA_CMD_GET_CONFIGURATION_DEFAULT(0, w->nid)); + w->wclass.pin.cap = hda_command(dev, + HDA_CMD_GET_PARAMETER(0, w->nid, HDA_PARAM_PIN_CAP));; + w->wclass.pin.ctrl = hda_command(dev, + HDA_CMD_GET_PIN_WIDGET_CTRL(0, nid)); + if (HDA_PARAM_PIN_CAP_EAPD_CAP(w->wclass.pin.cap)) { + w->param.eapdbtl = hda_command(dev, + HDA_CMD_GET_EAPD_BTL_ENABLE(0, nid)); + w->param.eapdbtl &= 0x7; + w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + } else + w->param.eapdbtl = HDA_INVALID; + + hdaa_unlock(w->devinfo); + snprintf(buf, sizeof(buf), "nid%d_config", w->nid); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + buf, CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, + &w->wclass.pin.newconf, sizeof(&w->wclass.pin.newconf), + hdaa_sysctl_config, "A", "Current pin configuration"); + snprintf(buf, sizeof(buf), "nid%d_original", w->nid); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + buf, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + &w->wclass.pin.original, sizeof(&w->wclass.pin.original), + hdaa_sysctl_config, "A", "Original pin configuration"); + hdaa_lock(w->devinfo); + } +} + +static void +hdaa_widget_postprocess(struct hdaa_widget *w) +{ + char *typestr; + + w->type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(w->param.widget_cap); + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: + typestr = "audio output"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: + typestr = "audio input"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + typestr = "audio mixer"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + typestr = "audio selector"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + typestr = "pin"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET: + typestr = "power widget"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET: + typestr = "volume widget"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET: + typestr = "beep widget"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VENDOR_WIDGET: + typestr = "vendor widget"; + break; + default: + typestr = "unknown type"; + break; + } + strlcpy(w->name, typestr, sizeof(w->name)); + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + uint32_t config; + const char *devstr; + int conn, color; + + config = w->wclass.pin.config; + devstr = HDA_DEVS[(config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) >> + HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT]; + conn = (config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) >> + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT; + color = (config & HDA_CONFIG_DEFAULTCONF_COLOR_MASK) >> + HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT; + strlcat(w->name, ": ", sizeof(w->name)); + strlcat(w->name, devstr, sizeof(w->name)); + strlcat(w->name, " (", sizeof(w->name)); + if (conn == 0 && color != 0 && color != 15) { + strlcat(w->name, HDA_COLORS[color], sizeof(w->name)); + strlcat(w->name, " ", sizeof(w->name)); + } + strlcat(w->name, HDA_CONNS[conn], sizeof(w->name)); + strlcat(w->name, ")", sizeof(w->name)); + } +} + +struct hdaa_widget * +hdaa_widget_get(struct hdaa_devinfo *devinfo, nid_t nid) +{ + if (devinfo == NULL || devinfo->widget == NULL || + nid < devinfo->startnode || nid >= devinfo->endnode) + return (NULL); + return (&devinfo->widget[nid - devinfo->startnode]); +} + +static void +hdaa_audio_ctl_amp_set_internal(struct hdaa_devinfo *devinfo, nid_t nid, + int index, int lmute, int rmute, + int left, int right, int dir) +{ + uint16_t v = 0; + + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + "Setting amplifier nid=%d index=%d %s mute=%d/%d vol=%d/%d\n", + nid,index,dir ? "in" : "out",lmute,rmute,left,right); + ); + if (left != right || lmute != rmute) { + v = (1 << (15 - dir)) | (1 << 13) | (index << 8) | + (lmute << 7) | left; + hda_command(devinfo->dev, + HDA_CMD_SET_AMP_GAIN_MUTE(0, nid, v)); + v = (1 << (15 - dir)) | (1 << 12) | (index << 8) | + (rmute << 7) | right; + } else + v = (1 << (15 - dir)) | (3 << 12) | (index << 8) | + (lmute << 7) | left; + + hda_command(devinfo->dev, + HDA_CMD_SET_AMP_GAIN_MUTE(0, nid, v)); +} + +static void +hdaa_audio_ctl_amp_set(struct hdaa_audio_ctl *ctl, uint32_t mute, + int left, int right) +{ + nid_t nid; + int lmute, rmute; + + nid = ctl->widget->nid; + + /* Save new values if valid. */ + if (mute != HDAA_AMP_MUTE_DEFAULT) + ctl->muted = mute; + if (left != HDAA_AMP_VOL_DEFAULT) + ctl->left = left; + if (right != HDAA_AMP_VOL_DEFAULT) + ctl->right = right; + /* Prepare effective values */ + if (ctl->forcemute) { + lmute = 1; + rmute = 1; + left = 0; + right = 0; + } else { + lmute = HDAA_AMP_LEFT_MUTED(ctl->muted); + rmute = HDAA_AMP_RIGHT_MUTED(ctl->muted); + left = ctl->left; + right = ctl->right; + } + /* Apply effective values */ + if (ctl->dir & HDAA_CTL_OUT) + hdaa_audio_ctl_amp_set_internal(ctl->widget->devinfo, nid, ctl->index, + lmute, rmute, left, right, 0); + if (ctl->dir & HDAA_CTL_IN) + hdaa_audio_ctl_amp_set_internal(ctl->widget->devinfo, nid, ctl->index, + lmute, rmute, left, right, 1); +} + +static void +hdaa_widget_connection_select(struct hdaa_widget *w, uint8_t index) +{ + if (w == NULL || w->nconns < 1 || index > (w->nconns - 1)) + return; + HDA_BOOTHVERBOSE( + device_printf(w->devinfo->dev, + "Setting selector nid=%d index=%d\n", w->nid, index); + ); + hda_command(w->devinfo->dev, + HDA_CMD_SET_CONNECTION_SELECT_CONTROL(0, w->nid, index)); + w->selconn = index; +} + +/**************************************************************************** + * Device Methods + ****************************************************************************/ + +static void * +hdaa_channel_init(kobj_t obj, void *data, struct snd_dbuf *b, + struct pcm_channel *c, int dir) +{ + struct hdaa_chan *ch = data; + struct hdaa_pcm_devinfo *pdevinfo = ch->pdevinfo; + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + + hdaa_lock(devinfo); + if (devinfo->quirks & HDAA_QUIRK_FIXEDRATE) { + ch->caps.minspeed = ch->caps.maxspeed = 48000; + ch->pcmrates[0] = 48000; + ch->pcmrates[1] = 0; + } + ch->dir = dir; + ch->b = b; + ch->c = c; + ch->blksz = pdevinfo->chan_size / pdevinfo->chan_blkcnt; + ch->blkcnt = pdevinfo->chan_blkcnt; + hdaa_unlock(devinfo); + + if (sndbuf_alloc(ch->b, bus_get_dma_tag(devinfo->dev), + hda_get_dma_nocache(devinfo->dev) ? BUS_DMA_NOCACHE : 0, + pdevinfo->chan_size) != 0) + return (NULL); + + return (ch); +} + +static int +hdaa_channel_setformat(kobj_t obj, void *data, uint32_t format) +{ + struct hdaa_chan *ch = data; + int i; + + for (i = 0; ch->caps.fmtlist[i] != 0; i++) { + if (format == ch->caps.fmtlist[i]) { + ch->fmt = format; + return (0); + } + } + + return (EINVAL); +} + +static uint32_t +hdaa_channel_setspeed(kobj_t obj, void *data, uint32_t speed) +{ + struct hdaa_chan *ch = data; + uint32_t spd = 0, threshold; + int i; + + /* First look for equal or multiple frequency. */ + for (i = 0; ch->pcmrates[i] != 0; i++) { + spd = ch->pcmrates[i]; + if (speed != 0 && spd / speed * speed == spd) { + ch->spd = spd; + return (spd); + } + } + /* If no match, just find nearest. */ + for (i = 0; ch->pcmrates[i] != 0; i++) { + spd = ch->pcmrates[i]; + threshold = spd + ((ch->pcmrates[i + 1] != 0) ? + ((ch->pcmrates[i + 1] - spd) >> 1) : 0); + if (speed < threshold) + break; + } + ch->spd = spd; + return (spd); +} + +static uint16_t +hdaa_stream_format(struct hdaa_chan *ch) +{ + int i; + uint16_t fmt; + + fmt = 0; + if (ch->fmt & AFMT_S16_LE) + fmt |= ch->bit16 << 4; + else if (ch->fmt & AFMT_S32_LE) + fmt |= ch->bit32 << 4; + else + fmt |= 1 << 4; + for (i = 0; i < HDA_RATE_TAB_LEN; i++) { + if (hda_rate_tab[i].valid && ch->spd == hda_rate_tab[i].rate) { + fmt |= hda_rate_tab[i].base; + fmt |= hda_rate_tab[i].mul; + fmt |= hda_rate_tab[i].div; + break; + } + } + fmt |= (AFMT_CHANNEL(ch->fmt) - 1); + + return (fmt); +} + +static void +hdaa_audio_setup(struct hdaa_chan *ch) +{ + struct hdaa_audio_as *as = &ch->devinfo->as[ch->as]; + struct hdaa_widget *w; + int i, chn, totalchn, c; + uint16_t fmt, dfmt; + uint16_t chmap[2][5] = {{ 0x0010, 0x0001, 0x0201, 0x0231, 0x0231 }, /* 5.1 */ + { 0x0010, 0x0001, 0x2001, 0x2031, 0x2431 }};/* 7.1 */ + int map = -1; + + totalchn = AFMT_CHANNEL(ch->fmt); + HDA_BOOTHVERBOSE( + device_printf(ch->pdevinfo->dev, + "PCMDIR_%s: Stream setup fmt=%08x speed=%d\n", + (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", + ch->fmt, ch->spd); + ); + fmt = hdaa_stream_format(ch); + + /* Set channel mapping for known speaker setups. */ + if ((as->pinset == 0x0007 || as->pinset == 0x0013)) /* Standard 5.1 */ + map = 0; + else if (as->pinset == 0x0017) /* Standard 7.1 */ + map = 1; + + dfmt = HDA_CMD_SET_DIGITAL_CONV_FMT1_DIGEN; + if (ch->fmt & AFMT_AC3) + dfmt |= HDA_CMD_SET_DIGITAL_CONV_FMT1_NAUDIO; + + chn = 0; + for (i = 0; ch->io[i] != -1; i++) { + w = hdaa_widget_get(ch->devinfo, ch->io[i]); + if (w == NULL) + continue; + + /* If HP redirection is enabled, but failed to use same + DAC, make last DAC to duplicate first one. */ + if (as->fakeredir && i == (as->pincnt - 1)) { + c = (ch->sid << 4); + } else { + if (map >= 0) /* Map known speaker setups. */ + chn = (((chmap[map][totalchn / 2] >> i * 4) & + 0xf) - 1) * 2; + if (chn < 0 || chn >= totalchn) { + c = 0; + } else { + c = (ch->sid << 4) | chn; + } + } + HDA_BOOTHVERBOSE( + device_printf(ch->pdevinfo->dev, + "PCMDIR_%s: Stream setup nid=%d: " + "fmt=0x%04x, dfmt=0x%04x, chan=0x%04x\n", + (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", + ch->io[i], fmt, dfmt, c); + ); + hda_command(ch->devinfo->dev, + HDA_CMD_SET_CONV_FMT(0, ch->io[i], fmt)); + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { + hda_command(ch->devinfo->dev, + HDA_CMD_SET_DIGITAL_CONV_FMT1(0, ch->io[i], dfmt)); + } + hda_command(ch->devinfo->dev, + HDA_CMD_SET_CONV_STREAM_CHAN(0, ch->io[i], c)); +#if 0 + hda_command(ch->devinfo->dev, + HDA_CMD_SET_CONV_CHAN_COUNT(0, ch->io[i], 1)); + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_CHAN_SLOT(0, ch->io[i], 0x00)); + hda_command(ch->devinfo->dev, + HDA_CMD_SET_HDMI_CHAN_SLOT(0, ch->io[i], 0x11)); +#endif + chn += HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) + 1; + } +} + +/* + * Greatest Common Divisor. + */ +static unsigned +gcd(unsigned a, unsigned b) +{ + u_int c; + + while (b != 0) { + c = a; + a = b; + b = (c % b); + } + return (a); +} + +/* + * Least Common Multiple. + */ +static unsigned +lcm(unsigned a, unsigned b) +{ + + return ((a * b) / gcd(a, b)); +} + +static int +hdaa_channel_setfragments(kobj_t obj, void *data, + uint32_t blksz, uint32_t blkcnt) +{ + struct hdaa_chan *ch = data; + + blksz -= blksz % lcm(HDA_DMA_ALIGNMENT, sndbuf_getalign(ch->b)); + + if (blksz > (sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN)) + blksz = sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN; + if (blksz < HDA_BLK_MIN) + blksz = HDA_BLK_MIN; + if (blkcnt > HDA_BDL_MAX) + blkcnt = HDA_BDL_MAX; + if (blkcnt < HDA_BDL_MIN) + blkcnt = HDA_BDL_MIN; + + while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->b)) { + if ((blkcnt >> 1) >= HDA_BDL_MIN) + blkcnt >>= 1; + else if ((blksz >> 1) >= HDA_BLK_MIN) + blksz >>= 1; + else + break; + } + + if ((sndbuf_getblksz(ch->b) != blksz || + sndbuf_getblkcnt(ch->b) != blkcnt) && + sndbuf_resize(ch->b, blkcnt, blksz) != 0) + device_printf(ch->devinfo->dev, "%s: failed blksz=%u blkcnt=%u\n", + __func__, blksz, blkcnt); + + ch->blksz = sndbuf_getblksz(ch->b); + ch->blkcnt = sndbuf_getblkcnt(ch->b); + + return (0); +} + +static uint32_t +hdaa_channel_setblocksize(kobj_t obj, void *data, uint32_t blksz) +{ + struct hdaa_chan *ch = data; + + hdaa_channel_setfragments(obj, data, blksz, ch->pdevinfo->chan_blkcnt); + + return (ch->blksz); +} + +static void +hdaa_channel_stop(struct hdaa_chan *ch) +{ + struct hdaa_devinfo *devinfo = ch->devinfo; + struct hdaa_widget *w; + int i; + + ch->flags &= ~HDAA_CHN_RUNNING; + HDAC_STREAM_STOP(device_get_parent(devinfo->dev), devinfo->dev, + ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid); + for (i = 0; ch->io[i] != -1; i++) { + w = hdaa_widget_get(ch->devinfo, ch->io[i]); + if (w == NULL) + continue; + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { + hda_command(devinfo->dev, + HDA_CMD_SET_DIGITAL_CONV_FMT1(0, ch->io[i], 0)); + } + hda_command(devinfo->dev, + HDA_CMD_SET_CONV_STREAM_CHAN(0, ch->io[i], + 0)); + } + HDAC_STREAM_FREE(device_get_parent(devinfo->dev), devinfo->dev, + ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid); +} + +static int +hdaa_channel_start(struct hdaa_chan *ch) +{ + struct hdaa_devinfo *devinfo = ch->devinfo; + + ch->ptr = 0; + ch->prevptr = 0; + ch->sid = HDAC_STREAM_ALLOC(device_get_parent(devinfo->dev), devinfo->dev, + ch->dir == PCMDIR_PLAY ? 1 : 0, hdaa_stream_format(ch), &ch->dmapos); + if (ch->sid <= 0) + return (EBUSY); + hdaa_audio_setup(ch); + HDAC_STREAM_RESET(device_get_parent(devinfo->dev), devinfo->dev, + ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid); + HDAC_STREAM_START(device_get_parent(devinfo->dev), devinfo->dev, + ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid, + sndbuf_getbufaddr(ch->b), ch->blksz, ch->blkcnt); + ch->flags |= HDAA_CHN_RUNNING; + return (0); +} + +static int +hdaa_channel_trigger(kobj_t obj, void *data, int go) +{ + struct hdaa_chan *ch = data; + int error = 0; + + if (!PCMTRIG_COMMON(go)) + return (0); + + hdaa_lock(ch->devinfo); + switch (go) { + case PCMTRIG_START: + error = hdaa_channel_start(ch); + break; + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + hdaa_channel_stop(ch); + break; + default: + break; + } + hdaa_unlock(ch->devinfo); + + return (error); +} + +static uint32_t +hdaa_channel_getptr(kobj_t obj, void *data) +{ + struct hdaa_chan *ch = data; + struct hdaa_devinfo *devinfo = ch->devinfo; + uint32_t ptr; + + hdaa_lock(devinfo); + if (ch->dmapos != NULL) { + ptr = *(ch->dmapos); + } else { + ptr = HDAC_STREAM_GETPTR( + device_get_parent(devinfo->dev), devinfo->dev, + ch->dir == PCMDIR_PLAY ? 1 : 0, ch->sid); + } + hdaa_unlock(devinfo); + + /* + * Round to available space and force 128 bytes aligment. + */ + ptr %= ch->blksz * ch->blkcnt; + ptr &= HDA_BLK_ALIGN; + + return (ptr); +} + +static struct pcmchan_caps * +hdaa_channel_getcaps(kobj_t obj, void *data) +{ + return (&((struct hdaa_chan *)data)->caps); +} + +static kobj_method_t hdaa_channel_methods[] = { + KOBJMETHOD(channel_init, hdaa_channel_init), + KOBJMETHOD(channel_setformat, hdaa_channel_setformat), + KOBJMETHOD(channel_setspeed, hdaa_channel_setspeed), + KOBJMETHOD(channel_setblocksize, hdaa_channel_setblocksize), + KOBJMETHOD(channel_setfragments, hdaa_channel_setfragments), + KOBJMETHOD(channel_trigger, hdaa_channel_trigger), + KOBJMETHOD(channel_getptr, hdaa_channel_getptr), + KOBJMETHOD(channel_getcaps, hdaa_channel_getcaps), + KOBJMETHOD_END +}; +CHANNEL_DECLARE(hdaa_channel); + +static int +hdaa_audio_ctl_ossmixer_init(struct snd_mixer *m) +{ + struct hdaa_pcm_devinfo *pdevinfo = mix_getdevinfo(m); + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_widget *w, *cw; + struct hdaa_audio_ctl *ctl; + uint32_t mask, recmask; + int i, j, softpcmvol; + + hdaa_lock(devinfo); + + /* Make sure that in case of soft volume it won't stay muted. */ + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + pdevinfo->left[i] = 100; + pdevinfo->right[i] = 100; + } + + mask = 0; + recmask = 0; + + /* Declate EAPD as ogain control. */ + if (pdevinfo->playas >= 0) { + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + w->param.eapdbtl == HDA_INVALID || + w->bindas != pdevinfo->playas) + continue; + mask |= SOUND_MASK_OGAIN; + break; + } + } + + /* Declare volume controls assigned to this association. */ + i = 0; + ctl = NULL; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0) + continue; + if ((pdevinfo->playas >= 0 && + ctl->widget->bindas == pdevinfo->playas) || + (pdevinfo->recas >= 0 && + ctl->widget->bindas == pdevinfo->recas) || + (ctl->widget->bindas == -2 && pdevinfo->index == 0)) + mask |= ctl->ossmask; + } + + /* Declare record sources available to this association. */ + if (pdevinfo->recas >= 0) { + for (i = 0; i < 16; i++) { + if (devinfo->as[pdevinfo->recas].dacs[0][i] < 0) + continue; + w = hdaa_widget_get(devinfo, + devinfo->as[pdevinfo->recas].dacs[0][i]); + if (w == NULL || w->enable == 0) + continue; + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j] == 0) + continue; + cw = hdaa_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + if (cw->bindas != pdevinfo->recas && + cw->bindas != -2) + continue; + recmask |= cw->ossmask; + } + } + } + + /* Declare soft PCM volume if needed. */ + if (pdevinfo->playas >= 0) { + ctl = NULL; + if ((mask & SOUND_MASK_PCM) == 0 || + (devinfo->quirks & HDAA_QUIRK_SOFTPCMVOL)) { + softpcmvol = 1; + mask |= SOUND_MASK_PCM; + } else { + softpcmvol = 0; + i = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0) + continue; + if (ctl->widget->bindas != pdevinfo->playas && + (ctl->widget->bindas != -2 || pdevinfo->index != 0)) + continue; + if (!(ctl->ossmask & SOUND_MASK_PCM)) + continue; + if (ctl->step > 0) + break; + } + } + + if (softpcmvol == 1 || ctl == NULL) { + pcm_setflags(pdevinfo->dev, pcm_getflags(pdevinfo->dev) | SD_F_SOFTPCMVOL); + HDA_BOOTVERBOSE( + device_printf(pdevinfo->dev, + "%s Soft PCM volume\n", + (softpcmvol == 1) ? "Forcing" : "Enabling"); + ); + } + } + + /* Declare master volume if needed. */ + if (pdevinfo->playas >= 0) { + if ((mask & (SOUND_MASK_VOLUME | SOUND_MASK_PCM)) == + SOUND_MASK_PCM) { + mask |= SOUND_MASK_VOLUME; + mix_setparentchild(m, SOUND_MIXER_VOLUME, + SOUND_MASK_PCM); + mix_setrealdev(m, SOUND_MIXER_VOLUME, + SOUND_MIXER_NONE); + HDA_BOOTVERBOSE( + device_printf(pdevinfo->dev, + "Forcing master volume with PCM\n"); + ); + } + } + + recmask &= (1 << SOUND_MIXER_NRDEVICES) - 1; + mask &= (1 << SOUND_MIXER_NRDEVICES) - 1; + + mix_setrecdevs(m, recmask); + mix_setdevs(m, mask); + + hdaa_unlock(devinfo); + + return (0); +} + +static int +hdaa_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev, + unsigned left, unsigned right) +{ + struct hdaa_pcm_devinfo *pdevinfo = mix_getdevinfo(m); + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_widget *w; + struct hdaa_audio_ctl *ctl; + uint32_t mute; + int lvol, rvol; + int i, j; + + hdaa_lock(devinfo); + /* Save new values. */ + pdevinfo->left[dev] = left; + pdevinfo->right[dev] = right; + + /* 'ogain' is the special case implemented with EAPD. */ + if (dev == SOUND_MIXER_OGAIN) { + uint32_t orig; + w = NULL; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + w->param.eapdbtl == HDA_INVALID) + continue; + break; + } + if (i >= devinfo->endnode) { + hdaa_unlock(devinfo); + return (-1); + } + orig = w->param.eapdbtl; + if (left == 0) + w->param.eapdbtl &= ~HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + else + w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + if (orig != w->param.eapdbtl) { + uint32_t val; + + val = w->param.eapdbtl; + if (devinfo->quirks & HDAA_QUIRK_EAPDINV) + val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + hda_command(devinfo->dev, + HDA_CMD_SET_EAPD_BTL_ENABLE(0, w->nid, val)); + } + hdaa_unlock(devinfo); + return (left | (left << 8)); + } + + /* Recalculate all controls related to this OSS device. */ + i = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || + !(ctl->ossmask & (1 << dev))) + continue; + if (!((pdevinfo->playas >= 0 && + ctl->widget->bindas == pdevinfo->playas) || + (pdevinfo->recas >= 0 && + ctl->widget->bindas == pdevinfo->recas) || + ctl->widget->bindas == -2)) + continue; + + lvol = 100; + rvol = 100; + for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) { + if (ctl->ossmask & (1 << j)) { + lvol = lvol * pdevinfo->left[j] / 100; + rvol = rvol * pdevinfo->right[j] / 100; + } + } + mute = (lvol == 0) ? HDAA_AMP_MUTE_LEFT : 0; + mute |= (rvol == 0) ? HDAA_AMP_MUTE_RIGHT : 0; + lvol = (lvol * ctl->step + 50) / 100; + rvol = (rvol * ctl->step + 50) / 100; + hdaa_audio_ctl_amp_set(ctl, mute, lvol, rvol); + } + hdaa_unlock(devinfo); + + return (left | (right << 8)); +} + +/* + * Commutate specified record source. + */ +static uint32_t +hdaa_audio_ctl_recsel_comm(struct hdaa_pcm_devinfo *pdevinfo, uint32_t src, nid_t nid, int depth) +{ + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_widget *w, *cw; + struct hdaa_audio_ctl *ctl; + char buf[64]; + int i, muted; + uint32_t res = 0; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + + w = hdaa_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + + for (i = 0; i < w->nconns; i++) { + if (w->connsenable[i] == 0) + continue; + cw = hdaa_widget_get(devinfo, w->conns[i]); + if (cw == NULL || cw->enable == 0 || cw->bindas == -1) + continue; + /* Call recursively to trace signal to it's source if needed. */ + if ((src & cw->ossmask) != 0) { + if (cw->ossdev < 0) { + res |= hdaa_audio_ctl_recsel_comm(pdevinfo, src, + w->conns[i], depth + 1); + } else { + res |= cw->ossmask; + } + } + /* We have two special cases: mixers and others (selectors). */ + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) { + ctl = hdaa_audio_ctl_amp_get(devinfo, + w->nid, HDAA_CTL_IN, i, 1); + if (ctl == NULL) + continue; + /* If we have input control on this node mute them + * according to requested sources. */ + muted = (src & cw->ossmask) ? 0 : 1; + if (muted != ctl->forcemute) { + ctl->forcemute = muted; + hdaa_audio_ctl_amp_set(ctl, + HDAA_AMP_MUTE_DEFAULT, + HDAA_AMP_VOL_DEFAULT, HDAA_AMP_VOL_DEFAULT); + } + HDA_BOOTHVERBOSE( + device_printf(pdevinfo->dev, + "Recsel (%s): nid %d source %d %s\n", + hdaa_audio_ctl_ossmixer_mask2allname( + src, buf, sizeof(buf)), + nid, i, muted?"mute":"unmute"); + ); + } else { + if (w->nconns == 1) + break; + if ((src & cw->ossmask) == 0) + continue; + /* If we found requested source - select it and exit. */ + hdaa_widget_connection_select(w, i); + HDA_BOOTHVERBOSE( + device_printf(pdevinfo->dev, + "Recsel (%s): nid %d source %d select\n", + hdaa_audio_ctl_ossmixer_mask2allname( + src, buf, sizeof(buf)), + nid, i); + ); + break; + } + } + return (res); +} + +static uint32_t +hdaa_audio_ctl_ossmixer_setrecsrc(struct snd_mixer *m, uint32_t src) +{ + struct hdaa_pcm_devinfo *pdevinfo = mix_getdevinfo(m); + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_widget *w; + struct hdaa_audio_as *as; + struct hdaa_chan *ch; + int i, j; + uint32_t ret = 0xffffffff; + + hdaa_lock(devinfo); + if (pdevinfo->recas < 0) { + hdaa_unlock(devinfo); + return (0); + } + as = &devinfo->as[pdevinfo->recas]; + + /* For non-mixed associations we always recording everything. */ + if (!as->mixed) { + hdaa_unlock(devinfo); + return (mix_getrecdevs(m)); + } + + /* Commutate requested recsrc for each ADC. */ + for (j = 0; j < as->num_chans; j++) { + ch = &devinfo->chans[as->chans[j]]; + for (i = 0; ch->io[i] >= 0; i++) { + w = hdaa_widget_get(devinfo, ch->io[i]); + if (w == NULL || w->enable == 0) + continue; + ret &= hdaa_audio_ctl_recsel_comm(pdevinfo, src, + ch->io[i], 0); + } + } + + hdaa_unlock(devinfo); + return ((ret == 0xffffffff)? 0 : ret); +} + +static kobj_method_t hdaa_audio_ctl_ossmixer_methods[] = { + KOBJMETHOD(mixer_init, hdaa_audio_ctl_ossmixer_init), + KOBJMETHOD(mixer_set, hdaa_audio_ctl_ossmixer_set), + KOBJMETHOD(mixer_setrecsrc, hdaa_audio_ctl_ossmixer_setrecsrc), + KOBJMETHOD_END +}; +MIXER_DECLARE(hdaa_audio_ctl_ossmixer); + +static void +hdaa_dump_gpi(struct hdaa_devinfo *devinfo) +{ + device_t dev = devinfo->dev; + int i; + uint32_t data, wake, unsol, sticky; + + if (HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap) > 0) { + data = hda_command(dev, + HDA_CMD_GET_GPI_DATA(0, devinfo->nid)); + wake = hda_command(dev, + HDA_CMD_GET_GPI_WAKE_ENABLE_MASK(0, devinfo->nid)); + unsol = hda_command(dev, + HDA_CMD_GET_GPI_UNSOLICITED_ENABLE_MASK(0, devinfo->nid)); + sticky = hda_command(dev, + HDA_CMD_GET_GPI_STICKY_MASK(0, devinfo->nid)); + for (i = 0; i < HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap); i++) { + device_printf(dev, " GPI%d:%s%s%s state=%d", i, + (sticky & (1 << i)) ? " sticky" : "", + (unsol & (1 << i)) ? " unsol" : "", + (wake & (1 << i)) ? " wake" : "", + (data >> i) & 1); + } + } +} + +static void +hdaa_dump_gpio(struct hdaa_devinfo *devinfo) +{ + device_t dev = devinfo->dev; + int i; + uint32_t data, dir, enable, wake, unsol, sticky; + + if (HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap) > 0) { + data = hda_command(dev, + HDA_CMD_GET_GPIO_DATA(0, devinfo->nid)); + enable = hda_command(dev, + HDA_CMD_GET_GPIO_ENABLE_MASK(0, devinfo->nid)); + dir = hda_command(dev, + HDA_CMD_GET_GPIO_DIRECTION(0, devinfo->nid)); + wake = hda_command(dev, + HDA_CMD_GET_GPIO_WAKE_ENABLE_MASK(0, devinfo->nid)); + unsol = hda_command(dev, + HDA_CMD_GET_GPIO_UNSOLICITED_ENABLE_MASK(0, devinfo->nid)); + sticky = hda_command(dev, + HDA_CMD_GET_GPIO_STICKY_MASK(0, devinfo->nid)); + for (i = 0; i < HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap); i++) { + device_printf(dev, " GPIO%d: ", i); + if ((enable & (1 << i)) == 0) { + printf("disabled\n"); + continue; + } + if ((dir & (1 << i)) == 0) { + printf("input%s%s%s", + (sticky & (1 << i)) ? " sticky" : "", + (unsol & (1 << i)) ? " unsol" : "", + (wake & (1 << i)) ? " wake" : ""); + } else + printf("output"); + printf(" state=%d\n", (data >> i) & 1); + } + } +} + +static void +hdaa_dump_gpo(struct hdaa_devinfo *devinfo) +{ + device_t dev = devinfo->dev; + int i; + uint32_t data; + + if (HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap) > 0) { + data = hda_command(dev, + HDA_CMD_GET_GPO_DATA(0, devinfo->nid)); + for (i = 0; i < HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap); i++) { + device_printf(dev, " GPO%d: state=%d", i, + (data >> i) & 1); + } + } +} + +static void +hdaa_audio_parse(struct hdaa_devinfo *devinfo) +{ + struct hdaa_widget *w; + uint32_t res; + int i; + nid_t nid; + + nid = devinfo->nid; + + res = hda_command(devinfo->dev, + HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_GPIO_COUNT)); + devinfo->gpio_cap = res; + + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "NumGPIO=%d NumGPO=%d " + "NumGPI=%d GPIWake=%d GPIUnsol=%d\n", + HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap), + HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap), + HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap), + HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->gpio_cap), + HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->gpio_cap)); + hdaa_dump_gpi(devinfo); + hdaa_dump_gpio(devinfo); + hdaa_dump_gpo(devinfo); + ); + + res = hda_command(devinfo->dev, + HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_SUPP_STREAM_FORMATS)); + devinfo->supp_stream_formats = res; + + res = hda_command(devinfo->dev, + HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_SUPP_PCM_SIZE_RATE)); + devinfo->supp_pcm_size_rate = res; + + res = hda_command(devinfo->dev, + HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_OUTPUT_AMP_CAP)); + devinfo->outamp_cap = res; + + res = hda_command(devinfo->dev, + HDA_CMD_GET_PARAMETER(0, nid, HDA_PARAM_INPUT_AMP_CAP)); + devinfo->inamp_cap = res; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL) + device_printf(devinfo->dev, "Ghost widget! nid=%d!\n", i); + else { + w->devinfo = devinfo; + w->nid = i; + w->enable = 1; + w->selconn = -1; + w->pflags = 0; + w->ossdev = -1; + w->bindas = -1; + w->param.eapdbtl = HDA_INVALID; + hdaa_widget_parse(w); + } + } +} + +static void +hdaa_audio_postprocess(struct hdaa_devinfo *devinfo) +{ + struct hdaa_widget *w; + int i; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL) + continue; + hdaa_widget_postprocess(w); + } +} + +static void +hdaa_audio_ctl_parse(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_ctl *ctls; + struct hdaa_widget *w, *cw; + int i, j, cnt, max, ocap, icap; + int mute, offset, step, size; + + /* XXX This is redundant */ + max = 0; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->param.outamp_cap != 0) + max++; + if (w->param.inamp_cap != 0) { + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + for (j = 0; j < w->nconns; j++) { + cw = hdaa_widget_get(devinfo, + w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + max++; + } + break; + default: + max++; + break; + } + } + } + devinfo->ctlcnt = max; + + if (max < 1) + return; + + ctls = (struct hdaa_audio_ctl *)malloc( + sizeof(*ctls) * max, M_HDAA, M_ZERO | M_NOWAIT); + + if (ctls == NULL) { + /* Blekh! */ + device_printf(devinfo->dev, "unable to allocate ctls!\n"); + devinfo->ctlcnt = 0; + return; + } + + cnt = 0; + for (i = devinfo->startnode; cnt < max && i < devinfo->endnode; i++) { + if (cnt >= max) { + device_printf(devinfo->dev, "%s: Ctl overflow!\n", + __func__); + break; + } + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + ocap = w->param.outamp_cap; + icap = w->param.inamp_cap; + if (ocap != 0) { + mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(ocap); + step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(ocap); + size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(ocap); + offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(ocap); + /*if (offset > step) { + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "BUGGY outamp: nid=%d " + "[offset=%d > step=%d]\n", + w->nid, offset, step); + ); + offset = step; + }*/ + ctls[cnt].enable = 1; + ctls[cnt].widget = w; + ctls[cnt].mute = mute; + ctls[cnt].step = step; + ctls[cnt].size = size; + ctls[cnt].offset = offset; + ctls[cnt].left = offset; + ctls[cnt].right = offset; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + w->waspin) + ctls[cnt].ndir = HDAA_CTL_IN; + else + ctls[cnt].ndir = HDAA_CTL_OUT; + ctls[cnt++].dir = HDAA_CTL_OUT; + } + + if (icap != 0) { + mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(icap); + step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(icap); + size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(icap); + offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(icap); + /*if (offset > step) { + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "BUGGY inamp: nid=%d " + "[offset=%d > step=%d]\n", + w->nid, offset, step); + ); + offset = step; + }*/ + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + for (j = 0; j < w->nconns; j++) { + if (cnt >= max) { + device_printf(devinfo->dev, + "%s: Ctl overflow!\n", + __func__); + break; + } + cw = hdaa_widget_get(devinfo, + w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + ctls[cnt].enable = 1; + ctls[cnt].widget = w; + ctls[cnt].childwidget = cw; + ctls[cnt].index = j; + ctls[cnt].mute = mute; + ctls[cnt].step = step; + ctls[cnt].size = size; + ctls[cnt].offset = offset; + ctls[cnt].left = offset; + ctls[cnt].right = offset; + ctls[cnt].ndir = HDAA_CTL_IN; + ctls[cnt++].dir = HDAA_CTL_IN; + } + break; + default: + if (cnt >= max) { + device_printf(devinfo->dev, + "%s: Ctl overflow!\n", + __func__); + break; + } + ctls[cnt].enable = 1; + ctls[cnt].widget = w; + ctls[cnt].mute = mute; + ctls[cnt].step = step; + ctls[cnt].size = size; + ctls[cnt].offset = offset; + ctls[cnt].left = offset; + ctls[cnt].right = offset; + if (w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + ctls[cnt].ndir = HDAA_CTL_OUT; + else + ctls[cnt].ndir = HDAA_CTL_IN; + ctls[cnt++].dir = HDAA_CTL_IN; + break; + } + } + } + + devinfo->ctl = ctls; +} + +static void +hdaa_audio_as_parse(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as; + struct hdaa_widget *w; + int i, j, cnt, max, type, dir, assoc, seq, first, hpredir; + + /* Count present associations */ + max = 0; + for (j = 1; j < 16; j++) { + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (HDA_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config) + != j) + continue; + max++; + if (j != 15) /* There could be many 1-pin assocs #15 */ + break; + } + } + + devinfo->ascnt = max; + + if (max < 1) + return; + + as = (struct hdaa_audio_as *)malloc( + sizeof(*as) * max, M_HDAA, M_ZERO | M_NOWAIT); + + if (as == NULL) { + /* Blekh! */ + device_printf(devinfo->dev, "unable to allocate assocs!\n"); + devinfo->ascnt = 0; + return; + } + + for (i = 0; i < max; i++) { + as[i].hpredir = -1; + as[i].digital = 0; + as[i].num_chans = 1; + as[i].unsol = -1; + as[i].location = -1; + } + + /* Scan associations skipping as=0. */ + cnt = 0; + for (j = 1; j < 16; j++) { + first = 16; + hpredir = 0; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + assoc = HDA_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config); + seq = HDA_CONFIG_DEFAULTCONF_SEQUENCE(w->wclass.pin.config); + if (assoc != j) { + continue; + } + KASSERT(cnt < max, + ("%s: Associations owerflow (%d of %d)", + __func__, cnt, max)); + type = w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + /* Get pin direction. */ + if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT || + type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER || + type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT || + type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT || + type == HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT) + dir = HDAA_CTL_OUT; + else + dir = HDAA_CTL_IN; + /* If this is a first pin - create new association. */ + if (as[cnt].pincnt == 0) { + as[cnt].enable = 1; + as[cnt].index = j; + as[cnt].dir = dir; + } + if (seq < first) + first = seq; + /* Check association correctness. */ + if (as[cnt].pins[seq] != 0) { + device_printf(devinfo->dev, "%s: Duplicate pin %d (%d) " + "in association %d! Disabling association.\n", + __func__, seq, w->nid, j); + as[cnt].enable = 0; + } + if (dir != as[cnt].dir) { + device_printf(devinfo->dev, "%s: Pin %d has wrong " + "direction for association %d! Disabling " + "association.\n", + __func__, w->nid, j); + as[cnt].enable = 0; + } + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { + if (HDA_PARAM_PIN_CAP_DP(w->wclass.pin.cap)) + as[cnt].digital = 3; + else if (HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap)) + as[cnt].digital = 2; + else + as[cnt].digital = 1; + } + if (as[cnt].location == -1) { + as[cnt].location = + HDA_CONFIG_DEFAULTCONF_LOCATION(w->wclass.pin.config); + } else if (as[cnt].location != + HDA_CONFIG_DEFAULTCONF_LOCATION(w->wclass.pin.config)) { + as[cnt].location = -2; + } + /* Headphones with seq=15 may mean redirection. */ + if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT && + seq == 15) + hpredir = 1; + as[cnt].pins[seq] = w->nid; + as[cnt].pincnt++; + /* Association 15 is a multiple unassociated pins. */ + if (j == 15) + cnt++; + } + if (j != 15 && as[cnt].pincnt > 0) { + if (hpredir && as[cnt].pincnt > 1) + as[cnt].hpredir = first; + cnt++; + } + } + for (i = 0; i < max; i++) { + if (as[i].dir == HDAA_CTL_IN && (as[i].pincnt == 1 || + as[i].pins[14] > 0 || as[i].pins[15] > 0)) + as[i].mixed = 1; + } + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "%d associations found:\n", max); + for (i = 0; i < max; i++) { + device_printf(devinfo->dev, + "Association %d (%d) %s%s:\n", + i, as[i].index, (as[i].dir == HDAA_CTL_IN)?"in":"out", + as[i].enable?"":" (disabled)"); + for (j = 0; j < 16; j++) { + if (as[i].pins[j] == 0) + continue; + device_printf(devinfo->dev, + " Pin nid=%d seq=%d\n", + as[i].pins[j], j); + } + } + ); + + devinfo->as = as; +} + +/* + * Trace path from DAC to pin. + */ +static nid_t +hdaa_audio_trace_dac(struct hdaa_devinfo *devinfo, int as, int seq, nid_t nid, + int dupseq, int min, int only, int depth) +{ + struct hdaa_widget *w; + int i, im = -1; + nid_t m = 0, ret; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + w = hdaa_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + HDA_BOOTHVERBOSE( + if (!only) { + device_printf(devinfo->dev, + " %*stracing via nid %d\n", + depth + 1, "", w->nid); + } + ); + /* Use only unused widgets */ + if (w->bindas >= 0 && w->bindas != as) { + HDA_BOOTHVERBOSE( + if (!only) { + device_printf(devinfo->dev, + " %*snid %d busy by association %d\n", + depth + 1, "", w->nid, w->bindas); + } + ); + return (0); + } + if (dupseq < 0) { + if (w->bindseqmask != 0) { + HDA_BOOTHVERBOSE( + if (!only) { + device_printf(devinfo->dev, + " %*snid %d busy by seqmask %x\n", + depth + 1, "", w->nid, w->bindseqmask); + } + ); + return (0); + } + } else { + /* If this is headphones - allow duplicate first pin. */ + if (w->bindseqmask != 0 && + (w->bindseqmask & (1 << dupseq)) == 0) { + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*snid %d busy by seqmask %x\n", + depth + 1, "", w->nid, w->bindseqmask); + ); + return (0); + } + } + + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: + /* Do not traverse input. AD1988 has digital monitor + for which we are not ready. */ + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: + /* If we are tracing HP take only dac of first pin. */ + if ((only == 0 || only == w->nid) && + (w->nid >= min) && (dupseq < 0 || w->nid == + devinfo->as[as].dacs[0][dupseq])) + m = w->nid; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + if (depth > 0) + break; + /* Fall */ + default: + /* Find reachable DACs with smallest nid respecting constraints. */ + for (i = 0; i < w->nconns; i++) { + if (w->connsenable[i] == 0) + continue; + if (w->selconn != -1 && w->selconn != i) + continue; + if ((ret = hdaa_audio_trace_dac(devinfo, as, seq, + w->conns[i], dupseq, min, only, depth + 1)) != 0) { + if (m == 0 || ret < m) { + m = ret; + im = i; + } + if (only || dupseq >= 0) + break; + } + } + if (im >= 0 && only && ((w->nconns > 1 && + w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) + w->selconn = im; + break; + } + if (m && only) { + w->bindas = as; + w->bindseqmask |= (1 << seq); + } + HDA_BOOTHVERBOSE( + if (!only) { + device_printf(devinfo->dev, + " %*snid %d returned %d\n", + depth + 1, "", w->nid, m); + } + ); + return (m); +} + +/* + * Trace path from widget to ADC. + */ +static nid_t +hdaa_audio_trace_adc(struct hdaa_devinfo *devinfo, int as, int seq, nid_t nid, + int mixed, int min, int only, int depth, int *length, int onlylength) +{ + struct hdaa_widget *w, *wc; + int i, j, im, lm = HDA_PARSE_MAXDEPTH; + nid_t m = 0, ret; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + w = hdaa_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*stracing via nid %d\n", + depth + 1, "", w->nid); + ); + /* Use only unused widgets */ + if (w->bindas >= 0 && w->bindas != as) { + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*snid %d busy by association %d\n", + depth + 1, "", w->nid, w->bindas); + ); + return (0); + } + if (!mixed && w->bindseqmask != 0) { + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*snid %d busy by seqmask %x\n", + depth + 1, "", w->nid, w->bindseqmask); + ); + return (0); + } + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: + if ((only == 0 || only == w->nid) && (w->nid >= min) && + (onlylength == 0 || onlylength == depth)) { + m = w->nid; + if (length != NULL) + *length = depth; + } + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + if (depth > 0) + break; + /* Fall */ + default: + /* Try to find reachable ADCs with specified nid. */ + for (j = devinfo->startnode; j < devinfo->endnode; j++) { + wc = hdaa_widget_get(devinfo, j); + if (wc == NULL || wc->enable == 0) + continue; + im = -1; + for (i = 0; i < wc->nconns; i++) { + if (wc->connsenable[i] == 0) + continue; + if (wc->conns[i] != nid) + continue; + if ((ret = hdaa_audio_trace_adc(devinfo, as, seq, + j, mixed, min, only, depth + 1, + length, onlylength)) != 0) { + if (m == 0 || ret < m || + (ret == m && length != NULL && + *length < lm)) { + m = ret; + im = i; + lm = *length; + } + if (only) + break; + } + } + if (im >= 0 && only && ((wc->nconns > 1 && + wc->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || + wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) + wc->selconn = im; + } + break; + } + if (m && only) { + w->bindas = as; + w->bindseqmask |= (1 << seq); + } + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*snid %d returned %d\n", + depth + 1, "", w->nid, m); + ); + return (m); +} + +/* + * Erase trace path of the specified association. + */ +static void +hdaa_audio_undo_trace(struct hdaa_devinfo *devinfo, int as, int seq) +{ + struct hdaa_widget *w; + int i; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->bindas == as) { + if (seq >= 0) { + w->bindseqmask &= ~(1 << seq); + if (w->bindseqmask == 0) { + w->bindas = -1; + w->selconn = -1; + } + } else { + w->bindas = -1; + w->bindseqmask = 0; + w->selconn = -1; + } + } + } +} + +/* + * Trace association path from DAC to output + */ +static int +hdaa_audio_trace_as_out(struct hdaa_devinfo *devinfo, int as, int seq) +{ + struct hdaa_audio_as *ases = devinfo->as; + int i, hpredir; + nid_t min, res; + + /* Find next pin */ + for (i = seq; i < 16 && ases[as].pins[i] == 0; i++) + ; + /* Check if there is no any left. If so - we succeeded. */ + if (i == 16) + return (1); + + hpredir = (i == 15 && ases[as].fakeredir == 0)?ases[as].hpredir:-1; + min = 0; + do { + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Tracing pin %d with min nid %d", + ases[as].pins[i], min); + if (hpredir >= 0) + printf(" and hpredir %d", hpredir); + printf("\n"); + ); + /* Trace this pin taking min nid into account. */ + res = hdaa_audio_trace_dac(devinfo, as, i, + ases[as].pins[i], hpredir, min, 0, 0); + if (res == 0) { + /* If we failed - return to previous and redo it. */ + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " Unable to trace pin %d seq %d with min " + "nid %d", + ases[as].pins[i], i, min); + if (hpredir >= 0) + printf(" and hpredir %d", hpredir); + printf("\n"); + ); + return (0); + } + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " Pin %d traced to DAC %d", + ases[as].pins[i], res); + if (hpredir >= 0) + printf(" and hpredir %d", hpredir); + if (ases[as].fakeredir) + printf(" with fake redirection"); + printf("\n"); + ); + /* Trace again to mark the path */ + hdaa_audio_trace_dac(devinfo, as, i, + ases[as].pins[i], hpredir, min, res, 0); + ases[as].dacs[0][i] = res; + /* We succeeded, so call next. */ + if (hdaa_audio_trace_as_out(devinfo, as, i + 1)) + return (1); + /* If next failed, we should retry with next min */ + hdaa_audio_undo_trace(devinfo, as, i); + ases[as].dacs[0][i] = 0; + min = res + 1; + } while (1); +} + +/* + * Check equivalency of two DACs. + */ +static int +hdaa_audio_dacs_equal(struct hdaa_widget *w1, struct hdaa_widget *w2) +{ + struct hdaa_devinfo *devinfo = w1->devinfo; + struct hdaa_widget *w3; + int i, j, c1, c2; + + if (memcmp(&w1->param, &w2->param, sizeof(w1->param))) + return (0); + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w3 = hdaa_widget_get(devinfo, i); + if (w3 == NULL || w3->enable == 0) + continue; + if (w3->bindas != w1->bindas) + continue; + if (w3->nconns == 0) + continue; + c1 = c2 = -1; + for (j = 0; j < w3->nconns; j++) { + if (w3->connsenable[j] == 0) + continue; + if (w3->conns[j] == w1->nid) + c1 = j; + if (w3->conns[j] == w2->nid) + c2 = j; + } + if (c1 < 0) + continue; + if (c2 < 0) + return (0); + if (w3->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + return (0); + } + return (1); +} + +/* + * Check equivalency of two ADCs. + */ +static int +hdaa_audio_adcs_equal(struct hdaa_widget *w1, struct hdaa_widget *w2) +{ + struct hdaa_devinfo *devinfo = w1->devinfo; + struct hdaa_widget *w3, *w4; + int i; + + if (memcmp(&w1->param, &w2->param, sizeof(w1->param))) + return (0); + if (w1->nconns != 1 || w2->nconns != 1) + return (0); + if (w1->conns[0] == w2->conns[0]) + return (1); + w3 = hdaa_widget_get(devinfo, w1->conns[0]); + if (w3 == NULL || w3->enable == 0) + return (0); + w4 = hdaa_widget_get(devinfo, w2->conns[0]); + if (w4 == NULL || w4->enable == 0) + return (0); + if (w3->bindas == w4->bindas && w3->bindseqmask == w4->bindseqmask) + return (1); + if (w4->bindas >= 0) + return (0); + if (w3->type != w4->type) + return (0); + if (memcmp(&w3->param, &w4->param, sizeof(w3->param))) + return (0); + if (w3->nconns != w4->nconns) + return (0); + for (i = 0; i < w3->nconns; i++) { + if (w3->conns[i] != w4->conns[i]) + return (0); + } + return (1); +} + +/* + * Look for equivalent DAC/ADC to implement second channel. + */ +static void +hdaa_audio_adddac(struct hdaa_devinfo *devinfo, int asid) +{ + struct hdaa_audio_as *as = &devinfo->as[asid]; + struct hdaa_widget *w1, *w2; + int i, pos; + nid_t nid1, nid2; + + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Looking for additional %sC " + "for association %d (%d)\n", + (as->dir == HDAA_CTL_OUT) ? "DA" : "AD", + asid, as->index); + ); + + /* Find the exisitng DAC position and return if found more the one. */ + pos = -1; + for (i = 0; i < 16; i++) { + if (as->dacs[0][i] <= 0) + continue; + if (pos >= 0 && as->dacs[0][i] != as->dacs[0][pos]) + return; + pos = i; + } + + nid1 = as->dacs[0][pos]; + w1 = hdaa_widget_get(devinfo, nid1); + w2 = NULL; + for (nid2 = devinfo->startnode; nid2 < devinfo->endnode; nid2++) { + w2 = hdaa_widget_get(devinfo, nid2); + if (w2 == NULL || w2->enable == 0) + continue; + if (w2->bindas >= 0) + continue; + if (w1->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) { + if (w2->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) + continue; + if (hdaa_audio_dacs_equal(w1, w2)) + break; + } else { + if (w2->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) + continue; + if (hdaa_audio_adcs_equal(w1, w2)) + break; + } + } + if (nid2 >= devinfo->endnode) + return; + w2->bindas = w1->bindas; + w2->bindseqmask = w1->bindseqmask; + if (w1->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " ADC %d considered equal to ADC %d\n", nid2, nid1); + ); + w1 = hdaa_widget_get(devinfo, w1->conns[0]); + w2 = hdaa_widget_get(devinfo, w2->conns[0]); + w2->bindas = w1->bindas; + w2->bindseqmask = w1->bindseqmask; + } else { + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " DAC %d considered equal to DAC %d\n", nid2, nid1); + ); + } + for (i = 0; i < 16; i++) { + if (as->dacs[0][i] <= 0) + continue; + as->dacs[as->num_chans][i] = nid2; + } + as->num_chans++; +} + +/* + * Trace association path from input to ADC + */ +static int +hdaa_audio_trace_as_in(struct hdaa_devinfo *devinfo, int as) +{ + struct hdaa_audio_as *ases = devinfo->as; + struct hdaa_widget *w; + int i, j, k, length; + + for (j = devinfo->startnode; j < devinfo->endnode; j++) { + w = hdaa_widget_get(devinfo, j); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) + continue; + if (w->bindas >= 0 && w->bindas != as) + continue; + + /* Find next pin */ + for (i = 0; i < 16; i++) { + if (ases[as].pins[i] == 0) + continue; + + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Tracing pin %d to ADC %d\n", + ases[as].pins[i], j); + ); + /* Trace this pin taking goal into account. */ + if (hdaa_audio_trace_adc(devinfo, as, i, + ases[as].pins[i], 1, 0, j, 0, &length, 0) == 0) { + /* If we failed - return to previous and redo it. */ + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " Unable to trace pin %d to ADC %d, undo traces\n", + ases[as].pins[i], j); + ); + hdaa_audio_undo_trace(devinfo, as, -1); + for (k = 0; k < 16; k++) + ases[as].dacs[0][k] = 0; + break; + } + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " Pin %d traced to ADC %d\n", + ases[as].pins[i], j); + ); + ases[as].dacs[0][i] = j; + } + if (i == 16) + return (1); + } + return (0); +} + +/* + * Trace association path from input to multiple ADCs + */ +static int +hdaa_audio_trace_as_in_mch(struct hdaa_devinfo *devinfo, int as, int seq) +{ + struct hdaa_audio_as *ases = devinfo->as; + int i, length; + nid_t min, res; + + /* Find next pin */ + for (i = seq; i < 16 && ases[as].pins[i] == 0; i++) + ; + /* Check if there is no any left. If so - we succeeded. */ + if (i == 16) + return (1); + + min = 0; + do { + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Tracing pin %d with min nid %d", + ases[as].pins[i], min); + printf("\n"); + ); + /* Trace this pin taking min nid into account. */ + res = hdaa_audio_trace_adc(devinfo, as, i, + ases[as].pins[i], 0, min, 0, 0, &length, 0); + if (res == 0) { + /* If we failed - return to previous and redo it. */ + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " Unable to trace pin %d seq %d with min " + "nid %d", + ases[as].pins[i], i, min); + printf("\n"); + ); + return (0); + } + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " Pin %d traced to ADC %d\n", + ases[as].pins[i], res); + ); + /* Trace again to mark the path */ + hdaa_audio_trace_adc(devinfo, as, i, + ases[as].pins[i], 0, min, res, 0, &length, length); + ases[as].dacs[0][i] = res; + /* We succeeded, so call next. */ + if (hdaa_audio_trace_as_in_mch(devinfo, as, i + 1)) + return (1); + /* If next failed, we should retry with next min */ + hdaa_audio_undo_trace(devinfo, as, i); + ases[as].dacs[0][i] = 0; + min = res + 1; + } while (1); +} + +/* + * Trace input monitor path from mixer to output association. + */ +static int +hdaa_audio_trace_to_out(struct hdaa_devinfo *devinfo, nid_t nid, int depth) +{ + struct hdaa_audio_as *ases = devinfo->as; + struct hdaa_widget *w, *wc; + int i, j; + nid_t res = 0; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + w = hdaa_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*stracing via nid %d\n", + depth + 1, "", w->nid); + ); + /* Use only unused widgets */ + if (depth > 0 && w->bindas != -1) { + if (w->bindas < 0 || ases[w->bindas].dir == HDAA_CTL_OUT) { + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*snid %d found output association %d\n", + depth + 1, "", w->nid, w->bindas); + ); + if (w->bindas >= 0) + w->pflags |= HDAA_ADC_MONITOR; + return (1); + } else { + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*snid %d busy by input association %d\n", + depth + 1, "", w->nid, w->bindas); + ); + return (0); + } + } + + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: + /* Do not traverse input. AD1988 has digital monitor + for which we are not ready. */ + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + if (depth > 0) + break; + /* Fall */ + default: + /* Try to find reachable ADCs with specified nid. */ + for (j = devinfo->startnode; j < devinfo->endnode; j++) { + wc = hdaa_widget_get(devinfo, j); + if (wc == NULL || wc->enable == 0) + continue; + for (i = 0; i < wc->nconns; i++) { + if (wc->connsenable[i] == 0) + continue; + if (wc->conns[i] != nid) + continue; + if (hdaa_audio_trace_to_out(devinfo, + j, depth + 1) != 0) { + res = 1; + if (wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + wc->selconn == -1) + wc->selconn = i; + } + } + } + break; + } + if (res && w->bindas == -1) + w->bindas = -2; + + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " %*snid %d returned %d\n", + depth + 1, "", w->nid, res); + ); + return (res); +} + +/* + * Trace extra associations (beeper, monitor) + */ +static void +hdaa_audio_trace_as_extra(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w; + int j; + + /* Input monitor */ + /* Find mixer associated with input, but supplying signal + for output associations. Hope it will be input monitor. */ + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Tracing input monitor\n"); + ); + for (j = devinfo->startnode; j < devinfo->endnode; j++) { + w = hdaa_widget_get(devinfo, j); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + continue; + if (w->bindas < 0 || as[w->bindas].dir != HDAA_CTL_IN) + continue; + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " Tracing nid %d to out\n", + j); + ); + if (hdaa_audio_trace_to_out(devinfo, w->nid, 0)) { + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " nid %d is input monitor\n", + w->nid); + ); + w->ossdev = SOUND_MIXER_IMIX; + } + } + + /* Other inputs monitor */ + /* Find input pins supplying signal for output associations. + Hope it will be input monitoring. */ + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Tracing other input monitors\n"); + ); + for (j = devinfo->startnode; j < devinfo->endnode; j++) { + w = hdaa_widget_get(devinfo, j); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (w->bindas < 0 || as[w->bindas].dir != HDAA_CTL_IN) + continue; + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " Tracing nid %d to out\n", + j); + ); + if (hdaa_audio_trace_to_out(devinfo, w->nid, 0)) { + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " nid %d is input monitor\n", + w->nid); + ); + } + } + + /* Beeper */ + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Tracing beeper\n"); + ); + for (j = devinfo->startnode; j < devinfo->endnode; j++) { + w = hdaa_widget_get(devinfo, j); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET) + continue; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Tracing nid %d to out\n", + j); + ); + if (hdaa_audio_trace_to_out(devinfo, w->nid, 0)) { + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + " nid %d traced to out\n", + j); + ); + } + w->bindas = -2; + } +} + +/* + * Bind assotiations to PCM channels + */ +static void +hdaa_audio_bind_as(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + int i, j, cnt = 0, free; + + for (j = 0; j < devinfo->ascnt; j++) { + if (as[j].enable) + cnt += as[j].num_chans; + } + if (devinfo->num_chans == 0) { + devinfo->chans = (struct hdaa_chan *)malloc( + sizeof(struct hdaa_chan) * cnt, + M_HDAA, M_ZERO | M_NOWAIT); + if (devinfo->chans == NULL) { + device_printf(devinfo->dev, + "Channels memory allocation failed!\n"); + return; + } + } else { + devinfo->chans = (struct hdaa_chan *)realloc(devinfo->chans, + sizeof(struct hdaa_chan) * (devinfo->num_chans + cnt), + M_HDAA, M_ZERO | M_NOWAIT); + if (devinfo->chans == NULL) { + devinfo->num_chans = 0; + device_printf(devinfo->dev, + "Channels memory allocation failed!\n"); + return; + } + /* Fixup relative pointers after realloc */ + for (j = 0; j < devinfo->num_chans; j++) + devinfo->chans[j].caps.fmtlist = devinfo->chans[j].fmtlist; + } + free = devinfo->num_chans; + devinfo->num_chans += cnt; + + for (j = free; j < free + cnt; j++) { + devinfo->chans[j].devinfo = devinfo; + devinfo->chans[j].as = -1; + } + + /* Assign associations in order of their numbers, */ + for (j = 0; j < devinfo->ascnt; j++) { + if (as[j].enable == 0) + continue; + for (i = 0; i < as[j].num_chans; i++) { + devinfo->chans[free].as = j; + devinfo->chans[free].asindex = i; + devinfo->chans[free].dir = + (as[j].dir == HDAA_CTL_IN) ? PCMDIR_REC : PCMDIR_PLAY; + hdaa_pcmchannel_setup(&devinfo->chans[free]); + as[j].chans[i] = free; + free++; + } + } +} + +static void +hdaa_audio_disable_nonaudio(struct hdaa_devinfo *devinfo) +{ + struct hdaa_widget *w; + int i; + + /* Disable power and volume widgets. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET) { + w->enable = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling nid %d due to it's" + " non-audio type.\n", + w->nid); + ); + } + } +} + +static void +hdaa_audio_disable_useless(struct hdaa_devinfo *devinfo) +{ + struct hdaa_widget *w, *cw; + struct hdaa_audio_ctl *ctl; + int done, found, i, j, k; + + /* Disable useless pins. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + if ((w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) { + w->enable = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling pin nid %d due" + " to None connectivity.\n", + w->nid); + ); + } else if ((w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK) == 0) { + w->enable = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling unassociated" + " pin nid %d.\n", + w->nid); + ); + } + } + } + do { + done = 1; + /* Disable and mute controls for disabled widgets. */ + i = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0) + continue; + if (ctl->widget->enable == 0 || + (ctl->childwidget != NULL && + ctl->childwidget->enable == 0)) { + ctl->forcemute = 1; + ctl->muted = HDAA_AMP_MUTE_ALL; + ctl->left = 0; + ctl->right = 0; + ctl->enable = 0; + if (ctl->ndir == HDAA_CTL_IN) + ctl->widget->connsenable[ctl->index] = 0; + done = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling ctl %d nid %d cnid %d due" + " to disabled widget.\n", i, + ctl->widget->nid, + (ctl->childwidget != NULL)? + ctl->childwidget->nid:-1); + ); + } + } + /* Disable useless widgets. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + /* Disable inputs with disabled child widgets. */ + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j]) { + cw = hdaa_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) { + w->connsenable[j] = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling nid %d connection %d due" + " to disabled child widget.\n", + i, j); + ); + } + } + } + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + continue; + /* Disable mixers and selectors without inputs. */ + found = 0; + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j]) { + found = 1; + break; + } + } + if (found == 0) { + w->enable = 0; + done = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling nid %d due to all it's" + " inputs disabled.\n", w->nid); + ); + } + /* Disable nodes without consumers. */ + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + continue; + found = 0; + for (k = devinfo->startnode; k < devinfo->endnode; k++) { + cw = hdaa_widget_get(devinfo, k); + if (cw == NULL || cw->enable == 0) + continue; + for (j = 0; j < cw->nconns; j++) { + if (cw->connsenable[j] && cw->conns[j] == i) { + found = 1; + break; + } + } + } + if (found == 0) { + w->enable = 0; + done = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling nid %d due to all it's" + " consumers disabled.\n", w->nid); + ); + } + } + } while (done == 0); + +} + +static void +hdaa_audio_disable_unas(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w, *cw; + struct hdaa_audio_ctl *ctl; + int i, j, k; + + /* Disable unassosiated widgets. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->bindas == -1) { + w->enable = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling unassociated nid %d.\n", + w->nid); + ); + } + } + /* Disable input connections on input pin and + * output on output. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (w->bindas < 0) + continue; + if (as[w->bindas].dir == HDAA_CTL_IN) { + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j] == 0) + continue; + w->connsenable[j] = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling connection to input pin " + "nid %d conn %d.\n", + i, j); + ); + } + ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, + HDAA_CTL_IN, -1, 1); + if (ctl && ctl->enable) { + ctl->forcemute = 1; + ctl->muted = HDAA_AMP_MUTE_ALL; + ctl->left = 0; + ctl->right = 0; + ctl->enable = 0; + } + } else { + ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, + HDAA_CTL_OUT, -1, 1); + if (ctl && ctl->enable) { + ctl->forcemute = 1; + ctl->muted = HDAA_AMP_MUTE_ALL; + ctl->left = 0; + ctl->right = 0; + ctl->enable = 0; + } + for (k = devinfo->startnode; k < devinfo->endnode; k++) { + cw = hdaa_widget_get(devinfo, k); + if (cw == NULL || cw->enable == 0) + continue; + for (j = 0; j < cw->nconns; j++) { + if (cw->connsenable[j] && cw->conns[j] == i) { + cw->connsenable[j] = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling connection from output pin " + "nid %d conn %d cnid %d.\n", + k, j, i); + ); + if (cw->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + cw->nconns > 1) + continue; + ctl = hdaa_audio_ctl_amp_get(devinfo, k, + HDAA_CTL_IN, j, 1); + if (ctl && ctl->enable) { + ctl->forcemute = 1; + ctl->muted = HDAA_AMP_MUTE_ALL; + ctl->left = 0; + ctl->right = 0; + ctl->enable = 0; + } + } + } + } + } + } +} + +static void +hdaa_audio_disable_notselected(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w; + int i, j; + + /* On playback path we can safely disable all unseleted inputs. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->nconns <= 1) + continue; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + continue; + if (w->bindas < 0 || as[w->bindas].dir == HDAA_CTL_IN) + continue; + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j] == 0) + continue; + if (w->selconn < 0 || w->selconn == j) + continue; + w->connsenable[j] = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling unselected connection " + "nid %d conn %d.\n", + i, j); + ); + } + } +} + +static void +hdaa_audio_disable_crossas(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *ases = devinfo->as; + struct hdaa_widget *w, *cw; + struct hdaa_audio_ctl *ctl; + int i, j; + + /* Disable crossassociatement and unwanted crosschannel connections. */ + /* ... using selectors */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->nconns <= 1) + continue; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + continue; + /* Allow any -> mix */ + if (w->bindas == -2) + continue; + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j] == 0) + continue; + cw = hdaa_widget_get(devinfo, w->conns[j]); + if (cw == NULL || w->enable == 0) + continue; + /* Allow mix -> out. */ + if (cw->bindas == -2 && w->bindas >= 0 && + ases[w->bindas].dir == HDAA_CTL_OUT) + continue; + /* Allow mix -> mixed-in. */ + if (cw->bindas == -2 && w->bindas >= 0 && + ases[w->bindas].mixed) + continue; + /* Allow in -> mix. */ + if ((w->pflags & HDAA_ADC_MONITOR) && + cw->bindas >= 0 && + ases[cw->bindas].dir == HDAA_CTL_IN) + continue; + /* Allow if have common as/seqs. */ + if (w->bindas == cw->bindas && + (w->bindseqmask & cw->bindseqmask) != 0) + continue; + w->connsenable[j] = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling crossassociatement connection " + "nid %d conn %d cnid %d.\n", + i, j, cw->nid); + ); + } + } + /* ... using controls */ + i = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->childwidget == NULL) + continue; + /* Allow any -> mix */ + if (ctl->widget->bindas == -2) + continue; + /* Allow mix -> out. */ + if (ctl->childwidget->bindas == -2 && + ctl->widget->bindas >= 0 && + ases[ctl->widget->bindas].dir == HDAA_CTL_OUT) + continue; + /* Allow mix -> mixed-in. */ + if (ctl->childwidget->bindas == -2 && + ctl->widget->bindas >= 0 && + ases[ctl->widget->bindas].mixed) + continue; + /* Allow in -> mix. */ + if ((ctl->widget->pflags & HDAA_ADC_MONITOR) && + ctl->childwidget->bindas >= 0 && + ases[ctl->childwidget->bindas].dir == HDAA_CTL_IN) + continue; + /* Allow if have common as/seqs. */ + if (ctl->widget->bindas == ctl->childwidget->bindas && + (ctl->widget->bindseqmask & ctl->childwidget->bindseqmask) != 0) + continue; + ctl->forcemute = 1; + ctl->muted = HDAA_AMP_MUTE_ALL; + ctl->left = 0; + ctl->right = 0; + ctl->enable = 0; + if (ctl->ndir == HDAA_CTL_IN) + ctl->widget->connsenable[ctl->index] = 0; + HDA_BOOTHVERBOSE( + device_printf(devinfo->dev, + " Disabling crossassociatement connection " + "ctl %d nid %d cnid %d.\n", i, + ctl->widget->nid, + ctl->childwidget->nid); + ); + } + +} + +#define HDA_CTL_GIVE(ctl) ((ctl)->step?1:0) + +/* + * Find controls to control amplification for source. + */ +static int +hdaa_audio_ctl_source_amp(struct hdaa_devinfo *devinfo, nid_t nid, int index, + int ossdev, int ctlable, int depth, int need) +{ + struct hdaa_widget *w, *wc; + struct hdaa_audio_ctl *ctl; + int i, j, conns = 0, rneed; + + if (depth > HDA_PARSE_MAXDEPTH) + return (need); + + w = hdaa_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (need); + + /* Count number of active inputs. */ + if (depth > 0) { + for (j = 0; j < w->nconns; j++) { + if (w->connsenable[j]) + conns++; + } + } + + /* If this is not a first step - use input mixer. + Pins have common input ctl so care must be taken. */ + if (depth > 0 && ctlable && (conns == 1 || + w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)) { + ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_IN, + index, 1); + if (ctl) { + if (HDA_CTL_GIVE(ctl) & need) + ctl->ossmask |= (1 << ossdev); + else + ctl->possmask |= (1 << ossdev); + need &= ~HDA_CTL_GIVE(ctl); + } + } + + /* If widget has own ossdev - not traverse it. + It will be traversed on it's own. */ + if (w->ossdev >= 0 && depth > 0) + return (need); + + /* We must not traverse pin */ + if ((w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) && + depth > 0) + return (need); + + /* record that this widget exports such signal, */ + w->ossmask |= (1 << ossdev); + + /* If signals mixed, we can't assign controls farther. + * Ignore this on depth zero. Caller must knows why. + * Ignore this for static selectors if this input selected. + */ + if (conns > 1) + ctlable = 0; + + if (ctlable) { + ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, HDAA_CTL_OUT, -1, 1); + if (ctl) { + if (HDA_CTL_GIVE(ctl) & need) + ctl->ossmask |= (1 << ossdev); + else + ctl->possmask |= (1 << ossdev); + need &= ~HDA_CTL_GIVE(ctl); + } + } + + rneed = 0; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + wc = hdaa_widget_get(devinfo, i); + if (wc == NULL || wc->enable == 0) + continue; + for (j = 0; j < wc->nconns; j++) { + if (wc->connsenable[j] && wc->conns[j] == nid) { + rneed |= hdaa_audio_ctl_source_amp(devinfo, + wc->nid, j, ossdev, ctlable, depth + 1, need); + } + } + } + rneed &= need; + + return (rneed); +} + +/* + * Find controls to control amplification for destination. + */ +static void +hdaa_audio_ctl_dest_amp(struct hdaa_devinfo *devinfo, nid_t nid, int index, + int ossdev, int depth, int need) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w, *wc; + struct hdaa_audio_ctl *ctl; + int i, j, consumers; + + if (depth > HDA_PARSE_MAXDEPTH) + return; + + w = hdaa_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return; + + if (depth > 0) { + /* If this node produce output for several consumers, + we can't touch it. */ + consumers = 0; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + wc = hdaa_widget_get(devinfo, i); + if (wc == NULL || wc->enable == 0) + continue; + for (j = 0; j < wc->nconns; j++) { + if (wc->connsenable[j] && wc->conns[j] == nid) + consumers++; + } + } + /* The only exception is if real HP redirection is configured + and this is a duplication point. + XXX: Actually exception is not completely correct. + XXX: Duplication point check is not perfect. */ + if ((consumers == 2 && (w->bindas < 0 || + as[w->bindas].hpredir < 0 || as[w->bindas].fakeredir || + (w->bindseqmask & (1 << 15)) == 0)) || + consumers > 2) + return; + + /* Else use it's output mixer. */ + ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, + HDAA_CTL_OUT, -1, 1); + if (ctl) { + if (HDA_CTL_GIVE(ctl) & need) + ctl->ossmask |= (1 << ossdev); + else + ctl->possmask |= (1 << ossdev); + need &= ~HDA_CTL_GIVE(ctl); + } + } + + /* We must not traverse pin */ + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + depth > 0) + return; + + for (i = 0; i < w->nconns; i++) { + int tneed = need; + if (w->connsenable[i] == 0) + continue; + if (index >= 0 && i != index) + continue; + ctl = hdaa_audio_ctl_amp_get(devinfo, w->nid, + HDAA_CTL_IN, i, 1); + if (ctl) { + if (HDA_CTL_GIVE(ctl) & tneed) + ctl->ossmask |= (1 << ossdev); + else + ctl->possmask |= (1 << ossdev); + tneed &= ~HDA_CTL_GIVE(ctl); + } + hdaa_audio_ctl_dest_amp(devinfo, w->conns[i], -1, ossdev, + depth + 1, tneed); + } +} + +/* + * Assign OSS names to sound sources + */ +static void +hdaa_audio_assign_names(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w; + int i, j; + int type = -1, use, used = 0; + static const int types[7][13] = { + { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2, + SOUND_MIXER_LINE3, -1 }, /* line */ + { SOUND_MIXER_MONITOR, SOUND_MIXER_MIC, -1 }, /* int mic */ + { SOUND_MIXER_MIC, SOUND_MIXER_MONITOR, -1 }, /* ext mic */ + { SOUND_MIXER_CD, -1 }, /* cd */ + { SOUND_MIXER_SPEAKER, -1 }, /* speaker */ + { SOUND_MIXER_DIGITAL1, SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3, + -1 }, /* digital */ + { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2, + SOUND_MIXER_LINE3, SOUND_MIXER_PHONEIN, SOUND_MIXER_PHONEOUT, + SOUND_MIXER_VIDEO, SOUND_MIXER_RADIO, SOUND_MIXER_DIGITAL1, + SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3, SOUND_MIXER_MONITOR, + -1 } /* others */ + }; + + /* Surely known names */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->bindas == -1) + continue; + use = -1; + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + if (as[w->bindas].dir == HDAA_CTL_OUT) + break; + type = -1; + switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { + case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN: + type = 0; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: + if ((w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) + == HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK) + break; + type = 1; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_CD: + type = 3; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: + type = 4; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_IN: + case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_IN: + type = 5; + break; + } + if (type == -1) + break; + j = 0; + while (types[type][j] >= 0 && + (used & (1 << types[type][j])) != 0) { + j++; + } + if (types[type][j] >= 0) + use = types[type][j]; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: + use = SOUND_MIXER_PCM; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET: + use = SOUND_MIXER_SPEAKER; + break; + default: + break; + } + if (use >= 0) { + w->ossdev = use; + used |= (1 << use); + } + } + /* Semi-known names */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->ossdev >= 0) + continue; + if (w->bindas == -1) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (as[w->bindas].dir == HDAA_CTL_OUT) + continue; + type = -1; + switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { + case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT: + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: + case HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT: + case HDA_CONFIG_DEFAULTCONF_DEVICE_AUX: + type = 0; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: + type = 2; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT: + case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT: + type = 5; + break; + } + if (type == -1) + break; + j = 0; + while (types[type][j] >= 0 && + (used & (1 << types[type][j])) != 0) { + j++; + } + if (types[type][j] >= 0) { + w->ossdev = types[type][j]; + used |= (1 << types[type][j]); + } + } + /* Others */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->ossdev >= 0) + continue; + if (w->bindas == -1) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (as[w->bindas].dir == HDAA_CTL_OUT) + continue; + j = 0; + while (types[6][j] >= 0 && + (used & (1 << types[6][j])) != 0) { + j++; + } + if (types[6][j] >= 0) { + w->ossdev = types[6][j]; + used |= (1 << types[6][j]); + } + } +} + +static void +hdaa_audio_build_tree(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + int j, res; + + /* Trace all associations in order of their numbers. */ + for (j = 0; j < devinfo->ascnt; j++) { + if (as[j].enable == 0) + continue; + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Tracing association %d (%d)\n", j, as[j].index); + ); + if (as[j].dir == HDAA_CTL_OUT) { +retry: + res = hdaa_audio_trace_as_out(devinfo, j, 0); + if (res == 0 && as[j].hpredir >= 0 && + as[j].fakeredir == 0) { + /* If CODEC can't do analog HP redirection + try to make it using one more DAC. */ + as[j].fakeredir = 1; + goto retry; + } + } else if (as[j].mixed) + res = hdaa_audio_trace_as_in(devinfo, j); + else + res = hdaa_audio_trace_as_in_mch(devinfo, j, 0); + if (res) { + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Association %d (%d) trace succeeded\n", + j, as[j].index); + ); + } else { + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, + "Association %d (%d) trace failed\n", + j, as[j].index); + ); + as[j].enable = 0; + } + } + + /* Look for additional DACs/ADCs. */ + for (j = 0; j < devinfo->ascnt; j++) { + if (as[j].enable == 0) + continue; + hdaa_audio_adddac(devinfo, j); + } + + /* Trace mixer and beeper pseudo associations. */ + hdaa_audio_trace_as_extra(devinfo); +} + +static void +hdaa_audio_assign_mixers(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_audio_ctl *ctl; + struct hdaa_widget *w, *cw; + int i, j; + + /* Assign mixers to the tree. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET || + (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + as[w->bindas].dir == HDAA_CTL_IN)) { + if (w->ossdev < 0) + continue; + hdaa_audio_ctl_source_amp(devinfo, w->nid, -1, + w->ossdev, 1, 0, 1); + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { + hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1, + SOUND_MIXER_RECLEV, 0, 1); + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + as[w->bindas].dir == HDAA_CTL_OUT) { + hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1, + SOUND_MIXER_VOLUME, 0, 1); + } + if (w->ossdev == SOUND_MIXER_IMIX) { + if (hdaa_audio_ctl_source_amp(devinfo, w->nid, -1, + w->ossdev, 1, 0, 1)) { + /* If we are unable to control input monitor + as source - try to control it as destination. */ + hdaa_audio_ctl_dest_amp(devinfo, w->nid, -1, + w->ossdev, 0, 1); + } + } + if (w->pflags & HDAA_ADC_MONITOR) { + for (j = 0; j < w->nconns; j++) { + if (!w->connsenable[j]) + continue; + cw = hdaa_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + if (cw->bindas == -1) + continue; + if (cw->bindas >= 0 && + as[cw->bindas].dir != HDAA_CTL_IN) + continue; + hdaa_audio_ctl_dest_amp(devinfo, + w->nid, j, SOUND_MIXER_IGAIN, 0, 1); + } + } + } + /* Treat unrequired as possible. */ + i = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->ossmask == 0) + ctl->ossmask = ctl->possmask; + } +} + +static void +hdaa_audio_prepare_pin_ctrl(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w; + uint32_t pincap; + int i; + + for (i = 0; i < devinfo->nodecnt; i++) { + w = &devinfo->widget[i]; + if (w == NULL) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + w->waspin == 0) + continue; + + pincap = w->wclass.pin.cap; + + /* Disable everything. */ + w->wclass.pin.ctrl &= ~( + HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK); + + if (w->enable == 0) { + /* Pin is unused so left it disabled. */ + continue; + } else if (w->waspin) { + /* Enable input for beeper input. */ + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE; + } else if (w->bindas < 0 || as[w->bindas].enable == 0) { + /* Pin is unused so left it disabled. */ + continue; + } else if (as[w->bindas].dir == HDAA_CTL_IN) { + /* Input pin, configure for input. */ + if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE; + + if ((devinfo->quirks & HDAA_QUIRK_IVREF100) && + HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); + else if ((devinfo->quirks & HDAA_QUIRK_IVREF80) && + HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); + else if ((devinfo->quirks & HDAA_QUIRK_IVREF50) && + HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); + } else { + /* Output pin, configure for output. */ + if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + + if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap) && + (w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) == + HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE; + + if ((devinfo->quirks & HDAA_QUIRK_OVREF100) && + HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); + else if ((devinfo->quirks & HDAA_QUIRK_OVREF80) && + HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); + else if ((devinfo->quirks & HDAA_QUIRK_OVREF50) && + HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( + HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); + } + } +} + +static void +hdaa_audio_ctl_commit(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_ctl *ctl; + int i, z; + + i = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->ossmask != 0) { + /* Mute disabled and mixer controllable controls. + * Last will be initialized by mixer_init(). + * This expected to reduce click on startup. */ + hdaa_audio_ctl_amp_set(ctl, HDAA_AMP_MUTE_ALL, 0, 0); + continue; + } + /* Init fixed controls to 0dB amplification. */ + z = ctl->offset; + if (z > ctl->step) + z = ctl->step; + hdaa_audio_ctl_amp_set(ctl, HDAA_AMP_MUTE_NONE, z, z); + } +} + +static void +hdaa_gpio_commit(struct hdaa_devinfo *devinfo) +{ + uint32_t gdata, gmask, gdir; + int i, numgpio; + + numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap); + if (devinfo->gpio != 0 && numgpio != 0) { + gdata = hda_command(devinfo->dev, + HDA_CMD_GET_GPIO_DATA(0, devinfo->nid)); + gmask = hda_command(devinfo->dev, + HDA_CMD_GET_GPIO_ENABLE_MASK(0, devinfo->nid)); + gdir = hda_command(devinfo->dev, + HDA_CMD_GET_GPIO_DIRECTION(0, devinfo->nid)); + for (i = 0; i < numgpio; i++) { + if ((devinfo->gpio & HDAA_GPIO_MASK(i)) == + HDAA_GPIO_SET(i)) { + gdata |= (1 << i); + gmask |= (1 << i); + gdir |= (1 << i); + } else if ((devinfo->gpio & HDAA_GPIO_MASK(i)) == + HDAA_GPIO_CLEAR(i)) { + gdata &= ~(1 << i); + gmask |= (1 << i); + gdir |= (1 << i); + } else if ((devinfo->gpio & HDAA_GPIO_MASK(i)) == + HDAA_GPIO_DISABLE(i)) { + gmask &= ~(1 << i); + } else if ((devinfo->gpio & HDAA_GPIO_MASK(i)) == + HDAA_GPIO_INPUT(i)) { + gmask |= (1 << i); + gdir &= ~(1 << i); + } + } + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, "GPIO commit\n"); + ); + hda_command(devinfo->dev, + HDA_CMD_SET_GPIO_ENABLE_MASK(0, devinfo->nid, gmask)); + hda_command(devinfo->dev, + HDA_CMD_SET_GPIO_DIRECTION(0, devinfo->nid, gdir)); + hda_command(devinfo->dev, + HDA_CMD_SET_GPIO_DATA(0, devinfo->nid, gdata)); + HDA_BOOTVERBOSE( + hdaa_dump_gpio(devinfo); + ); + } +} + +static void +hdaa_gpo_commit(struct hdaa_devinfo *devinfo) +{ + uint32_t gdata; + int i, numgpo; + + numgpo = HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap); + if (devinfo->gpo != 0 && numgpo != 0) { + gdata = hda_command(devinfo->dev, + HDA_CMD_GET_GPO_DATA(0, devinfo->nid)); + for (i = 0; i < numgpo; i++) { + if ((devinfo->gpio & HDAA_GPIO_MASK(i)) == + HDAA_GPIO_SET(i)) { + gdata |= (1 << i); + } else if ((devinfo->gpio & HDAA_GPIO_MASK(i)) == + HDAA_GPIO_CLEAR(i)) { + gdata &= ~(1 << i); + } + } + HDA_BOOTVERBOSE( + device_printf(devinfo->dev, "GPO commit\n"); + ); + hda_command(devinfo->dev, + HDA_CMD_SET_GPO_DATA(0, devinfo->nid, gdata)); + HDA_BOOTVERBOSE( + hdaa_dump_gpo(devinfo); + ); + } +} + +static void +hdaa_audio_commit(struct hdaa_devinfo *devinfo) +{ + struct hdaa_widget *w; + int i; + + /* Commit controls. */ + hdaa_audio_ctl_commit(devinfo); + + /* Commit selectors, pins and EAPD. */ + for (i = 0; i < devinfo->nodecnt; i++) { + w = &devinfo->widget[i]; + if (w == NULL) + continue; + if (w->selconn == -1) + w->selconn = 0; + if (w->nconns > 0) + hdaa_widget_connection_select(w, w->selconn); + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + w->waspin) { + hda_command(devinfo->dev, + HDA_CMD_SET_PIN_WIDGET_CTRL(0, w->nid, + w->wclass.pin.ctrl)); + } + if (w->param.eapdbtl != HDA_INVALID) { + uint32_t val; + + val = w->param.eapdbtl; + if (devinfo->quirks & + HDAA_QUIRK_EAPDINV) + val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + hda_command(devinfo->dev, + HDA_CMD_SET_EAPD_BTL_ENABLE(0, w->nid, + val)); + } + } + + hdaa_gpio_commit(devinfo); + hdaa_gpo_commit(devinfo); +} + +static void +hdaa_powerup(struct hdaa_devinfo *devinfo) +{ + int i; + + hda_command(devinfo->dev, + HDA_CMD_SET_POWER_STATE(0, + devinfo->nid, HDA_CMD_POWER_STATE_D0)); + DELAY(100); + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + hda_command(devinfo->dev, + HDA_CMD_SET_POWER_STATE(0, + i, HDA_CMD_POWER_STATE_D0)); + } + DELAY(1000); +} + +static int +hdaa_pcmchannel_setup(struct hdaa_chan *ch) +{ + struct hdaa_devinfo *devinfo = ch->devinfo; + struct hdaa_audio_as *as = devinfo->as; + struct hdaa_widget *w; + uint32_t cap, fmtcap, pcmcap; + int i, j, ret, channels, onlystereo; + uint16_t pinset; + + ch->caps = hdaa_caps; + ch->caps.fmtlist = ch->fmtlist; + ch->bit16 = 1; + ch->bit32 = 0; + ch->pcmrates[0] = 48000; + ch->pcmrates[1] = 0; + + ret = 0; + channels = 0; + onlystereo = 1; + pinset = 0; + fmtcap = devinfo->supp_stream_formats; + pcmcap = devinfo->supp_pcm_size_rate; + + for (i = 0; i < 16; i++) { + /* Check as is correct */ + if (ch->as < 0) + break; + /* Cound only present DACs */ + if (as[ch->as].dacs[ch->asindex][i] <= 0) + continue; + /* Ignore duplicates */ + for (j = 0; j < ret; j++) { + if (ch->io[j] == as[ch->as].dacs[ch->asindex][i]) + break; + } + if (j < ret) + continue; + + w = hdaa_widget_get(devinfo, as[ch->as].dacs[ch->asindex][i]); + if (w == NULL || w->enable == 0) + continue; + cap = w->param.supp_stream_formats; + if (!HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap) && + !HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) + continue; + /* Many CODECs does not declare AC3 support on SPDIF. + I don't beleave that they doesn't support it! */ + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) + cap |= HDA_PARAM_SUPP_STREAM_FORMATS_AC3_MASK; + if (ret == 0) { + fmtcap = cap; + pcmcap = w->param.supp_pcm_size_rate; + } else { + fmtcap &= cap; + pcmcap &= w->param.supp_pcm_size_rate; + } + ch->io[ret++] = as[ch->as].dacs[ch->asindex][i]; + /* Do not count redirection pin/dac channels. */ + if (i == 15 && as[ch->as].hpredir >= 0) + continue; + channels += HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) + 1; + if (HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) != 1) + onlystereo = 0; + pinset |= (1 << i); + } + ch->io[ret] = -1; + ch->channels = channels; + + if (as[ch->as].fakeredir) + ret--; + /* Standard speaks only about stereo pins and playback, ... */ + if ((!onlystereo) || as[ch->as].mixed) + pinset = 0; + /* ..., but there it gives us info about speakers layout. */ + as[ch->as].pinset = pinset; + + ch->supp_stream_formats = fmtcap; + ch->supp_pcm_size_rate = pcmcap; + + /* + * 8bit = 0 + * 16bit = 1 + * 20bit = 2 + * 24bit = 3 + * 32bit = 4 + */ + if (ret > 0) { + i = 0; + if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(fmtcap)) { + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(pcmcap)) + ch->bit16 = 1; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(pcmcap)) + ch->bit16 = 0; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(pcmcap)) + ch->bit32 = 4; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(pcmcap)) + ch->bit32 = 3; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(pcmcap)) + ch->bit32 = 2; + if (!(devinfo->quirks & HDAA_QUIRK_FORCESTEREO)) { + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 1, 0); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 1, 0); + } + if (channels >= 2) { + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 2, 0); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 2, 0); + } + if (channels == 4 || /* Any 4-channel */ + pinset == 0x0007 || /* 5.1 */ + pinset == 0x0013 || /* 5.1 */ + pinset == 0x0017) { /* 7.1 */ + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 4, 0); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 4, 0); + } + if (channels == 6 || /* Any 6-channel */ + pinset == 0x0017) { /* 7.1 */ + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 6, 1); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 6, 1); + } + if (channels == 8) { /* Any 8-channel */ + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 8, 1); + if (ch->bit32) + ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 8, 1); + } + } + if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(fmtcap)) { + ch->fmtlist[i++] = SND_FORMAT(AFMT_AC3, 2, 0); + } + ch->fmtlist[i] = 0; + i = 0; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(pcmcap)) + ch->pcmrates[i++] = 8000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(pcmcap)) + ch->pcmrates[i++] = 11025; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(pcmcap)) + ch->pcmrates[i++] = 16000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(pcmcap)) + ch->pcmrates[i++] = 22050; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(pcmcap)) + ch->pcmrates[i++] = 32000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(pcmcap)) + ch->pcmrates[i++] = 44100; + /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ(pcmcap)) */ + ch->pcmrates[i++] = 48000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(pcmcap)) + ch->pcmrates[i++] = 88200; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(pcmcap)) + ch->pcmrates[i++] = 96000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(pcmcap)) + ch->pcmrates[i++] = 176400; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(pcmcap)) + ch->pcmrates[i++] = 192000; + /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ(pcmcap)) */ + ch->pcmrates[i] = 0; + if (i > 0) { + ch->caps.minspeed = ch->pcmrates[0]; + ch->caps.maxspeed = ch->pcmrates[i - 1]; + } + } + + return (ret); +} + +static void +hdaa_create_pcms(struct hdaa_devinfo *devinfo) +{ + struct hdaa_audio_as *as = devinfo->as; + int i, j, k, apdev = 0, ardev = 0, dpdev = 0, drdev = 0; + + for (i = 0; i < devinfo->ascnt; i++) { + if (as[i].enable == 0) + continue; + if (as[i].dir == HDAA_CTL_IN) { + if (as[i].digital) + drdev++; + else + ardev++; + } else { + if (as[i].digital) + dpdev++; + else + apdev++; + } + } + devinfo->num_devs = + max(ardev, apdev) + max(drdev, dpdev); + devinfo->devs = + (struct hdaa_pcm_devinfo *)malloc( + devinfo->num_devs * sizeof(struct hdaa_pcm_devinfo), + M_HDAA, M_ZERO | M_NOWAIT); + if (devinfo->devs == NULL) { + device_printf(devinfo->dev, + "Unable to allocate memory for devices\n"); + return; + } + for (i = 0; i < devinfo->num_devs; i++) { + devinfo->devs[i].index = i; + devinfo->devs[i].devinfo = devinfo; + devinfo->devs[i].playas = -1; + devinfo->devs[i].recas = -1; + devinfo->devs[i].digital = 255; + } + for (i = 0; i < devinfo->ascnt; i++) { + if (as[i].enable == 0) + continue; + for (j = 0; j < devinfo->num_devs; j++) { + if (devinfo->devs[j].digital != 255 && + (!devinfo->devs[j].digital) != + (!as[i].digital)) + continue; + if (as[i].dir == HDAA_CTL_IN) { + if (devinfo->devs[j].recas >= 0) + continue; + devinfo->devs[j].recas = i; + } else { + if (devinfo->devs[j].playas >= 0) + continue; + devinfo->devs[j].playas = i; + } + for (k = 0; k < as[i].num_chans; k++) { + devinfo->chans[as[i].chans[k]].pdevinfo = + &devinfo->devs[j]; + } + devinfo->devs[j].digital = as[i].digital; + break; + } + } + for (i = 0; i < devinfo->num_devs; i++) { + struct hdaa_pcm_devinfo *pdevinfo = &devinfo->devs[i]; + + pdevinfo->dev = device_add_child(devinfo->dev, "pcm", -1); + device_set_ivars(pdevinfo->dev, (void *)pdevinfo); + } +} + +static void +hdaa_dump_ctls(struct hdaa_pcm_devinfo *pdevinfo, const char *banner, uint32_t flag) +{ + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_audio_ctl *ctl; + char buf[64]; + int i, j, printed; + + if (flag == 0) { + flag = ~(SOUND_MASK_VOLUME | SOUND_MASK_PCM | + SOUND_MASK_CD | SOUND_MASK_LINE | SOUND_MASK_RECLEV | + SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | + SOUND_MASK_OGAIN | SOUND_MASK_IMIX | SOUND_MASK_MONITOR); + } + + for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) { + if ((flag & (1 << j)) == 0) + continue; + i = 0; + printed = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || + ctl->widget->enable == 0) + continue; + if (!((pdevinfo->playas >= 0 && + ctl->widget->bindas == pdevinfo->playas) || + (pdevinfo->recas >= 0 && + ctl->widget->bindas == pdevinfo->recas) || + (ctl->widget->bindas == -2 && pdevinfo->index == 0))) + continue; + if ((ctl->ossmask & (1 << j)) == 0) + continue; + + if (printed == 0) { + device_printf(pdevinfo->dev, "\n"); + if (banner != NULL) { + device_printf(pdevinfo->dev, "%s", banner); + } else { + device_printf(pdevinfo->dev, "Unknown Ctl"); + } + printf(" (OSS: %s)\n", + hdaa_audio_ctl_ossmixer_mask2allname(1 << j, + buf, sizeof(buf))); + device_printf(pdevinfo->dev, " |\n"); + printed = 1; + } + device_printf(pdevinfo->dev, " +- ctl %2d (nid %3d %s", i, + ctl->widget->nid, + (ctl->ndir == HDAA_CTL_IN)?"in ":"out"); + if (ctl->ndir == HDAA_CTL_IN && ctl->ndir == ctl->dir) + printf(" %2d): ", ctl->index); + else + printf("): "); + if (ctl->step > 0) { + printf("%+d/%+ddB (%d steps)%s\n", + (0 - ctl->offset) * (ctl->size + 1) / 4, + (ctl->step - ctl->offset) * (ctl->size + 1) / 4, + ctl->step + 1, + ctl->mute?" + mute":""); + } else + printf("%s\n", ctl->mute?"mute":""); + } + } +} + +static void +hdaa_dump_audio_formats(device_t dev, uint32_t fcap, uint32_t pcmcap) +{ + uint32_t cap; + + cap = fcap; + if (cap != 0) { + device_printf(dev, " Stream cap: 0x%08x\n", cap); + device_printf(dev, " "); + if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) + printf(" AC3"); + if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap)) + printf(" FLOAT32"); + if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap)) + printf(" PCM"); + printf("\n"); + } + cap = pcmcap; + if (cap != 0) { + device_printf(dev, " PCM cap: 0x%08x\n", cap); + device_printf(dev, " "); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(cap)) + printf(" 8"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(cap)) + printf(" 16"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(cap)) + printf(" 20"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(cap)) + printf(" 24"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(cap)) + printf(" 32"); + printf(" bits,"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(cap)) + printf(" 8"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(cap)) + printf(" 11"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(cap)) + printf(" 16"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(cap)) + printf(" 22"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(cap)) + printf(" 32"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(cap)) + printf(" 44"); + printf(" 48"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(cap)) + printf(" 88"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(cap)) + printf(" 96"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(cap)) + printf(" 176"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(cap)) + printf(" 192"); + printf(" KHz\n"); + } +} + +static void +hdaa_dump_pin(struct hdaa_widget *w) +{ + uint32_t pincap; + + pincap = w->wclass.pin.cap; + + device_printf(w->devinfo->dev, " Pin cap: 0x%08x\n", pincap); + device_printf(w->devinfo->dev, " "); + if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap)) + printf(" ISC"); + if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) + printf(" TRQD"); + if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) + printf(" PDC"); + if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)) + printf(" HP"); + if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)) + printf(" OUT"); + if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) + printf(" IN"); + if (HDA_PARAM_PIN_CAP_BALANCED_IO_PINS(pincap)) + printf(" BAL"); + if (HDA_PARAM_PIN_CAP_HDMI(pincap)) + printf(" HDMI"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)) { + printf(" VREF["); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) + printf(" 50"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) + printf(" 80"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) + printf(" 100"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND(pincap)) + printf(" GROUND"); + if (HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ(pincap)) + printf(" HIZ"); + printf(" ]"); + } + if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) + printf(" EAPD"); + if (HDA_PARAM_PIN_CAP_DP(pincap)) + printf(" DP"); + if (HDA_PARAM_PIN_CAP_HBR(pincap)) + printf(" HBR"); + printf("\n"); + device_printf(w->devinfo->dev, " Pin config: 0x%08x\n", + w->wclass.pin.config); + device_printf(w->devinfo->dev, " Pin control: 0x%08x", w->wclass.pin.ctrl); + if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE) + printf(" HP"); + if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE) + printf(" IN"); + if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE) + printf(" OUT"); + if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) + printf(" VREFs"); + printf("\n"); +} + +static void +hdaa_dump_pin_config(struct hdaa_widget *w, uint32_t conf) +{ + + device_printf(w->devinfo->dev, "%2d %08x %-2d %-2d " + "%-13s %-5s %-7s %-10s %-7s %d%s\n", + w->nid, conf, + HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf), + HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf), + HDA_DEVS[HDA_CONFIG_DEFAULTCONF_DEVICE(conf)], + HDA_CONNS[HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf)], + HDA_CONNECTORS[HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf)], + HDA_LOCS[HDA_CONFIG_DEFAULTCONF_LOCATION(conf)], + HDA_COLORS[HDA_CONFIG_DEFAULTCONF_COLOR(conf)], + HDA_CONFIG_DEFAULTCONF_MISC(conf), + (w->enable == 0)?" DISA":""); +} + +static void +hdaa_dump_pin_configs(struct hdaa_devinfo *devinfo) +{ + struct hdaa_widget *w; + int i; + + device_printf(devinfo->dev, "nid 0x as seq " + "device conn jack loc color misc\n"); + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + hdaa_dump_pin_config(w, w->wclass.pin.config); + } +} + +static void +hdaa_dump_amp(device_t dev, uint32_t cap, char *banner) +{ + device_printf(dev, " %s amp: 0x%08x\n", banner, cap); + device_printf(dev, " " + "mute=%d step=%d size=%d offset=%d\n", + HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(cap), + HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(cap), + HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(cap), + HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(cap)); +} + +static void +hdaa_dump_nodes(struct hdaa_devinfo *devinfo) +{ + static char *ossname[] = SOUND_DEVICE_NAMES; + struct hdaa_widget *w, *cw; + char buf[64]; + int i, j; + + device_printf(devinfo->dev, "\n"); + device_printf(devinfo->dev, "Default Parameter\n"); + device_printf(devinfo->dev, "-----------------\n"); + hdaa_dump_audio_formats(devinfo->dev, + devinfo->supp_stream_formats, + devinfo->supp_pcm_size_rate); + device_printf(devinfo->dev, " IN amp: 0x%08x\n", + devinfo->inamp_cap); + device_printf(devinfo->dev, " OUT amp: 0x%08x\n", + devinfo->outamp_cap); + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL) { + device_printf(devinfo->dev, "Ghost widget nid=%d\n", i); + continue; + } + device_printf(devinfo->dev, "\n"); + device_printf(devinfo->dev, " nid: %d%s\n", w->nid, + (w->enable == 0) ? " [DISABLED]" : ""); + device_printf(devinfo->dev, " Name: %s\n", w->name); + device_printf(devinfo->dev, " Widget cap: 0x%08x\n", + w->param.widget_cap); + if (w->param.widget_cap & 0x0ee1) { + device_printf(devinfo->dev, " "); + if (HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP(w->param.widget_cap)) + printf(" LRSWAP"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(w->param.widget_cap)) + printf(" PWR"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) + printf(" DIGITAL"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) + printf(" UNSOL"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET(w->param.widget_cap)) + printf(" PROC"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap)) + printf(" STRIPE"); + j = HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap); + if (j == 1) + printf(" STEREO"); + else if (j > 1) + printf(" %dCH", j + 1); + printf("\n"); + } + if (w->bindas != -1) { + device_printf(devinfo->dev, " Association: %d (0x%08x)\n", + w->bindas, w->bindseqmask); + } + if (w->ossmask != 0 || w->ossdev >= 0) { + device_printf(devinfo->dev, " OSS: %s", + hdaa_audio_ctl_ossmixer_mask2allname(w->ossmask, buf, sizeof(buf))); + if (w->ossdev >= 0) + printf(" (%s)", ossname[w->ossdev]); + printf("\n"); + } + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { + hdaa_dump_audio_formats(devinfo->dev, + w->param.supp_stream_formats, + w->param.supp_pcm_size_rate); + } else if (w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || w->waspin) + hdaa_dump_pin(w); + if (w->param.eapdbtl != HDA_INVALID) + device_printf(devinfo->dev, " EAPD: 0x%08x\n", + w->param.eapdbtl); + if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(w->param.widget_cap) && + w->param.outamp_cap != 0) + hdaa_dump_amp(devinfo->dev, w->param.outamp_cap, "Output"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(w->param.widget_cap) && + w->param.inamp_cap != 0) + hdaa_dump_amp(devinfo->dev, w->param.inamp_cap, " Input"); + if (w->nconns > 0) { + device_printf(devinfo->dev, " connections: %d\n", w->nconns); + device_printf(devinfo->dev, " |\n"); + } + for (j = 0; j < w->nconns; j++) { + cw = hdaa_widget_get(devinfo, w->conns[j]); + device_printf(devinfo->dev, " + %s<- nid=%d [%s]", + (w->connsenable[j] == 0)?"[DISABLED] ":"", + w->conns[j], (cw == NULL) ? "GHOST!" : cw->name); + if (cw == NULL) + printf(" [UNKNOWN]"); + else if (cw->enable == 0) + printf(" [DISABLED]"); + if (w->nconns > 1 && w->selconn == j && w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + printf(" (selected)"); + printf("\n"); + } + } + +} + +static void +hdaa_dump_dst_nid(struct hdaa_pcm_devinfo *pdevinfo, nid_t nid, int depth) +{ + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_widget *w, *cw; + char buf[64]; + int i, printed = 0; + + if (depth > HDA_PARSE_MAXDEPTH) + return; + + w = hdaa_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return; + + if (depth == 0) + device_printf(pdevinfo->dev, "%*s", 4, ""); + else + device_printf(pdevinfo->dev, "%*s + <- ", 4 + (depth - 1) * 7, ""); + printf("nid=%d [%s]", w->nid, w->name); + + if (depth > 0) { + if (w->ossmask == 0) { + printf("\n"); + return; + } + printf(" [src: %s]", + hdaa_audio_ctl_ossmixer_mask2allname( + w->ossmask, buf, sizeof(buf))); + if (w->ossdev >= 0) { + printf("\n"); + return; + } + } + printf("\n"); + + for (i = 0; i < w->nconns; i++) { + if (w->connsenable[i] == 0) + continue; + cw = hdaa_widget_get(devinfo, w->conns[i]); + if (cw == NULL || cw->enable == 0 || cw->bindas == -1) + continue; + if (printed == 0) { + device_printf(pdevinfo->dev, "%*s |\n", 4 + (depth) * 7, ""); + printed = 1; + } + hdaa_dump_dst_nid(pdevinfo, w->conns[i], depth + 1); + } + +} + +static void +hdaa_dump_dac(struct hdaa_pcm_devinfo *pdevinfo) +{ + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_audio_as *as; + struct hdaa_widget *w; + int i, printed = 0; + + if (pdevinfo->playas < 0) + return; + + as = &devinfo->as[pdevinfo->playas]; + for (i = 0; i < 16; i++) { + if (as->pins[i] <= 0) + continue; + w = hdaa_widget_get(devinfo, as->pins[i]); + if (w == NULL || w->enable == 0) + continue; + if (printed == 0) { + printed = 1; + device_printf(pdevinfo->dev, "\n"); + device_printf(pdevinfo->dev, "Playback:\n"); + } + device_printf(pdevinfo->dev, "\n"); + hdaa_dump_dst_nid(pdevinfo, as->pins[i], 0); + } +} + +static void +hdaa_dump_adc(struct hdaa_pcm_devinfo *pdevinfo) +{ + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_widget *w; + int i; + int printed = 0; + + if (pdevinfo->recas < 0) + return; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) + continue; + if (w->bindas != pdevinfo->recas) + continue; + if (printed == 0) { + printed = 1; + device_printf(pdevinfo->dev, "\n"); + device_printf(pdevinfo->dev, "Record:\n"); + } + device_printf(pdevinfo->dev, "\n"); + hdaa_dump_dst_nid(pdevinfo, i, 0); + } +} + +static void +hdaa_dump_mix(struct hdaa_pcm_devinfo *pdevinfo) +{ + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_widget *w; + int i; + int printed = 0; + + if (pdevinfo->index != 0) + return; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->ossdev != SOUND_MIXER_IMIX) + continue; + if (printed == 0) { + printed = 1; + device_printf(pdevinfo->dev, "\n"); + device_printf(pdevinfo->dev, "Input Mix:\n"); + } + device_printf(pdevinfo->dev, "\n"); + hdaa_dump_dst_nid(pdevinfo, i, 0); + } +} + +static void +hdaa_dump_pcmchannels(struct hdaa_pcm_devinfo *pdevinfo) +{ + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + nid_t *nids; + int chid, i; + + if (pdevinfo->playas >= 0) { + device_printf(pdevinfo->dev, "\n"); + device_printf(pdevinfo->dev, "Playback:\n"); + device_printf(pdevinfo->dev, "\n"); + chid = devinfo->as[pdevinfo->playas].chans[0]; + hdaa_dump_audio_formats(pdevinfo->dev, + devinfo->chans[chid].supp_stream_formats, + devinfo->chans[chid].supp_pcm_size_rate); + for (i = 0; i < devinfo->as[pdevinfo->playas].num_chans; i++) { + chid = devinfo->as[pdevinfo->playas].chans[i]; + device_printf(pdevinfo->dev, " DAC:"); + for (nids = devinfo->chans[chid].io; *nids != -1; nids++) + printf(" %d", *nids); + printf("\n"); + } + } + if (pdevinfo->recas >= 0) { + device_printf(pdevinfo->dev, "\n"); + device_printf(pdevinfo->dev, "Record:\n"); + device_printf(pdevinfo->dev, "\n"); + chid = devinfo->as[pdevinfo->recas].chans[0]; + hdaa_dump_audio_formats(pdevinfo->dev, + devinfo->chans[chid].supp_stream_formats, + devinfo->chans[chid].supp_pcm_size_rate); + for (i = 0; i < devinfo->as[pdevinfo->recas].num_chans; i++) { + chid = devinfo->as[pdevinfo->recas].chans[i]; + device_printf(pdevinfo->dev, " DAC:"); + for (nids = devinfo->chans[chid].io; *nids != -1; nids++) + printf(" %d", *nids); + printf("\n"); + } + } +} + +static void +hdaa_pindump(device_t dev) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + struct hdaa_widget *w; + uint32_t res, pincap, delay; + int i; + + device_printf(dev, "Dumping AFG pins:\n"); + device_printf(dev, "nid 0x as seq " + "device conn jack loc color misc\n"); + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL || w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + hdaa_dump_pin_config(w, w->wclass.pin.config); + pincap = w->wclass.pin.cap; + device_printf(dev, " Caps: %2s %3s %2s %4s %4s", + HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)?"IN":"", + HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)?"OUT":"", + HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)?"HP":"", + HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)?"EAPD":"", + HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)?"VREF":""); + if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap) || + HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) { + if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) { + delay = 0; + hda_command(dev, + HDA_CMD_SET_PIN_SENSE(0, w->nid, 0)); + do { + res = hda_command(dev, + HDA_CMD_GET_PIN_SENSE(0, w->nid)); + if (res != 0x7fffffff && res != 0xffffffff) + break; + DELAY(10); + } while (++delay < 10000); + } else { + delay = 0; + res = hda_command(dev, HDA_CMD_GET_PIN_SENSE(0, + w->nid)); + } + printf(" Sense: 0x%08x (%sconnected)", res, + (res & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT) ? + "" : "dis"); + if (delay > 0) + printf(" delay %dus", delay * 10); + } + printf("\n"); + } + device_printf(dev, + "NumGPIO=%d NumGPO=%d NumGPI=%d GPIWake=%d GPIUnsol=%d\n", + HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap), + HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap), + HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap), + HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->gpio_cap), + HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->gpio_cap)); + hdaa_dump_gpi(devinfo); + hdaa_dump_gpio(devinfo); + hdaa_dump_gpo(devinfo); +} + +static void +hdaa_configure(device_t dev) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + struct hdaa_audio_ctl *ctl; + int i; + + HDA_BOOTHVERBOSE( + device_printf(dev, "Applying built-in patches...\n"); + ); + hdaa_patch(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Applying local patches...\n"); + ); + hdaa_local_patch(devinfo); + hdaa_audio_postprocess(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Parsing Ctls...\n"); + ); + hdaa_audio_ctl_parse(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Disabling nonaudio...\n"); + ); + hdaa_audio_disable_nonaudio(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Disabling useless...\n"); + ); + hdaa_audio_disable_useless(devinfo); + HDA_BOOTVERBOSE( + device_printf(dev, "Patched pins configuration:\n"); + hdaa_dump_pin_configs(devinfo); + ); + HDA_BOOTHVERBOSE( + device_printf(dev, "Parsing pin associations...\n"); + ); + hdaa_audio_as_parse(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Building AFG tree...\n"); + ); + hdaa_audio_build_tree(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Disabling unassociated " + "widgets...\n"); + ); + hdaa_audio_disable_unas(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Disabling nonselected " + "inputs...\n"); + ); + hdaa_audio_disable_notselected(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Disabling useless...\n"); + ); + hdaa_audio_disable_useless(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Disabling " + "crossassociatement connections...\n"); + ); + hdaa_audio_disable_crossas(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Disabling useless...\n"); + ); + hdaa_audio_disable_useless(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Binding associations to channels...\n"); + ); + hdaa_audio_bind_as(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Assigning names to signal sources...\n"); + ); + hdaa_audio_assign_names(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Assigning mixers to the tree...\n"); + ); + hdaa_audio_assign_mixers(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Preparing pin controls...\n"); + ); + hdaa_audio_prepare_pin_ctrl(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "AFG commit...\n"); + ); + hdaa_audio_commit(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Applying direct built-in patches...\n"); + ); + hdaa_patch_direct(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "HP switch init...\n"); + ); + hdaa_hp_switch_init(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Creating PCM devices...\n"); + ); + hdaa_create_pcms(devinfo); + + HDA_BOOTVERBOSE( + if (devinfo->quirks != 0) { + device_printf(dev, "FG config/quirks:"); + for (i = 0; i < HDAA_QUIRKS_TAB_LEN; i++) { + if ((devinfo->quirks & + hdaa_quirks_tab[i].value) == + hdaa_quirks_tab[i].value) + printf(" %s", hdaa_quirks_tab[i].key); + } + printf("\n"); + } + + device_printf(dev, "\n"); + device_printf(dev, "+-------------------+\n"); + device_printf(dev, "| DUMPING HDA NODES |\n"); + device_printf(dev, "+-------------------+\n"); + hdaa_dump_nodes(devinfo); + ); + + HDA_BOOTHVERBOSE( + device_printf(dev, "\n"); + device_printf(dev, "+------------------------+\n"); + device_printf(dev, "| DUMPING HDA AMPLIFIERS |\n"); + device_printf(dev, "+------------------------+\n"); + device_printf(dev, "\n"); + i = 0; + while ((ctl = hdaa_audio_ctl_each(devinfo, &i)) != NULL) { + device_printf(dev, "%3d: nid %3d %s (%s) index %d", i, + (ctl->widget != NULL) ? ctl->widget->nid : -1, + (ctl->ndir == HDAA_CTL_IN)?"in ":"out", + (ctl->dir == HDAA_CTL_IN)?"in ":"out", + ctl->index); + if (ctl->childwidget != NULL) + printf(" cnid %3d", ctl->childwidget->nid); + else + printf(" "); + printf(" ossmask=0x%08x\n", + ctl->ossmask); + device_printf(dev, + " mute: %d step: %3d size: %3d off: %3d%s\n", + ctl->mute, ctl->step, ctl->size, ctl->offset, + (ctl->enable == 0) ? " [DISABLED]" : + ((ctl->ossmask == 0) ? " [UNUSED]" : "")); + } + ); + + HDA_BOOTVERBOSE( + device_printf(dev, "\n"); + ); +} + +static void +hdaa_unconfigure(device_t dev) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + struct hdaa_widget *w; + int i, j; + + HDA_BOOTHVERBOSE( + device_printf(dev, "HP switch deinit...\n"); + ); + hdaa_hp_switch_deinit(devinfo); + free(devinfo->ctl, M_HDAA); + devinfo->ctl = NULL; + devinfo->ctlcnt = 0; + free(devinfo->as, M_HDAA); + devinfo->as = NULL; + devinfo->ascnt = 0; + free(devinfo->devs, M_HDAA); + devinfo->devs = NULL; + devinfo->num_devs = 0; + free(devinfo->chans, M_HDAA); + devinfo->chans = NULL; + devinfo->num_chans = 0; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL) + continue; + w->enable = 1; + w->selconn = -1; + w->pflags = 0; + w->bindas = -1; + w->bindseqmask = 0; + w->ossdev = -1; + w->ossmask = 0; + for (j = 0; j < w->nconns; j++) + w->connsenable[j] = 1; + w->wclass.pin.config = w->wclass.pin.newconf; + } +} + +static int +hdaa_sysctl_gpi_state(SYSCTL_HANDLER_ARGS) +{ + struct hdaa_devinfo *devinfo = oidp->oid_arg1; + device_t dev = devinfo->dev; + char buf[256]; + int n = 0, i, numgpi; + uint32_t data = 0; + + buf[0] = 0; + hdaa_lock(devinfo); + numgpi = HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->gpio_cap); + if (numgpi > 0) { + data = hda_command(dev, + HDA_CMD_GET_GPI_DATA(0, devinfo->nid)); + } + hdaa_unlock(devinfo); + for (i = 0; i < numgpi; i++) { + n += snprintf(buf + n, sizeof(buf) - n, "%s%d=%d", + n != 0 ? " " : "", i, ((data >> i) & 1)); + } + return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); +} + +static int +hdaa_sysctl_gpio_state(SYSCTL_HANDLER_ARGS) +{ + struct hdaa_devinfo *devinfo = oidp->oid_arg1; + device_t dev = devinfo->dev; + char buf[256]; + int n = 0, i, numgpio; + uint32_t data = 0, enable = 0, dir = 0; + + buf[0] = 0; + hdaa_lock(devinfo); + numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap); + if (numgpio > 0) { + data = hda_command(dev, + HDA_CMD_GET_GPIO_DATA(0, devinfo->nid)); + enable = hda_command(dev, + HDA_CMD_GET_GPIO_ENABLE_MASK(0, devinfo->nid)); + dir = hda_command(dev, + HDA_CMD_GET_GPIO_DIRECTION(0, devinfo->nid)); + } + hdaa_unlock(devinfo); + for (i = 0; i < numgpio; i++) { + n += snprintf(buf + n, sizeof(buf) - n, "%s%d=", + n != 0 ? " " : "", i); + if ((enable & (1 << i)) == 0) { + n += snprintf(buf + n, sizeof(buf) - n, "disabled"); + continue; + } + n += snprintf(buf + n, sizeof(buf) - n, "%sput(%d)", + ((dir >> i) & 1) ? "out" : "in", ((data >> i) & 1)); + } + return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); +} + +static int +hdaa_sysctl_gpio_config(SYSCTL_HANDLER_ARGS) +{ + struct hdaa_devinfo *devinfo = oidp->oid_arg1; + char buf[256]; + int error, n = 0, i, numgpio; + uint32_t gpio, x; + + gpio = devinfo->newgpio; + numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->gpio_cap); + buf[0] = 0; + for (i = 0; i < numgpio; i++) { + x = (gpio & HDAA_GPIO_MASK(i)) >> HDAA_GPIO_SHIFT(i); + n += snprintf(buf + n, sizeof(buf) - n, "%s%d=%s", + n != 0 ? " " : "", i, HDA_GPIO_ACTIONS[x]); + } + error = sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (error != 0 || req->newptr == NULL) + return (error); + if (strncmp(buf, "0x", 2) == 0) + gpio = strtol(buf + 2, NULL, 16); + else + gpio = hdaa_gpio_patch(gpio, buf); + hdaa_lock(devinfo); + devinfo->newgpio = devinfo->gpio = gpio; + hdaa_gpio_commit(devinfo); + hdaa_unlock(devinfo); + return (0); +} + +static int +hdaa_sysctl_gpo_state(SYSCTL_HANDLER_ARGS) +{ + struct hdaa_devinfo *devinfo = oidp->oid_arg1; + device_t dev = devinfo->dev; + char buf[256]; + int n = 0, i, numgpo; + uint32_t data = 0; + + buf[0] = 0; + hdaa_lock(devinfo); + numgpo = HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap); + if (numgpo > 0) { + data = hda_command(dev, + HDA_CMD_GET_GPO_DATA(0, devinfo->nid)); + } + hdaa_unlock(devinfo); + for (i = 0; i < numgpo; i++) { + n += snprintf(buf + n, sizeof(buf) - n, "%s%d=%d", + n != 0 ? " " : "", i, ((data >> i) & 1)); + } + return (sysctl_handle_string(oidp, buf, sizeof(buf), req)); +} + +static int +hdaa_sysctl_gpo_config(SYSCTL_HANDLER_ARGS) +{ + struct hdaa_devinfo *devinfo = oidp->oid_arg1; + char buf[256]; + int error, n = 0, i, numgpo; + uint32_t gpo, x; + + gpo = devinfo->newgpo; + numgpo = HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->gpio_cap); + buf[0] = 0; + for (i = 0; i < numgpo; i++) { + x = (gpo & HDAA_GPIO_MASK(i)) >> HDAA_GPIO_SHIFT(i); + n += snprintf(buf + n, sizeof(buf) - n, "%s%d=%s", + n != 0 ? " " : "", i, HDA_GPIO_ACTIONS[x]); + } + error = sysctl_handle_string(oidp, buf, sizeof(buf), req); + if (error != 0 || req->newptr == NULL) + return (error); + if (strncmp(buf, "0x", 2) == 0) + gpo = strtol(buf + 2, NULL, 16); + else + gpo = hdaa_gpio_patch(gpo, buf); + hdaa_lock(devinfo); + devinfo->newgpo = devinfo->gpo = gpo; + hdaa_gpo_commit(devinfo); + hdaa_unlock(devinfo); + return (0); +} + +static int +hdaa_sysctl_reconfig(SYSCTL_HANDLER_ARGS) +{ + device_t dev; + struct hdaa_devinfo *devinfo; + int error, val; + + dev = oidp->oid_arg1; + devinfo = device_get_softc(dev); + if (devinfo == NULL) + return (EINVAL); + val = 0; + error = sysctl_handle_int(oidp, &val, 0, req); + if (error != 0 || req->newptr == NULL || val == 0) + return (error); + + HDA_BOOTHVERBOSE( + device_printf(dev, "Reconfiguration...\n"); + ); + if ((error = device_delete_children(dev)) != 0) + return (error); + hdaa_lock(devinfo); + hdaa_unconfigure(dev); + hdaa_configure(dev); + hdaa_unlock(devinfo); + bus_generic_attach(dev); + HDA_BOOTHVERBOSE( + device_printf(dev, "Reconfiguration done\n"); + ); + return (0); +} + +static int +hdaa_suspend(device_t dev) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + int i; + + HDA_BOOTHVERBOSE( + device_printf(dev, "Suspend...\n"); + ); + hdaa_lock(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Stop streams...\n"); + ); + for (i = 0; i < devinfo->num_chans; i++) { + if (devinfo->chans[i].flags & HDAA_CHN_RUNNING) { + devinfo->chans[i].flags |= HDAA_CHN_SUSPEND; + hdaa_channel_stop(&devinfo->chans[i]); + } + } + HDA_BOOTHVERBOSE( + device_printf(dev, "HP switch deinit...\n"); + ); + hdaa_hp_switch_deinit(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Power down FG" + " nid=%d to the D3 state...\n", + devinfo->nid); + ); + hda_command(devinfo->dev, + HDA_CMD_SET_POWER_STATE(0, + devinfo->nid, HDA_CMD_POWER_STATE_D3)); + callout_stop(&devinfo->poll_jack); + hdaa_unlock(devinfo); + callout_drain(&devinfo->poll_jack); + HDA_BOOTHVERBOSE( + device_printf(dev, "Suspend done\n"); + ); + return (0); +} + +static int +hdaa_resume(device_t dev) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + int i; + + HDA_BOOTHVERBOSE( + device_printf(dev, "Resume...\n"); + ); + hdaa_lock(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Power up audio FG nid=%d...\n", + devinfo->nid); + ); + hdaa_powerup(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "AFG commit...\n"); + ); + hdaa_audio_commit(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Applying direct built-in patches...\n"); + ); + hdaa_patch_direct(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "HP switch init...\n"); + ); + hdaa_hp_switch_init(devinfo); + + hdaa_unlock(devinfo); + for (i = 0; i < devinfo->num_devs; i++) { + struct hdaa_pcm_devinfo *pdevinfo = &devinfo->devs[i]; + HDA_BOOTHVERBOSE( + device_printf(pdevinfo->dev, + "OSS mixer reinitialization...\n"); + ); + if (mixer_reinit(pdevinfo->dev) == -1) + device_printf(pdevinfo->dev, + "unable to reinitialize the mixer\n"); + } + hdaa_lock(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Start streams...\n"); + ); + for (i = 0; i < devinfo->num_chans; i++) { + if (devinfo->chans[i].flags & HDAA_CHN_SUSPEND) { + devinfo->chans[i].flags &= ~HDAA_CHN_SUSPEND; + hdaa_channel_start(&devinfo->chans[i]); + } + } + hdaa_unlock(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Resume done\n"); + ); + return (0); +} + +static int +hdaa_probe(device_t dev) +{ + char buf[128]; + + if (hda_get_node_type(dev) != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) + return (ENXIO); + snprintf(buf, sizeof(buf), "%s Audio Function Group", + device_get_desc(device_get_parent(dev))); + device_set_desc_copy(dev, buf); + return (BUS_PROBE_DEFAULT); +} + +static int +hdaa_attach(device_t dev) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + uint32_t res; + nid_t nid = hda_get_node_id(dev); + + devinfo->dev = dev; + devinfo->lock = HDAC_GET_MTX(device_get_parent(dev), dev); + devinfo->nid = nid; + devinfo->newquirks = -1; + devinfo->newgpio = -1; + devinfo->newgpo = -1; + callout_init(&devinfo->poll_jack, CALLOUT_MPSAFE); + devinfo->poll_ival = hz; + + hdaa_lock(devinfo); + res = hda_command(dev, + HDA_CMD_GET_PARAMETER(0 , nid, HDA_PARAM_SUB_NODE_COUNT)); + hdaa_unlock(devinfo); + + devinfo->nodecnt = HDA_PARAM_SUB_NODE_COUNT_TOTAL(res); + devinfo->startnode = HDA_PARAM_SUB_NODE_COUNT_START(res); + devinfo->endnode = devinfo->startnode + devinfo->nodecnt; + + HDA_BOOTVERBOSE( + device_printf(dev, + "Audio Function Group at nid=%d: %d subnodes %d-%d\n", + nid, devinfo->nodecnt, + devinfo->startnode, devinfo->endnode - 1); + ); + + if (devinfo->nodecnt > 0) + devinfo->widget = (struct hdaa_widget *)malloc( + sizeof(*(devinfo->widget)) * devinfo->nodecnt, M_HDAA, + M_WAITOK | M_ZERO); + else + devinfo->widget = NULL; + + hdaa_lock(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Powering up...\n"); + ); + hdaa_powerup(devinfo); + HDA_BOOTHVERBOSE( + device_printf(dev, "Parsing audio FG...\n"); + ); + hdaa_audio_parse(devinfo); + HDA_BOOTVERBOSE( + device_printf(dev, "Original pins configuration:\n"); + hdaa_dump_pin_configs(devinfo); + ); + hdaa_configure(dev); + hdaa_unlock(devinfo); + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "config", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, + &devinfo->newquirks, sizeof(&devinfo->newquirks), + hdaa_sysctl_quirks, "A", "Configuration options"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "gpi_state", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + devinfo, sizeof(devinfo), + hdaa_sysctl_gpi_state, "A", "GPI state"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "gpio_state", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + devinfo, sizeof(devinfo), + hdaa_sysctl_gpio_state, "A", "GPIO state"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "gpio_config", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, + devinfo, sizeof(devinfo), + hdaa_sysctl_gpio_config, "A", "GPIO configuration"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "gpo_state", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + devinfo, sizeof(devinfo), + hdaa_sysctl_gpo_state, "A", "GPO state"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "gpo_config", CTLTYPE_STRING | CTLFLAG_RW | CTLFLAG_MPSAFE, + devinfo, sizeof(devinfo), + hdaa_sysctl_gpo_config, "A", "GPO configuration"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "reconfig", CTLTYPE_INT | CTLFLAG_RW, + dev, sizeof(dev), + hdaa_sysctl_reconfig, "I", "Reprocess configuration"); + bus_generic_attach(dev); + return (0); +} + +static int +hdaa_detach(device_t dev) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + int error; + + if ((error = device_delete_children(dev)) != 0) + return (error); + + hdaa_lock(devinfo); + hdaa_unconfigure(dev); + devinfo->poll_ival = 0; + callout_stop(&devinfo->poll_jack); + hdaa_unlock(devinfo); + callout_drain(&devinfo->poll_jack); + + free(devinfo->widget, M_HDAA); + return (0); +} + +static int +hdaa_print_child(device_t dev, device_t child) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + struct hdaa_pcm_devinfo *pdevinfo = + (struct hdaa_pcm_devinfo *)device_get_ivars(child); + struct hdaa_audio_as *as; + int retval, first = 1, i; + + retval = bus_print_child_header(dev, child); + retval += printf(" at nid "); + if (pdevinfo->playas >= 0) { + as = &devinfo->as[pdevinfo->playas]; + for (i = 0; i < 16; i++) { + if (as->pins[i] <= 0) + continue; + retval += printf("%s%d", first ? "" : ",", as->pins[i]); + first = 0; + } + } + if (pdevinfo->recas >= 0) { + if (pdevinfo->playas >= 0) { + retval += printf(" and "); + first = 1; + } + as = &devinfo->as[pdevinfo->recas]; + for (i = 0; i < 16; i++) { + if (as->pins[i] <= 0) + continue; + retval += printf("%s%d", first ? "" : ",", as->pins[i]); + first = 0; + } + } + retval += bus_print_child_footer(dev, child); + + return (retval); +} + +static int +hdaa_child_location_str(device_t dev, device_t child, char *buf, + size_t buflen) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + struct hdaa_pcm_devinfo *pdevinfo = + (struct hdaa_pcm_devinfo *)device_get_ivars(child); + struct hdaa_audio_as *as; + int first = 1, i, len = 0; + + len += snprintf(buf + len, buflen - len, "nid="); + if (pdevinfo->playas >= 0) { + as = &devinfo->as[pdevinfo->playas]; + for (i = 0; i < 16; i++) { + if (as->pins[i] <= 0) + continue; + len += snprintf(buf + len, buflen - len, + "%s%d", first ? "" : ",", as->pins[i]); + first = 0; + } + } + if (pdevinfo->recas >= 0) { + as = &devinfo->as[pdevinfo->recas]; + for (i = 0; i < 16; i++) { + if (as->pins[i] <= 0) + continue; + len += snprintf(buf + len, buflen - len, + "%s%d", first ? "" : ",", as->pins[i]); + first = 0; + } + } + return (0); +} + +static void +hdaa_stream_intr(device_t dev, int dir, int stream) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + struct hdaa_chan *ch; + int i; + + for (i = 0; i < devinfo->num_chans; i++) { + ch = &devinfo->chans[i]; + if (!(ch->flags & HDAA_CHN_RUNNING)) + continue; + if (ch->dir == ((dir == 1) ? PCMDIR_PLAY : PCMDIR_REC) && + ch->sid == stream) { + hdaa_unlock(devinfo); + chn_intr(ch->c); + hdaa_lock(devinfo); + } + } +} + +static void +hdaa_unsol_intr(device_t dev, uint32_t resp) +{ + struct hdaa_devinfo *devinfo = device_get_softc(dev); + int i, tag; + + tag = resp >> 26; + for (i = 0; i < devinfo->ascnt; i++) { + if (devinfo->as[i].unsol == tag) + hdaa_hp_switch_handler(devinfo, i); + } +} + +static device_method_t hdaa_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, hdaa_probe), + DEVMETHOD(device_attach, hdaa_attach), + DEVMETHOD(device_detach, hdaa_detach), + DEVMETHOD(device_suspend, hdaa_suspend), + DEVMETHOD(device_resume, hdaa_resume), + /* Bus interface */ + DEVMETHOD(bus_print_child, hdaa_print_child), + DEVMETHOD(bus_child_location_str, hdaa_child_location_str), + DEVMETHOD(hdac_stream_intr, hdaa_stream_intr), + DEVMETHOD(hdac_unsol_intr, hdaa_unsol_intr), + DEVMETHOD(hdac_pindump, hdaa_pindump), + { 0, 0 } +}; + +static driver_t hdaa_driver = { + "hdaa", + hdaa_methods, + sizeof(struct hdaa_devinfo), +}; + +static devclass_t hdaa_devclass; + +DRIVER_MODULE(snd_hda, hdacc, hdaa_driver, hdaa_devclass, 0, 0); + +static void +hdaa_chan_formula(struct hdaa_devinfo *devinfo, int asid, + char *buf, int buflen) +{ + struct hdaa_audio_as *as; + int c; + + as = &devinfo->as[asid]; + c = devinfo->chans[as->chans[0]].channels; + if (c == 1) + snprintf(buf, buflen, "mono"); + else if (c == 2) + buf[0] = 0; + else if (as->pinset == 0x0003) + snprintf(buf, buflen, "3.1"); + else if (as->pinset == 0x0005 || as->pinset == 0x0011) + snprintf(buf, buflen, "4.0"); + else if (as->pinset == 0x0007 || as->pinset == 0x0013) + snprintf(buf, buflen, "5.1"); + else if (as->pinset == 0x0017) + snprintf(buf, buflen, "7.1"); + else + snprintf(buf, buflen, "%dch", c); +} + +static int +hdaa_pcm_probe(device_t dev) +{ + struct hdaa_pcm_devinfo *pdevinfo = + (struct hdaa_pcm_devinfo *)device_get_ivars(dev); + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + char chans1[8], chans2[8]; + char buf[128]; + int loc1, loc2; + + if (pdevinfo->playas >= 0) + loc1 = devinfo->as[pdevinfo->playas].location; + else + loc1 = devinfo->as[pdevinfo->recas].location; + if (pdevinfo->recas >= 0) + loc2 = devinfo->as[pdevinfo->recas].location; + else + loc2 = loc1; + if (loc1 != loc2) + loc1 = -2; + if (loc1 >= 0 && HDA_LOCS[loc1][0] == '0') + loc1 = -2; + chans1[0] = 0; + chans2[0] = 0; + if (pdevinfo->playas >= 0) + hdaa_chan_formula(devinfo, pdevinfo->playas, + chans1, sizeof(chans1)); + if (pdevinfo->recas >= 0) + hdaa_chan_formula(devinfo, pdevinfo->recas, + chans2, sizeof(chans2)); + if (chans1[0] != 0 || chans2[0] != 0) { + if (chans1[0] == 0 && pdevinfo->playas >= 0) + snprintf(chans1, sizeof(chans1), "2.0"); + else if (chans2[0] == 0 && pdevinfo->recas >= 0) + snprintf(chans2, sizeof(chans2), "2.0"); + if (strcmp(chans1, chans2) == 0) + chans2[0] = 0; + } + snprintf(buf, sizeof(buf), "%s PCM (%s%s%s%s%s%s%s)", + device_get_desc(device_get_parent(device_get_parent(dev))), + loc1 >= 0 ? HDA_LOCS[loc1] : "", loc1 >= 0 ? " " : "", + (pdevinfo->digital == 3)?"DisplayPort": + ((pdevinfo->digital == 2)?"HDMI": + ((pdevinfo->digital)?"Digital":"Analog")), + chans1[0] ? " " : "", chans1, + chans2[0] ? "/" : "", chans2); + device_set_desc_copy(dev, buf); + return (BUS_PROBE_SPECIFIC); +} + +static int +hdaa_pcm_attach(device_t dev) +{ + struct hdaa_pcm_devinfo *pdevinfo = + (struct hdaa_pcm_devinfo *)device_get_ivars(dev); + struct hdaa_devinfo *devinfo = pdevinfo->devinfo; + struct hdaa_audio_as *as; + char status[SND_STATUSLEN]; + int i; + + pdevinfo->chan_size = pcm_getbuffersize(dev, + HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_MAX); + + HDA_BOOTVERBOSE( + device_printf(dev, "+--------------------------------------+\n"); + device_printf(dev, "| DUMPING PCM Playback/Record Channels |\n"); + device_printf(dev, "+--------------------------------------+\n"); + hdaa_dump_pcmchannels(pdevinfo); + device_printf(dev, "\n"); + device_printf(dev, "+-------------------------------+\n"); + device_printf(dev, "| DUMPING Playback/Record Paths |\n"); + device_printf(dev, "+-------------------------------+\n"); + hdaa_dump_dac(pdevinfo); + hdaa_dump_adc(pdevinfo); + hdaa_dump_mix(pdevinfo); + device_printf(dev, "\n"); + device_printf(dev, "+-------------------------+\n"); + device_printf(dev, "| DUMPING Volume Controls |\n"); + device_printf(dev, "+-------------------------+\n"); + hdaa_dump_ctls(pdevinfo, "Master Volume", SOUND_MASK_VOLUME); + hdaa_dump_ctls(pdevinfo, "PCM Volume", SOUND_MASK_PCM); + hdaa_dump_ctls(pdevinfo, "CD Volume", SOUND_MASK_CD); + hdaa_dump_ctls(pdevinfo, "Microphone Volume", SOUND_MASK_MIC); + hdaa_dump_ctls(pdevinfo, "Microphone2 Volume", SOUND_MASK_MONITOR); + hdaa_dump_ctls(pdevinfo, "Line-in Volume", SOUND_MASK_LINE); + hdaa_dump_ctls(pdevinfo, "Speaker/Beep Volume", SOUND_MASK_SPEAKER); + hdaa_dump_ctls(pdevinfo, "Recording Level", SOUND_MASK_RECLEV); + hdaa_dump_ctls(pdevinfo, "Input Mix Level", SOUND_MASK_IMIX); + hdaa_dump_ctls(pdevinfo, "Input Monitoring Level", SOUND_MASK_IGAIN); + hdaa_dump_ctls(pdevinfo, NULL, 0); + device_printf(dev, "\n"); + ); + + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "blocksize", &i) == 0 && i > 0) { + i &= HDA_BLK_ALIGN; + if (i < HDA_BLK_MIN) + i = HDA_BLK_MIN; + pdevinfo->chan_blkcnt = pdevinfo->chan_size / i; + i = 0; + while (pdevinfo->chan_blkcnt >> i) + i++; + pdevinfo->chan_blkcnt = 1 << (i - 1); + if (pdevinfo->chan_blkcnt < HDA_BDL_MIN) + pdevinfo->chan_blkcnt = HDA_BDL_MIN; + else if (pdevinfo->chan_blkcnt > HDA_BDL_MAX) + pdevinfo->chan_blkcnt = HDA_BDL_MAX; + } else + pdevinfo->chan_blkcnt = HDA_BDL_DEFAULT; + + /* + * We don't register interrupt handler with snd_setup_intr + * in pcm device. Mark pcm device as MPSAFE manually. + */ + pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); + + HDA_BOOTHVERBOSE( + device_printf(dev, "OSS mixer initialization...\n"); + ); + if (mixer_init(dev, &hdaa_audio_ctl_ossmixer_class, pdevinfo) != 0) + device_printf(dev, "Can't register mixer\n"); + + HDA_BOOTHVERBOSE( + device_printf(dev, "Registering PCM channels...\n"); + ); + if (pcm_register(dev, pdevinfo, (pdevinfo->playas >= 0)?1:0, + (pdevinfo->recas >= 0)?1:0) != 0) + device_printf(dev, "Can't register PCM\n"); + + pdevinfo->registered++; + + if (pdevinfo->playas >= 0) { + as = &devinfo->as[pdevinfo->playas]; + for (i = 0; i < as->num_chans; i++) + pcm_addchan(dev, PCMDIR_PLAY, &hdaa_channel_class, + &devinfo->chans[as->chans[i]]); + } + if (pdevinfo->recas >= 0) { + as = &devinfo->as[pdevinfo->recas]; + for (i = 0; i < as->num_chans; i++) + pcm_addchan(dev, PCMDIR_REC, &hdaa_channel_class, + &devinfo->chans[as->chans[i]]); + } + + snprintf(status, SND_STATUSLEN, "on %s %s", + device_get_nameunit(device_get_parent(dev)), + PCM_KLDSTRING(snd_hda)); + pcm_setstatus(dev, status); + + return (0); +} + +static int +hdaa_pcm_detach(device_t dev) +{ + struct hdaa_pcm_devinfo *pdevinfo = + (struct hdaa_pcm_devinfo *)device_get_ivars(dev); + int err; + + if (pdevinfo->registered > 0) { + err = pcm_unregister(dev); + if (err != 0) + return (err); + } + + return (0); +} + +static device_method_t hdaa_pcm_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, hdaa_pcm_probe), + DEVMETHOD(device_attach, hdaa_pcm_attach), + DEVMETHOD(device_detach, hdaa_pcm_detach), + { 0, 0 } +}; + +static driver_t hdaa_pcm_driver = { + "pcm", + hdaa_pcm_methods, + PCM_SOFTC_SIZE, +}; + +DRIVER_MODULE(snd_hda_pcm, hdaa, hdaa_pcm_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_hda, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(snd_hda, 1); diff --git a/sys/dev/sound/pci/hda/hdaa.h b/sys/dev/sound/pci/hda/hdaa.h new file mode 100644 index 0000000..b56b6d7 --- /dev/null +++ b/sys/dev/sound/pci/hda/hdaa.h @@ -0,0 +1,230 @@ +/*-7 + * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca> + * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org> + * Copyright (c) 2008-2012 Alexander Motin <mav@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * Intel High Definition Audio (Audio function quirks) driver for FreeBSD. + */ + +#ifndef _HDAA_QUIRKS_H_ +#define _HDAA_QUIRKS_H_ + +#define HDAA_GPIO_SHIFT(n) (n * 3) +#define HDAA_GPIO_MASK(n) (0x7 << (n * 3)) +#define HDAA_GPIO_KEEP(n) (0x0 << (n * 3)) +#define HDAA_GPIO_SET(n) (0x1 << (n * 3)) +#define HDAA_GPIO_CLEAR(n) (0x2 << (n * 3)) +#define HDAA_GPIO_DISABLE(n) (0x3 << (n * 3)) +#define HDAA_GPIO_INPUT(n) (0x4 << (n * 3)) + +/* 9 - 25 = anything else */ +#define HDAA_QUIRK_SOFTPCMVOL (1 << 9) +#define HDAA_QUIRK_FIXEDRATE (1 << 10) +#define HDAA_QUIRK_FORCESTEREO (1 << 11) +#define HDAA_QUIRK_EAPDINV (1 << 12) +#define HDAA_QUIRK_SENSEINV (1 << 14) + +/* 26 - 31 = vrefs */ +#define HDAA_QUIRK_IVREF50 (1 << 26) +#define HDAA_QUIRK_IVREF80 (1 << 27) +#define HDAA_QUIRK_IVREF100 (1 << 28) +#define HDAA_QUIRK_OVREF50 (1 << 29) +#define HDAA_QUIRK_OVREF80 (1 << 30) +#define HDAA_QUIRK_OVREF100 (1 << 31) + +#define HDAA_QUIRK_IVREF (HDAA_QUIRK_IVREF50 | HDAA_QUIRK_IVREF80 | \ + HDAA_QUIRK_IVREF100) +#define HDAA_QUIRK_OVREF (HDAA_QUIRK_OVREF50 | HDAA_QUIRK_OVREF80 | \ + HDAA_QUIRK_OVREF100) +#define HDAA_QUIRK_VREF (HDAA_QUIRK_IVREF | HDAA_QUIRK_OVREF) + +#define HDAA_AMP_VOL_DEFAULT (-1) +#define HDAA_AMP_MUTE_DEFAULT (0xffffffff) +#define HDAA_AMP_MUTE_NONE (0) +#define HDAA_AMP_MUTE_LEFT (1 << 0) +#define HDAA_AMP_MUTE_RIGHT (1 << 1) +#define HDAA_AMP_MUTE_ALL (HDAA_AMP_MUTE_LEFT | HDAA_AMP_MUTE_RIGHT) + +#define HDAA_AMP_LEFT_MUTED(v) ((v) & (HDAA_AMP_MUTE_LEFT)) +#define HDAA_AMP_RIGHT_MUTED(v) (((v) & HDAA_AMP_MUTE_RIGHT) >> 1) + +#define HDAA_ADC_MONITOR (1 << 0) + +#define HDAA_CTL_OUT 1 +#define HDAA_CTL_IN 2 + +#define HDA_MAX_CONNS 32 +#define HDA_MAX_NAMELEN 32 + +struct hdaa_widget { + nid_t nid; + int type; + int enable; + int nconns, selconn; + int waspin; + uint32_t pflags; + int bindas; + int bindseqmask; + int ossdev; + uint32_t ossmask; + nid_t conns[HDA_MAX_CONNS]; + u_char connsenable[HDA_MAX_CONNS]; + char name[HDA_MAX_NAMELEN]; + struct hdaa_devinfo *devinfo; + struct { + uint32_t widget_cap; + uint32_t outamp_cap; + uint32_t inamp_cap; + uint32_t supp_stream_formats; + uint32_t supp_pcm_size_rate; + uint32_t eapdbtl; + } param; + union { + struct { + uint32_t config; + uint32_t original; + uint32_t newconf; + uint32_t cap; + uint32_t ctrl; + } pin; + } wclass; +}; + +struct hdaa_audio_ctl { + struct hdaa_widget *widget, *childwidget; + int enable; + int index, dir, ndir; + int mute, step, size, offset; + int left, right, forcemute; + uint32_t muted; + uint32_t ossmask, possmask; +}; + +/* Association is a group of pins bound for some special function. */ +struct hdaa_audio_as { + u_char enable; + u_char index; + u_char dir; + u_char pincnt; + u_char fakeredir; + u_char digital; + uint16_t pinset; + nid_t hpredir; + nid_t pins[16]; + nid_t dacs[2][16]; + int num_chans; + int chans[2]; + int unsol; + int location; /* Pins location, if all have the same */ + int mixed; /* Mixed/multiplexed recording, not multichannel. */ +}; + +struct hdaa_pcm_devinfo { + device_t dev; + struct hdaa_devinfo *devinfo; + int index; + int registered; + int playas, recas; + u_char left[SOUND_MIXER_NRDEVICES]; + u_char right[SOUND_MIXER_NRDEVICES]; + int chan_size; + int chan_blkcnt; + u_char digital; +}; + +struct hdaa_devinfo { + device_t dev; + struct mtx *lock; + nid_t nid; + nid_t startnode, endnode; + uint32_t outamp_cap; + uint32_t inamp_cap; + uint32_t supp_stream_formats; + uint32_t supp_pcm_size_rate; + uint32_t gpio_cap; + uint32_t quirks; + uint32_t newquirks; + uint32_t gpio; + uint32_t newgpio; + uint32_t gpo; + uint32_t newgpo; + int nodecnt; + int ctlcnt; + int ascnt; + int num_devs; + int num_chans; + struct hdaa_widget *widget; + struct hdaa_audio_ctl *ctl; + struct hdaa_audio_as *as; + struct hdaa_pcm_devinfo *devs; + struct hdaa_chan *chans; + struct callout poll_jack; + int poll_ival; +}; + +#define HDAA_CHN_RUNNING 0x00000001 +#define HDAA_CHN_SUSPEND 0x00000002 + +struct hdaa_chan { + struct snd_dbuf *b; + struct pcm_channel *c; + struct pcmchan_caps caps; + struct hdaa_devinfo *devinfo; + struct hdaa_pcm_devinfo *pdevinfo; + uint32_t spd, fmt, fmtlist[16], pcmrates[16]; + uint32_t supp_stream_formats, supp_pcm_size_rate; + uint32_t ptr, prevptr, blkcnt, blksz; + uint32_t *dmapos; + uint32_t flags; + int dir; + int off; + int sid; + int bit16, bit32; + int channels; /* Number of audio channels. */ + int as; /* Number of association. */ + int asindex; /* Index within association. */ + nid_t io[16]; +}; + +#define hdaa_codec_id(devinfo) \ + (((uint32_t)hda_get_vendor_id(devinfo->dev) << 16) + \ + hda_get_device_id(devinfo->dev)) + +#define hdaa_subvendor_id(devinfo) \ + (((uint32_t)hda_get_subvendor_id(devinfo->dev) << 16) + \ + hda_get_subdevice_id(devinfo->dev)) + +struct hdaa_widget *hdaa_widget_get(struct hdaa_devinfo *, nid_t); +uint32_t hdaa_widget_pin_patch(uint32_t config, const char *str); +uint32_t hdaa_gpio_patch(uint32_t gpio, const char *str); + +void hdaa_patch(struct hdaa_devinfo *devinfo); +void hdaa_patch_direct(struct hdaa_devinfo *devinfo); + +#endif diff --git a/sys/dev/sound/pci/hda/hdaa_patches.c b/sys/dev/sound/pci/hda/hdaa_patches.c new file mode 100644 index 0000000..b20c339 --- /dev/null +++ b/sys/dev/sound/pci/hda/hdaa_patches.c @@ -0,0 +1,638 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca> + * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org> + * Copyright (c) 2008-2012 Alexander Motin <mav@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Intel High Definition Audio (Audio function quirks) driver for FreeBSD. + */ + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + +#include <dev/sound/pcm/sound.h> + +#include <sys/ctype.h> + +#include <dev/sound/pci/hda/hdac.h> +#include <dev/sound/pci/hda/hdaa.h> +#include <dev/sound/pci/hda/hda_reg.h> + +SND_DECLARE_FILE("$FreeBSD$"); + +static const struct { + uint32_t model; + uint32_t id; + uint32_t set, unset; + uint32_t gpio; +} hdac_quirks[] = { + /* + * XXX Force stereo quirk. Monoural recording / playback + * on few codecs (especially ALC880) seems broken or + * perhaps unsupported. + */ + { HDA_MATCH_ALL, HDA_MATCH_ALL, + HDAA_QUIRK_FORCESTEREO | HDAA_QUIRK_IVREF, 0, + 0 }, + { ACER_ALL_SUBVENDOR, HDA_MATCH_ALL, + 0, 0, + HDAA_GPIO_SET(0) }, + { ASUS_G2K_SUBVENDOR, HDA_CODEC_ALC660, + 0, 0, + HDAA_GPIO_SET(0) }, + { ASUS_M5200_SUBVENDOR, HDA_CODEC_ALC880, + 0, 0, + HDAA_GPIO_SET(0) }, + { ASUS_A7M_SUBVENDOR, HDA_CODEC_ALC880, + 0, 0, + HDAA_GPIO_SET(0) }, + { ASUS_A7T_SUBVENDOR, HDA_CODEC_ALC882, + 0, 0, + HDAA_GPIO_SET(0) }, + { ASUS_W2J_SUBVENDOR, HDA_CODEC_ALC882, + 0, 0, + HDAA_GPIO_SET(0) }, + { ASUS_U5F_SUBVENDOR, HDA_CODEC_AD1986A, + HDAA_QUIRK_EAPDINV, 0, + 0 }, + { ASUS_A8X_SUBVENDOR, HDA_CODEC_AD1986A, + HDAA_QUIRK_EAPDINV, 0, + 0 }, + { ASUS_F3JC_SUBVENDOR, HDA_CODEC_ALC861, + HDAA_QUIRK_OVREF, 0, + 0 }, + { UNIWILL_9075_SUBVENDOR, HDA_CODEC_ALC861, + HDAA_QUIRK_OVREF, 0, + 0 }, + /*{ ASUS_M2N_SUBVENDOR, HDA_CODEC_AD1988, + HDAA_QUIRK_IVREF80, HDAA_QUIRK_IVREF50 | HDAA_QUIRK_IVREF100, + 0 },*/ + { MEDION_MD95257_SUBVENDOR, HDA_CODEC_ALC880, + 0, 0, + HDAA_GPIO_SET(1) }, + { LENOVO_3KN100_SUBVENDOR, HDA_CODEC_AD1986A, + HDAA_QUIRK_EAPDINV | HDAA_QUIRK_SENSEINV, 0, + 0 }, + { SAMSUNG_Q1_SUBVENDOR, HDA_CODEC_AD1986A, + HDAA_QUIRK_EAPDINV, 0, + 0 }, + { APPLE_MB3_SUBVENDOR, HDA_CODEC_ALC885, + HDAA_QUIRK_OVREF50, 0, + HDAA_GPIO_SET(0) }, + { APPLE_INTEL_MAC, HDA_CODEC_STAC9221, + 0, 0, + HDAA_GPIO_SET(0) | HDAA_GPIO_SET(1) }, + { APPLE_MACBOOKPRO55, HDA_CODEC_CS4206, + 0, 0, + HDAA_GPIO_SET(1) | HDAA_GPIO_SET(3) }, + { DELL_D630_SUBVENDOR, HDA_CODEC_STAC9205X, + 0, 0, + HDAA_GPIO_SET(0) }, + { DELL_V1400_SUBVENDOR, HDA_CODEC_STAC9228X, + 0, 0, + HDAA_GPIO_SET(2) }, + { DELL_V1500_SUBVENDOR, HDA_CODEC_STAC9205X, + 0, 0, + HDAA_GPIO_SET(0) }, + { HDA_MATCH_ALL, HDA_CODEC_AD1988, + HDAA_QUIRK_IVREF80, HDAA_QUIRK_IVREF50 | HDAA_QUIRK_IVREF100, + 0 }, + { HDA_MATCH_ALL, HDA_CODEC_AD1988B, + HDAA_QUIRK_IVREF80, HDAA_QUIRK_IVREF50 | HDAA_QUIRK_IVREF100, + 0 }, + { HDA_MATCH_ALL, HDA_CODEC_CX20549, + 0, HDAA_QUIRK_FORCESTEREO, + 0 } +}; +#define HDAC_QUIRKS_LEN (sizeof(hdac_quirks) / sizeof(hdac_quirks[0])) + +static void +hdac_pin_patch(struct hdaa_widget *w) +{ + const char *patch = NULL; + uint32_t config, orig, id, subid; + nid_t nid = w->nid; + + config = orig = w->wclass.pin.config; + id = hdaa_codec_id(w->devinfo); + subid = hdaa_subvendor_id(w->devinfo); + + /* XXX: Old patches require complete review. + * Now they may create more problem then solve due to + * incorrect associations. + */ + if (id == HDA_CODEC_ALC880 && subid == LG_LW20_SUBVENDOR) { + switch (nid) { + case 26: + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; + break; + case 27: + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT; + break; + default: + break; + } + } else if (id == HDA_CODEC_ALC880 && + (subid == CLEVO_D900T_SUBVENDOR || + subid == ASUS_M5200_SUBVENDOR)) { + /* + * Super broken BIOS + */ + switch (nid) { + case 24: /* MIC1 */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; + break; + case 25: /* XXX MIC2 */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; + break; + case 26: /* LINE1 */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; + break; + case 27: /* XXX LINE2 */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; + break; + case 28: /* CD */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_CD; + break; + } + } else if (id == HDA_CODEC_ALC883 && + (subid == MSI_MS034A_SUBVENDOR || + HDA_DEV_MATCH(ACER_ALL_SUBVENDOR, subid))) { + switch (nid) { + case 25: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + case 28: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + } + } else if (id == HDA_CODEC_CX20549 && subid == + HP_V3000_SUBVENDOR) { + switch (nid) { + case 18: + config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; + config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE; + break; + case 20: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + case 21: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + } + } else if (id == HDA_CODEC_CX20551 && subid == + HP_DV5000_SUBVENDOR) { + switch (nid) { + case 20: + case 21: + config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; + config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE; + break; + } + } else if (id == HDA_CODEC_ALC861 && subid == + ASUS_W6F_SUBVENDOR) { + switch (nid) { + case 11: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + case 12: + case 14: + case 16: + case 31: + case 32: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); + break; + case 15: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); + break; + } + } else if (id == HDA_CODEC_ALC861 && subid == + UNIWILL_9075_SUBVENDOR) { + switch (nid) { + case 15: + config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); + config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); + break; + } + } + + /* New patches */ + if (id == HDA_CODEC_AD1986A && + (subid == ASUS_M2NPVMX_SUBVENDOR || + subid == ASUS_A8NVMCSM_SUBVENDOR || + subid == ASUS_P5PL2_SUBVENDOR)) { + switch (nid) { + case 26: /* Headphones with redirection */ + patch = "as=1 seq=15"; + break; + case 28: /* 5.1 out => 2.0 out + 1 input */ + patch = "device=Line-in as=8 seq=1"; + break; + case 29: /* Can't use this as input, as the only available mic + * preamplifier is busy by front panel mic (nid 31). + * If you want to use this rear connector as mic input, + * you have to disable the front panel one. */ + patch = "as=0"; + break; + case 31: /* Lot of inputs configured with as=15 and unusable */ + patch = "as=8 seq=3"; + break; + case 32: + patch = "as=8 seq=4"; + break; + case 34: + patch = "as=8 seq=5"; + break; + case 36: + patch = "as=8 seq=6"; + break; + } + } else if (id == HDA_CODEC_ALC260 && + HDA_DEV_MATCH(SONY_S5_SUBVENDOR, subid)) { + switch (nid) { + case 16: + patch = "seq=15 device=Headphones"; + break; + } + } else if (id == HDA_CODEC_ALC268) { + if (subid == ACER_T5320_SUBVENDOR) { + switch (nid) { + case 20: /* Headphones Jack */ + patch = "as=1 seq=15"; + break; + } + } + } else if (id == HDA_CODEC_CX20561 && + subid == LENOVO_B450_SUBVENDOR) { + switch (nid) { + case 22: + patch = "as=1 seq=15"; + break; + } + } + + if (patch != NULL) + config = hdaa_widget_pin_patch(config, patch); + HDA_BOOTVERBOSE( + if (config != orig) + device_printf(w->devinfo->dev, + "Patching pin config nid=%u 0x%08x -> 0x%08x\n", + nid, orig, config); + ); + w->wclass.pin.config = config; +} + +static void +hdaa_widget_patch(struct hdaa_widget *w) +{ + struct hdaa_devinfo *devinfo = w->devinfo; + uint32_t orig; + nid_t beeper = -1; + + orig = w->param.widget_cap; + /* On some codecs beeper is an input pin, but it is not recordable + alone. Also most of BIOSes does not declare beeper pin. + Change beeper pin node type to beeper to help parser. */ + switch (hdaa_codec_id(devinfo)) { + case HDA_CODEC_AD1882: + case HDA_CODEC_AD1883: + case HDA_CODEC_AD1984: + case HDA_CODEC_AD1984A: + case HDA_CODEC_AD1984B: + case HDA_CODEC_AD1987: + case HDA_CODEC_AD1988: + case HDA_CODEC_AD1988B: + case HDA_CODEC_AD1989B: + beeper = 26; + break; + case HDA_CODEC_ALC260: + beeper = 23; + break; + } + if (hda_get_vendor_id(devinfo->dev) == REALTEK_VENDORID && + hdaa_codec_id(devinfo) != HDA_CODEC_ALC260) + beeper = 29; + if (w->nid == beeper) { + w->param.widget_cap &= ~HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK; + w->param.widget_cap |= HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET << + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT; + w->waspin = 1; + } + HDA_BOOTVERBOSE( + if (w->param.widget_cap != orig) { + device_printf(w->devinfo->dev, + "Patching widget caps nid=%u 0x%08x -> 0x%08x\n", + w->nid, orig, w->param.widget_cap); + } + ); + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + hdac_pin_patch(w); +} + +void +hdaa_patch(struct hdaa_devinfo *devinfo) +{ + struct hdaa_widget *w; + uint32_t id, subid; + int i; + + id = hdaa_codec_id(devinfo); + subid = hdaa_subvendor_id(devinfo); + + /* + * Quirks + */ + for (i = 0; i < HDAC_QUIRKS_LEN; i++) { + if (!(HDA_DEV_MATCH(hdac_quirks[i].model, subid) && + HDA_DEV_MATCH(hdac_quirks[i].id, id))) + continue; + if (hdac_quirks[i].set != 0) + devinfo->quirks |= + hdac_quirks[i].set; + if (hdac_quirks[i].unset != 0) + devinfo->quirks &= + ~(hdac_quirks[i].unset); + } + + /* Apply per-widget patch. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdaa_widget_get(devinfo, i); + if (w == NULL) + continue; + hdaa_widget_patch(w); + } + + switch (id) { + case HDA_CODEC_AD1983: + /* + * This CODEC has several possible usages, but none + * fit the parser best. Help parser to choose better. + */ + /* Disable direct unmixed playback to get pcm volume. */ + w = hdaa_widget_get(devinfo, 5); + if (w != NULL) + w->connsenable[0] = 0; + w = hdaa_widget_get(devinfo, 6); + if (w != NULL) + w->connsenable[0] = 0; + w = hdaa_widget_get(devinfo, 11); + if (w != NULL) + w->connsenable[0] = 0; + /* Disable mic and line selectors. */ + w = hdaa_widget_get(devinfo, 12); + if (w != NULL) + w->connsenable[1] = 0; + w = hdaa_widget_get(devinfo, 13); + if (w != NULL) + w->connsenable[1] = 0; + /* Disable recording from mono playback mix. */ + w = hdaa_widget_get(devinfo, 20); + if (w != NULL) + w->connsenable[3] = 0; + break; + case HDA_CODEC_AD1986A: + /* + * This CODEC has overcomplicated input mixing. + * Make some cleaning there. + */ + /* Disable input mono mixer. Not needed and not supported. */ + w = hdaa_widget_get(devinfo, 43); + if (w != NULL) + w->enable = 0; + /* Disable any with any input mixing mesh. Use separately. */ + w = hdaa_widget_get(devinfo, 39); + if (w != NULL) + w->enable = 0; + w = hdaa_widget_get(devinfo, 40); + if (w != NULL) + w->enable = 0; + w = hdaa_widget_get(devinfo, 41); + if (w != NULL) + w->enable = 0; + w = hdaa_widget_get(devinfo, 42); + if (w != NULL) + w->enable = 0; + /* Disable duplicate mixer node connector. */ + w = hdaa_widget_get(devinfo, 15); + if (w != NULL) + w->connsenable[3] = 0; + /* There is only one mic preamplifier, use it effectively. */ + w = hdaa_widget_get(devinfo, 31); + if (w != NULL) { + if ((w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) == + HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN) { + w = hdaa_widget_get(devinfo, 16); + if (w != NULL) + w->connsenable[2] = 0; + } else { + w = hdaa_widget_get(devinfo, 15); + if (w != NULL) + w->connsenable[0] = 0; + } + } + w = hdaa_widget_get(devinfo, 32); + if (w != NULL) { + if ((w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) == + HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN) { + w = hdaa_widget_get(devinfo, 16); + if (w != NULL) + w->connsenable[0] = 0; + } else { + w = hdaa_widget_get(devinfo, 15); + if (w != NULL) + w->connsenable[1] = 0; + } + } + + if (subid == ASUS_A8X_SUBVENDOR) { + /* + * This is just plain ridiculous.. There + * are several A8 series that share the same + * pci id but works differently (EAPD). + */ + w = hdaa_widget_get(devinfo, 26); + if (w != NULL && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + (w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) != + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) + devinfo->quirks &= + ~HDAA_QUIRK_EAPDINV; + } + break; + case HDA_CODEC_AD1981HD: + /* + * This CODEC has very unusual design with several + * points inappropriate for the present parser. + */ + /* Disable recording from mono playback mix. */ + w = hdaa_widget_get(devinfo, 21); + if (w != NULL) + w->connsenable[3] = 0; + /* Disable rear to front mic mixer, use separately. */ + w = hdaa_widget_get(devinfo, 31); + if (w != NULL) + w->enable = 0; + /* Disable direct playback, use mixer. */ + w = hdaa_widget_get(devinfo, 5); + if (w != NULL) + w->connsenable[0] = 0; + w = hdaa_widget_get(devinfo, 6); + if (w != NULL) + w->connsenable[0] = 0; + w = hdaa_widget_get(devinfo, 9); + if (w != NULL) + w->connsenable[0] = 0; + w = hdaa_widget_get(devinfo, 24); + if (w != NULL) + w->connsenable[0] = 0; + break; + case HDA_CODEC_CX20582: + case HDA_CODEC_CX20583: + case HDA_CODEC_CX20584: + case HDA_CODEC_CX20585: + case HDA_CODEC_CX20590: + /* + * These codecs have extra connectivity on record side + * too reach for the present parser. + */ + w = hdaa_widget_get(devinfo, 20); + if (w != NULL) + w->connsenable[1] = 0; + w = hdaa_widget_get(devinfo, 21); + if (w != NULL) + w->connsenable[1] = 0; + w = hdaa_widget_get(devinfo, 22); + if (w != NULL) + w->connsenable[0] = 0; + break; + case HDA_CODEC_VT1708S_0: + case HDA_CODEC_VT1708S_1: + case HDA_CODEC_VT1708S_2: + case HDA_CODEC_VT1708S_3: + case HDA_CODEC_VT1708S_4: + case HDA_CODEC_VT1708S_5: + case HDA_CODEC_VT1708S_6: + case HDA_CODEC_VT1708S_7: + /* + * These codecs have hidden mic boost controls. + */ + w = hdaa_widget_get(devinfo, 26); + if (w != NULL) + w->param.inamp_cap = + (40 << HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT) | + (3 << HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT) | + (0 << HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT); + w = hdaa_widget_get(devinfo, 30); + if (w != NULL) + w->param.inamp_cap = + (40 << HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT) | + (3 << HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT) | + (0 << HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT); + break; + } +} + +void +hdaa_patch_direct(struct hdaa_devinfo *devinfo) +{ + device_t dev = devinfo->dev; + uint32_t id, subid, val; + + id = hdaa_codec_id(devinfo); + subid = hdaa_subvendor_id(devinfo); + + switch (id) { + case HDA_CODEC_VT1708S_0: + case HDA_CODEC_VT1708S_1: + case HDA_CODEC_VT1708S_2: + case HDA_CODEC_VT1708S_3: + case HDA_CODEC_VT1708S_4: + case HDA_CODEC_VT1708S_5: + case HDA_CODEC_VT1708S_6: + case HDA_CODEC_VT1708S_7: + /* Enable Mic Boost Volume controls. */ + hda_command(dev, HDA_CMD_12BIT(0, devinfo->nid, + 0xf98, 0x01)); + /* Don't bypass mixer. */ + hda_command(dev, HDA_CMD_12BIT(0, devinfo->nid, + 0xf88, 0xc0)); + break; + } + if (subid == APPLE_INTEL_MAC) + hda_command(dev, HDA_CMD_12BIT(0, devinfo->nid, + 0x7e7, 0)); + if (id == HDA_CODEC_ALC269) { + if (subid == 0x104316e3 || subid == 0x1043831a || + subid == 0x1043834a || subid == 0x10438398 || + subid == 0x104383ce) { + /* + * The ditital mics on some Asus laptops produce + * differential signals instead of expected stereo. + * That results in silence if downmix it to mono. + * To workaround, make codec to handle signal as mono. + */ + hda_command(dev, HDA_CMD_SET_COEFF_INDEX(0, 0x20, 0x07)); + val = hda_command(dev, HDA_CMD_GET_PROCESSING_COEFF(0, 0x20)); + hda_command(dev, HDA_CMD_SET_COEFF_INDEX(0, 0x20, 0x07)); + hda_command(dev, HDA_CMD_SET_PROCESSING_COEFF(0, 0x20, val|0x80)); + } + } +} diff --git a/sys/dev/sound/pci/hda/hdac.c b/sys/dev/sound/pci/hda/hdac.c index a190e0b..0f11b29 100644 --- a/sys/dev/sound/pci/hda/hdac.c +++ b/sys/dev/sound/pci/hda/hdac.c @@ -1,7 +1,7 @@ /*- * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca> * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org> - * Copyright (c) 2008 Alexander Motin <mav@FreeBSD.org> + * Copyright (c) 2008-2012 Alexander Motin <mav@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,45 +27,7 @@ */ /* - * Intel High Definition Audio (Controller) driver for FreeBSD. Be advised - * that this driver still in its early stage, and possible of rewrite are - * pretty much guaranteed. There are supposedly several distinct parent/child - * busses to make this "perfect", but as for now and for the sake of - * simplicity, everything is gobble up within single source. - * - * List of subsys: - * 1) HDA Controller support - * 2) HDA Codecs support, which may include - * - HDA - * - Modem - * 3) Widget parser - the real magic of why this driver works on so - * many hardwares with minimal vendor specific quirk. The original - * parser was written using Ruby and can be found at - * http://people.freebsd.org/~ariff/HDA/parser.rb . This crude - * ruby parser take the verbose dmesg dump as its input. Refer to - * http://www.microsoft.com/whdc/device/audio/default.mspx for various - * interesting documents, especially UAA (Universal Audio Architecture). - * 4) Possible vendor specific support. - * (snd_hda_intel, snd_hda_ati, etc..) - * - * Thanks to Ahmad Ubaidah Omar @ Defenxis Sdn. Bhd. for the - * Compaq V3000 with Conexant HDA. - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * * - * * This driver is a collaborative effort made by: * - * * * - * * Stephane E. Potvin <sepotvin@videotron.ca> * - * * Andrea Bittau <a.bittau@cs.ucl.ac.uk> * - * * Wesley Morgan <morganw@chemikals.org> * - * * Daniel Eischen <deischen@FreeBSD.org> * - * * Maxime Guillaud <bsd-ports@mguillaud.net> * - * * Ariff Abdullah <ariff@FreeBSD.org> * - * * Alexander Motin <mav@FreeBSD.org> * - * * * - * * ....and various people from freebsd-multimedia@FreeBSD.org * - * * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Intel High Definition Audio (Controller) driver for FreeBSD. */ #ifdef HAVE_KERNEL_OPTION_HEADERS @@ -84,370 +46,26 @@ #include <dev/sound/pci/hda/hda_reg.h> #include <dev/sound/pci/hda/hdac.h> -#include "mixer_if.h" - -#define HDA_DRV_TEST_REV "20100226_0142" +#define HDA_DRV_TEST_REV "20120111_0001" SND_DECLARE_FILE("$FreeBSD$"); -#define HDA_BOOTVERBOSE(stmt) do { \ - if (bootverbose != 0 || snd_verbose > 3) { \ - stmt \ - } \ -} while (0) - -#define HDA_BOOTHVERBOSE(stmt) do { \ - if (snd_verbose > 3) { \ - stmt \ - } \ -} while (0) - -#if 1 -#undef HDAC_INTR_EXTRA -#define HDAC_INTR_EXTRA 1 -#endif - #define hdac_lock(sc) snd_mtxlock((sc)->lock) #define hdac_unlock(sc) snd_mtxunlock((sc)->lock) #define hdac_lockassert(sc) snd_mtxassert((sc)->lock) #define hdac_lockowned(sc) mtx_owned((sc)->lock) -#define HDA_FLAG_MATCH(fl, v) (((fl) & (v)) == (v)) -#define HDA_DEV_MATCH(fl, v) ((fl) == (v) || \ - (fl) == 0xffffffff || \ - (((fl) & 0xffff0000) == 0xffff0000 && \ - ((fl) & 0x0000ffff) == ((v) & 0x0000ffff)) || \ - (((fl) & 0x0000ffff) == 0x0000ffff && \ - ((fl) & 0xffff0000) == ((v) & 0xffff0000))) -#define HDA_MATCH_ALL 0xffffffff -#define HDAC_INVALID 0xffffffff - -/* Default controller / jack sense poll: 250ms */ -#define HDAC_POLL_INTERVAL max(hz >> 2, 1) - -/* - * Make room for possible 4096 playback/record channels, in 100 years to come. - */ -#define HDAC_TRIGGER_NONE 0x00000000 -#define HDAC_TRIGGER_PLAY 0x00000fff -#define HDAC_TRIGGER_REC 0x00fff000 -#define HDAC_TRIGGER_UNSOL 0x80000000 - -#define HDA_MODEL_CONSTRUCT(vendor, model) \ - (((uint32_t)(model) << 16) | ((vendor##_VENDORID) & 0xffff)) - -/* Controller models */ - -/* Intel */ -#define INTEL_VENDORID 0x8086 -#define HDA_INTEL_CPT HDA_MODEL_CONSTRUCT(INTEL, 0x1c20) -#define HDA_INTEL_PATSBURG HDA_MODEL_CONSTRUCT(INTEL, 0x1d20) -#define HDA_INTEL_PPT1 HDA_MODEL_CONSTRUCT(INTEL, 0x1e20) -#define HDA_INTEL_82801F HDA_MODEL_CONSTRUCT(INTEL, 0x2668) -#define HDA_INTEL_63XXESB HDA_MODEL_CONSTRUCT(INTEL, 0x269a) -#define HDA_INTEL_82801G HDA_MODEL_CONSTRUCT(INTEL, 0x27d8) -#define HDA_INTEL_82801H HDA_MODEL_CONSTRUCT(INTEL, 0x284b) -#define HDA_INTEL_82801I HDA_MODEL_CONSTRUCT(INTEL, 0x293e) -#define HDA_INTEL_82801JI HDA_MODEL_CONSTRUCT(INTEL, 0x3a3e) -#define HDA_INTEL_82801JD HDA_MODEL_CONSTRUCT(INTEL, 0x3a6e) -#define HDA_INTEL_PCH HDA_MODEL_CONSTRUCT(INTEL, 0x3b56) -#define HDA_INTEL_PCH2 HDA_MODEL_CONSTRUCT(INTEL, 0x3b57) -#define HDA_INTEL_SCH HDA_MODEL_CONSTRUCT(INTEL, 0x811b) -#define HDA_INTEL_ALL HDA_MODEL_CONSTRUCT(INTEL, 0xffff) - -/* Nvidia */ -#define NVIDIA_VENDORID 0x10de -#define HDA_NVIDIA_MCP51 HDA_MODEL_CONSTRUCT(NVIDIA, 0x026c) -#define HDA_NVIDIA_MCP55 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0371) -#define HDA_NVIDIA_MCP61_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x03e4) -#define HDA_NVIDIA_MCP61_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x03f0) -#define HDA_NVIDIA_MCP65_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x044a) -#define HDA_NVIDIA_MCP65_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x044b) -#define HDA_NVIDIA_MCP67_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x055c) -#define HDA_NVIDIA_MCP67_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x055d) -#define HDA_NVIDIA_MCP78_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0774) -#define HDA_NVIDIA_MCP78_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0775) -#define HDA_NVIDIA_MCP78_3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0776) -#define HDA_NVIDIA_MCP78_4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0777) -#define HDA_NVIDIA_MCP73_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x07fc) -#define HDA_NVIDIA_MCP73_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x07fd) -#define HDA_NVIDIA_MCP79_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac0) -#define HDA_NVIDIA_MCP79_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac1) -#define HDA_NVIDIA_MCP79_3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac2) -#define HDA_NVIDIA_MCP79_4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac3) -#define HDA_NVIDIA_MCP89_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d94) -#define HDA_NVIDIA_MCP89_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d95) -#define HDA_NVIDIA_MCP89_3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d96) -#define HDA_NVIDIA_MCP89_4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d97) -#define HDA_NVIDIA_ALL HDA_MODEL_CONSTRUCT(NVIDIA, 0xffff) - -/* ATI */ -#define ATI_VENDORID 0x1002 -#define HDA_ATI_SB450 HDA_MODEL_CONSTRUCT(ATI, 0x437b) -#define HDA_ATI_SB600 HDA_MODEL_CONSTRUCT(ATI, 0x4383) -#define HDA_ATI_RS600 HDA_MODEL_CONSTRUCT(ATI, 0x793b) -#define HDA_ATI_RS690 HDA_MODEL_CONSTRUCT(ATI, 0x7919) -#define HDA_ATI_RS780 HDA_MODEL_CONSTRUCT(ATI, 0x960f) -#define HDA_ATI_R600 HDA_MODEL_CONSTRUCT(ATI, 0xaa00) -#define HDA_ATI_RV630 HDA_MODEL_CONSTRUCT(ATI, 0xaa08) -#define HDA_ATI_RV610 HDA_MODEL_CONSTRUCT(ATI, 0xaa10) -#define HDA_ATI_RV670 HDA_MODEL_CONSTRUCT(ATI, 0xaa18) -#define HDA_ATI_RV635 HDA_MODEL_CONSTRUCT(ATI, 0xaa20) -#define HDA_ATI_RV620 HDA_MODEL_CONSTRUCT(ATI, 0xaa28) -#define HDA_ATI_RV770 HDA_MODEL_CONSTRUCT(ATI, 0xaa30) -#define HDA_ATI_RV730 HDA_MODEL_CONSTRUCT(ATI, 0xaa38) -#define HDA_ATI_RV710 HDA_MODEL_CONSTRUCT(ATI, 0xaa40) -#define HDA_ATI_RV740 HDA_MODEL_CONSTRUCT(ATI, 0xaa48) -#define HDA_ATI_ALL HDA_MODEL_CONSTRUCT(ATI, 0xffff) - -/* RDC */ -#define RDC_VENDORID 0x17f3 -#define HDA_RDC_M3010 HDA_MODEL_CONSTRUCT(RDC, 0x3010) - -/* VIA */ -#define VIA_VENDORID 0x1106 -#define HDA_VIA_VT82XX HDA_MODEL_CONSTRUCT(VIA, 0x3288) -#define HDA_VIA_ALL HDA_MODEL_CONSTRUCT(VIA, 0xffff) - -/* SiS */ -#define SIS_VENDORID 0x1039 -#define HDA_SIS_966 HDA_MODEL_CONSTRUCT(SIS, 0x7502) -#define HDA_SIS_ALL HDA_MODEL_CONSTRUCT(SIS, 0xffff) - -/* ULI */ -#define ULI_VENDORID 0x10b9 -#define HDA_ULI_M5461 HDA_MODEL_CONSTRUCT(ULI, 0x5461) -#define HDA_ULI_ALL HDA_MODEL_CONSTRUCT(ULI, 0xffff) - -/* OEM/subvendors */ - -/* Intel */ -#define INTEL_D101GGC_SUBVENDOR HDA_MODEL_CONSTRUCT(INTEL, 0xd600) - -/* HP/Compaq */ -#define HP_VENDORID 0x103c -#define HP_V3000_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30b5) -#define HP_NX7400_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30a2) -#define HP_NX6310_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30aa) -#define HP_NX6325_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30b0) -#define HP_XW4300_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x3013) -#define HP_3010_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x3010) -#define HP_DV5000_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30a5) -#define HP_DC7700S_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x2801) -#define HP_DC7700_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x2802) -#define HP_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0xffff) -/* What is wrong with XN 2563 anyway? (Got the picture ?) */ -#define HP_NX6325_SUBVENDORX 0x103c30b0 - -/* Dell */ -#define DELL_VENDORID 0x1028 -#define DELL_D630_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01f9) -#define DELL_D820_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01cc) -#define DELL_V1400_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x0227) -#define DELL_V1500_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x0228) -#define DELL_I1300_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01c9) -#define DELL_XPSM1210_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01d7) -#define DELL_OPLX745_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01da) -#define DELL_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0xffff) - -/* Clevo */ -#define CLEVO_VENDORID 0x1558 -#define CLEVO_D900T_SUBVENDOR HDA_MODEL_CONSTRUCT(CLEVO, 0x0900) -#define CLEVO_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(CLEVO, 0xffff) - -/* Acer */ -#define ACER_VENDORID 0x1025 -#define ACER_A5050_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x010f) -#define ACER_A4520_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0127) -#define ACER_A4710_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x012f) -#define ACER_A4715_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0133) -#define ACER_3681WXM_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0110) -#define ACER_T6292_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x011b) -#define ACER_T5320_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x011f) -#define ACER_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0xffff) - -/* Asus */ -#define ASUS_VENDORID 0x1043 -#define ASUS_A8X_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1153) -#define ASUS_U5F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) -#define ASUS_W6F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) -#define ASUS_A7M_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1323) -#define ASUS_F3JC_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1338) -#define ASUS_G2K_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1339) -#define ASUS_A7T_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x13c2) -#define ASUS_W2J_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1971) -#define ASUS_M5200_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1993) -#define ASUS_P5PL2_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x817f) -#define ASUS_P1AH2_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb) -#define ASUS_M2NPVMX_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb) -#define ASUS_M2V_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81e7) -#define ASUS_P5BWD_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81ec) -#define ASUS_M2N_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x8234) -#define ASUS_A8NVMCSM_SUBVENDOR HDA_MODEL_CONSTRUCT(NVIDIA, 0xcb84) -#define ASUS_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0xffff) - -/* IBM / Lenovo */ -#define IBM_VENDORID 0x1014 -#define IBM_M52_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0x02f6) -#define IBM_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0xffff) - -/* Lenovo */ -#define LENOVO_VENDORID 0x17aa -#define LENOVO_3KN100_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x2066) -#define LENOVO_3KN200_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x384e) -#define LENOVO_B450_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x3a0d) -#define LENOVO_TCA55_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x1015) -#define LENOVO_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0xffff) - -/* Samsung */ -#define SAMSUNG_VENDORID 0x144d -#define SAMSUNG_Q1_SUBVENDOR HDA_MODEL_CONSTRUCT(SAMSUNG, 0xc027) -#define SAMSUNG_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(SAMSUNG, 0xffff) - -/* Medion ? */ -#define MEDION_VENDORID 0x161f -#define MEDION_MD95257_SUBVENDOR HDA_MODEL_CONSTRUCT(MEDION, 0x203d) -#define MEDION_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(MEDION, 0xffff) - -/* Apple Computer Inc. */ -#define APPLE_VENDORID 0x106b -#define APPLE_MB3_SUBVENDOR HDA_MODEL_CONSTRUCT(APPLE, 0x00a1) - -/* Sony */ -#define SONY_VENDORID 0x104d -#define SONY_S5_SUBVENDOR HDA_MODEL_CONSTRUCT(SONY, 0x81cc) -#define SONY_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(SONY, 0xffff) - -/* - * Apple Intel MacXXXX seems using Sigmatel codec/vendor id - * instead of their own, which is beyond my comprehension - * (see HDA_CODEC_STAC9221 below). - */ -#define APPLE_INTEL_MAC 0x76808384 -#define APPLE_MACBOOKPRO55 0xcb7910de - -/* LG Electronics */ -#define LG_VENDORID 0x1854 -#define LG_LW20_SUBVENDOR HDA_MODEL_CONSTRUCT(LG, 0x0018) -#define LG_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LG, 0xffff) - -/* Fujitsu Siemens */ -#define FS_VENDORID 0x1734 -#define FS_PA1510_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0x10b8) -#define FS_SI1848_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0x10cd) -#define FS_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0xffff) - -/* Fujitsu Limited */ -#define FL_VENDORID 0x10cf -#define FL_S7020D_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0x1326) -#define FL_U1010_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0x142d) -#define FL_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0xffff) - -/* Toshiba */ -#define TOSHIBA_VENDORID 0x1179 -#define TOSHIBA_U200_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0x0001) -#define TOSHIBA_A135_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0xff01) -#define TOSHIBA_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0xffff) - -/* Micro-Star International (MSI) */ -#define MSI_VENDORID 0x1462 -#define MSI_MS1034_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0x0349) -#define MSI_MS034A_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0x034a) -#define MSI_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0xffff) - -/* Giga-Byte Technology */ -#define GB_VENDORID 0x1458 -#define GB_G33S2H_SUBVENDOR HDA_MODEL_CONSTRUCT(GB, 0xa022) -#define GP_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(GB, 0xffff) - -/* Uniwill ? */ -#define UNIWILL_VENDORID 0x1584 -#define UNIWILL_9075_SUBVENDOR HDA_MODEL_CONSTRUCT(UNIWILL, 0x9075) -#define UNIWILL_9080_SUBVENDOR HDA_MODEL_CONSTRUCT(UNIWILL, 0x9080) - - -/* Misc constants.. */ -#define HDA_AMP_VOL_DEFAULT (-1) -#define HDA_AMP_MUTE_DEFAULT (0xffffffff) -#define HDA_AMP_MUTE_NONE (0) -#define HDA_AMP_MUTE_LEFT (1 << 0) -#define HDA_AMP_MUTE_RIGHT (1 << 1) -#define HDA_AMP_MUTE_ALL (HDA_AMP_MUTE_LEFT | HDA_AMP_MUTE_RIGHT) - -#define HDA_AMP_LEFT_MUTED(v) ((v) & (HDA_AMP_MUTE_LEFT)) -#define HDA_AMP_RIGHT_MUTED(v) (((v) & HDA_AMP_MUTE_RIGHT) >> 1) - -#define HDA_ADC_MONITOR (1 << 0) - -#define HDA_CTL_OUT 1 -#define HDA_CTL_IN 2 - -#define HDA_GPIO_MAX 8 -/* 0 - 7 = GPIO , 8 = Flush */ -#define HDA_QUIRK_GPIO0 (1 << 0) -#define HDA_QUIRK_GPIO1 (1 << 1) -#define HDA_QUIRK_GPIO2 (1 << 2) -#define HDA_QUIRK_GPIO3 (1 << 3) -#define HDA_QUIRK_GPIO4 (1 << 4) -#define HDA_QUIRK_GPIO5 (1 << 5) -#define HDA_QUIRK_GPIO6 (1 << 6) -#define HDA_QUIRK_GPIO7 (1 << 7) -#define HDA_QUIRK_GPIOFLUSH (1 << 8) - -/* 9 - 25 = anything else */ -#define HDA_QUIRK_SOFTPCMVOL (1 << 9) -#define HDA_QUIRK_FIXEDRATE (1 << 10) -#define HDA_QUIRK_FORCESTEREO (1 << 11) -#define HDA_QUIRK_EAPDINV (1 << 12) -#define HDA_QUIRK_DMAPOS (1 << 13) -#define HDA_QUIRK_SENSEINV (1 << 14) - -/* 26 - 31 = vrefs */ -#define HDA_QUIRK_IVREF50 (1 << 26) -#define HDA_QUIRK_IVREF80 (1 << 27) -#define HDA_QUIRK_IVREF100 (1 << 28) -#define HDA_QUIRK_OVREF50 (1 << 29) -#define HDA_QUIRK_OVREF80 (1 << 30) -#define HDA_QUIRK_OVREF100 (1 << 31) - -#define HDA_QUIRK_IVREF (HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF80 | \ - HDA_QUIRK_IVREF100) -#define HDA_QUIRK_OVREF (HDA_QUIRK_OVREF50 | HDA_QUIRK_OVREF80 | \ - HDA_QUIRK_OVREF100) -#define HDA_QUIRK_VREF (HDA_QUIRK_IVREF | HDA_QUIRK_OVREF) - -#if __FreeBSD_version < 600000 -#define taskqueue_drain(...) -#endif +#define HDAC_QUIRK_64BIT (1 << 0) +#define HDAC_QUIRK_DMAPOS (1 << 1) +#define HDAC_QUIRK_MSI (1 << 2) static const struct { char *key; uint32_t value; } hdac_quirks_tab[] = { - { "gpio0", HDA_QUIRK_GPIO0 }, - { "gpio1", HDA_QUIRK_GPIO1 }, - { "gpio2", HDA_QUIRK_GPIO2 }, - { "gpio3", HDA_QUIRK_GPIO3 }, - { "gpio4", HDA_QUIRK_GPIO4 }, - { "gpio5", HDA_QUIRK_GPIO5 }, - { "gpio6", HDA_QUIRK_GPIO6 }, - { "gpio7", HDA_QUIRK_GPIO7 }, - { "gpioflush", HDA_QUIRK_GPIOFLUSH }, - { "softpcmvol", HDA_QUIRK_SOFTPCMVOL }, - { "fixedrate", HDA_QUIRK_FIXEDRATE }, - { "forcestereo", HDA_QUIRK_FORCESTEREO }, - { "eapdinv", HDA_QUIRK_EAPDINV }, - { "dmapos", HDA_QUIRK_DMAPOS }, - { "senseinv", HDA_QUIRK_SENSEINV }, - { "ivref50", HDA_QUIRK_IVREF50 }, - { "ivref80", HDA_QUIRK_IVREF80 }, - { "ivref100", HDA_QUIRK_IVREF100 }, - { "ovref50", HDA_QUIRK_OVREF50 }, - { "ovref80", HDA_QUIRK_OVREF80 }, - { "ovref100", HDA_QUIRK_OVREF100 }, - { "ivref", HDA_QUIRK_IVREF }, - { "ovref", HDA_QUIRK_OVREF }, - { "vref", HDA_QUIRK_VREF }, + { "64bit", HDAC_QUIRK_DMAPOS }, + { "dmapos", HDAC_QUIRK_DMAPOS }, + { "msi", HDAC_QUIRK_MSI }, }; #define HDAC_QUIRKS_TAB_LEN \ (sizeof(hdac_quirks_tab) / sizeof(hdac_quirks_tab[0])) @@ -456,105 +74,81 @@ static const struct { #define HDA_BDL_MAX 256 #define HDA_BDL_DEFAULT HDA_BDL_MIN -#define HDA_BLK_MIN HDAC_DMA_ALIGNMENT +#define HDA_BLK_MIN HDA_DMA_ALIGNMENT #define HDA_BLK_ALIGN (~(HDA_BLK_MIN - 1)) #define HDA_BUFSZ_MIN 4096 #define HDA_BUFSZ_MAX 65536 #define HDA_BUFSZ_DEFAULT 16384 -#define HDA_PARSE_MAXDEPTH 10 - -#define HDAC_UNSOLTAG_EVENT_HP 0x00 - -MALLOC_DEFINE(M_HDAC, "hdac", "High Definition Audio Controller"); - -const char *HDA_COLORS[16] = {"Unknown", "Black", "Grey", "Blue", "Green", "Red", - "Orange", "Yellow", "Purple", "Pink", "Res.A", "Res.B", "Res.C", "Res.D", - "White", "Other"}; - -const char *HDA_DEVS[16] = {"Line-out", "Speaker", "Headphones", "CD", - "SPDIF-out", "Digital-out", "Modem-line", "Modem-handset", "Line-in", - "AUX", "Mic", "Telephony", "SPDIF-in", "Digital-in", "Res.E", "Other"}; - -const char *HDA_CONNS[4] = {"Jack", "None", "Fixed", "Both"}; - -/* Default */ -static uint32_t hdac_fmt[] = { - SND_FORMAT(AFMT_S16_LE, 2, 0), - 0 -}; - -static struct pcmchan_caps hdac_caps = {48000, 48000, hdac_fmt, 0}; - -#define HDAC_NO_MSI 1 -#define HDAC_NO_64BIT 2 +MALLOC_DEFINE(M_HDAC, "hdac", "HDA Controller"); static const struct { uint32_t model; char *desc; - char flags; + char quirks_on; + char quirks_off; } hdac_devices[] = { - { HDA_INTEL_CPT, "Intel Cougar Point", 0 }, - { HDA_INTEL_PATSBURG,"Intel Patsburg", 0 }, - { HDA_INTEL_PPT1, "Intel Panther Point", 0 }, - { HDA_INTEL_82801F, "Intel 82801F", 0 }, - { HDA_INTEL_63XXESB, "Intel 631x/632xESB", 0 }, - { HDA_INTEL_82801G, "Intel 82801G", 0 }, - { HDA_INTEL_82801H, "Intel 82801H", 0 }, - { HDA_INTEL_82801I, "Intel 82801I", 0 }, - { HDA_INTEL_82801JI, "Intel 82801JI", 0 }, - { HDA_INTEL_82801JD, "Intel 82801JD", 0 }, - { HDA_INTEL_PCH, "Intel 5 Series/3400 Series", 0 }, - { HDA_INTEL_PCH2, "Intel 5 Series/3400 Series", 0 }, - { HDA_INTEL_SCH, "Intel SCH", 0 }, - { HDA_NVIDIA_MCP51, "NVidia MCP51", HDAC_NO_MSI }, - { HDA_NVIDIA_MCP55, "NVidia MCP55", HDAC_NO_MSI }, - { HDA_NVIDIA_MCP61_1, "NVidia MCP61", 0 }, - { HDA_NVIDIA_MCP61_2, "NVidia MCP61", 0 }, - { HDA_NVIDIA_MCP65_1, "NVidia MCP65", 0 }, - { HDA_NVIDIA_MCP65_2, "NVidia MCP65", 0 }, - { HDA_NVIDIA_MCP67_1, "NVidia MCP67", 0 }, - { HDA_NVIDIA_MCP67_2, "NVidia MCP67", 0 }, - { HDA_NVIDIA_MCP73_1, "NVidia MCP73", 0 }, - { HDA_NVIDIA_MCP73_2, "NVidia MCP73", 0 }, - { HDA_NVIDIA_MCP78_1, "NVidia MCP78", HDAC_NO_64BIT }, - { HDA_NVIDIA_MCP78_2, "NVidia MCP78", HDAC_NO_64BIT }, - { HDA_NVIDIA_MCP78_3, "NVidia MCP78", HDAC_NO_64BIT }, - { HDA_NVIDIA_MCP78_4, "NVidia MCP78", HDAC_NO_64BIT }, - { HDA_NVIDIA_MCP79_1, "NVidia MCP79", 0 }, - { HDA_NVIDIA_MCP79_2, "NVidia MCP79", 0 }, - { HDA_NVIDIA_MCP79_3, "NVidia MCP79", 0 }, - { HDA_NVIDIA_MCP79_4, "NVidia MCP79", 0 }, - { HDA_NVIDIA_MCP89_1, "NVidia MCP89", 0 }, - { HDA_NVIDIA_MCP89_2, "NVidia MCP89", 0 }, - { HDA_NVIDIA_MCP89_3, "NVidia MCP89", 0 }, - { HDA_NVIDIA_MCP89_4, "NVidia MCP89", 0 }, - { HDA_ATI_SB450, "ATI SB450", 0 }, - { HDA_ATI_SB600, "ATI SB600", 0 }, - { HDA_ATI_RS600, "ATI RS600", 0 }, - { HDA_ATI_RS690, "ATI RS690", 0 }, - { HDA_ATI_RS780, "ATI RS780", 0 }, - { HDA_ATI_R600, "ATI R600", 0 }, - { HDA_ATI_RV610, "ATI RV610", 0 }, - { HDA_ATI_RV620, "ATI RV620", 0 }, - { HDA_ATI_RV630, "ATI RV630", 0 }, - { HDA_ATI_RV635, "ATI RV635", 0 }, - { HDA_ATI_RV710, "ATI RV710", 0 }, - { HDA_ATI_RV730, "ATI RV730", 0 }, - { HDA_ATI_RV740, "ATI RV740", 0 }, - { HDA_ATI_RV770, "ATI RV770", 0 }, - { HDA_RDC_M3010, "RDC M3010", 0 }, - { HDA_VIA_VT82XX, "VIA VT8251/8237A",0 }, - { HDA_SIS_966, "SiS 966", 0 }, - { HDA_ULI_M5461, "ULI M5461", 0 }, + { HDA_INTEL_CPT, "Intel Cougar Point", 0, 0 }, + { HDA_INTEL_PATSBURG,"Intel Patsburg", 0, 0 }, + { HDA_INTEL_PPT1, "Intel Panther Point", 0, 0 }, + { HDA_INTEL_82801F, "Intel 82801F", 0, 0 }, + { HDA_INTEL_63XXESB, "Intel 631x/632xESB", 0, 0 }, + { HDA_INTEL_82801G, "Intel 82801G", 0, 0 }, + { HDA_INTEL_82801H, "Intel 82801H", 0, 0 }, + { HDA_INTEL_82801I, "Intel 82801I", 0, 0 }, + { HDA_INTEL_82801JI, "Intel 82801JI", 0, 0 }, + { HDA_INTEL_82801JD, "Intel 82801JD", 0, 0 }, + { HDA_INTEL_PCH, "Intel 5 Series/3400 Series", 0, 0 }, + { HDA_INTEL_PCH2, "Intel 5 Series/3400 Series", 0, 0 }, + { HDA_INTEL_SCH, "Intel SCH", 0, 0 }, + { HDA_NVIDIA_MCP51, "NVIDIA MCP51", 0, HDAC_QUIRK_MSI }, + { HDA_NVIDIA_MCP55, "NVIDIA MCP55", 0, HDAC_QUIRK_MSI }, + { HDA_NVIDIA_MCP61_1, "NVIDIA MCP61", 0, 0 }, + { HDA_NVIDIA_MCP61_2, "NVIDIA MCP61", 0, 0 }, + { HDA_NVIDIA_MCP65_1, "NVIDIA MCP65", 0, 0 }, + { HDA_NVIDIA_MCP65_2, "NVIDIA MCP65", 0, 0 }, + { HDA_NVIDIA_MCP67_1, "NVIDIA MCP67", 0, 0 }, + { HDA_NVIDIA_MCP67_2, "NVIDIA MCP67", 0, 0 }, + { HDA_NVIDIA_MCP73_1, "NVIDIA MCP73", 0, 0 }, + { HDA_NVIDIA_MCP73_2, "NVIDIA MCP73", 0, 0 }, + { HDA_NVIDIA_MCP78_1, "NVIDIA MCP78", 0, HDAC_QUIRK_64BIT }, + { HDA_NVIDIA_MCP78_2, "NVIDIA MCP78", 0, HDAC_QUIRK_64BIT }, + { HDA_NVIDIA_MCP78_3, "NVIDIA MCP78", 0, HDAC_QUIRK_64BIT }, + { HDA_NVIDIA_MCP78_4, "NVIDIA MCP78", 0, HDAC_QUIRK_64BIT }, + { HDA_NVIDIA_MCP79_1, "NVIDIA MCP79", 0, 0 }, + { HDA_NVIDIA_MCP79_2, "NVIDIA MCP79", 0, 0 }, + { HDA_NVIDIA_MCP79_3, "NVIDIA MCP79", 0, 0 }, + { HDA_NVIDIA_MCP79_4, "NVIDIA MCP79", 0, 0 }, + { HDA_NVIDIA_MCP89_1, "NVIDIA MCP89", 0, 0 }, + { HDA_NVIDIA_MCP89_2, "NVIDIA MCP89", 0, 0 }, + { HDA_NVIDIA_MCP89_3, "NVIDIA MCP89", 0, 0 }, + { HDA_NVIDIA_MCP89_4, "NVIDIA MCP89", 0, 0 }, + { HDA_ATI_SB450, "ATI SB450", 0, 0 }, + { HDA_ATI_SB600, "ATI SB600", 0, 0 }, + { HDA_ATI_RS600, "ATI RS600", 0, 0 }, + { HDA_ATI_RS690, "ATI RS690", 0, 0 }, + { HDA_ATI_RS780, "ATI RS780", 0, 0 }, + { HDA_ATI_R600, "ATI R600", 0, 0 }, + { HDA_ATI_RV610, "ATI RV610", 0, 0 }, + { HDA_ATI_RV620, "ATI RV620", 0, 0 }, + { HDA_ATI_RV630, "ATI RV630", 0, 0 }, + { HDA_ATI_RV635, "ATI RV635", 0, 0 }, + { HDA_ATI_RV710, "ATI RV710", 0, 0 }, + { HDA_ATI_RV730, "ATI RV730", 0, 0 }, + { HDA_ATI_RV740, "ATI RV740", 0, 0 }, + { HDA_ATI_RV770, "ATI RV770", 0, 0 }, + { HDA_RDC_M3010, "RDC M3010", 0, 0 }, + { HDA_VIA_VT82XX, "VIA VT8251/8237A",0, 0 }, + { HDA_SIS_966, "SiS 966", 0, 0 }, + { HDA_ULI_M5461, "ULI M5461", 0, 0 }, /* Unknown */ - { HDA_INTEL_ALL, "Intel (Unknown)" }, - { HDA_NVIDIA_ALL, "NVidia (Unknown)" }, - { HDA_ATI_ALL, "ATI (Unknown)" }, - { HDA_VIA_ALL, "VIA (Unknown)" }, - { HDA_SIS_ALL, "SiS (Unknown)" }, - { HDA_ULI_ALL, "ULI (Unknown)" }, + { HDA_INTEL_ALL, "Intel (Unknown)", 0, 0 }, + { HDA_NVIDIA_ALL, "NVIDIA (Unknown)", 0, 0 }, + { HDA_ATI_ALL, "ATI (Unknown)", 0, 0 }, + { HDA_VIA_ALL, "VIA (Unknown)", 0, 0 }, + { HDA_SIS_ALL, "SiS (Unknown)", 0, 0 }, + { HDA_ULI_ALL, "ULI (Unknown)", 0, 0 }, }; #define HDAC_DEVICES_LEN (sizeof(hdac_devices) / sizeof(hdac_devices[0])) @@ -571,482 +165,6 @@ static const struct { #define HDAC_PCIESNOOP_LEN \ (sizeof(hdac_pcie_snoop) / sizeof(hdac_pcie_snoop[0])) -static const struct { - uint32_t rate; - int valid; - uint16_t base; - uint16_t mul; - uint16_t div; -} hda_rate_tab[] = { - { 8000, 1, 0x0000, 0x0000, 0x0500 }, /* (48000 * 1) / 6 */ - { 9600, 0, 0x0000, 0x0000, 0x0400 }, /* (48000 * 1) / 5 */ - { 12000, 0, 0x0000, 0x0000, 0x0300 }, /* (48000 * 1) / 4 */ - { 16000, 1, 0x0000, 0x0000, 0x0200 }, /* (48000 * 1) / 3 */ - { 18000, 0, 0x0000, 0x1000, 0x0700 }, /* (48000 * 3) / 8 */ - { 19200, 0, 0x0000, 0x0800, 0x0400 }, /* (48000 * 2) / 5 */ - { 24000, 0, 0x0000, 0x0000, 0x0100 }, /* (48000 * 1) / 2 */ - { 28800, 0, 0x0000, 0x1000, 0x0400 }, /* (48000 * 3) / 5 */ - { 32000, 1, 0x0000, 0x0800, 0x0200 }, /* (48000 * 2) / 3 */ - { 36000, 0, 0x0000, 0x1000, 0x0300 }, /* (48000 * 3) / 4 */ - { 38400, 0, 0x0000, 0x1800, 0x0400 }, /* (48000 * 4) / 5 */ - { 48000, 1, 0x0000, 0x0000, 0x0000 }, /* (48000 * 1) / 1 */ - { 64000, 0, 0x0000, 0x1800, 0x0200 }, /* (48000 * 4) / 3 */ - { 72000, 0, 0x0000, 0x1000, 0x0100 }, /* (48000 * 3) / 2 */ - { 96000, 1, 0x0000, 0x0800, 0x0000 }, /* (48000 * 2) / 1 */ - { 144000, 0, 0x0000, 0x1000, 0x0000 }, /* (48000 * 3) / 1 */ - { 192000, 1, 0x0000, 0x1800, 0x0000 }, /* (48000 * 4) / 1 */ - { 8820, 0, 0x4000, 0x0000, 0x0400 }, /* (44100 * 1) / 5 */ - { 11025, 1, 0x4000, 0x0000, 0x0300 }, /* (44100 * 1) / 4 */ - { 12600, 0, 0x4000, 0x0800, 0x0600 }, /* (44100 * 2) / 7 */ - { 14700, 0, 0x4000, 0x0000, 0x0200 }, /* (44100 * 1) / 3 */ - { 17640, 0, 0x4000, 0x0800, 0x0400 }, /* (44100 * 2) / 5 */ - { 18900, 0, 0x4000, 0x1000, 0x0600 }, /* (44100 * 3) / 7 */ - { 22050, 1, 0x4000, 0x0000, 0x0100 }, /* (44100 * 1) / 2 */ - { 25200, 0, 0x4000, 0x1800, 0x0600 }, /* (44100 * 4) / 7 */ - { 26460, 0, 0x4000, 0x1000, 0x0400 }, /* (44100 * 3) / 5 */ - { 29400, 0, 0x4000, 0x0800, 0x0200 }, /* (44100 * 2) / 3 */ - { 33075, 0, 0x4000, 0x1000, 0x0300 }, /* (44100 * 3) / 4 */ - { 35280, 0, 0x4000, 0x1800, 0x0400 }, /* (44100 * 4) / 5 */ - { 44100, 1, 0x4000, 0x0000, 0x0000 }, /* (44100 * 1) / 1 */ - { 58800, 0, 0x4000, 0x1800, 0x0200 }, /* (44100 * 4) / 3 */ - { 66150, 0, 0x4000, 0x1000, 0x0100 }, /* (44100 * 3) / 2 */ - { 88200, 1, 0x4000, 0x0800, 0x0000 }, /* (44100 * 2) / 1 */ - { 132300, 0, 0x4000, 0x1000, 0x0000 }, /* (44100 * 3) / 1 */ - { 176400, 1, 0x4000, 0x1800, 0x0000 }, /* (44100 * 4) / 1 */ -}; -#define HDA_RATE_TAB_LEN (sizeof(hda_rate_tab) / sizeof(hda_rate_tab[0])) - -/* All codecs you can eat... */ -#define HDA_CODEC_CONSTRUCT(vendor, id) \ - (((uint32_t)(vendor##_VENDORID) << 16) | ((id) & 0xffff)) - -/* Cirrus Logic */ -#define CIRRUSLOGIC_VENDORID 0x1013 -#define HDA_CODEC_CS4206 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4206) -#define HDA_CODEC_CS4207 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4207) -#define HDA_CODEC_CSXXXX HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0xffff) - -/* Realtek */ -#define REALTEK_VENDORID 0x10ec -#define HDA_CODEC_ALC260 HDA_CODEC_CONSTRUCT(REALTEK, 0x0260) -#define HDA_CODEC_ALC262 HDA_CODEC_CONSTRUCT(REALTEK, 0x0262) -#define HDA_CODEC_ALC267 HDA_CODEC_CONSTRUCT(REALTEK, 0x0267) -#define HDA_CODEC_ALC268 HDA_CODEC_CONSTRUCT(REALTEK, 0x0268) -#define HDA_CODEC_ALC269 HDA_CODEC_CONSTRUCT(REALTEK, 0x0269) -#define HDA_CODEC_ALC270 HDA_CODEC_CONSTRUCT(REALTEK, 0x0270) -#define HDA_CODEC_ALC272 HDA_CODEC_CONSTRUCT(REALTEK, 0x0272) -#define HDA_CODEC_ALC273 HDA_CODEC_CONSTRUCT(REALTEK, 0x0273) -#define HDA_CODEC_ALC275 HDA_CODEC_CONSTRUCT(REALTEK, 0x0275) -#define HDA_CODEC_ALC660 HDA_CODEC_CONSTRUCT(REALTEK, 0x0660) -#define HDA_CODEC_ALC662 HDA_CODEC_CONSTRUCT(REALTEK, 0x0662) -#define HDA_CODEC_ALC663 HDA_CODEC_CONSTRUCT(REALTEK, 0x0663) -#define HDA_CODEC_ALC665 HDA_CODEC_CONSTRUCT(REALTEK, 0x0665) -#define HDA_CODEC_ALC861 HDA_CODEC_CONSTRUCT(REALTEK, 0x0861) -#define HDA_CODEC_ALC861VD HDA_CODEC_CONSTRUCT(REALTEK, 0x0862) -#define HDA_CODEC_ALC880 HDA_CODEC_CONSTRUCT(REALTEK, 0x0880) -#define HDA_CODEC_ALC882 HDA_CODEC_CONSTRUCT(REALTEK, 0x0882) -#define HDA_CODEC_ALC883 HDA_CODEC_CONSTRUCT(REALTEK, 0x0883) -#define HDA_CODEC_ALC885 HDA_CODEC_CONSTRUCT(REALTEK, 0x0885) -#define HDA_CODEC_ALC887 HDA_CODEC_CONSTRUCT(REALTEK, 0x0887) -#define HDA_CODEC_ALC888 HDA_CODEC_CONSTRUCT(REALTEK, 0x0888) -#define HDA_CODEC_ALC889 HDA_CODEC_CONSTRUCT(REALTEK, 0x0889) -#define HDA_CODEC_ALC892 HDA_CODEC_CONSTRUCT(REALTEK, 0x0892) -#define HDA_CODEC_ALCXXXX HDA_CODEC_CONSTRUCT(REALTEK, 0xffff) - -/* Analog Devices */ -#define ANALOGDEVICES_VENDORID 0x11d4 -#define HDA_CODEC_AD1884A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x184a) -#define HDA_CODEC_AD1882 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1882) -#define HDA_CODEC_AD1883 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1883) -#define HDA_CODEC_AD1884 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1884) -#define HDA_CODEC_AD1984A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x194a) -#define HDA_CODEC_AD1984B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x194b) -#define HDA_CODEC_AD1981HD HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1981) -#define HDA_CODEC_AD1983 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1983) -#define HDA_CODEC_AD1984 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1984) -#define HDA_CODEC_AD1986A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1986) -#define HDA_CODEC_AD1987 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1987) -#define HDA_CODEC_AD1988 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1988) -#define HDA_CODEC_AD1988B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x198b) -#define HDA_CODEC_AD1882A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x882a) -#define HDA_CODEC_AD1989B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x989b) -#define HDA_CODEC_ADXXXX HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0xffff) - -/* CMedia */ -#define CMEDIA_VENDORID 0x434d -#define HDA_CODEC_CMI9880 HDA_CODEC_CONSTRUCT(CMEDIA, 0x4980) -#define HDA_CODEC_CMIXXXX HDA_CODEC_CONSTRUCT(CMEDIA, 0xffff) - -/* Sigmatel */ -#define SIGMATEL_VENDORID 0x8384 -#define HDA_CODEC_STAC9230X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7612) -#define HDA_CODEC_STAC9230D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7613) -#define HDA_CODEC_STAC9229X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7614) -#define HDA_CODEC_STAC9229D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7615) -#define HDA_CODEC_STAC9228X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7616) -#define HDA_CODEC_STAC9228D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7617) -#define HDA_CODEC_STAC9227X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7618) -#define HDA_CODEC_STAC9227D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7619) -#define HDA_CODEC_STAC9274 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7620) -#define HDA_CODEC_STAC9274D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7621) -#define HDA_CODEC_STAC9273X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7622) -#define HDA_CODEC_STAC9273D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7623) -#define HDA_CODEC_STAC9272X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7624) -#define HDA_CODEC_STAC9272D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7625) -#define HDA_CODEC_STAC9271X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7626) -#define HDA_CODEC_STAC9271D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7627) -#define HDA_CODEC_STAC9274X5NH HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7628) -#define HDA_CODEC_STAC9274D5NH HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7629) -#define HDA_CODEC_STAC9250 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7634) -#define HDA_CODEC_STAC9251 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7636) -#define HDA_CODEC_IDT92HD700X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7638) -#define HDA_CODEC_IDT92HD700D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7639) -#define HDA_CODEC_IDT92HD206X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7645) -#define HDA_CODEC_IDT92HD206D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7646) -#define HDA_CODEC_CXD9872RDK HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7661) -#define HDA_CODEC_STAC9872AK HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7662) -#define HDA_CODEC_CXD9872AKD HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7664) -#define HDA_CODEC_STAC9221 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7680) -#define HDA_CODEC_STAC922XD HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7681) -#define HDA_CODEC_STAC9221_A2 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7682) -#define HDA_CODEC_STAC9221D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7683) -#define HDA_CODEC_STAC9220 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7690) -#define HDA_CODEC_STAC9200D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7691) -#define HDA_CODEC_IDT92HD005 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7698) -#define HDA_CODEC_IDT92HD005D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7699) -#define HDA_CODEC_STAC9205X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a0) -#define HDA_CODEC_STAC9205D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a1) -#define HDA_CODEC_STAC9204X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a2) -#define HDA_CODEC_STAC9204D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a3) -#define HDA_CODEC_STAC9220_A2 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7880) -#define HDA_CODEC_STAC9220_A1 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7882) -#define HDA_CODEC_STACXXXX HDA_CODEC_CONSTRUCT(SIGMATEL, 0xffff) - -/* IDT */ -#define IDT_VENDORID 0x111d -#define HDA_CODEC_IDT92HD75BX HDA_CODEC_CONSTRUCT(IDT, 0x7603) -#define HDA_CODEC_IDT92HD83C1X HDA_CODEC_CONSTRUCT(IDT, 0x7604) -#define HDA_CODEC_IDT92HD81B1X HDA_CODEC_CONSTRUCT(IDT, 0x7605) -#define HDA_CODEC_IDT92HD75B3 HDA_CODEC_CONSTRUCT(IDT, 0x7608) -#define HDA_CODEC_IDT92HD73D1 HDA_CODEC_CONSTRUCT(IDT, 0x7674) -#define HDA_CODEC_IDT92HD73C1 HDA_CODEC_CONSTRUCT(IDT, 0x7675) -#define HDA_CODEC_IDT92HD73E1 HDA_CODEC_CONSTRUCT(IDT, 0x7676) -#define HDA_CODEC_IDT92HD71B8 HDA_CODEC_CONSTRUCT(IDT, 0x76b0) -#define HDA_CODEC_IDT92HD71B7 HDA_CODEC_CONSTRUCT(IDT, 0x76b2) -#define HDA_CODEC_IDT92HD71B5 HDA_CODEC_CONSTRUCT(IDT, 0x76b6) -#define HDA_CODEC_IDT92HD83C1C HDA_CODEC_CONSTRUCT(IDT, 0x76d4) -#define HDA_CODEC_IDT92HD81B1C HDA_CODEC_CONSTRUCT(IDT, 0x76d5) -#define HDA_CODEC_IDTXXXX HDA_CODEC_CONSTRUCT(IDT, 0xffff) - -/* Silicon Image */ -#define SII_VENDORID 0x1095 -#define HDA_CODEC_SII1390 HDA_CODEC_CONSTRUCT(SII, 0x1390) -#define HDA_CODEC_SII1392 HDA_CODEC_CONSTRUCT(SII, 0x1392) -#define HDA_CODEC_SIIXXXX HDA_CODEC_CONSTRUCT(SII, 0xffff) - -/* Lucent/Agere */ -#define AGERE_VENDORID 0x11c1 -#define HDA_CODEC_AGEREXXXX HDA_CODEC_CONSTRUCT(AGERE, 0xffff) - -/* Conexant */ -#define CONEXANT_VENDORID 0x14f1 -#define HDA_CODEC_CX20549 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5045) -#define HDA_CODEC_CX20551 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5047) -#define HDA_CODEC_CX20561 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5051) -#define HDA_CODEC_CX20582 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5066) -#define HDA_CODEC_CX20583 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5067) -#define HDA_CODEC_CX20584 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5068) -#define HDA_CODEC_CX20585 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5069) -#define HDA_CODEC_CX20590 HDA_CODEC_CONSTRUCT(CONEXANT, 0x506e) -#define HDA_CODEC_CX20631 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5097) -#define HDA_CODEC_CX20632 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5098) -#define HDA_CODEC_CX20641 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50a1) -#define HDA_CODEC_CX20642 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50a2) -#define HDA_CODEC_CX20651 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50ab) -#define HDA_CODEC_CX20652 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50ac) -#define HDA_CODEC_CX20664 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50b8) -#define HDA_CODEC_CX20665 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50b9) -#define HDA_CODEC_CXXXXX HDA_CODEC_CONSTRUCT(CONEXANT, 0xffff) - -/* VIA */ -#define HDA_CODEC_VT1708_8 HDA_CODEC_CONSTRUCT(VIA, 0x1708) -#define HDA_CODEC_VT1708_9 HDA_CODEC_CONSTRUCT(VIA, 0x1709) -#define HDA_CODEC_VT1708_A HDA_CODEC_CONSTRUCT(VIA, 0x170a) -#define HDA_CODEC_VT1708_B HDA_CODEC_CONSTRUCT(VIA, 0x170b) -#define HDA_CODEC_VT1709_0 HDA_CODEC_CONSTRUCT(VIA, 0xe710) -#define HDA_CODEC_VT1709_1 HDA_CODEC_CONSTRUCT(VIA, 0xe711) -#define HDA_CODEC_VT1709_2 HDA_CODEC_CONSTRUCT(VIA, 0xe712) -#define HDA_CODEC_VT1709_3 HDA_CODEC_CONSTRUCT(VIA, 0xe713) -#define HDA_CODEC_VT1709_4 HDA_CODEC_CONSTRUCT(VIA, 0xe714) -#define HDA_CODEC_VT1709_5 HDA_CODEC_CONSTRUCT(VIA, 0xe715) -#define HDA_CODEC_VT1709_6 HDA_CODEC_CONSTRUCT(VIA, 0xe716) -#define HDA_CODEC_VT1709_7 HDA_CODEC_CONSTRUCT(VIA, 0xe717) -#define HDA_CODEC_VT1708B_0 HDA_CODEC_CONSTRUCT(VIA, 0xe720) -#define HDA_CODEC_VT1708B_1 HDA_CODEC_CONSTRUCT(VIA, 0xe721) -#define HDA_CODEC_VT1708B_2 HDA_CODEC_CONSTRUCT(VIA, 0xe722) -#define HDA_CODEC_VT1708B_3 HDA_CODEC_CONSTRUCT(VIA, 0xe723) -#define HDA_CODEC_VT1708B_4 HDA_CODEC_CONSTRUCT(VIA, 0xe724) -#define HDA_CODEC_VT1708B_5 HDA_CODEC_CONSTRUCT(VIA, 0xe725) -#define HDA_CODEC_VT1708B_6 HDA_CODEC_CONSTRUCT(VIA, 0xe726) -#define HDA_CODEC_VT1708B_7 HDA_CODEC_CONSTRUCT(VIA, 0xe727) -#define HDA_CODEC_VT1708S_0 HDA_CODEC_CONSTRUCT(VIA, 0x0397) -#define HDA_CODEC_VT1708S_1 HDA_CODEC_CONSTRUCT(VIA, 0x1397) -#define HDA_CODEC_VT1708S_2 HDA_CODEC_CONSTRUCT(VIA, 0x2397) -#define HDA_CODEC_VT1708S_3 HDA_CODEC_CONSTRUCT(VIA, 0x3397) -#define HDA_CODEC_VT1708S_4 HDA_CODEC_CONSTRUCT(VIA, 0x4397) -#define HDA_CODEC_VT1708S_5 HDA_CODEC_CONSTRUCT(VIA, 0x5397) -#define HDA_CODEC_VT1708S_6 HDA_CODEC_CONSTRUCT(VIA, 0x6397) -#define HDA_CODEC_VT1708S_7 HDA_CODEC_CONSTRUCT(VIA, 0x7397) -#define HDA_CODEC_VT1702_0 HDA_CODEC_CONSTRUCT(VIA, 0x0398) -#define HDA_CODEC_VT1702_1 HDA_CODEC_CONSTRUCT(VIA, 0x1398) -#define HDA_CODEC_VT1702_2 HDA_CODEC_CONSTRUCT(VIA, 0x2398) -#define HDA_CODEC_VT1702_3 HDA_CODEC_CONSTRUCT(VIA, 0x3398) -#define HDA_CODEC_VT1702_4 HDA_CODEC_CONSTRUCT(VIA, 0x4398) -#define HDA_CODEC_VT1702_5 HDA_CODEC_CONSTRUCT(VIA, 0x5398) -#define HDA_CODEC_VT1702_6 HDA_CODEC_CONSTRUCT(VIA, 0x6398) -#define HDA_CODEC_VT1702_7 HDA_CODEC_CONSTRUCT(VIA, 0x7398) -#define HDA_CODEC_VT1716S_0 HDA_CODEC_CONSTRUCT(VIA, 0x0433) -#define HDA_CODEC_VT1716S_1 HDA_CODEC_CONSTRUCT(VIA, 0xa721) -#define HDA_CODEC_VT1718S_0 HDA_CODEC_CONSTRUCT(VIA, 0x0428) -#define HDA_CODEC_VT1718S_1 HDA_CODEC_CONSTRUCT(VIA, 0x4428) -#define HDA_CODEC_VT1812 HDA_CODEC_CONSTRUCT(VIA, 0x0448) -#define HDA_CODEC_VT1818S HDA_CODEC_CONSTRUCT(VIA, 0x0440) -#define HDA_CODEC_VT1828S HDA_CODEC_CONSTRUCT(VIA, 0x4441) -#define HDA_CODEC_VT2002P_0 HDA_CODEC_CONSTRUCT(VIA, 0x0438) -#define HDA_CODEC_VT2002P_1 HDA_CODEC_CONSTRUCT(VIA, 0x4438) -#define HDA_CODEC_VT2020 HDA_CODEC_CONSTRUCT(VIA, 0x0441) -#define HDA_CODEC_VTXXXX HDA_CODEC_CONSTRUCT(VIA, 0xffff) - -/* ATI */ -#define HDA_CODEC_ATIRS600_1 HDA_CODEC_CONSTRUCT(ATI, 0x793c) -#define HDA_CODEC_ATIRS600_2 HDA_CODEC_CONSTRUCT(ATI, 0x7919) -#define HDA_CODEC_ATIRS690 HDA_CODEC_CONSTRUCT(ATI, 0x791a) -#define HDA_CODEC_ATIR6XX HDA_CODEC_CONSTRUCT(ATI, 0xaa01) -#define HDA_CODEC_ATIXXXX HDA_CODEC_CONSTRUCT(ATI, 0xffff) - -/* NVIDIA */ -#define HDA_CODEC_NVIDIAMCP78 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0002) -#define HDA_CODEC_NVIDIAMCP78_2 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0006) -#define HDA_CODEC_NVIDIAMCP7A HDA_CODEC_CONSTRUCT(NVIDIA, 0x0007) -#define HDA_CODEC_NVIDIAGT220 HDA_CODEC_CONSTRUCT(NVIDIA, 0x000a) -#define HDA_CODEC_NVIDIAGT21X HDA_CODEC_CONSTRUCT(NVIDIA, 0x000b) -#define HDA_CODEC_NVIDIAMCP89 HDA_CODEC_CONSTRUCT(NVIDIA, 0x000c) -#define HDA_CODEC_NVIDIAGT240 HDA_CODEC_CONSTRUCT(NVIDIA, 0x000d) -#define HDA_CODEC_NVIDIAMCP67 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0067) -#define HDA_CODEC_NVIDIAMCP73 HDA_CODEC_CONSTRUCT(NVIDIA, 0x8001) -#define HDA_CODEC_NVIDIAXXXX HDA_CODEC_CONSTRUCT(NVIDIA, 0xffff) - -/* INTEL */ -#define HDA_CODEC_INTELIP HDA_CODEC_CONSTRUCT(INTEL, 0x0054) -#define HDA_CODEC_INTELBL HDA_CODEC_CONSTRUCT(INTEL, 0x2801) -#define HDA_CODEC_INTELCA HDA_CODEC_CONSTRUCT(INTEL, 0x2802) -#define HDA_CODEC_INTELEL HDA_CODEC_CONSTRUCT(INTEL, 0x2803) -#define HDA_CODEC_INTELIP2 HDA_CODEC_CONSTRUCT(INTEL, 0x2804) -#define HDA_CODEC_INTELCPT HDA_CODEC_CONSTRUCT(INTEL, 0x2805) -#define HDA_CODEC_INTELCL HDA_CODEC_CONSTRUCT(INTEL, 0x29fb) -#define HDA_CODEC_INTELXXXX HDA_CODEC_CONSTRUCT(INTEL, 0xffff) - -/* Codecs */ -static const struct { - uint32_t id; - char *name; -} hdac_codecs[] = { - { HDA_CODEC_CS4206, "Cirrus Logic CS4206" }, - { HDA_CODEC_CS4207, "Cirrus Logic CS4207" }, - { HDA_CODEC_ALC260, "Realtek ALC260" }, - { HDA_CODEC_ALC262, "Realtek ALC262" }, - { HDA_CODEC_ALC267, "Realtek ALC267" }, - { HDA_CODEC_ALC268, "Realtek ALC268" }, - { HDA_CODEC_ALC269, "Realtek ALC269" }, - { HDA_CODEC_ALC270, "Realtek ALC270" }, - { HDA_CODEC_ALC272, "Realtek ALC272" }, - { HDA_CODEC_ALC273, "Realtek ALC273" }, - { HDA_CODEC_ALC275, "Realtek ALC275" }, - { HDA_CODEC_ALC660, "Realtek ALC660" }, - { HDA_CODEC_ALC662, "Realtek ALC662" }, - { HDA_CODEC_ALC663, "Realtek ALC663" }, - { HDA_CODEC_ALC665, "Realtek ALC665" }, - { HDA_CODEC_ALC861, "Realtek ALC861" }, - { HDA_CODEC_ALC861VD, "Realtek ALC861-VD" }, - { HDA_CODEC_ALC880, "Realtek ALC880" }, - { HDA_CODEC_ALC882, "Realtek ALC882" }, - { HDA_CODEC_ALC883, "Realtek ALC883" }, - { HDA_CODEC_ALC885, "Realtek ALC885" }, - { HDA_CODEC_ALC887, "Realtek ALC887" }, - { HDA_CODEC_ALC888, "Realtek ALC888" }, - { HDA_CODEC_ALC889, "Realtek ALC889" }, - { HDA_CODEC_ALC892, "Realtek ALC892" }, - { HDA_CODEC_AD1882, "Analog Devices AD1882" }, - { HDA_CODEC_AD1882A, "Analog Devices AD1882A" }, - { HDA_CODEC_AD1883, "Analog Devices AD1883" }, - { HDA_CODEC_AD1884, "Analog Devices AD1884" }, - { HDA_CODEC_AD1884A, "Analog Devices AD1884A" }, - { HDA_CODEC_AD1981HD, "Analog Devices AD1981HD" }, - { HDA_CODEC_AD1983, "Analog Devices AD1983" }, - { HDA_CODEC_AD1984, "Analog Devices AD1984" }, - { HDA_CODEC_AD1984A, "Analog Devices AD1984A" }, - { HDA_CODEC_AD1984B, "Analog Devices AD1984B" }, - { HDA_CODEC_AD1986A, "Analog Devices AD1986A" }, - { HDA_CODEC_AD1987, "Analog Devices AD1987" }, - { HDA_CODEC_AD1988, "Analog Devices AD1988A" }, - { HDA_CODEC_AD1988B, "Analog Devices AD1988B" }, - { HDA_CODEC_AD1989B, "Analog Devices AD1989B" }, - { HDA_CODEC_CMI9880, "CMedia CMI9880" }, - { HDA_CODEC_CXD9872RDK, "Sigmatel CXD9872RD/K" }, - { HDA_CODEC_CXD9872AKD, "Sigmatel CXD9872AKD" }, - { HDA_CODEC_STAC9200D, "Sigmatel STAC9200D" }, - { HDA_CODEC_STAC9204X, "Sigmatel STAC9204X" }, - { HDA_CODEC_STAC9204D, "Sigmatel STAC9204D" }, - { HDA_CODEC_STAC9205X, "Sigmatel STAC9205X" }, - { HDA_CODEC_STAC9205D, "Sigmatel STAC9205D" }, - { HDA_CODEC_STAC9220, "Sigmatel STAC9220" }, - { HDA_CODEC_STAC9220_A1, "Sigmatel STAC9220_A1" }, - { HDA_CODEC_STAC9220_A2, "Sigmatel STAC9220_A2" }, - { HDA_CODEC_STAC9221, "Sigmatel STAC9221" }, - { HDA_CODEC_STAC9221_A2, "Sigmatel STAC9221_A2" }, - { HDA_CODEC_STAC9221D, "Sigmatel STAC9221D" }, - { HDA_CODEC_STAC922XD, "Sigmatel STAC9220D/9223D" }, - { HDA_CODEC_STAC9227X, "Sigmatel STAC9227X" }, - { HDA_CODEC_STAC9227D, "Sigmatel STAC9227D" }, - { HDA_CODEC_STAC9228X, "Sigmatel STAC9228X" }, - { HDA_CODEC_STAC9228D, "Sigmatel STAC9228D" }, - { HDA_CODEC_STAC9229X, "Sigmatel STAC9229X" }, - { HDA_CODEC_STAC9229D, "Sigmatel STAC9229D" }, - { HDA_CODEC_STAC9230X, "Sigmatel STAC9230X" }, - { HDA_CODEC_STAC9230D, "Sigmatel STAC9230D" }, - { HDA_CODEC_STAC9250, "Sigmatel STAC9250" }, - { HDA_CODEC_STAC9251, "Sigmatel STAC9251" }, - { HDA_CODEC_STAC9271X, "Sigmatel STAC9271X" }, - { HDA_CODEC_STAC9271D, "Sigmatel STAC9271D" }, - { HDA_CODEC_STAC9272X, "Sigmatel STAC9272X" }, - { HDA_CODEC_STAC9272D, "Sigmatel STAC9272D" }, - { HDA_CODEC_STAC9273X, "Sigmatel STAC9273X" }, - { HDA_CODEC_STAC9273D, "Sigmatel STAC9273D" }, - { HDA_CODEC_STAC9274, "Sigmatel STAC9274" }, - { HDA_CODEC_STAC9274D, "Sigmatel STAC9274D" }, - { HDA_CODEC_STAC9274X5NH, "Sigmatel STAC9274X5NH" }, - { HDA_CODEC_STAC9274D5NH, "Sigmatel STAC9274D5NH" }, - { HDA_CODEC_STAC9872AK, "Sigmatel STAC9872AK" }, - { HDA_CODEC_IDT92HD005, "IDT 92HD005" }, - { HDA_CODEC_IDT92HD005D, "IDT 92HD005D" }, - { HDA_CODEC_IDT92HD206X, "IDT 92HD206X" }, - { HDA_CODEC_IDT92HD206D, "IDT 92HD206D" }, - { HDA_CODEC_IDT92HD700X, "IDT 92HD700X" }, - { HDA_CODEC_IDT92HD700D, "IDT 92HD700D" }, - { HDA_CODEC_IDT92HD71B5, "IDT 92HD71B5" }, - { HDA_CODEC_IDT92HD71B7, "IDT 92HD71B7" }, - { HDA_CODEC_IDT92HD71B8, "IDT 92HD71B8" }, - { HDA_CODEC_IDT92HD73C1, "IDT 92HD73C1" }, - { HDA_CODEC_IDT92HD73D1, "IDT 92HD73D1" }, - { HDA_CODEC_IDT92HD73E1, "IDT 92HD73E1" }, - { HDA_CODEC_IDT92HD75B3, "IDT 92HD75B3" }, - { HDA_CODEC_IDT92HD75BX, "IDT 92HD75BX" }, - { HDA_CODEC_IDT92HD81B1C, "IDT 92HD81B1C" }, - { HDA_CODEC_IDT92HD81B1X, "IDT 92HD81B1X" }, - { HDA_CODEC_IDT92HD83C1C, "IDT 92HD83C1C" }, - { HDA_CODEC_IDT92HD83C1X, "IDT 92HD83C1X" }, - { HDA_CODEC_CX20549, "Conexant CX20549 (Venice)" }, - { HDA_CODEC_CX20551, "Conexant CX20551 (Waikiki)" }, - { HDA_CODEC_CX20561, "Conexant CX20561 (Hermosa)" }, - { HDA_CODEC_CX20582, "Conexant CX20582 (Pebble)" }, - { HDA_CODEC_CX20583, "Conexant CX20583 (Pebble HSF)" }, - { HDA_CODEC_CX20584, "Conexant CX20584" }, - { HDA_CODEC_CX20585, "Conexant CX20585" }, - { HDA_CODEC_CX20590, "Conexant CX20590" }, - { HDA_CODEC_CX20631, "Conexant CX20631" }, - { HDA_CODEC_CX20632, "Conexant CX20632" }, - { HDA_CODEC_CX20641, "Conexant CX20641" }, - { HDA_CODEC_CX20642, "Conexant CX20642" }, - { HDA_CODEC_CX20651, "Conexant CX20651" }, - { HDA_CODEC_CX20652, "Conexant CX20652" }, - { HDA_CODEC_CX20664, "Conexant CX20664" }, - { HDA_CODEC_CX20665, "Conexant CX20665" }, - { HDA_CODEC_VT1708_8, "VIA VT1708_8" }, - { HDA_CODEC_VT1708_9, "VIA VT1708_9" }, - { HDA_CODEC_VT1708_A, "VIA VT1708_A" }, - { HDA_CODEC_VT1708_B, "VIA VT1708_B" }, - { HDA_CODEC_VT1709_0, "VIA VT1709_0" }, - { HDA_CODEC_VT1709_1, "VIA VT1709_1" }, - { HDA_CODEC_VT1709_2, "VIA VT1709_2" }, - { HDA_CODEC_VT1709_3, "VIA VT1709_3" }, - { HDA_CODEC_VT1709_4, "VIA VT1709_4" }, - { HDA_CODEC_VT1709_5, "VIA VT1709_5" }, - { HDA_CODEC_VT1709_6, "VIA VT1709_6" }, - { HDA_CODEC_VT1709_7, "VIA VT1709_7" }, - { HDA_CODEC_VT1708B_0, "VIA VT1708B_0" }, - { HDA_CODEC_VT1708B_1, "VIA VT1708B_1" }, - { HDA_CODEC_VT1708B_2, "VIA VT1708B_2" }, - { HDA_CODEC_VT1708B_3, "VIA VT1708B_3" }, - { HDA_CODEC_VT1708B_4, "VIA VT1708B_4" }, - { HDA_CODEC_VT1708B_5, "VIA VT1708B_5" }, - { HDA_CODEC_VT1708B_6, "VIA VT1708B_6" }, - { HDA_CODEC_VT1708B_7, "VIA VT1708B_7" }, - { HDA_CODEC_VT1708S_0, "VIA VT1708S_0" }, - { HDA_CODEC_VT1708S_1, "VIA VT1708S_1" }, - { HDA_CODEC_VT1708S_2, "VIA VT1708S_2" }, - { HDA_CODEC_VT1708S_3, "VIA VT1708S_3" }, - { HDA_CODEC_VT1708S_4, "VIA VT1708S_4" }, - { HDA_CODEC_VT1708S_5, "VIA VT1708S_5" }, - { HDA_CODEC_VT1708S_6, "VIA VT1708S_6" }, - { HDA_CODEC_VT1708S_7, "VIA VT1708S_7" }, - { HDA_CODEC_VT1702_0, "VIA VT1702_0" }, - { HDA_CODEC_VT1702_1, "VIA VT1702_1" }, - { HDA_CODEC_VT1702_2, "VIA VT1702_2" }, - { HDA_CODEC_VT1702_3, "VIA VT1702_3" }, - { HDA_CODEC_VT1702_4, "VIA VT1702_4" }, - { HDA_CODEC_VT1702_5, "VIA VT1702_5" }, - { HDA_CODEC_VT1702_6, "VIA VT1702_6" }, - { HDA_CODEC_VT1702_7, "VIA VT1702_7" }, - { HDA_CODEC_VT1716S_0, "VIA VT1716S_0" }, - { HDA_CODEC_VT1716S_1, "VIA VT1716S_1" }, - { HDA_CODEC_VT1718S_0, "VIA VT1718S_0" }, - { HDA_CODEC_VT1718S_1, "VIA VT1718S_1" }, - { HDA_CODEC_VT1812, "VIA VT1812" }, - { HDA_CODEC_VT1818S, "VIA VT1818S" }, - { HDA_CODEC_VT1828S, "VIA VT1828S" }, - { HDA_CODEC_VT2002P_0, "VIA VT2002P_0" }, - { HDA_CODEC_VT2002P_1, "VIA VT2002P_1" }, - { HDA_CODEC_VT2020, "VIA VT2020" }, - { HDA_CODEC_ATIRS600_1,"ATI RS600 HDMI" }, - { HDA_CODEC_ATIRS600_2,"ATI RS600 HDMI" }, - { HDA_CODEC_ATIRS690, "ATI RS690/780 HDMI" }, - { HDA_CODEC_ATIR6XX, "ATI R6xx HDMI" }, - { HDA_CODEC_NVIDIAMCP67, "NVidia MCP67 HDMI" }, - { HDA_CODEC_NVIDIAMCP73, "NVidia MCP73 HDMI" }, - { HDA_CODEC_NVIDIAMCP78, "NVidia MCP78 HDMI" }, - { HDA_CODEC_NVIDIAMCP78_2, "NVidia MCP78 HDMI" }, - { HDA_CODEC_NVIDIAMCP7A, "NVidia MCP7A HDMI" }, - { HDA_CODEC_NVIDIAGT220, "NVidia GT220 HDMI" }, - { HDA_CODEC_NVIDIAGT21X, "NVidia GT21x HDMI" }, - { HDA_CODEC_NVIDIAMCP89, "NVidia MCP89 HDMI" }, - { HDA_CODEC_NVIDIAGT240, "NVidia GT240 HDMI" }, - { HDA_CODEC_INTELIP, "Intel Ibex Peak HDMI" }, - { HDA_CODEC_INTELBL, "Intel Bearlake HDMI" }, - { HDA_CODEC_INTELCA, "Intel Cantiga HDMI" }, - { HDA_CODEC_INTELEL, "Intel Eaglelake HDMI" }, - { HDA_CODEC_INTELIP2, "Intel Ibex Peak HDMI" }, - { HDA_CODEC_INTELCPT, "Intel Cougar Point HDMI" }, - { HDA_CODEC_INTELCL, "Intel Crestline HDMI" }, - { HDA_CODEC_SII1390, "Silicon Image SiI1390 HDMI" }, - { HDA_CODEC_SII1392, "Silicon Image SiI1392 HDMI" }, - /* Unknown codec */ - { HDA_CODEC_ALCXXXX, "Realtek (Unknown)" }, - { HDA_CODEC_ADXXXX, "Analog Devices (Unknown)" }, - { HDA_CODEC_CSXXXX, "Cirrus Logic (Unknown)" }, - { HDA_CODEC_CMIXXXX, "CMedia (Unknown)" }, - { HDA_CODEC_STACXXXX, "Sigmatel (Unknown)" }, - { HDA_CODEC_SIIXXXX, "Silicon Image (Unknown)" }, - { HDA_CODEC_AGEREXXXX, "Lucent/Agere Systems (Unknown)" }, - { HDA_CODEC_CXXXXX, "Conexant (Unknown)" }, - { HDA_CODEC_VTXXXX, "VIA (Unknown)" }, - { HDA_CODEC_ATIXXXX, "ATI (Unknown)" }, - { HDA_CODEC_NVIDIAXXXX,"NVidia (Unknown)" }, - { HDA_CODEC_INTELXXXX, "Intel (Unknown)" }, - { HDA_CODEC_IDTXXXX, "IDT (Unknown)" }, -}; -#define HDAC_CODECS_LEN (sizeof(hdac_codecs) / sizeof(hdac_codecs[0])) - - /**************************************************************************** * Function prototypes ****************************************************************************/ @@ -1065,392 +183,78 @@ static void hdac_corb_init(struct hdac_softc *); static void hdac_rirb_init(struct hdac_softc *); static void hdac_corb_start(struct hdac_softc *); static void hdac_rirb_start(struct hdac_softc *); -static void hdac_scan_codecs(struct hdac_softc *); -static void hdac_probe_codec(struct hdac_codec *); -static void hdac_probe_function(struct hdac_codec *, nid_t); -static int hdac_pcmchannel_setup(struct hdac_chan *); static void hdac_attach2(void *); -static uint32_t hdac_command_sendone_internal(struct hdac_softc *, - uint32_t, int); -static void hdac_command_send_internal(struct hdac_softc *, - struct hdac_command_list *, int); +static uint32_t hdac_send_command(struct hdac_softc *, nid_t, uint32_t); static int hdac_probe(device_t); static int hdac_attach(device_t); static int hdac_detach(device_t); static int hdac_suspend(device_t); static int hdac_resume(device_t); -static void hdac_widget_connection_select(struct hdac_widget *, uint8_t); -static void hdac_audio_ctl_amp_set(struct hdac_audio_ctl *, - uint32_t, int, int); -static struct hdac_audio_ctl *hdac_audio_ctl_amp_get(struct hdac_devinfo *, - nid_t, int, int, int); -static void hdac_audio_ctl_amp_set_internal(struct hdac_softc *, - nid_t, nid_t, int, int, int, int, int, int); -static struct hdac_widget *hdac_widget_get(struct hdac_devinfo *, nid_t); static int hdac_rirb_flush(struct hdac_softc *sc); static int hdac_unsolq_flush(struct hdac_softc *sc); -static void hdac_dump_pin_config(struct hdac_widget *w, uint32_t conf); - #define hdac_command(a1, a2, a3) \ - hdac_command_sendone_internal(a1, a2, a3) - -#define hdac_codec_id(c) \ - ((uint32_t)((c == NULL) ? 0x00000000 : \ - ((((uint32_t)(c)->vendor_id & 0x0000ffff) << 16) | \ - ((uint32_t)(c)->device_id & 0x0000ffff)))) - -static char * -hdac_codec_name(struct hdac_codec *codec) -{ - uint32_t id; - int i; + hdac_send_command(a1, a3, a2) - id = hdac_codec_id(codec); - - for (i = 0; i < HDAC_CODECS_LEN; i++) { - if (HDA_DEV_MATCH(hdac_codecs[i].id, id)) - return (hdac_codecs[i].name); - } - - return ((id == 0x00000000) ? "NULL Codec" : "Unknown Codec"); -} - -static char * -hdac_audio_ctl_ossmixer_mask2allname(uint32_t mask, char *buf, size_t len) -{ - static char *ossname[] = SOUND_DEVICE_NAMES; - int i, first = 1; - - bzero(buf, len); - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (mask & (1 << i)) { - if (first == 0) - strlcat(buf, ", ", len); - strlcat(buf, ossname[i], len); - first = 0; - } - } - return (buf); -} - -static struct hdac_audio_ctl * -hdac_audio_ctl_each(struct hdac_devinfo *devinfo, int *index) -{ - if (devinfo == NULL || - devinfo->node_type != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO || - index == NULL || devinfo->function.audio.ctl == NULL || - devinfo->function.audio.ctlcnt < 1 || - *index < 0 || *index >= devinfo->function.audio.ctlcnt) - return (NULL); - return (&devinfo->function.audio.ctl[(*index)++]); -} - -static struct hdac_audio_ctl * -hdac_audio_ctl_amp_get(struct hdac_devinfo *devinfo, nid_t nid, int dir, - int index, int cnt) -{ - struct hdac_audio_ctl *ctl; - int i, found = 0; - - if (devinfo == NULL || devinfo->function.audio.ctl == NULL) - return (NULL); - - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0) - continue; - if (ctl->widget->nid != nid) - continue; - if (dir && ctl->ndir != dir) - continue; - if (index >= 0 && ctl->ndir == HDA_CTL_IN && - ctl->dir == ctl->ndir && ctl->index != index) - continue; - found++; - if (found == cnt || cnt <= 0) - return (ctl); - } - - return (NULL); -} - -/* - * Jack detection (Speaker/HP redirection) event handler. - */ +/* This function surely going to make its way into upper level someday. */ static void -hdac_hp_switch_handler(struct hdac_devinfo *devinfo) +hdac_config_fetch(struct hdac_softc *sc, uint32_t *on, uint32_t *off) { - struct hdac_audio_as *as; - struct hdac_softc *sc; - struct hdac_widget *w; - struct hdac_audio_ctl *ctl; - uint32_t val, res; - int i, j; - nid_t cad; + const char *res = NULL; + int i = 0, j, k, len, inv; - if (devinfo == NULL || devinfo->codec == NULL || - devinfo->codec->sc == NULL) + if (resource_string_value(device_get_name(sc->dev), + device_get_unit(sc->dev), "config", &res) != 0) return; - - sc = devinfo->codec->sc; - cad = devinfo->codec->cad; - as = devinfo->function.audio.as; - for (i = 0; i < devinfo->function.audio.ascnt; i++) { - if (as[i].hpredir < 0) - continue; - - w = hdac_widget_get(devinfo, as[i].pins[15]); - if (w == NULL || w->enable == 0 || w->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - - res = hdac_command(sc, - HDA_CMD_GET_PIN_SENSE(cad, as[i].pins[15]), cad); - - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "Pin sense: nid=%d res=0x%08x\n", - as[i].pins[15], res); - ); - - res = HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT(res); - if (devinfo->function.audio.quirks & HDA_QUIRK_SENSEINV) - res ^= 1; - - /* (Un)Mute headphone pin. */ - ctl = hdac_audio_ctl_amp_get(devinfo, - as[i].pins[15], HDA_CTL_IN, -1, 1); - if (ctl != NULL && ctl->mute) { - /* If pin has muter - use it. */ - val = (res != 0) ? 0 : 1; - if (val != ctl->forcemute) { - ctl->forcemute = val; - hdac_audio_ctl_amp_set(ctl, - HDA_AMP_MUTE_DEFAULT, - HDA_AMP_VOL_DEFAULT, HDA_AMP_VOL_DEFAULT); - } - } else { - /* If there is no muter - disable pin output. */ - w = hdac_widget_get(devinfo, as[i].pins[15]); - if (w != NULL && w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { - if (res != 0) - val = w->wclass.pin.ctrl | - HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - else - val = w->wclass.pin.ctrl & - ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - if (val != w->wclass.pin.ctrl) { - w->wclass.pin.ctrl = val; - hdac_command(sc, - HDA_CMD_SET_PIN_WIDGET_CTRL(cad, - w->nid, w->wclass.pin.ctrl), cad); - } - } + if (!(res != NULL && strlen(res) > 0)) + return; + HDA_BOOTVERBOSE( + device_printf(sc->dev, "Config options:"); + ); + for (;;) { + while (res[i] != '\0' && + (res[i] == ',' || isspace(res[i]) != 0)) + i++; + if (res[i] == '\0') { + HDA_BOOTVERBOSE( + printf("\n"); + ); + return; } - /* (Un)Mute other pins. */ - for (j = 0; j < 15; j++) { - if (as[i].pins[j] <= 0) + j = i; + while (res[j] != '\0' && + !(res[j] == ',' || isspace(res[j]) != 0)) + j++; + len = j - i; + if (len > 2 && strncmp(res + i, "no", 2) == 0) + inv = 2; + else + inv = 0; + for (k = 0; len > inv && k < HDAC_QUIRKS_TAB_LEN; k++) { + if (strncmp(res + i + inv, + hdac_quirks_tab[k].key, len - inv) != 0) continue; - ctl = hdac_audio_ctl_amp_get(devinfo, - as[i].pins[j], HDA_CTL_IN, -1, 1); - if (ctl != NULL && ctl->mute) { - /* If pin has muter - use it. */ - val = (res != 0) ? 1 : 0; - if (val == ctl->forcemute) - continue; - ctl->forcemute = val; - hdac_audio_ctl_amp_set(ctl, - HDA_AMP_MUTE_DEFAULT, - HDA_AMP_VOL_DEFAULT, HDA_AMP_VOL_DEFAULT); + if (len - inv != strlen(hdac_quirks_tab[k].key)) continue; + HDA_BOOTVERBOSE( + printf(" %s%s", (inv != 0) ? "no" : "", + hdac_quirks_tab[k].key); + ); + if (inv == 0) { + *on |= hdac_quirks_tab[k].value; + *on &= ~hdac_quirks_tab[k].value; + } else if (inv != 0) { + *off |= hdac_quirks_tab[k].value; + *off &= ~hdac_quirks_tab[k].value; } - /* If there is no muter - disable pin output. */ - w = hdac_widget_get(devinfo, as[i].pins[j]); - if (w != NULL && w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { - if (res != 0) - val = w->wclass.pin.ctrl & - ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - else - val = w->wclass.pin.ctrl | - HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - if (val != w->wclass.pin.ctrl) { - w->wclass.pin.ctrl = val; - hdac_command(sc, - HDA_CMD_SET_PIN_WIDGET_CTRL(cad, - w->nid, w->wclass.pin.ctrl), cad); - } - } - } - } -} - -/* - * Callback for poll based jack detection. - */ -static void -hdac_jack_poll_callback(void *arg) -{ - struct hdac_devinfo *devinfo = arg; - struct hdac_softc *sc; - - if (devinfo == NULL || devinfo->codec == NULL || - devinfo->codec->sc == NULL) - return; - sc = devinfo->codec->sc; - hdac_lock(sc); - if (sc->poll_ival == 0) { - hdac_unlock(sc); - return; - } - hdac_hp_switch_handler(devinfo); - callout_reset(&sc->poll_jack, sc->poll_ival, - hdac_jack_poll_callback, devinfo); - hdac_unlock(sc); -} - -/* - * Jack detection initializer. - */ -static void -hdac_hp_switch_init(struct hdac_devinfo *devinfo) -{ - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_widget *w; - uint32_t id; - int i, enable = 0, poll = 0; - nid_t cad; - - id = hdac_codec_id(devinfo->codec); - cad = devinfo->codec->cad; - for (i = 0; i < devinfo->function.audio.ascnt; i++) { - if (as[i].hpredir < 0) - continue; - - w = hdac_widget_get(devinfo, as[i].pins[15]); - if (w == NULL || w->enable == 0 || w->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(w->wclass.pin.cap) == 0 || - (HDA_CONFIG_DEFAULTCONF_MISC(w->wclass.pin.config) & 1) != 0) { - device_printf(sc->dev, - "No jack detection support at pin %d\n", - as[i].pins[15]); - continue; - } - enable = 1; - if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) { - hdac_command(sc, - HDA_CMD_SET_UNSOLICITED_RESPONSE(cad, w->nid, - HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE | - HDAC_UNSOLTAG_EVENT_HP), cad); - } else - poll = 1; - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "Enabling headphone/speaker " - "audio routing switching:\n"); - device_printf(sc->dev, "\tas=%d sense nid=%d [%s]\n", - i, w->nid, (poll != 0) ? "POLL" : "UNSOL"); - ); - } - if (enable) { - hdac_hp_switch_handler(devinfo); - if (poll) { - callout_reset(&sc->poll_jack, 1, - hdac_jack_poll_callback, devinfo); - } - } -} - -/* - * Unsolicited messages handler. - */ -static void -hdac_unsolicited_handler(struct hdac_codec *codec, uint32_t tag) -{ - struct hdac_softc *sc; - struct hdac_devinfo *devinfo = NULL; - int i; - - if (codec == NULL || codec->sc == NULL) - return; - - sc = codec->sc; - - HDA_BOOTVERBOSE( - device_printf(sc->dev, "Unsol Tag: 0x%08x\n", tag); - ); - - for (i = 0; i < codec->num_fgs; i++) { - if (codec->fgs[i].node_type == - HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { - devinfo = &codec->fgs[i]; break; } + i = j; } - - if (devinfo == NULL) - return; - - switch (tag) { - case HDAC_UNSOLTAG_EVENT_HP: - hdac_hp_switch_handler(devinfo); - break; - default: - device_printf(sc->dev, "Unknown unsol tag: 0x%08x!\n", tag); - break; - } -} - -static int -hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch) -{ - /* XXX to be removed */ -#ifdef HDAC_INTR_EXTRA - uint32_t res; -#endif - - if (!(ch->flags & HDAC_CHN_RUNNING)) - return (0); - - /* XXX to be removed */ -#ifdef HDAC_INTR_EXTRA - res = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDSTS); -#endif - - /* XXX to be removed */ -#ifdef HDAC_INTR_EXTRA - HDA_BOOTVERBOSE( - if (res & (HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE)) - device_printf(ch->pdevinfo->dev, - "PCMDIR_%s intr triggered beyond stream boundary:" - "%08x\n", - (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", res); - ); -#endif - - HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDSTS, - HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE | HDAC_SDSTS_BCIS ); - - /* XXX to be removed */ -#ifdef HDAC_INTR_EXTRA - if (res & HDAC_SDSTS_BCIS) { -#endif - return (1); - /* XXX to be removed */ -#ifdef HDAC_INTR_EXTRA - } -#endif - - return (0); } /**************************************************************************** @@ -1462,69 +266,70 @@ static void hdac_intr_handler(void *context) { struct hdac_softc *sc; + device_t dev; uint32_t intsts; uint8_t rirbsts; - struct hdac_rirb *rirb_base; - uint32_t trigger; int i; sc = (struct hdac_softc *)context; - hdac_lock(sc); - if (sc->polling != 0) { - hdac_unlock(sc); - return; - } /* Do we have anything to do? */ intsts = HDAC_READ_4(&sc->mem, HDAC_INTSTS); - if (!HDA_FLAG_MATCH(intsts, HDAC_INTSTS_GIS)) { + if ((intsts & HDAC_INTSTS_GIS) == 0) { hdac_unlock(sc); return; } - trigger = 0; - /* Was this a controller interrupt? */ - if (HDA_FLAG_MATCH(intsts, HDAC_INTSTS_CIS)) { - rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr; + if (intsts & HDAC_INTSTS_CIS) { rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS); /* Get as many responses that we can */ - while (HDA_FLAG_MATCH(rirbsts, HDAC_RIRBSTS_RINTFL)) { + while (rirbsts & HDAC_RIRBSTS_RINTFL) { HDAC_WRITE_1(&sc->mem, HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL); - if (hdac_rirb_flush(sc) != 0) - trigger |= HDAC_TRIGGER_UNSOL; + hdac_rirb_flush(sc); rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS); } - /* XXX to be removed */ - /* Clear interrupt and exit */ -#ifdef HDAC_INTR_EXTRA - HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, HDAC_INTSTS_CIS); -#endif + if (sc->unsolq_rp != sc->unsolq_wp) + taskqueue_enqueue(taskqueue_thread, &sc->unsolq_task); } if (intsts & HDAC_INTSTS_SIS_MASK) { - for (i = 0; i < sc->num_chans; i++) { - if ((intsts & (1 << (sc->chans[i].off >> 5))) && - hdac_stream_intr(sc, &sc->chans[i]) != 0) - trigger |= (1 << i); + for (i = 0; i < sc->num_ss; i++) { + if ((intsts & (1 << i)) == 0) + continue; + HDAC_WRITE_1(&sc->mem, (i << 5) + HDAC_SDSTS, + HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE | HDAC_SDSTS_BCIS ); + if ((dev = sc->streams[i].dev) != NULL) { + HDAC_STREAM_INTR(dev, + sc->streams[i].dir, sc->streams[i].stream); + } } - /* XXX to be removed */ -#ifdef HDAC_INTR_EXTRA - HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts & - HDAC_INTSTS_SIS_MASK); -#endif } + HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts); hdac_unlock(sc); +} - for (i = 0; i < sc->num_chans; i++) { - if (trigger & (1 << i)) - chn_intr(sc->chans[i].c); +static void +hdac_poll_callback(void *arg) +{ + struct hdac_softc *sc = arg; + + if (sc == NULL) + return; + + hdac_lock(sc); + if (sc->polling == 0) { + hdac_unlock(sc); + return; } - if (trigger & HDAC_TRIGGER_UNSOL) - taskqueue_enqueue(taskqueue_thread, &sc->unsolq_task); + callout_reset(&sc->poll_callout, sc->poll_ival, + hdac_poll_callback, sc); + hdac_unlock(sc); + + hdac_intr_handler(sc); } /**************************************************************************** @@ -1577,11 +382,11 @@ hdac_reset(struct hdac_softc *sc, int wakeup) device_printf(sc->dev, "Unable to put hdac in reset\n"); return (ENXIO); } - + /* If wakeup is not requested - leave the controller in reset state. */ if (!wakeup) return (0); - + DELAY(100); gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); HDAC_WRITE_4(&sc->mem, HDAC_GCTL, gctl | HDAC_GCTL_CRST); @@ -1629,8 +434,13 @@ hdac_get_capabilities(struct hdac_softc *sc) sc->num_iss = HDAC_GCAP_ISS(gcap); sc->num_oss = HDAC_GCAP_OSS(gcap); sc->num_bss = HDAC_GCAP_BSS(gcap); + sc->num_ss = sc->num_iss + sc->num_oss + sc->num_bss; sc->num_sdo = HDAC_GCAP_NSDO(gcap); - sc->support_64bit = HDA_FLAG_MATCH(gcap, HDAC_GCAP_64OK); + sc->support_64bit = (gcap & HDAC_GCAP_64OK) != 0; + if (sc->quirks_on & HDAC_QUIRK_64BIT) + sc->support_64bit = 1; + else if (sc->quirks_off & HDAC_QUIRK_64BIT) + sc->support_64bit = 0; corbsize = HDAC_READ_1(&sc->mem, HDAC_CORBSIZE); if ((corbsize & HDAC_CORBSIZE_CORBSZCAP_256) == @@ -1707,7 +517,7 @@ hdac_dma_alloc(struct hdac_softc *sc, struct hdac_dma *dma, bus_size_t size) bus_size_t roundsz; int result; - roundsz = roundup2(size, HDAC_DMA_ALIGNMENT); + roundsz = roundup2(size, HDA_DMA_ALIGNMENT); bzero(dma, sizeof(*dma)); /* @@ -1715,7 +525,7 @@ hdac_dma_alloc(struct hdac_softc *sc, struct hdac_dma *dma, bus_size_t size) */ result = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* parent */ - HDAC_DMA_ALIGNMENT, /* alignment */ + HDA_DMA_ALIGNMENT, /* alignment */ 0, /* boundary */ (sc->support_64bit) ? BUS_SPACE_MAXADDR : BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ @@ -1863,12 +673,10 @@ hdac_irq_alloc(struct hdac_softc *sc) irq = &sc->irq; irq->irq_rid = 0x0; - if ((sc->flags & HDAC_F_MSI) && + if ((sc->quirks_off & HDAC_QUIRK_MSI) == 0 && (result = pci_msi_count(sc->dev)) == 1 && pci_alloc_msi(sc->dev, &result) == 0) irq->irq_rid = 0x1; - else - sc->flags &= ~HDAC_F_MSI; irq->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &irq->irq_rid, RF_SHAREABLE | RF_ACTIVE); @@ -2058,884 +866,10 @@ hdac_rirb_start(struct hdac_softc *sc) HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, rirbctl); } - -/**************************************************************************** - * void hdac_scan_codecs(struct hdac_softc *, int) - * - * Scan the bus for available codecs, starting with num. - ****************************************************************************/ -static void -hdac_scan_codecs(struct hdac_softc *sc) -{ - struct hdac_codec *codec; - int i; - uint16_t statests; - - statests = HDAC_READ_2(&sc->mem, HDAC_STATESTS); - for (i = 0; i < HDAC_CODEC_MAX; i++) { - if (HDAC_STATESTS_SDIWAKE(statests, i)) { - /* We have found a codec. */ - codec = (struct hdac_codec *)malloc(sizeof(*codec), - M_HDAC, M_ZERO | M_NOWAIT); - if (codec == NULL) { - device_printf(sc->dev, - "Unable to allocate memory for codec\n"); - continue; - } - codec->commands = NULL; - codec->responses_received = 0; - codec->verbs_sent = 0; - codec->sc = sc; - codec->cad = i; - sc->codecs[i] = codec; - hdac_probe_codec(codec); - } - } - /* All codecs have been probed, now try to attach drivers to them */ - /* bus_generic_attach(sc->dev); */ -} - -/**************************************************************************** - * void hdac_probe_codec(struct hdac_softc *, int) - * - * Probe a the given codec_id for available function groups. - ****************************************************************************/ -static void -hdac_probe_codec(struct hdac_codec *codec) -{ - struct hdac_softc *sc = codec->sc; - uint32_t vendorid, revisionid, subnode; - int startnode; - int endnode; - int i; - nid_t cad = codec->cad; - - HDA_BOOTVERBOSE( - device_printf(sc->dev, "Probing codec #%d...\n", cad); - ); - vendorid = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_VENDOR_ID), - cad); - revisionid = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_REVISION_ID), - cad); - codec->vendor_id = HDA_PARAM_VENDOR_ID_VENDOR_ID(vendorid); - codec->device_id = HDA_PARAM_VENDOR_ID_DEVICE_ID(vendorid); - codec->revision_id = HDA_PARAM_REVISION_ID_REVISION_ID(revisionid); - codec->stepping_id = HDA_PARAM_REVISION_ID_STEPPING_ID(revisionid); - - if (vendorid == HDAC_INVALID && revisionid == HDAC_INVALID) { - device_printf(sc->dev, "Codec #%d is not responding!" - " Probing aborted.\n", cad); - return; - } - - device_printf(sc->dev, "HDA Codec #%d: %s\n", - cad, hdac_codec_name(codec)); - HDA_BOOTVERBOSE( - device_printf(sc->dev, " HDA Codec ID: 0x%08x\n", - hdac_codec_id(codec)); - device_printf(sc->dev, " Vendor: 0x%04x\n", - codec->vendor_id); - device_printf(sc->dev, " Device: 0x%04x\n", - codec->device_id); - device_printf(sc->dev, " Revision: 0x%02x\n", - codec->revision_id); - device_printf(sc->dev, " Stepping: 0x%02x\n", - codec->stepping_id); - device_printf(sc->dev, "PCI Subvendor: 0x%08x\n", - sc->pci_subvendor); - ); - subnode = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_SUB_NODE_COUNT), - cad); - startnode = HDA_PARAM_SUB_NODE_COUNT_START(subnode); - endnode = startnode + HDA_PARAM_SUB_NODE_COUNT_TOTAL(subnode); - - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "\tstartnode=%d endnode=%d\n", - startnode, endnode); - ); - - codec->fgs = (struct hdac_devinfo *)malloc(sizeof(struct hdac_devinfo) * - (endnode - startnode), M_HDAC, M_NOWAIT | M_ZERO); - if (codec->fgs == NULL) { - device_printf(sc->dev, "%s: Unable to allocate function groups\n", - __func__); - return; - } - - for (i = startnode; i < endnode; i++) - hdac_probe_function(codec, i); - return; -} - -/* - * Probe codec function and add it to the list. - */ -static void -hdac_probe_function(struct hdac_codec *codec, nid_t nid) -{ - struct hdac_softc *sc = codec->sc; - struct hdac_devinfo *devinfo = &codec->fgs[codec->num_fgs]; - uint32_t fctgrptype; - uint32_t res; - nid_t cad = codec->cad; - - fctgrptype = HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE(hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_FCT_GRP_TYPE), cad)); - - devinfo->nid = nid; - devinfo->node_type = fctgrptype; - devinfo->codec = codec; - - res = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad , nid, HDA_PARAM_SUB_NODE_COUNT), cad); - - devinfo->nodecnt = HDA_PARAM_SUB_NODE_COUNT_TOTAL(res); - devinfo->startnode = HDA_PARAM_SUB_NODE_COUNT_START(res); - devinfo->endnode = devinfo->startnode + devinfo->nodecnt; - - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "\tFound %s FG nid=%d startnode=%d endnode=%d total=%d\n", - (fctgrptype == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) ? "audio": - (fctgrptype == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MODEM) ? "modem": - "unknown", nid, devinfo->startnode, devinfo->endnode, - devinfo->nodecnt); - ); - - if (devinfo->nodecnt > 0) - devinfo->widget = (struct hdac_widget *)malloc( - sizeof(*(devinfo->widget)) * devinfo->nodecnt, M_HDAC, - M_NOWAIT | M_ZERO); - else - devinfo->widget = NULL; - - if (devinfo->widget == NULL) { - device_printf(sc->dev, "unable to allocate widgets!\n"); - devinfo->endnode = devinfo->startnode; - devinfo->nodecnt = 0; - return; - } - - codec->num_fgs++; -} - -static void -hdac_widget_connection_parse(struct hdac_widget *w) -{ - struct hdac_softc *sc = w->devinfo->codec->sc; - uint32_t res; - int i, j, max, ents, entnum; - nid_t cad = w->devinfo->codec->cad; - nid_t nid = w->nid; - nid_t cnid, addcnid, prevcnid; - - w->nconns = 0; - - res = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_CONN_LIST_LENGTH), cad); - - ents = HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(res); - - if (ents < 1) - return; - - entnum = HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM(res) ? 2 : 4; - max = (sizeof(w->conns) / sizeof(w->conns[0])) - 1; - prevcnid = 0; - -#define CONN_RMASK(e) (1 << ((32 / (e)) - 1)) -#define CONN_NMASK(e) (CONN_RMASK(e) - 1) -#define CONN_RESVAL(r, e, n) ((r) >> ((32 / (e)) * (n))) -#define CONN_RANGE(r, e, n) (CONN_RESVAL(r, e, n) & CONN_RMASK(e)) -#define CONN_CNID(r, e, n) (CONN_RESVAL(r, e, n) & CONN_NMASK(e)) - - for (i = 0; i < ents; i += entnum) { - res = hdac_command(sc, - HDA_CMD_GET_CONN_LIST_ENTRY(cad, nid, i), cad); - for (j = 0; j < entnum; j++) { - cnid = CONN_CNID(res, entnum, j); - if (cnid == 0) { - if (w->nconns < ents) - device_printf(sc->dev, - "%s: nid=%d WARNING: zero cnid " - "entnum=%d j=%d index=%d " - "entries=%d found=%d res=0x%08x\n", - __func__, nid, entnum, j, i, - ents, w->nconns, res); - else - goto getconns_out; - } - if (cnid < w->devinfo->startnode || - cnid >= w->devinfo->endnode) { - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "GHOST: nid=%d j=%d " - "entnum=%d index=%d res=0x%08x\n", - nid, j, entnum, i, res); - ); - } - if (CONN_RANGE(res, entnum, j) == 0) - addcnid = cnid; - else if (prevcnid == 0 || prevcnid >= cnid) { - device_printf(sc->dev, - "%s: WARNING: Invalid child range " - "nid=%d index=%d j=%d entnum=%d " - "prevcnid=%d cnid=%d res=0x%08x\n", - __func__, nid, i, j, entnum, prevcnid, - cnid, res); - addcnid = cnid; - } else - addcnid = prevcnid + 1; - while (addcnid <= cnid) { - if (w->nconns > max) { - device_printf(sc->dev, - "Adding %d (nid=%d): " - "Max connection reached! max=%d\n", - addcnid, nid, max + 1); - goto getconns_out; - } - w->connsenable[w->nconns] = 1; - w->conns[w->nconns++] = addcnid++; - } - prevcnid = cnid; - } - } - -getconns_out: - return; -} - -static uint32_t -hdac_widget_pin_patch(uint32_t config, const char *str) -{ - char buf[256]; - char *key, *value, *rest, *bad; - int ival, i; - - strlcpy(buf, str, sizeof(buf)); - rest = buf; - while ((key = strsep(&rest, "=")) != NULL) { - value = strsep(&rest, " \t"); - if (value == NULL) - break; - ival = strtol(value, &bad, 10); - if (strcmp(key, "seq") == 0) { - config &= ~HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK; - config |= ((ival << HDA_CONFIG_DEFAULTCONF_SEQUENCE_SHIFT) & - HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK); - } else if (strcmp(key, "as") == 0) { - config &= ~HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK; - config |= ((ival << HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT) & - HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK); - } else if (strcmp(key, "misc") == 0) { - config &= ~HDA_CONFIG_DEFAULTCONF_MISC_MASK; - config |= ((ival << HDA_CONFIG_DEFAULTCONF_MISC_SHIFT) & - HDA_CONFIG_DEFAULTCONF_MISC_MASK); - } else if (strcmp(key, "color") == 0) { - config &= ~HDA_CONFIG_DEFAULTCONF_COLOR_MASK; - if (bad[0] == 0) { - config |= ((ival << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT) & - HDA_CONFIG_DEFAULTCONF_COLOR_MASK); - }; - for (i = 0; i < 16; i++) { - if (strcasecmp(HDA_COLORS[i], value) == 0) { - config |= (i << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT); - break; - } - } - } else if (strcmp(key, "ctype") == 0) { - config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK; - config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT) & - HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK); - } else if (strcmp(key, "device") == 0) { - config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - if (bad[0] == 0) { - config |= ((ival << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT) & - HDA_CONFIG_DEFAULTCONF_DEVICE_MASK); - continue; - }; - for (i = 0; i < 16; i++) { - if (strcasecmp(HDA_DEVS[i], value) == 0) { - config |= (i << HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT); - break; - } - } - } else if (strcmp(key, "loc") == 0) { - config &= ~HDA_CONFIG_DEFAULTCONF_LOCATION_MASK; - config |= ((ival << HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT) & - HDA_CONFIG_DEFAULTCONF_LOCATION_MASK); - } else if (strcmp(key, "conn") == 0) { - config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; - if (bad[0] == 0) { - config |= ((ival << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT) & - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - continue; - }; - for (i = 0; i < 4; i++) { - if (strcasecmp(HDA_CONNS[i], value) == 0) { - config |= (i << HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT); - break; - } - } - } - } - return (config); -} - -static uint32_t -hdac_widget_pin_getconfig(struct hdac_widget *w) -{ - struct hdac_softc *sc; - uint32_t config, orig, id; - nid_t cad, nid; - char buf[32]; - const char *res = NULL, *patch = NULL; - - sc = w->devinfo->codec->sc; - cad = w->devinfo->codec->cad; - nid = w->nid; - id = hdac_codec_id(w->devinfo->codec); - - config = hdac_command(sc, - HDA_CMD_GET_CONFIGURATION_DEFAULT(cad, nid), - cad); - orig = config; - - HDA_BOOTVERBOSE( - hdac_dump_pin_config(w, orig); - ); - - /* XXX: Old patches require complete review. - * Now they may create more problem then solve due to - * incorrect associations. - */ - if (id == HDA_CODEC_ALC880 && sc->pci_subvendor == LG_LW20_SUBVENDOR) { - switch (nid) { - case 26: - config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; - break; - case 27: - config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - config |= HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT; - break; - default: - break; - } - } else if (id == HDA_CODEC_ALC880 && - (sc->pci_subvendor == CLEVO_D900T_SUBVENDOR || - sc->pci_subvendor == ASUS_M5200_SUBVENDOR)) { - /* - * Super broken BIOS - */ - switch (nid) { - case 24: /* MIC1 */ - config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; - break; - case 25: /* XXX MIC2 */ - config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; - break; - case 26: /* LINE1 */ - config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; - break; - case 27: /* XXX LINE2 */ - config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; - break; - case 28: /* CD */ - config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - config |= HDA_CONFIG_DEFAULTCONF_DEVICE_CD; - break; - } - } else if (id == HDA_CODEC_ALC883 && - (sc->pci_subvendor == MSI_MS034A_SUBVENDOR || - HDA_DEV_MATCH(ACER_ALL_SUBVENDOR, sc->pci_subvendor))) { - switch (nid) { - case 25: - config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); - break; - case 28: - config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); - break; - } - } else if (id == HDA_CODEC_CX20549 && sc->pci_subvendor == - HP_V3000_SUBVENDOR) { - switch (nid) { - case 18: - config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; - config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE; - break; - case 20: - config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); - break; - case 21: - config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_CD | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); - break; - } - } else if (id == HDA_CODEC_CX20551 && sc->pci_subvendor == - HP_DV5000_SUBVENDOR) { - switch (nid) { - case 20: - case 21: - config &= ~HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK; - config |= HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE; - break; - } - } else if (id == HDA_CODEC_ALC861 && sc->pci_subvendor == - ASUS_W6F_SUBVENDOR) { - switch (nid) { - case 11: - config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); - break; - case 12: - case 14: - case 16: - case 31: - case 32: - config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED); - break; - case 15: - config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); - break; - } - } else if (id == HDA_CODEC_ALC861 && sc->pci_subvendor == - UNIWILL_9075_SUBVENDOR) { - switch (nid) { - case 15: - config &= ~(HDA_CONFIG_DEFAULTCONF_DEVICE_MASK | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK); - config |= (HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT | - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK); - break; - } - } - - /* New patches */ - if (id == HDA_CODEC_AD1986A && - (sc->pci_subvendor == ASUS_M2NPVMX_SUBVENDOR || - sc->pci_subvendor == ASUS_A8NVMCSM_SUBVENDOR || - sc->pci_subvendor == ASUS_P5PL2_SUBVENDOR)) { - switch (nid) { - case 26: /* Headphones with redirection */ - patch = "as=1 seq=15"; - break; - case 28: /* 5.1 out => 2.0 out + 1 input */ - patch = "device=Line-in as=8 seq=1"; - break; - case 29: /* Can't use this as input, as the only available mic - * preamplifier is busy by front panel mic (nid 31). - * If you want to use this rear connector as mic input, - * you have to disable the front panel one. */ - patch = "as=0"; - break; - case 31: /* Lot of inputs configured with as=15 and unusable */ - patch = "as=8 seq=3"; - break; - case 32: - patch = "as=8 seq=4"; - break; - case 34: - patch = "as=8 seq=5"; - break; - case 36: - patch = "as=8 seq=6"; - break; - } - } else if (id == HDA_CODEC_ALC260 && - HDA_DEV_MATCH(SONY_S5_SUBVENDOR, sc->pci_subvendor)) { - switch (nid) { - case 16: - patch = "seq=15 device=Headphones"; - break; - } - } else if (id == HDA_CODEC_ALC268) { - if (sc->pci_subvendor == ACER_T5320_SUBVENDOR) { - switch (nid) { - case 20: /* Headphones Jack */ - patch = "as=1 seq=15"; - break; - } - } - } else if (id == HDA_CODEC_CX20561 && - sc->pci_subvendor == LENOVO_B450_SUBVENDOR) { - switch (nid) { - case 22: - patch = "as=1 seq=15"; - break; - } - } - - if (patch != NULL) - config = hdac_widget_pin_patch(config, patch); - - snprintf(buf, sizeof(buf), "cad%u.nid%u.config", cad, nid); - if (resource_string_value(device_get_name(sc->dev), - device_get_unit(sc->dev), buf, &res) == 0) { - if (strncmp(res, "0x", 2) == 0) { - config = strtol(res + 2, NULL, 16); - } else { - config = hdac_widget_pin_patch(config, res); - } - } - - HDA_BOOTVERBOSE( - if (config != orig) - device_printf(sc->dev, - "Patching pin config nid=%u 0x%08x -> 0x%08x\n", - nid, orig, config); - ); - - return (config); -} - -static uint32_t -hdac_widget_pin_getcaps(struct hdac_widget *w) -{ - struct hdac_softc *sc; - uint32_t caps, orig, id; - nid_t cad, nid; - - sc = w->devinfo->codec->sc; - cad = w->devinfo->codec->cad; - nid = w->nid; - id = hdac_codec_id(w->devinfo->codec); - - caps = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_PIN_CAP), cad); - orig = caps; - - HDA_BOOTVERBOSE( - if (caps != orig) - device_printf(sc->dev, - "Patching pin caps nid=%u 0x%08x -> 0x%08x\n", - nid, orig, caps); - ); - - return (caps); -} - -static void -hdac_widget_pin_parse(struct hdac_widget *w) -{ - struct hdac_softc *sc = w->devinfo->codec->sc; - uint32_t config, pincap; - const char *devstr; - nid_t cad = w->devinfo->codec->cad; - nid_t nid = w->nid; - int conn, color; - - config = hdac_widget_pin_getconfig(w); - w->wclass.pin.config = config; - - pincap = hdac_widget_pin_getcaps(w); - w->wclass.pin.cap = pincap; - - w->wclass.pin.ctrl = hdac_command(sc, - HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid), cad); - - if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) { - w->param.eapdbtl = hdac_command(sc, - HDA_CMD_GET_EAPD_BTL_ENABLE(cad, nid), cad); - w->param.eapdbtl &= 0x7; - w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; - } else - w->param.eapdbtl = HDAC_INVALID; - - devstr = HDA_DEVS[(config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) >> - HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT]; - - conn = (config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) >> - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT; - color = (config & HDA_CONFIG_DEFAULTCONF_COLOR_MASK) >> - HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT; - - strlcat(w->name, ": ", sizeof(w->name)); - strlcat(w->name, devstr, sizeof(w->name)); - strlcat(w->name, " (", sizeof(w->name)); - if (conn == 0 && color != 0 && color != 15) { - strlcat(w->name, HDA_COLORS[color], sizeof(w->name)); - strlcat(w->name, " ", sizeof(w->name)); - } - strlcat(w->name, HDA_CONNS[conn], sizeof(w->name)); - strlcat(w->name, ")", sizeof(w->name)); -} - -static uint32_t -hdac_widget_getcaps(struct hdac_widget *w, int *waspin) -{ - struct hdac_softc *sc; - uint32_t caps, orig, id; - nid_t cad, nid, beeper = -1; - - sc = w->devinfo->codec->sc; - cad = w->devinfo->codec->cad; - nid = w->nid; - id = hdac_codec_id(w->devinfo->codec); - - caps = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_AUDIO_WIDGET_CAP), - cad); - orig = caps; - - /* On some codecs beeper is an input pin, but it is not recordable - alone. Also most of BIOSes does not declare beeper pin. - Change beeper pin node type to beeper to help parser. */ - *waspin = 0; - switch (id) { - case HDA_CODEC_AD1882: - case HDA_CODEC_AD1883: - case HDA_CODEC_AD1984: - case HDA_CODEC_AD1984A: - case HDA_CODEC_AD1984B: - case HDA_CODEC_AD1987: - case HDA_CODEC_AD1988: - case HDA_CODEC_AD1988B: - case HDA_CODEC_AD1989B: - beeper = 26; - break; - case HDA_CODEC_ALC260: - beeper = 23; - break; - case HDA_CODEC_ALC262: - case HDA_CODEC_ALC268: - case HDA_CODEC_ALC880: - case HDA_CODEC_ALC882: - case HDA_CODEC_ALC883: - case HDA_CODEC_ALC885: - case HDA_CODEC_ALC888: - case HDA_CODEC_ALC889: - beeper = 29; - break; - } - if (nid == beeper) { - caps &= ~HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK; - caps |= HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET << - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT; - *waspin = 1; - } - - HDA_BOOTVERBOSE( - if (caps != orig) { - device_printf(sc->dev, - "Patching widget caps nid=%u 0x%08x -> 0x%08x\n", - nid, orig, caps); - } - ); - - return (caps); -} - -static void -hdac_widget_parse(struct hdac_widget *w) -{ - struct hdac_softc *sc = w->devinfo->codec->sc; - uint32_t wcap, cap; - char *typestr; - nid_t cad = w->devinfo->codec->cad; - nid_t nid = w->nid; - - wcap = hdac_widget_getcaps(w, &w->waspin); - - w->param.widget_cap = wcap; - w->type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(wcap); - - switch (w->type) { - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: - typestr = "audio output"; - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: - typestr = "audio input"; - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: - typestr = "audio mixer"; - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: - typestr = "audio selector"; - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: - typestr = "pin"; - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET: - typestr = "power widget"; - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET: - typestr = "volume widget"; - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET: - typestr = "beep widget"; - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VENDOR_WIDGET: - typestr = "vendor widget"; - break; - default: - typestr = "unknown type"; - break; - } - - strlcpy(w->name, typestr, sizeof(w->name)); - - hdac_widget_connection_parse(w); - - if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(wcap)) { - if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap)) - w->param.outamp_cap = - hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, nid, - HDA_PARAM_OUTPUT_AMP_CAP), cad); - else - w->param.outamp_cap = - w->devinfo->function.audio.outamp_cap; - } else - w->param.outamp_cap = 0; - - if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(wcap)) { - if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap)) - w->param.inamp_cap = - hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, nid, - HDA_PARAM_INPUT_AMP_CAP), cad); - else - w->param.inamp_cap = - w->devinfo->function.audio.inamp_cap; - } else - w->param.inamp_cap = 0; - - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || - w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { - if (HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR(wcap)) { - cap = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, nid, - HDA_PARAM_SUPP_STREAM_FORMATS), cad); - w->param.supp_stream_formats = (cap != 0) ? cap : - w->devinfo->function.audio.supp_stream_formats; - cap = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, nid, - HDA_PARAM_SUPP_PCM_SIZE_RATE), cad); - w->param.supp_pcm_size_rate = (cap != 0) ? cap : - w->devinfo->function.audio.supp_pcm_size_rate; - } else { - w->param.supp_stream_formats = - w->devinfo->function.audio.supp_stream_formats; - w->param.supp_pcm_size_rate = - w->devinfo->function.audio.supp_pcm_size_rate; - } - } else { - w->param.supp_stream_formats = 0; - w->param.supp_pcm_size_rate = 0; - } - - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - hdac_widget_pin_parse(w); -} - -static struct hdac_widget * -hdac_widget_get(struct hdac_devinfo *devinfo, nid_t nid) -{ - if (devinfo == NULL || devinfo->widget == NULL || - nid < devinfo->startnode || nid >= devinfo->endnode) - return (NULL); - return (&devinfo->widget[nid - devinfo->startnode]); -} - -static __inline int -hda_poll_channel(struct hdac_chan *ch) -{ - uint32_t sz, delta; - volatile uint32_t ptr; - - if (!(ch->flags & HDAC_CHN_RUNNING)) - return (0); - - sz = ch->blksz * ch->blkcnt; - if (ch->dmapos != NULL) - ptr = *(ch->dmapos); - else - ptr = HDAC_READ_4(&ch->devinfo->codec->sc->mem, - ch->off + HDAC_SDLPIB); - ch->ptr = ptr; - ptr %= sz; - ptr &= ~(ch->blksz - 1); - delta = (sz + ptr - ch->prevptr) % sz; - - if (delta < ch->blksz) - return (0); - - ch->prevptr = ptr; - - return (1); -} - -static void -hda_poll_callback(void *arg) -{ - struct hdac_softc *sc = arg; - uint32_t trigger; - int i, active = 0; - - if (sc == NULL) - return; - - hdac_lock(sc); - if (sc->polling == 0) { - hdac_unlock(sc); - return; - } - - trigger = 0; - for (i = 0; i < sc->num_chans; i++) { - if ((sc->chans[i].flags & HDAC_CHN_RUNNING) == 0) - continue; - active = 1; - if (hda_poll_channel(&sc->chans[i])) - trigger |= (1 << i); - } - - /* XXX */ - if (active) - callout_reset(&sc->poll_hda, sc->poll_ticks, - hda_poll_callback, sc); - - hdac_unlock(sc); - - for (i = 0; i < sc->num_chans; i++) { - if (trigger & (1 << i)) - chn_intr(sc->chans[i].c); - } -} - static int hdac_rirb_flush(struct hdac_softc *sc) { struct hdac_rirb *rirb_base, *rirb; - struct hdac_codec *codec; - struct hdac_command_list *commands; nid_t cad; uint32_t resp; uint8_t rirbwp; @@ -2949,46 +883,46 @@ hdac_rirb_flush(struct hdac_softc *sc) #endif ret = 0; - while (sc->rirb_rp != rirbwp) { sc->rirb_rp++; sc->rirb_rp %= sc->rirb_size; rirb = &rirb_base[sc->rirb_rp]; cad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex); - if (cad < 0 || cad >= HDAC_CODEC_MAX || - sc->codecs[cad] == NULL) - continue; resp = rirb->response; - codec = sc->codecs[cad]; - commands = codec->commands; if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) { - sc->unsolq[sc->unsolq_wp++] = (cad << 16) | - ((resp >> 26) & 0xffff); + sc->unsolq[sc->unsolq_wp++] = resp; sc->unsolq_wp %= HDAC_UNSOLQ_MAX; - } else if (commands != NULL && commands->num_commands > 0 && - codec->responses_received < commands->num_commands) - commands->responses[codec->responses_received++] = - resp; + sc->unsolq[sc->unsolq_wp++] = cad; + sc->unsolq_wp %= HDAC_UNSOLQ_MAX; + } else if (sc->codecs[cad].pending <= 0) { + device_printf(sc->dev, "Unexpected unsolicited " + "response from address %d: %08x\n", cad, resp); + } else { + sc->codecs[cad].response = resp; + sc->codecs[cad].pending--; + } ret++; } - return (ret); } static int hdac_unsolq_flush(struct hdac_softc *sc) { + device_t child; nid_t cad; - uint32_t tag; + uint32_t resp; int ret = 0; if (sc->unsolq_st == HDAC_UNSOLQ_READY) { sc->unsolq_st = HDAC_UNSOLQ_BUSY; while (sc->unsolq_rp != sc->unsolq_wp) { - cad = sc->unsolq[sc->unsolq_rp] >> 16; - tag = sc->unsolq[sc->unsolq_rp++] & 0xffff; + resp = sc->unsolq[sc->unsolq_rp++]; + sc->unsolq_rp %= HDAC_UNSOLQ_MAX; + cad = sc->unsolq[sc->unsolq_rp++]; sc->unsolq_rp %= HDAC_UNSOLQ_MAX; - hdac_unsolicited_handler(sc->codecs[cad], tag); + if ((child = sc->codecs[cad].dev) != NULL) + HDAC_UNSOL_INTR(child, resp); ret++; } sc->unsolq_st = HDAC_UNSOLQ_READY; @@ -2997,375 +931,54 @@ hdac_unsolq_flush(struct hdac_softc *sc) return (ret); } -static void -hdac_poll_callback(void *arg) -{ - struct hdac_softc *sc = arg; - if (sc == NULL) - return; - - hdac_lock(sc); - if (sc->polling == 0 || sc->poll_ival == 0) { - hdac_unlock(sc); - return; - } - if (hdac_rirb_flush(sc) != 0) - hdac_unsolq_flush(sc); - callout_reset(&sc->poll_hdac, sc->poll_ival, hdac_poll_callback, sc); - hdac_unlock(sc); -} - -static void -hdac_poll_reinit(struct hdac_softc *sc) -{ - int i, pollticks, min = 1000000; - struct hdac_chan *ch; - - for (i = 0; i < sc->num_chans; i++) { - if ((sc->chans[i].flags & HDAC_CHN_RUNNING) == 0) - continue; - ch = &sc->chans[i]; - pollticks = ((uint64_t)hz * ch->blksz) / - ((uint64_t)sndbuf_getalign(ch->b) * sndbuf_getspd(ch->b)); - pollticks >>= 1; - if (pollticks > hz) - pollticks = hz; - if (pollticks < 1) { - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "%s: pollticks=%d < 1 !\n", - __func__, pollticks); - ); - pollticks = 1; - } - if (min > pollticks) - min = pollticks; - } - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "%s: pollticks %d -> %d\n", - __func__, sc->poll_ticks, min); - ); - sc->poll_ticks = min; - if (min == 1000000) - callout_stop(&sc->poll_hda); - else - callout_reset(&sc->poll_hda, 1, hda_poll_callback, sc); -} - -static void -hdac_stream_stop(struct hdac_chan *ch) -{ - struct hdac_softc *sc = ch->devinfo->codec->sc; - uint32_t ctl; - - ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); - ctl &= ~(HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | - HDAC_SDCTL_RUN); - HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); - - ch->flags &= ~HDAC_CHN_RUNNING; - - if (sc->polling != 0) - hdac_poll_reinit(sc); - - ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); - ctl &= ~(1 << (ch->off >> 5)); - HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); -} - -static void -hdac_stream_start(struct hdac_chan *ch) -{ - struct hdac_softc *sc = ch->devinfo->codec->sc; - uint32_t ctl; - - ch->flags |= HDAC_CHN_RUNNING; - - if (sc->polling != 0) - hdac_poll_reinit(sc); - - ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); - ctl |= 1 << (ch->off >> 5); - HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); - - ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); - ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | - HDAC_SDCTL_RUN; - HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); -} - -static void -hdac_stream_reset(struct hdac_chan *ch) -{ - struct hdac_softc *sc = ch->devinfo->codec->sc; - int timeout = 1000; - int to = timeout; - uint32_t ctl; - - ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); - ctl |= HDAC_SDCTL_SRST; - HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); - do { - ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); - if (ctl & HDAC_SDCTL_SRST) - break; - DELAY(10); - } while (--to); - if (!(ctl & HDAC_SDCTL_SRST)) { - device_printf(sc->dev, "timeout in reset\n"); - } - ctl &= ~HDAC_SDCTL_SRST; - HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); - to = timeout; - do { - ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); - if (!(ctl & HDAC_SDCTL_SRST)) - break; - DELAY(10); - } while (--to); - if (ctl & HDAC_SDCTL_SRST) - device_printf(sc->dev, "can't reset!\n"); -} - -static void -hdac_stream_setid(struct hdac_chan *ch) -{ - struct hdac_softc *sc = ch->devinfo->codec->sc; - uint32_t ctl; - - ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL2); - ctl &= ~HDAC_SDCTL2_STRM_MASK; - ctl |= ch->sid << HDAC_SDCTL2_STRM_SHIFT; - HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL2, ctl); -} - -static void -hdac_bdl_setup(struct hdac_chan *ch) -{ - struct hdac_softc *sc = ch->devinfo->codec->sc; - struct hdac_bdle *bdle; - uint64_t addr; - uint32_t blksz, blkcnt; - int i; - - addr = (uint64_t)sndbuf_getbufaddr(ch->b); - bdle = (struct hdac_bdle *)ch->bdl_dma.dma_vaddr; - - blksz = ch->blksz; - blkcnt = ch->blkcnt; - - for (i = 0; i < blkcnt; i++, bdle++) { - bdle->addrl = (uint32_t)addr; - bdle->addrh = (uint32_t)(addr >> 32); - bdle->len = blksz; - bdle->ioc = 1; - addr += blksz; - } - - HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDCBL, blksz * blkcnt); - HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDLVI, blkcnt - 1); - addr = ch->bdl_dma.dma_paddr; - HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPL, (uint32_t)addr); - HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPU, (uint32_t)(addr >> 32)); - if (ch->dmapos != NULL && - !(HDAC_READ_4(&sc->mem, HDAC_DPIBLBASE) & 0x00000001)) { - addr = sc->pos_dma.dma_paddr; - HDAC_WRITE_4(&sc->mem, HDAC_DPIBLBASE, - ((uint32_t)addr & HDAC_DPLBASE_DPLBASE_MASK) | 0x00000001); - HDAC_WRITE_4(&sc->mem, HDAC_DPIBUBASE, (uint32_t)(addr >> 32)); - } -} - -static int -hdac_bdl_alloc(struct hdac_chan *ch) -{ - struct hdac_softc *sc = ch->devinfo->codec->sc; - int rc; - - rc = hdac_dma_alloc(sc, &ch->bdl_dma, - sizeof(struct hdac_bdle) * HDA_BDL_MAX); - if (rc) { - device_printf(sc->dev, "can't alloc bdl\n"); - return (rc); - } - - return (0); -} - -static void -hdac_audio_ctl_amp_set_internal(struct hdac_softc *sc, nid_t cad, nid_t nid, - int index, int lmute, int rmute, - int left, int right, int dir) -{ - uint16_t v = 0; - - if (sc == NULL) - return; - - if (left != right || lmute != rmute) { - v = (1 << (15 - dir)) | (1 << 13) | (index << 8) | - (lmute << 7) | left; - hdac_command(sc, - HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad); - v = (1 << (15 - dir)) | (1 << 12) | (index << 8) | - (rmute << 7) | right; - } else - v = (1 << (15 - dir)) | (3 << 12) | (index << 8) | - (lmute << 7) | left; - - hdac_command(sc, - HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad); -} - -static void -hdac_audio_ctl_amp_set(struct hdac_audio_ctl *ctl, uint32_t mute, - int left, int right) -{ - struct hdac_softc *sc; - nid_t nid, cad; - int lmute, rmute; - - sc = ctl->widget->devinfo->codec->sc; - cad = ctl->widget->devinfo->codec->cad; - nid = ctl->widget->nid; - - /* Save new values if valid. */ - if (mute != HDA_AMP_MUTE_DEFAULT) - ctl->muted = mute; - if (left != HDA_AMP_VOL_DEFAULT) - ctl->left = left; - if (right != HDA_AMP_VOL_DEFAULT) - ctl->right = right; - /* Prepare effective values */ - if (ctl->forcemute) { - lmute = 1; - rmute = 1; - left = 0; - right = 0; - } else { - lmute = HDA_AMP_LEFT_MUTED(ctl->muted); - rmute = HDA_AMP_RIGHT_MUTED(ctl->muted); - left = ctl->left; - right = ctl->right; - } - /* Apply effective values */ - if (ctl->dir & HDA_CTL_OUT) - hdac_audio_ctl_amp_set_internal(sc, cad, nid, ctl->index, - lmute, rmute, left, right, 0); - if (ctl->dir & HDA_CTL_IN) - hdac_audio_ctl_amp_set_internal(sc, cad, nid, ctl->index, - lmute, rmute, left, right, 1); -} - -static void -hdac_widget_connection_select(struct hdac_widget *w, uint8_t index) -{ - if (w == NULL || w->nconns < 1 || index > (w->nconns - 1)) - return; - hdac_command(w->devinfo->codec->sc, - HDA_CMD_SET_CONNECTION_SELECT_CONTROL(w->devinfo->codec->cad, - w->nid, index), w->devinfo->codec->cad); - w->selconn = index; -} - - /**************************************************************************** * uint32_t hdac_command_sendone_internal * * Wrapper function that sends only one command to a given codec ****************************************************************************/ static uint32_t -hdac_command_sendone_internal(struct hdac_softc *sc, uint32_t verb, nid_t cad) +hdac_send_command(struct hdac_softc *sc, nid_t cad, uint32_t verb) { - struct hdac_command_list cl; - uint32_t response = HDAC_INVALID; + int timeout; + uint32_t *corb; if (!hdac_lockowned(sc)) device_printf(sc->dev, "WARNING!!!! mtx not owned!!!!\n"); - cl.num_commands = 1; - cl.verbs = &verb; - cl.responses = &response; - - hdac_command_send_internal(sc, &cl, cad); - - return (response); -} - -/**************************************************************************** - * hdac_command_send_internal - * - * Send a command list to the codec via the corb. We queue as much verbs as - * we can and msleep on the codec. When the interrupt get the responses - * back from the rirb, it will wake us up so we can queue the remaining verbs - * if any. - ****************************************************************************/ -static void -hdac_command_send_internal(struct hdac_softc *sc, - struct hdac_command_list *commands, nid_t cad) -{ - struct hdac_codec *codec; - int corbrp; - uint32_t *corb; - int timeout; - int retry = 10; - struct hdac_rirb *rirb_base; + verb &= ~HDA_CMD_CAD_MASK; + verb |= ((uint32_t)cad) << HDA_CMD_CAD_SHIFT; + sc->codecs[cad].response = HDA_INVALID; - if (sc == NULL || sc->codecs[cad] == NULL || commands == NULL || - commands->num_commands < 1) - return; - - codec = sc->codecs[cad]; - codec->commands = commands; - codec->responses_received = 0; - codec->verbs_sent = 0; + sc->codecs[cad].pending++; + sc->corb_wp++; + sc->corb_wp %= sc->corb_size; corb = (uint32_t *)sc->corb_dma.dma_vaddr; - rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr; - - do { - if (codec->verbs_sent != commands->num_commands) { - /* Queue as many verbs as possible */ - corbrp = HDAC_READ_2(&sc->mem, HDAC_CORBRP); #if 0 - bus_dmamap_sync(sc->corb_dma.dma_tag, - sc->corb_dma.dma_map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->corb_dma.dma_tag, + sc->corb_dma.dma_map, BUS_DMASYNC_PREWRITE); #endif - while (codec->verbs_sent != commands->num_commands && - ((sc->corb_wp + 1) % sc->corb_size) != corbrp) { - sc->corb_wp++; - sc->corb_wp %= sc->corb_size; - corb[sc->corb_wp] = - commands->verbs[codec->verbs_sent++]; - } - - /* Send the verbs to the codecs */ + corb[sc->corb_wp] = verb; #if 0 - bus_dmamap_sync(sc->corb_dma.dma_tag, - sc->corb_dma.dma_map, BUS_DMASYNC_POSTWRITE); + bus_dmamap_sync(sc->corb_dma.dma_tag, + sc->corb_dma.dma_map, BUS_DMASYNC_POSTWRITE); #endif - HDAC_WRITE_2(&sc->mem, HDAC_CORBWP, sc->corb_wp); - } + HDAC_WRITE_2(&sc->mem, HDAC_CORBWP, sc->corb_wp); - timeout = 1000; - while (hdac_rirb_flush(sc) == 0 && --timeout) + timeout = 10000; + do { + if (hdac_rirb_flush(sc) == 0) DELAY(10); - } while ((codec->verbs_sent != commands->num_commands || - codec->responses_received != commands->num_commands) && --retry); - - if (retry == 0) - device_printf(sc->dev, - "%s: TIMEOUT numcmd=%d, sent=%d, received=%d\n", - __func__, commands->num_commands, codec->verbs_sent, - codec->responses_received); + } while (sc->codecs[cad].pending != 0 && --timeout); - codec->commands = NULL; - codec->responses_received = 0; - codec->verbs_sent = 0; + if (sc->codecs[cad].pending != 0) { + device_printf(sc->dev, "Command timeout on address %d\n", cad); + sc->codecs[cad].pending = 0; + } - hdac_unsolq_flush(sc); + if (sc->unsolq_rp != sc->unsolq_wp) + taskqueue_enqueue(taskqueue_thread, &sc->unsolq_task); + return (sc->codecs[cad].response); } - /**************************************************************************** * Device Methods ****************************************************************************/ @@ -3411,7 +1024,7 @@ hdac_probe(device_t dev) result = BUS_PROBE_GENERIC; } if (result != ENXIO) { - strlcat(desc, " High Definition Audio Controller", + strlcat(desc, " HDA Controller", sizeof(desc)); device_set_desc_copy(dev, desc); } @@ -3419,711 +1032,6 @@ hdac_probe(device_t dev) return (result); } -static void * -hdac_channel_init(kobj_t obj, void *data, struct snd_dbuf *b, - struct pcm_channel *c, int dir) -{ - struct hdac_pcm_devinfo *pdevinfo = data; - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_chan *ch; - int i, ord = 0, chid; - - hdac_lock(sc); - - chid = (dir == PCMDIR_PLAY)?pdevinfo->play:pdevinfo->rec; - ch = &sc->chans[chid]; - for (i = 0; i < sc->num_chans && i < chid; i++) { - if (ch->dir == sc->chans[i].dir) - ord++; - } - if (dir == PCMDIR_PLAY) { - ch->off = (sc->num_iss + ord) << 5; - } else { - ch->off = ord << 5; - } - - if (devinfo->function.audio.quirks & HDA_QUIRK_FIXEDRATE) { - ch->caps.minspeed = ch->caps.maxspeed = 48000; - ch->pcmrates[0] = 48000; - ch->pcmrates[1] = 0; - } - if (sc->pos_dma.dma_vaddr != NULL) - ch->dmapos = (uint32_t *)(sc->pos_dma.dma_vaddr + - (sc->streamcnt * 8)); - else - ch->dmapos = NULL; - ch->sid = ++sc->streamcnt; - ch->dir = dir; - ch->b = b; - ch->c = c; - ch->blksz = pdevinfo->chan_size / pdevinfo->chan_blkcnt; - ch->blkcnt = pdevinfo->chan_blkcnt; - hdac_unlock(sc); - - if (hdac_bdl_alloc(ch) != 0) { - ch->blkcnt = 0; - return (NULL); - } - - if (sndbuf_alloc(ch->b, sc->chan_dmat, - (sc->flags & HDAC_F_DMA_NOCACHE) ? BUS_DMA_NOCACHE : 0, - pdevinfo->chan_size) != 0) - return (NULL); - - return (ch); -} - -static int -hdac_channel_setformat(kobj_t obj, void *data, uint32_t format) -{ - struct hdac_chan *ch = data; - int i; - - for (i = 0; ch->caps.fmtlist[i] != 0; i++) { - if (format == ch->caps.fmtlist[i]) { - ch->fmt = format; - return (0); - } - } - - return (EINVAL); -} - -static uint32_t -hdac_channel_setspeed(kobj_t obj, void *data, uint32_t speed) -{ - struct hdac_chan *ch = data; - uint32_t spd = 0, threshold; - int i; - - for (i = 0; ch->pcmrates[i] != 0; i++) { - spd = ch->pcmrates[i]; - threshold = spd + ((ch->pcmrates[i + 1] != 0) ? - ((ch->pcmrates[i + 1] - spd) >> 1) : 0); - if (speed < threshold) - break; - } - - if (spd == 0) /* impossible */ - ch->spd = 48000; - else - ch->spd = spd; - - return (ch->spd); -} - -static void -hdac_stream_setup(struct hdac_chan *ch) -{ - struct hdac_softc *sc = ch->devinfo->codec->sc; - struct hdac_audio_as *as = &ch->devinfo->function.audio.as[ch->as]; - struct hdac_widget *w; - int i, chn, totalchn, c; - nid_t cad = ch->devinfo->codec->cad; - uint16_t fmt, dfmt; - uint16_t chmap[2][5] = {{ 0x0010, 0x0001, 0x0201, 0x0231, 0x0231 }, /* 5.1 */ - { 0x0010, 0x0001, 0x2001, 0x2031, 0x2431 }};/* 7.1 */ - int map = -1; - - totalchn = AFMT_CHANNEL(ch->fmt); - HDA_BOOTHVERBOSE( - device_printf(ch->pdevinfo->dev, - "PCMDIR_%s: Stream setup fmt=%08x speed=%d\n", - (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", - ch->fmt, ch->spd); - ); - fmt = 0; - if (ch->fmt & AFMT_S16_LE) - fmt |= ch->bit16 << 4; - else if (ch->fmt & AFMT_S32_LE) - fmt |= ch->bit32 << 4; - else - fmt |= 1 << 4; - for (i = 0; i < HDA_RATE_TAB_LEN; i++) { - if (hda_rate_tab[i].valid && ch->spd == hda_rate_tab[i].rate) { - fmt |= hda_rate_tab[i].base; - fmt |= hda_rate_tab[i].mul; - fmt |= hda_rate_tab[i].div; - break; - } - } - fmt |= (totalchn - 1); - - /* Set channel mapping for known speaker setups. */ - if (as->pinset == 0x0007 || as->pinset == 0x0013) /* Standard 5.1 */ - map = 0; - else if (as->pinset == 0x0017) /* Standard 7.1 */ - map = 1; - - HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDFMT, fmt); - - dfmt = HDA_CMD_SET_DIGITAL_CONV_FMT1_DIGEN; - if (ch->fmt & AFMT_AC3) - dfmt |= HDA_CMD_SET_DIGITAL_CONV_FMT1_NAUDIO; - - chn = 0; - for (i = 0; ch->io[i] != -1; i++) { - w = hdac_widget_get(ch->devinfo, ch->io[i]); - if (w == NULL) - continue; - - /* If HP redirection is enabled, but failed to use same - DAC, make last DAC to duplicate first one. */ - if (as->fakeredir && i == (as->pincnt - 1)) { - c = (ch->sid << 4); - } else { - if (map >= 0) /* Map known speaker setups. */ - chn = (((chmap[map][totalchn / 2] >> i * 4) & - 0xf) - 1) * 2; - if (chn < 0 || chn >= totalchn) { - c = 0; - } else { - c = (ch->sid << 4) | chn; - } - } - HDA_BOOTHVERBOSE( - device_printf(ch->pdevinfo->dev, - "PCMDIR_%s: Stream setup nid=%d: " - "fmt=0x%04x, dfmt=0x%04x, chan=0x%04x\n", - (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", - ch->io[i], fmt, dfmt, c); - ); - hdac_command(sc, - HDA_CMD_SET_CONV_FMT(cad, ch->io[i], fmt), cad); - if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { - hdac_command(sc, - HDA_CMD_SET_DIGITAL_CONV_FMT1(cad, ch->io[i], dfmt), - cad); - } - hdac_command(sc, - HDA_CMD_SET_CONV_STREAM_CHAN(cad, ch->io[i], c), cad); -#if 0 - hdac_command(sc, - HDA_CMD_SET_CONV_CHAN_COUNT(cad, ch->io[i], 1), cad); - hdac_command(sc, - HDA_CMD_SET_HDMI_CHAN_SLOT(cad, ch->io[i], 0x00), cad); - hdac_command(sc, - HDA_CMD_SET_HDMI_CHAN_SLOT(cad, ch->io[i], 0x11), cad); -#endif - chn += HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) + 1; - } -} - -/* - * Greatest Common Divisor. - */ -static unsigned -gcd(unsigned a, unsigned b) -{ - u_int c; - - while (b != 0) { - c = a; - a = b; - b = (c % b); - } - return (a); -} - -/* - * Least Common Multiple. - */ -static unsigned -lcm(unsigned a, unsigned b) -{ - - return ((a * b) / gcd(a, b)); -} - -static int -hdac_channel_setfragments(kobj_t obj, void *data, - uint32_t blksz, uint32_t blkcnt) -{ - struct hdac_chan *ch = data; - struct hdac_softc *sc = ch->devinfo->codec->sc; - - blksz -= blksz % lcm(HDAC_DMA_ALIGNMENT, sndbuf_getalign(ch->b)); - - if (blksz > (sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN)) - blksz = sndbuf_getmaxsize(ch->b) / HDA_BDL_MIN; - if (blksz < HDA_BLK_MIN) - blksz = HDA_BLK_MIN; - if (blkcnt > HDA_BDL_MAX) - blkcnt = HDA_BDL_MAX; - if (blkcnt < HDA_BDL_MIN) - blkcnt = HDA_BDL_MIN; - - while ((blksz * blkcnt) > sndbuf_getmaxsize(ch->b)) { - if ((blkcnt >> 1) >= HDA_BDL_MIN) - blkcnt >>= 1; - else if ((blksz >> 1) >= HDA_BLK_MIN) - blksz >>= 1; - else - break; - } - - if ((sndbuf_getblksz(ch->b) != blksz || - sndbuf_getblkcnt(ch->b) != blkcnt) && - sndbuf_resize(ch->b, blkcnt, blksz) != 0) - device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n", - __func__, blksz, blkcnt); - - ch->blksz = sndbuf_getblksz(ch->b); - ch->blkcnt = sndbuf_getblkcnt(ch->b); - - return (0); -} - -static uint32_t -hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blksz) -{ - struct hdac_chan *ch = data; - - hdac_channel_setfragments(obj, data, blksz, ch->pdevinfo->chan_blkcnt); - - return (ch->blksz); -} - -static void -hdac_channel_stop(struct hdac_softc *sc, struct hdac_chan *ch) -{ - struct hdac_devinfo *devinfo = ch->devinfo; - struct hdac_widget *w; - nid_t cad = devinfo->codec->cad; - int i; - - hdac_stream_stop(ch); - - for (i = 0; ch->io[i] != -1; i++) { - w = hdac_widget_get(ch->devinfo, ch->io[i]); - if (w == NULL) - continue; - if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { - hdac_command(sc, - HDA_CMD_SET_DIGITAL_CONV_FMT1(cad, ch->io[i], 0), - cad); - } - hdac_command(sc, - HDA_CMD_SET_CONV_STREAM_CHAN(cad, ch->io[i], - 0), cad); - } -} - -static void -hdac_channel_start(struct hdac_softc *sc, struct hdac_chan *ch) -{ - ch->ptr = 0; - ch->prevptr = 0; - hdac_stream_stop(ch); - hdac_stream_reset(ch); - hdac_bdl_setup(ch); - hdac_stream_setid(ch); - hdac_stream_setup(ch); - hdac_stream_start(ch); -} - -static int -hdac_channel_trigger(kobj_t obj, void *data, int go) -{ - struct hdac_chan *ch = data; - struct hdac_softc *sc = ch->devinfo->codec->sc; - - if (!PCMTRIG_COMMON(go)) - return (0); - - hdac_lock(sc); - switch (go) { - case PCMTRIG_START: - hdac_channel_start(sc, ch); - break; - case PCMTRIG_STOP: - case PCMTRIG_ABORT: - hdac_channel_stop(sc, ch); - break; - default: - break; - } - hdac_unlock(sc); - - return (0); -} - -static uint32_t -hdac_channel_getptr(kobj_t obj, void *data) -{ - struct hdac_chan *ch = data; - struct hdac_softc *sc = ch->devinfo->codec->sc; - uint32_t ptr; - - hdac_lock(sc); - if (sc->polling != 0) - ptr = ch->ptr; - else if (ch->dmapos != NULL) - ptr = *(ch->dmapos); - else - ptr = HDAC_READ_4(&sc->mem, ch->off + HDAC_SDLPIB); - hdac_unlock(sc); - - /* - * Round to available space and force 128 bytes aligment. - */ - ptr %= ch->blksz * ch->blkcnt; - ptr &= HDA_BLK_ALIGN; - - return (ptr); -} - -static struct pcmchan_caps * -hdac_channel_getcaps(kobj_t obj, void *data) -{ - return (&((struct hdac_chan *)data)->caps); -} - -static kobj_method_t hdac_channel_methods[] = { - KOBJMETHOD(channel_init, hdac_channel_init), - KOBJMETHOD(channel_setformat, hdac_channel_setformat), - KOBJMETHOD(channel_setspeed, hdac_channel_setspeed), - KOBJMETHOD(channel_setblocksize, hdac_channel_setblocksize), - KOBJMETHOD(channel_setfragments, hdac_channel_setfragments), - KOBJMETHOD(channel_trigger, hdac_channel_trigger), - KOBJMETHOD(channel_getptr, hdac_channel_getptr), - KOBJMETHOD(channel_getcaps, hdac_channel_getcaps), - KOBJMETHOD_END -}; -CHANNEL_DECLARE(hdac_channel); - -static int -hdac_audio_ctl_ossmixer_init(struct snd_mixer *m) -{ - struct hdac_pcm_devinfo *pdevinfo = mix_getdevinfo(m); - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_widget *w, *cw; - struct hdac_audio_ctl *ctl; - uint32_t mask, recmask, id; - int i, j, softpcmvol; - - hdac_lock(sc); - - /* Make sure that in case of soft volume it won't stay muted. */ - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - pdevinfo->left[i] = 100; - pdevinfo->right[i] = 100; - } - - mask = 0; - recmask = 0; - id = hdac_codec_id(devinfo->codec); - - /* Declate EAPD as ogain control. */ - if (pdevinfo->play >= 0) { - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || - w->param.eapdbtl == HDAC_INVALID || - w->bindas != sc->chans[pdevinfo->play].as) - continue; - mask |= SOUND_MASK_OGAIN; - break; - } - } - - /* Declare volume controls assigned to this association. */ - i = 0; - ctl = NULL; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0) - continue; - if ((pdevinfo->play >= 0 && - ctl->widget->bindas == sc->chans[pdevinfo->play].as) || - (pdevinfo->rec >= 0 && - ctl->widget->bindas == sc->chans[pdevinfo->rec].as) || - (ctl->widget->bindas == -2 && pdevinfo->index == 0)) - mask |= ctl->ossmask; - } - - /* Declare record sources available to this association. */ - if (pdevinfo->rec >= 0) { - struct hdac_chan *ch = &sc->chans[pdevinfo->rec]; - for (i = 0; ch->io[i] != -1; i++) { - w = hdac_widget_get(devinfo, ch->io[i]); - if (w == NULL || w->enable == 0) - continue; - for (j = 0; j < w->nconns; j++) { - if (w->connsenable[j] == 0) - continue; - cw = hdac_widget_get(devinfo, w->conns[j]); - if (cw == NULL || cw->enable == 0) - continue; - if (cw->bindas != sc->chans[pdevinfo->rec].as && - cw->bindas != -2) - continue; - recmask |= cw->ossmask; - } - } - } - - /* Declare soft PCM volume if needed. */ - if (pdevinfo->play >= 0) { - ctl = NULL; - if ((mask & SOUND_MASK_PCM) == 0 || - (devinfo->function.audio.quirks & HDA_QUIRK_SOFTPCMVOL)) { - softpcmvol = 1; - mask |= SOUND_MASK_PCM; - } else { - softpcmvol = 0; - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0) - continue; - if (ctl->widget->bindas != sc->chans[pdevinfo->play].as && - (ctl->widget->bindas != -2 || pdevinfo->index != 0)) - continue; - if (!(ctl->ossmask & SOUND_MASK_PCM)) - continue; - if (ctl->step > 0) - break; - } - } - - if (softpcmvol == 1 || ctl == NULL) { - pcm_setflags(pdevinfo->dev, pcm_getflags(pdevinfo->dev) | SD_F_SOFTPCMVOL); - HDA_BOOTVERBOSE( - device_printf(pdevinfo->dev, - "%s Soft PCM volume\n", - (softpcmvol == 1) ? "Forcing" : "Enabling"); - ); - } - } - - /* Declare master volume if needed. */ - if (pdevinfo->play >= 0) { - if ((mask & (SOUND_MASK_VOLUME | SOUND_MASK_PCM)) == - SOUND_MASK_PCM) { - mask |= SOUND_MASK_VOLUME; - mix_setparentchild(m, SOUND_MIXER_VOLUME, - SOUND_MASK_PCM); - mix_setrealdev(m, SOUND_MIXER_VOLUME, - SOUND_MIXER_NONE); - HDA_BOOTVERBOSE( - device_printf(pdevinfo->dev, - "Forcing master volume with PCM\n"); - ); - } - } - - recmask &= (1 << SOUND_MIXER_NRDEVICES) - 1; - mask &= (1 << SOUND_MIXER_NRDEVICES) - 1; - - mix_setrecdevs(m, recmask); - mix_setdevs(m, mask); - - hdac_unlock(sc); - - return (0); -} - -static int -hdac_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev, - unsigned left, unsigned right) -{ - struct hdac_pcm_devinfo *pdevinfo = mix_getdevinfo(m); - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_widget *w; - struct hdac_audio_ctl *ctl; - uint32_t mute; - int lvol, rvol; - int i, j; - - hdac_lock(sc); - /* Save new values. */ - pdevinfo->left[dev] = left; - pdevinfo->right[dev] = right; - - /* 'ogain' is the special case implemented with EAPD. */ - if (dev == SOUND_MIXER_OGAIN) { - uint32_t orig; - w = NULL; - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || - w->param.eapdbtl == HDAC_INVALID) - continue; - break; - } - if (i >= devinfo->endnode) { - hdac_unlock(sc); - return (-1); - } - orig = w->param.eapdbtl; - if (left == 0) - w->param.eapdbtl &= ~HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; - else - w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; - if (orig != w->param.eapdbtl) { - uint32_t val; - - val = w->param.eapdbtl; - if (devinfo->function.audio.quirks & HDA_QUIRK_EAPDINV) - val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; - hdac_command(sc, - HDA_CMD_SET_EAPD_BTL_ENABLE(devinfo->codec->cad, - w->nid, val), devinfo->codec->cad); - } - hdac_unlock(sc); - return (left | (left << 8)); - } - - /* Recalculate all controls related to this OSS device. */ - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0 || - !(ctl->ossmask & (1 << dev))) - continue; - if (!((pdevinfo->play >= 0 && - ctl->widget->bindas == sc->chans[pdevinfo->play].as) || - (pdevinfo->rec >= 0 && - ctl->widget->bindas == sc->chans[pdevinfo->rec].as) || - ctl->widget->bindas == -2)) - continue; - - lvol = 100; - rvol = 100; - for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) { - if (ctl->ossmask & (1 << j)) { - lvol = lvol * pdevinfo->left[j] / 100; - rvol = rvol * pdevinfo->right[j] / 100; - } - } - mute = (lvol == 0) ? HDA_AMP_MUTE_LEFT : 0; - mute |= (rvol == 0) ? HDA_AMP_MUTE_RIGHT : 0; - lvol = (lvol * ctl->step + 50) / 100; - rvol = (rvol * ctl->step + 50) / 100; - hdac_audio_ctl_amp_set(ctl, mute, lvol, rvol); - } - hdac_unlock(sc); - - return (left | (right << 8)); -} - -/* - * Commutate specified record source. - */ -static uint32_t -hdac_audio_ctl_recsel_comm(struct hdac_pcm_devinfo *pdevinfo, uint32_t src, nid_t nid, int depth) -{ - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_widget *w, *cw; - struct hdac_audio_ctl *ctl; - char buf[64]; - int i, muted; - uint32_t res = 0; - - if (depth > HDA_PARSE_MAXDEPTH) - return (0); - - w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0) - return (0); - - for (i = 0; i < w->nconns; i++) { - if (w->connsenable[i] == 0) - continue; - cw = hdac_widget_get(devinfo, w->conns[i]); - if (cw == NULL || cw->enable == 0 || cw->bindas == -1) - continue; - /* Call recursively to trace signal to it's source if needed. */ - if ((src & cw->ossmask) != 0) { - if (cw->ossdev < 0) { - res |= hdac_audio_ctl_recsel_comm(pdevinfo, src, - w->conns[i], depth + 1); - } else { - res |= cw->ossmask; - } - } - /* We have two special cases: mixers and others (selectors). */ - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) { - ctl = hdac_audio_ctl_amp_get(devinfo, - w->nid, HDA_CTL_IN, i, 1); - if (ctl == NULL) - continue; - /* If we have input control on this node mute them - * according to requested sources. */ - muted = (src & cw->ossmask) ? 0 : 1; - if (muted != ctl->forcemute) { - ctl->forcemute = muted; - hdac_audio_ctl_amp_set(ctl, - HDA_AMP_MUTE_DEFAULT, - HDA_AMP_VOL_DEFAULT, HDA_AMP_VOL_DEFAULT); - } - HDA_BOOTHVERBOSE( - device_printf(pdevinfo->dev, - "Recsel (%s): nid %d source %d %s\n", - hdac_audio_ctl_ossmixer_mask2allname( - src, buf, sizeof(buf)), - nid, i, muted?"mute":"unmute"); - ); - } else { - if (w->nconns == 1) - break; - if ((src & cw->ossmask) == 0) - continue; - /* If we found requested source - select it and exit. */ - hdac_widget_connection_select(w, i); - HDA_BOOTHVERBOSE( - device_printf(pdevinfo->dev, - "Recsel (%s): nid %d source %d select\n", - hdac_audio_ctl_ossmixer_mask2allname( - src, buf, sizeof(buf)), - nid, i); - ); - break; - } - } - return (res); -} - -static uint32_t -hdac_audio_ctl_ossmixer_setrecsrc(struct snd_mixer *m, uint32_t src) -{ - struct hdac_pcm_devinfo *pdevinfo = mix_getdevinfo(m); - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_widget *w; - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_chan *ch; - int i; - uint32_t ret = 0xffffffff; - - hdac_lock(sc); - - /* Commutate requested recsrc for each ADC. */ - ch = &sc->chans[pdevinfo->rec]; - for (i = 0; ch->io[i] != -1; i++) { - w = hdac_widget_get(devinfo, ch->io[i]); - if (w == NULL || w->enable == 0) - continue; - ret &= hdac_audio_ctl_recsel_comm(pdevinfo, src, ch->io[i], 0); - } - - hdac_unlock(sc); - - return ((ret == 0xffffffff)? 0 : ret); -} - -static kobj_method_t hdac_audio_ctl_ossmixer_methods[] = { - KOBJMETHOD(mixer_init, hdac_audio_ctl_ossmixer_init), - KOBJMETHOD(mixer_set, hdac_audio_ctl_ossmixer_set), - KOBJMETHOD(mixer_setrecsrc, hdac_audio_ctl_ossmixer_setrecsrc), - KOBJMETHOD_END -}; -MIXER_DECLARE(hdac_audio_ctl_ossmixer); - static void hdac_unsolq_task(void *context, int pending) { @@ -4154,6 +1062,7 @@ hdac_attach(device_t dev) uint16_t vendor; uint8_t v; + sc = device_get_softc(dev); HDA_BOOTVERBOSE( device_printf(dev, "HDA Driver Revision: %s\n", HDA_DRV_TEST_REV); @@ -4177,38 +1086,44 @@ hdac_attach(device_t dev) } } - sc = device_get_softc(dev); - sc->lock = snd_mtxcreate(device_get_nameunit(dev), HDAC_MTX_NAME); + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "HDA driver mutex"); sc->dev = dev; - sc->pci_subvendor = (uint32_t)pci_get_subdevice(sc->dev) << 16; - sc->pci_subvendor |= (uint32_t)pci_get_subvendor(sc->dev) & 0x0000ffff; - vendor = pci_get_vendor(dev); - - if (sc->pci_subvendor == HP_NX6325_SUBVENDORX) { - /* Screw nx6325 - subdevice/subvendor swapped */ - sc->pci_subvendor = HP_NX6325_SUBVENDOR; - } - - callout_init(&sc->poll_hda, CALLOUT_MPSAFE); - callout_init(&sc->poll_hdac, CALLOUT_MPSAFE); - callout_init(&sc->poll_jack, CALLOUT_MPSAFE); - TASK_INIT(&sc->unsolq_task, 0, hdac_unsolq_task, sc); - - sc->poll_ticks = 1000000; - sc->poll_ival = HDAC_POLL_INTERVAL; + callout_init(&sc->poll_callout, CALLOUT_MPSAFE); + for (i = 0; i < HDAC_CODEC_MAX; i++) + sc->codecs[i].dev = NULL; + if (devid >= 0) { + sc->quirks_on = hdac_devices[devid].quirks_on; + sc->quirks_off = hdac_devices[devid].quirks_off; + } else { + sc->quirks_on = 0; + sc->quirks_off = 0; + } + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "msi", &i) == 0) { + if (i == 0) + sc->quirks_off |= HDAC_QUIRK_MSI; + else { + sc->quirks_on |= HDAC_QUIRK_MSI; + sc->quirks_off |= ~HDAC_QUIRK_MSI; + } + } + hdac_config_fetch(sc, &sc->quirks_on, &sc->quirks_off); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "Config options: on=0x%08x off=0x%08x\n", + sc->quirks_on, sc->quirks_off); + ); + sc->poll_ival = hz; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "polling", &i) == 0 && i != 0) sc->polling = 1; else sc->polling = 0; - sc->hdabus = NULL; - for (i = 0; i < HDAC_CODEC_MAX; i++) - sc->codecs[i] = NULL; - pci_enable_busmaster(dev); + vendor = pci_get_vendor(dev); if (vendor == INTEL_VENDORID) { /* TCSEL -> TC0 */ v = pci_read_config(dev, 0x44, 1); @@ -4219,18 +1134,6 @@ hdac_attach(device_t dev) ); } - if (devid >= 0 && (hdac_devices[devid].flags & HDAC_NO_MSI)) - sc->flags &= ~HDAC_F_MSI; - else - sc->flags |= HDAC_F_MSI; - if (resource_int_value(device_get_name(dev), - device_get_unit(dev), "msi", &i) == 0) { - if (i == 0) - sc->flags &= ~HDAC_F_MSI; - else - sc->flags |= HDAC_F_MSI; - } - #if defined(__i386__) || defined(__amd64__) sc->flags |= HDAC_F_DMA_NOCACHE; @@ -4299,10 +1202,7 @@ hdac_attach(device_t dev) if (result != 0) goto hdac_attach_fail; - if (devid >= 0 && (hdac_devices[devid].flags & HDAC_NO_64BIT)) - sc->support_64bit = 0; - - /* Allocate CORB and RIRB dma memory */ + /* Allocate CORB, RIRB, POS and BDLs dma memory */ result = hdac_dma_alloc(sc, &sc->corb_dma, sc->corb_size * sizeof(uint32_t)); if (result != 0) @@ -4311,10 +1211,34 @@ hdac_attach(device_t dev) sc->rirb_size * sizeof(struct hdac_rirb)); if (result != 0) goto hdac_attach_fail; + sc->streams = malloc(sizeof(struct hdac_stream) * sc->num_ss, + M_HDAC, M_ZERO | M_WAITOK); + for (i = 0; i < sc->num_ss; i++) { + result = hdac_dma_alloc(sc, &sc->streams[i].bdl, + sizeof(struct hdac_bdle) * HDA_BDL_MAX); + if (result != 0) + goto hdac_attach_fail; + } + if (sc->quirks_on & HDAC_QUIRK_DMAPOS) { + if (hdac_dma_alloc(sc, &sc->pos_dma, (sc->num_ss) * 8) != 0) { + HDA_BOOTVERBOSE( + device_printf(dev, "Failed to " + "allocate DMA pos buffer " + "(non-fatal)\n"); + ); + } else { + uint64_t addr = sc->pos_dma.dma_paddr; + + HDAC_WRITE_4(&sc->mem, HDAC_DPIBUBASE, addr >> 32); + HDAC_WRITE_4(&sc->mem, HDAC_DPIBLBASE, + (addr & HDAC_DPLBASE_DPLBASE_MASK) | + HDAC_DPLBASE_DPLBASE_DMAPBE); + } + } result = bus_dma_tag_create( bus_get_dma_tag(sc->dev), /* parent */ - HDAC_DMA_ALIGNMENT, /* alignment */ + HDA_DMA_ALIGNMENT, /* alignment */ 0, /* boundary */ (sc->support_64bit) ? BUS_SPACE_MAXADDR : BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ @@ -4356,6 +1280,9 @@ hdac_attach(device_t dev) hdac_attach_fail: hdac_irq_free(sc); + for (i = 0; i < sc->num_ss; i++) + hdac_dma_free(sc, &sc->streams[i].bdl); + free(sc->streams, M_HDAC); hdac_dma_free(sc, &sc->rirb_dma); hdac_dma_free(sc, &sc->corb_dma); hdac_mem_free(sc); @@ -4364,3127 +1291,97 @@ hdac_attach_fail: return (ENXIO); } -static void -hdac_audio_parse(struct hdac_devinfo *devinfo) -{ - struct hdac_codec *codec = devinfo->codec; - struct hdac_softc *sc = codec->sc; - struct hdac_widget *w; - uint32_t res; - int i; - nid_t cad, nid; - - cad = devinfo->codec->cad; - nid = devinfo->nid; - - res = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad , nid, HDA_PARAM_GPIO_COUNT), cad); - devinfo->function.audio.gpio = res; - - HDA_BOOTVERBOSE( - device_printf(sc->dev, "GPIO: 0x%08x " - "NumGPIO=%d NumGPO=%d " - "NumGPI=%d GPIWake=%d GPIUnsol=%d\n", - devinfo->function.audio.gpio, - HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->function.audio.gpio)); - ); - - res = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_SUPP_STREAM_FORMATS), - cad); - devinfo->function.audio.supp_stream_formats = res; - - res = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_SUPP_PCM_SIZE_RATE), - cad); - devinfo->function.audio.supp_pcm_size_rate = res; - - res = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_OUTPUT_AMP_CAP), - cad); - devinfo->function.audio.outamp_cap = res; - - res = hdac_command(sc, - HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_INPUT_AMP_CAP), - cad); - devinfo->function.audio.inamp_cap = res; - - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL) - device_printf(sc->dev, "Ghost widget! nid=%d!\n", i); - else { - w->devinfo = devinfo; - w->nid = i; - w->enable = 1; - w->selconn = -1; - w->pflags = 0; - w->ossdev = -1; - w->bindas = -1; - w->param.eapdbtl = HDAC_INVALID; - hdac_widget_parse(w); - } - } -} - -static void -hdac_audio_ctl_parse(struct hdac_devinfo *devinfo) -{ - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_audio_ctl *ctls; - struct hdac_widget *w, *cw; - int i, j, cnt, max, ocap, icap; - int mute, offset, step, size; - - /* XXX This is redundant */ - max = 0; - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->param.outamp_cap != 0) - max++; - if (w->param.inamp_cap != 0) { - switch (w->type) { - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: - for (j = 0; j < w->nconns; j++) { - cw = hdac_widget_get(devinfo, - w->conns[j]); - if (cw == NULL || cw->enable == 0) - continue; - max++; - } - break; - default: - max++; - break; - } - } - } - - devinfo->function.audio.ctlcnt = max; - - if (max < 1) - return; - - ctls = (struct hdac_audio_ctl *)malloc( - sizeof(*ctls) * max, M_HDAC, M_ZERO | M_NOWAIT); - - if (ctls == NULL) { - /* Blekh! */ - device_printf(sc->dev, "unable to allocate ctls!\n"); - devinfo->function.audio.ctlcnt = 0; - return; - } - - cnt = 0; - for (i = devinfo->startnode; cnt < max && i < devinfo->endnode; i++) { - if (cnt >= max) { - device_printf(sc->dev, "%s: Ctl overflow!\n", - __func__); - break; - } - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - ocap = w->param.outamp_cap; - icap = w->param.inamp_cap; - if (ocap != 0) { - mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(ocap); - step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(ocap); - size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(ocap); - offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(ocap); - /*if (offset > step) { - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "BUGGY outamp: nid=%d " - "[offset=%d > step=%d]\n", - w->nid, offset, step); - ); - offset = step; - }*/ - ctls[cnt].enable = 1; - ctls[cnt].widget = w; - ctls[cnt].mute = mute; - ctls[cnt].step = step; - ctls[cnt].size = size; - ctls[cnt].offset = offset; - ctls[cnt].left = offset; - ctls[cnt].right = offset; - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || - w->waspin) - ctls[cnt].ndir = HDA_CTL_IN; - else - ctls[cnt].ndir = HDA_CTL_OUT; - ctls[cnt++].dir = HDA_CTL_OUT; - } - - if (icap != 0) { - mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(icap); - step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(icap); - size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(icap); - offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(icap); - /*if (offset > step) { - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "BUGGY inamp: nid=%d " - "[offset=%d > step=%d]\n", - w->nid, offset, step); - ); - offset = step; - }*/ - switch (w->type) { - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: - for (j = 0; j < w->nconns; j++) { - if (cnt >= max) { - device_printf(sc->dev, - "%s: Ctl overflow!\n", - __func__); - break; - } - cw = hdac_widget_get(devinfo, - w->conns[j]); - if (cw == NULL || cw->enable == 0) - continue; - ctls[cnt].enable = 1; - ctls[cnt].widget = w; - ctls[cnt].childwidget = cw; - ctls[cnt].index = j; - ctls[cnt].mute = mute; - ctls[cnt].step = step; - ctls[cnt].size = size; - ctls[cnt].offset = offset; - ctls[cnt].left = offset; - ctls[cnt].right = offset; - ctls[cnt].ndir = HDA_CTL_IN; - ctls[cnt++].dir = HDA_CTL_IN; - } - break; - default: - if (cnt >= max) { - device_printf(sc->dev, - "%s: Ctl overflow!\n", - __func__); - break; - } - ctls[cnt].enable = 1; - ctls[cnt].widget = w; - ctls[cnt].mute = mute; - ctls[cnt].step = step; - ctls[cnt].size = size; - ctls[cnt].offset = offset; - ctls[cnt].left = offset; - ctls[cnt].right = offset; - if (w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - ctls[cnt].ndir = HDA_CTL_OUT; - else - ctls[cnt].ndir = HDA_CTL_IN; - ctls[cnt++].dir = HDA_CTL_IN; - break; - } - } - } - - devinfo->function.audio.ctl = ctls; -} - -static void -hdac_audio_as_parse(struct hdac_devinfo *devinfo) -{ - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_audio_as *as; - struct hdac_widget *w; - int i, j, cnt, max, type, dir, assoc, seq, first, hpredir; - - /* Count present associations */ - max = 0; - for (j = 1; j < 16; j++) { - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - if (HDA_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config) - != j) - continue; - max++; - if (j != 15) /* There could be many 1-pin assocs #15 */ - break; - } - } - - devinfo->function.audio.ascnt = max; - - if (max < 1) - return; - - as = (struct hdac_audio_as *)malloc( - sizeof(*as) * max, M_HDAC, M_ZERO | M_NOWAIT); - - if (as == NULL) { - /* Blekh! */ - device_printf(sc->dev, "unable to allocate assocs!\n"); - devinfo->function.audio.ascnt = 0; - return; - } - - for (i = 0; i < max; i++) { - as[i].hpredir = -1; - as[i].chan = -1; - as[i].digital = 0; - } - - /* Scan associations skipping as=0. */ - cnt = 0; - for (j = 1; j < 16; j++) { - first = 16; - hpredir = 0; - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - assoc = HDA_CONFIG_DEFAULTCONF_ASSOCIATION(w->wclass.pin.config); - seq = HDA_CONFIG_DEFAULTCONF_SEQUENCE(w->wclass.pin.config); - if (assoc != j) { - continue; - } - KASSERT(cnt < max, - ("%s: Associations owerflow (%d of %d)", - __func__, cnt, max)); - type = w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; - /* Get pin direction. */ - if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT || - type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER || - type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT || - type == HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT || - type == HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT) - dir = HDA_CTL_OUT; - else - dir = HDA_CTL_IN; - /* If this is a first pin - create new association. */ - if (as[cnt].pincnt == 0) { - as[cnt].enable = 1; - as[cnt].index = j; - as[cnt].dir = dir; - } - if (seq < first) - first = seq; - /* Check association correctness. */ - if (as[cnt].pins[seq] != 0) { - device_printf(sc->dev, "%s: Duplicate pin %d (%d) " - "in association %d! Disabling association.\n", - __func__, seq, w->nid, j); - as[cnt].enable = 0; - } - if (dir != as[cnt].dir) { - device_printf(sc->dev, "%s: Pin %d has wrong " - "direction for association %d! Disabling " - "association.\n", - __func__, w->nid, j); - as[cnt].enable = 0; - } - if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { - if (HDA_PARAM_PIN_CAP_DP(w->wclass.pin.cap)) - as[cnt].digital = 3; - else if (HDA_PARAM_PIN_CAP_HDMI(w->wclass.pin.cap)) - as[cnt].digital = 2; - else - as[cnt].digital = 1; - } - /* Headphones with seq=15 may mean redirection. */ - if (type == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT && - seq == 15) - hpredir = 1; - as[cnt].pins[seq] = w->nid; - as[cnt].pincnt++; - /* Association 15 is a multiple unassociated pins. */ - if (j == 15) - cnt++; - } - if (j != 15 && as[cnt].pincnt > 0) { - if (hpredir && as[cnt].pincnt > 1) - as[cnt].hpredir = first; - cnt++; - } - } - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "%d associations found:\n", max); - for (i = 0; i < max; i++) { - device_printf(sc->dev, - "Association %d (%d) %s%s:\n", - i, as[i].index, (as[i].dir == HDA_CTL_IN)?"in":"out", - as[i].enable?"":" (disabled)"); - for (j = 0; j < 16; j++) { - if (as[i].pins[j] == 0) - continue; - device_printf(sc->dev, - " Pin nid=%d seq=%d\n", - as[i].pins[j], j); - } - } - ); - - devinfo->function.audio.as = as; -} - -static const struct { - uint32_t model; - uint32_t id; - uint32_t set, unset; -} hdac_quirks[] = { - /* - * XXX Force stereo quirk. Monoural recording / playback - * on few codecs (especially ALC880) seems broken or - * perhaps unsupported. - */ - { HDA_MATCH_ALL, HDA_MATCH_ALL, - HDA_QUIRK_FORCESTEREO | HDA_QUIRK_IVREF, 0 }, - { ACER_ALL_SUBVENDOR, HDA_MATCH_ALL, - HDA_QUIRK_GPIO0, 0 }, - { ASUS_G2K_SUBVENDOR, HDA_CODEC_ALC660, - HDA_QUIRK_GPIO0, 0 }, - { ASUS_M5200_SUBVENDOR, HDA_CODEC_ALC880, - HDA_QUIRK_GPIO0, 0 }, - { ASUS_A7M_SUBVENDOR, HDA_CODEC_ALC880, - HDA_QUIRK_GPIO0, 0 }, - { ASUS_A7T_SUBVENDOR, HDA_CODEC_ALC882, - HDA_QUIRK_GPIO0, 0 }, - { ASUS_W2J_SUBVENDOR, HDA_CODEC_ALC882, - HDA_QUIRK_GPIO0, 0 }, - { ASUS_U5F_SUBVENDOR, HDA_CODEC_AD1986A, - HDA_QUIRK_EAPDINV, 0 }, - { ASUS_A8X_SUBVENDOR, HDA_CODEC_AD1986A, - HDA_QUIRK_EAPDINV, 0 }, - { ASUS_F3JC_SUBVENDOR, HDA_CODEC_ALC861, - HDA_QUIRK_OVREF, 0 }, - { UNIWILL_9075_SUBVENDOR, HDA_CODEC_ALC861, - HDA_QUIRK_OVREF, 0 }, - /*{ ASUS_M2N_SUBVENDOR, HDA_CODEC_AD1988, - HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 },*/ - { MEDION_MD95257_SUBVENDOR, HDA_CODEC_ALC880, - HDA_QUIRK_GPIO1, 0 }, - { LENOVO_3KN100_SUBVENDOR, HDA_CODEC_AD1986A, - HDA_QUIRK_EAPDINV | HDA_QUIRK_SENSEINV, 0 }, - { SAMSUNG_Q1_SUBVENDOR, HDA_CODEC_AD1986A, - HDA_QUIRK_EAPDINV, 0 }, - { APPLE_MB3_SUBVENDOR, HDA_CODEC_ALC885, - HDA_QUIRK_GPIO0 | HDA_QUIRK_OVREF50, 0}, - { APPLE_INTEL_MAC, HDA_CODEC_STAC9221, - HDA_QUIRK_GPIO0 | HDA_QUIRK_GPIO1, 0 }, - { APPLE_MACBOOKPRO55, HDA_CODEC_CS4206, - HDA_QUIRK_GPIO1 | HDA_QUIRK_GPIO3, 0 }, - { DELL_D630_SUBVENDOR, HDA_CODEC_STAC9205X, - HDA_QUIRK_GPIO0, 0 }, - { DELL_V1400_SUBVENDOR, HDA_CODEC_STAC9228X, - HDA_QUIRK_GPIO2, 0 }, - { DELL_V1500_SUBVENDOR, HDA_CODEC_STAC9205X, - HDA_QUIRK_GPIO0, 0 }, - { HDA_MATCH_ALL, HDA_CODEC_AD1988, - HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 }, - { HDA_MATCH_ALL, HDA_CODEC_AD1988B, - HDA_QUIRK_IVREF80, HDA_QUIRK_IVREF50 | HDA_QUIRK_IVREF100 }, - { HDA_MATCH_ALL, HDA_CODEC_CX20549, - 0, HDA_QUIRK_FORCESTEREO } -}; -#define HDAC_QUIRKS_LEN (sizeof(hdac_quirks) / sizeof(hdac_quirks[0])) - -static void -hdac_vendor_patch_parse(struct hdac_devinfo *devinfo) -{ - struct hdac_widget *w; - uint32_t id, subvendor; - int i; - - id = hdac_codec_id(devinfo->codec); - subvendor = devinfo->codec->sc->pci_subvendor; - - /* - * Quirks - */ - for (i = 0; i < HDAC_QUIRKS_LEN; i++) { - if (!(HDA_DEV_MATCH(hdac_quirks[i].model, subvendor) && - HDA_DEV_MATCH(hdac_quirks[i].id, id))) - continue; - if (hdac_quirks[i].set != 0) - devinfo->function.audio.quirks |= - hdac_quirks[i].set; - if (hdac_quirks[i].unset != 0) - devinfo->function.audio.quirks &= - ~(hdac_quirks[i].unset); - } - - switch (id) { - case HDA_CODEC_AD1983: - /* - * This codec has several possible usages, but none - * fit the parser best. Help parser to choose better. - */ - /* Disable direct unmixed playback to get pcm volume. */ - w = hdac_widget_get(devinfo, 5); - if (w != NULL) - w->connsenable[0] = 0; - w = hdac_widget_get(devinfo, 6); - if (w != NULL) - w->connsenable[0] = 0; - w = hdac_widget_get(devinfo, 11); - if (w != NULL) - w->connsenable[0] = 0; - /* Disable mic and line selectors. */ - w = hdac_widget_get(devinfo, 12); - if (w != NULL) - w->connsenable[1] = 0; - w = hdac_widget_get(devinfo, 13); - if (w != NULL) - w->connsenable[1] = 0; - /* Disable recording from mono playback mix. */ - w = hdac_widget_get(devinfo, 20); - if (w != NULL) - w->connsenable[3] = 0; - break; - case HDA_CODEC_AD1986A: - /* - * This codec has overcomplicated input mixing. - * Make some cleaning there. - */ - /* Disable input mono mixer. Not needed and not supported. */ - w = hdac_widget_get(devinfo, 43); - if (w != NULL) - w->enable = 0; - /* Disable any with any input mixing mesh. Use separately. */ - w = hdac_widget_get(devinfo, 39); - if (w != NULL) - w->enable = 0; - w = hdac_widget_get(devinfo, 40); - if (w != NULL) - w->enable = 0; - w = hdac_widget_get(devinfo, 41); - if (w != NULL) - w->enable = 0; - w = hdac_widget_get(devinfo, 42); - if (w != NULL) - w->enable = 0; - /* Disable duplicate mixer node connector. */ - w = hdac_widget_get(devinfo, 15); - if (w != NULL) - w->connsenable[3] = 0; - /* There is only one mic preamplifier, use it effectively. */ - w = hdac_widget_get(devinfo, 31); - if (w != NULL) { - if ((w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) == - HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN) { - w = hdac_widget_get(devinfo, 16); - if (w != NULL) - w->connsenable[2] = 0; - } else { - w = hdac_widget_get(devinfo, 15); - if (w != NULL) - w->connsenable[0] = 0; - } - } - w = hdac_widget_get(devinfo, 32); - if (w != NULL) { - if ((w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) == - HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN) { - w = hdac_widget_get(devinfo, 16); - if (w != NULL) - w->connsenable[0] = 0; - } else { - w = hdac_widget_get(devinfo, 15); - if (w != NULL) - w->connsenable[1] = 0; - } - } - - if (subvendor == ASUS_A8X_SUBVENDOR) { - /* - * This is just plain ridiculous.. There - * are several A8 series that share the same - * pci id but works differently (EAPD). - */ - w = hdac_widget_get(devinfo, 26); - if (w != NULL && w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && - (w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) != - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) - devinfo->function.audio.quirks &= - ~HDA_QUIRK_EAPDINV; - } - break; - case HDA_CODEC_AD1981HD: - /* - * This codec has very unusual design with several - * points inappropriate for the present parser. - */ - /* Disable recording from mono playback mix. */ - w = hdac_widget_get(devinfo, 21); - if (w != NULL) - w->connsenable[3] = 0; - /* Disable rear to front mic mixer, use separately. */ - w = hdac_widget_get(devinfo, 31); - if (w != NULL) - w->enable = 0; - /* Disable direct playback, use mixer. */ - w = hdac_widget_get(devinfo, 5); - if (w != NULL) - w->connsenable[0] = 0; - w = hdac_widget_get(devinfo, 6); - if (w != NULL) - w->connsenable[0] = 0; - w = hdac_widget_get(devinfo, 9); - if (w != NULL) - w->connsenable[0] = 0; - w = hdac_widget_get(devinfo, 24); - if (w != NULL) - w->connsenable[0] = 0; - break; - case HDA_CODEC_CX20582: - case HDA_CODEC_CX20583: - case HDA_CODEC_CX20584: - case HDA_CODEC_CX20585: - case HDA_CODEC_CX20590: - /* - * These codecs have extra connectivity on record side - * too reach for the present parser. - */ - w = hdac_widget_get(devinfo, 20); - if (w != NULL) - w->connsenable[1] = 0; - w = hdac_widget_get(devinfo, 21); - if (w != NULL) - w->connsenable[1] = 0; - w = hdac_widget_get(devinfo, 22); - if (w != NULL) - w->connsenable[0] = 0; - break; - case HDA_CODEC_VT1708S_0: - case HDA_CODEC_VT1708S_1: - case HDA_CODEC_VT1708S_2: - case HDA_CODEC_VT1708S_3: - case HDA_CODEC_VT1708S_4: - case HDA_CODEC_VT1708S_5: - case HDA_CODEC_VT1708S_6: - case HDA_CODEC_VT1708S_7: - /* - * These codecs have hidden mic boost controls. - */ - w = hdac_widget_get(devinfo, 26); - if (w != NULL) - w->param.inamp_cap = - (40 << HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT) | - (3 << HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT) | - (0 << HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT); - w = hdac_widget_get(devinfo, 30); - if (w != NULL) - w->param.inamp_cap = - (40 << HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT) | - (3 << HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT) | - (0 << HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT); - break; - } -} - -/* - * Trace path from DAC to pin. - */ -static nid_t -hdac_audio_trace_dac(struct hdac_devinfo *devinfo, int as, int seq, nid_t nid, - int dupseq, int min, int only, int depth) -{ - struct hdac_widget *w; - int i, im = -1; - nid_t m = 0, ret; - - if (depth > HDA_PARSE_MAXDEPTH) - return (0); - w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0) - return (0); - HDA_BOOTHVERBOSE( - if (!only) { - device_printf(devinfo->codec->sc->dev, - " %*stracing via nid %d\n", - depth + 1, "", w->nid); - } - ); - /* Use only unused widgets */ - if (w->bindas >= 0 && w->bindas != as) { - HDA_BOOTHVERBOSE( - if (!only) { - device_printf(devinfo->codec->sc->dev, - " %*snid %d busy by association %d\n", - depth + 1, "", w->nid, w->bindas); - } - ); - return (0); - } - if (dupseq < 0) { - if (w->bindseqmask != 0) { - HDA_BOOTHVERBOSE( - if (!only) { - device_printf(devinfo->codec->sc->dev, - " %*snid %d busy by seqmask %x\n", - depth + 1, "", w->nid, w->bindseqmask); - } - ); - return (0); - } - } else { - /* If this is headphones - allow duplicate first pin. */ - if (w->bindseqmask != 0 && - (w->bindseqmask & (1 << dupseq)) == 0) { - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " %*snid %d busy by seqmask %x\n", - depth + 1, "", w->nid, w->bindseqmask); - ); - return (0); - } - } - - switch (w->type) { - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: - /* Do not traverse input. AD1988 has digital monitor - for which we are not ready. */ - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: - /* If we are tracing HP take only dac of first pin. */ - if ((only == 0 || only == w->nid) && - (w->nid >= min) && (dupseq < 0 || w->nid == - devinfo->function.audio.as[as].dacs[dupseq])) - m = w->nid; - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: - if (depth > 0) - break; - /* Fall */ - default: - /* Find reachable DACs with smallest nid respecting constraints. */ - for (i = 0; i < w->nconns; i++) { - if (w->connsenable[i] == 0) - continue; - if (w->selconn != -1 && w->selconn != i) - continue; - if ((ret = hdac_audio_trace_dac(devinfo, as, seq, - w->conns[i], dupseq, min, only, depth + 1)) != 0) { - if (m == 0 || ret < m) { - m = ret; - im = i; - } - if (only || dupseq >= 0) - break; - } - } - if (m && only && ((w->nconns > 1 && - w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || - w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) - w->selconn = im; - break; - } - if (m && only) { - w->bindas = as; - w->bindseqmask |= (1 << seq); - } - HDA_BOOTHVERBOSE( - if (!only) { - device_printf(devinfo->codec->sc->dev, - " %*snid %d returned %d\n", - depth + 1, "", w->nid, m); - } - ); - return (m); -} - -/* - * Trace path from widget to ADC. - */ -static nid_t -hdac_audio_trace_adc(struct hdac_devinfo *devinfo, int as, int seq, nid_t nid, - int only, int depth) -{ - struct hdac_widget *w, *wc; - int i, j; - nid_t res = 0; - - if (depth > HDA_PARSE_MAXDEPTH) - return (0); - w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0) - return (0); - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " %*stracing via nid %d\n", - depth + 1, "", w->nid); - ); - /* Use only unused widgets */ - if (w->bindas >= 0 && w->bindas != as) { - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " %*snid %d busy by association %d\n", - depth + 1, "", w->nid, w->bindas); - ); - return (0); - } - - switch (w->type) { - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: - /* If we are tracing HP take only dac of first pin. */ - if (only == w->nid) - res = 1; - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: - if (depth > 0) - break; - /* Fall */ - default: - /* Try to find reachable ADCs with specified nid. */ - for (j = devinfo->startnode; j < devinfo->endnode; j++) { - wc = hdac_widget_get(devinfo, j); - if (wc == NULL || wc->enable == 0) - continue; - for (i = 0; i < wc->nconns; i++) { - if (wc->connsenable[i] == 0) - continue; - if (wc->conns[i] != nid) - continue; - if (hdac_audio_trace_adc(devinfo, as, seq, - j, only, depth + 1) != 0) { - res = 1; - if (((wc->nconns > 1 && - wc->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || - wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR) && - wc->selconn == -1) - wc->selconn = i; - } - } - } - break; - } - if (res) { - w->bindas = as; - w->bindseqmask |= (1 << seq); - } - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " %*snid %d returned %d\n", - depth + 1, "", w->nid, res); - ); - return (res); -} - -/* - * Erase trace path of the specified association. - */ -static void -hdac_audio_undo_trace(struct hdac_devinfo *devinfo, int as, int seq) -{ - struct hdac_widget *w; - int i; - - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->bindas == as) { - if (seq >= 0) { - w->bindseqmask &= ~(1 << seq); - if (w->bindseqmask == 0) { - w->bindas = -1; - w->selconn = -1; - } - } else { - w->bindas = -1; - w->bindseqmask = 0; - w->selconn = -1; - } - } - } -} - -/* - * Trace association path from DAC to output - */ -static int -hdac_audio_trace_as_out(struct hdac_devinfo *devinfo, int as, int seq) -{ - struct hdac_audio_as *ases = devinfo->function.audio.as; - int i, hpredir; - nid_t min, res; - - /* Find next pin */ - for (i = seq; i < 16 && ases[as].pins[i] == 0; i++) - ; - /* Check if there is no any left. If so - we succeeded. */ - if (i == 16) - return (1); - - hpredir = (i == 15 && ases[as].fakeredir == 0)?ases[as].hpredir:-1; - min = 0; - res = 0; - do { - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Tracing pin %d with min nid %d", - ases[as].pins[i], min); - if (hpredir >= 0) - printf(" and hpredir %d", hpredir); - printf("\n"); - ); - /* Trace this pin taking min nid into account. */ - res = hdac_audio_trace_dac(devinfo, as, i, - ases[as].pins[i], hpredir, min, 0, 0); - if (res == 0) { - /* If we failed - return to previous and redo it. */ - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Unable to trace pin %d seq %d with min " - "nid %d", - ases[as].pins[i], i, min); - if (hpredir >= 0) - printf(" and hpredir %d", hpredir); - printf("\n"); - ); - return (0); - } - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Pin %d traced to DAC %d", - ases[as].pins[i], res); - if (hpredir >= 0) - printf(" and hpredir %d", hpredir); - if (ases[as].fakeredir) - printf(" with fake redirection"); - printf("\n"); - ); - /* Trace again to mark the path */ - hdac_audio_trace_dac(devinfo, as, i, - ases[as].pins[i], hpredir, min, res, 0); - ases[as].dacs[i] = res; - /* We succeeded, so call next. */ - if (hdac_audio_trace_as_out(devinfo, as, i + 1)) - return (1); - /* If next failed, we should retry with next min */ - hdac_audio_undo_trace(devinfo, as, i); - ases[as].dacs[i] = 0; - min = res + 1; - } while (1); -} - -/* - * Trace association path from input to ADC - */ static int -hdac_audio_trace_as_in(struct hdac_devinfo *devinfo, int as) +sysctl_hdac_pindump(SYSCTL_HANDLER_ARGS) { - struct hdac_audio_as *ases = devinfo->function.audio.as; - struct hdac_widget *w; - int i, j, k; - - for (j = devinfo->startnode; j < devinfo->endnode; j++) { - w = hdac_widget_get(devinfo, j); - if (w == NULL || w->enable == 0) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) - continue; - if (w->bindas >= 0 && w->bindas != as) - continue; - - /* Find next pin */ - for (i = 0; i < 16; i++) { - if (ases[as].pins[i] == 0) - continue; - - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Tracing pin %d to ADC %d\n", - ases[as].pins[i], j); - ); - /* Trace this pin taking goal into account. */ - if (hdac_audio_trace_adc(devinfo, as, i, - ases[as].pins[i], j, 0) == 0) { - /* If we failed - return to previous and redo it. */ - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Unable to trace pin %d to ADC %d, undo traces\n", - ases[as].pins[i], j); - ); - hdac_audio_undo_trace(devinfo, as, -1); - for (k = 0; k < 16; k++) - ases[as].dacs[k] = 0; - break; - } - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Pin %d traced to ADC %d\n", - ases[as].pins[i], j); - ); - ases[as].dacs[i] = j; - } - if (i == 16) - return (1); - } - return (0); -} + struct hdac_softc *sc; + device_t *devlist; + device_t dev; + int devcount, i, err, val; -/* - * Trace input monitor path from mixer to output association. - */ -static int -hdac_audio_trace_to_out(struct hdac_devinfo *devinfo, nid_t nid, int depth) -{ - struct hdac_audio_as *ases = devinfo->function.audio.as; - struct hdac_widget *w, *wc; - int i, j; - nid_t res = 0; + dev = oidp->oid_arg1; + sc = device_get_softc(dev); + if (sc == NULL) + return (EINVAL); + val = 0; + err = sysctl_handle_int(oidp, &val, 0, req); + if (err != 0 || req->newptr == NULL || val == 0) + return (err); - if (depth > HDA_PARSE_MAXDEPTH) + /* XXX: Temporary. For debugging. */ + if (val == 100) { + hdac_suspend(dev); return (0); - w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0) + } else if (val == 101) { + hdac_resume(dev); return (0); - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " %*stracing via nid %d\n", - depth + 1, "", w->nid); - ); - /* Use only unused widgets */ - if (depth > 0 && w->bindas != -1) { - if (w->bindas < 0 || ases[w->bindas].dir == HDA_CTL_OUT) { - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " %*snid %d found output association %d\n", - depth + 1, "", w->nid, w->bindas); - ); - if (w->bindas >= 0) - w->pflags |= HDA_ADC_MONITOR; - return (1); - } else { - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " %*snid %d busy by input association %d\n", - depth + 1, "", w->nid, w->bindas); - ); - return (0); - } - } - - switch (w->type) { - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: - /* Do not traverse input. AD1988 has digital monitor - for which we are not ready. */ - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: - if (depth > 0) - break; - /* Fall */ - default: - /* Try to find reachable ADCs with specified nid. */ - for (j = devinfo->startnode; j < devinfo->endnode; j++) { - wc = hdac_widget_get(devinfo, j); - if (wc == NULL || wc->enable == 0) - continue; - for (i = 0; i < wc->nconns; i++) { - if (wc->connsenable[i] == 0) - continue; - if (wc->conns[i] != nid) - continue; - if (hdac_audio_trace_to_out(devinfo, - j, depth + 1) != 0) { - res = 1; - if (wc->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && - wc->selconn == -1) - wc->selconn = i; - } - } - } - break; - } - if (res && w->bindas == -1) - w->bindas = -2; - - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " %*snid %d returned %d\n", - depth + 1, "", w->nid, res); - ); - return (res); -} - -/* - * Trace extra associations (beeper, monitor) - */ -static void -hdac_audio_trace_as_extra(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_widget *w; - int j; - - /* Input monitor */ - /* Find mixer associated with input, but supplying signal - for output associations. Hope it will be input monitor. */ - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - "Tracing input monitor\n"); - ); - for (j = devinfo->startnode; j < devinfo->endnode; j++) { - w = hdac_widget_get(devinfo, j); - if (w == NULL || w->enable == 0) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) - continue; - if (w->bindas < 0 || as[w->bindas].dir != HDA_CTL_IN) - continue; - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Tracing nid %d to out\n", - j); - ); - if (hdac_audio_trace_to_out(devinfo, w->nid, 0)) { - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " nid %d is input monitor\n", - w->nid); - ); - w->ossdev = SOUND_MIXER_IMIX; - } - } - - /* Other inputs monitor */ - /* Find input pins supplying signal for output associations. - Hope it will be input monitoring. */ - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - "Tracing other input monitors\n"); - ); - for (j = devinfo->startnode; j < devinfo->endnode; j++) { - w = hdac_widget_get(devinfo, j); - if (w == NULL || w->enable == 0) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - if (w->bindas < 0 || as[w->bindas].dir != HDA_CTL_IN) - continue; - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Tracing nid %d to out\n", - j); - ); - if (hdac_audio_trace_to_out(devinfo, w->nid, 0)) { - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " nid %d is input monitor\n", - w->nid); - ); - } - } - - /* Beeper */ - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - "Tracing beeper\n"); - ); - for (j = devinfo->startnode; j < devinfo->endnode; j++) { - w = hdac_widget_get(devinfo, j); - if (w == NULL || w->enable == 0) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET) - continue; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Tracing nid %d to out\n", - j); - ); - if (hdac_audio_trace_to_out(devinfo, w->nid, 0)) { - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - " nid %d traced to out\n", - j); - ); - } - w->bindas = -2; - } -} - -/* - * Bind assotiations to PCM channels - */ -static void -hdac_audio_bind_as(struct hdac_devinfo *devinfo) -{ - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_audio_as *as = devinfo->function.audio.as; - int j, cnt = 0, free; - - for (j = 0; j < devinfo->function.audio.ascnt; j++) { - if (as[j].enable) - cnt++; - } - if (sc->num_chans == 0) { - sc->chans = (struct hdac_chan *)malloc( - sizeof(struct hdac_chan) * cnt, - M_HDAC, M_ZERO | M_NOWAIT); - if (sc->chans == NULL) { - device_printf(sc->dev, - "Channels memory allocation failed!\n"); - return; - } - } else { - sc->chans = (struct hdac_chan *)realloc(sc->chans, - sizeof(struct hdac_chan) * (sc->num_chans + cnt), - M_HDAC, M_ZERO | M_NOWAIT); - if (sc->chans == NULL) { - sc->num_chans = 0; - device_printf(sc->dev, - "Channels memory allocation failed!\n"); - return; - } - /* Fixup relative pointers after realloc */ - for (j = 0; j < sc->num_chans; j++) - sc->chans[j].caps.fmtlist = sc->chans[j].fmtlist; - } - free = sc->num_chans; - sc->num_chans += cnt; - - for (j = free; j < free + cnt; j++) { - sc->chans[j].devinfo = devinfo; - sc->chans[j].as = -1; - } - - /* Assign associations in order of their numbers, */ - for (j = 0; j < devinfo->function.audio.ascnt; j++) { - if (as[j].enable == 0) - continue; - - as[j].chan = free; - sc->chans[free].as = j; - sc->chans[free].dir = - (as[j].dir == HDA_CTL_IN) ? PCMDIR_REC : PCMDIR_PLAY; - hdac_pcmchannel_setup(&sc->chans[free]); - free++; - } -} - -static void -hdac_audio_disable_nonaudio(struct hdac_devinfo *devinfo) -{ - struct hdac_widget *w; - int i; - - /* Disable power and volume widgets. */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET || - w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET) { - w->enable = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling nid %d due to it's" - " non-audio type.\n", - w->nid); - ); - } - } -} - -static void -hdac_audio_disable_useless(struct hdac_devinfo *devinfo) -{ - struct hdac_widget *w, *cw; - struct hdac_audio_ctl *ctl; - int done, found, i, j, k; - - /* Disable useless pins. */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { - if ((w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == - HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) { - w->enable = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling pin nid %d due" - " to None connectivity.\n", - w->nid); - ); - } else if ((w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK) == 0) { - w->enable = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling unassociated" - " pin nid %d.\n", - w->nid); - ); - } - } - } - do { - done = 1; - /* Disable and mute controls for disabled widgets. */ - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0) - continue; - if (ctl->widget->enable == 0 || - (ctl->childwidget != NULL && - ctl->childwidget->enable == 0)) { - ctl->forcemute = 1; - ctl->muted = HDA_AMP_MUTE_ALL; - ctl->left = 0; - ctl->right = 0; - ctl->enable = 0; - if (ctl->ndir == HDA_CTL_IN) - ctl->widget->connsenable[ctl->index] = 0; - done = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling ctl %d nid %d cnid %d due" - " to disabled widget.\n", i, - ctl->widget->nid, - (ctl->childwidget != NULL)? - ctl->childwidget->nid:-1); - ); - } - } - /* Disable useless widgets. */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - /* Disable inputs with disabled child widgets. */ - for (j = 0; j < w->nconns; j++) { - if (w->connsenable[j]) { - cw = hdac_widget_get(devinfo, w->conns[j]); - if (cw == NULL || cw->enable == 0) { - w->connsenable[j] = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling nid %d connection %d due" - " to disabled child widget.\n", - i, j); - ); - } - } - } - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && - w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) - continue; - /* Disable mixers and selectors without inputs. */ - found = 0; - for (j = 0; j < w->nconns; j++) { - if (w->connsenable[j]) { - found = 1; - break; - } - } - if (found == 0) { - w->enable = 0; - done = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling nid %d due to all it's" - " inputs disabled.\n", w->nid); - ); - } - /* Disable nodes without consumers. */ - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && - w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) - continue; - found = 0; - for (k = devinfo->startnode; k < devinfo->endnode; k++) { - cw = hdac_widget_get(devinfo, k); - if (cw == NULL || cw->enable == 0) - continue; - for (j = 0; j < cw->nconns; j++) { - if (cw->connsenable[j] && cw->conns[j] == i) { - found = 1; - break; - } - } - } - if (found == 0) { - w->enable = 0; - done = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling nid %d due to all it's" - " consumers disabled.\n", w->nid); - ); - } - } - } while (done == 0); - -} - -static void -hdac_audio_disable_unas(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_widget *w, *cw; - struct hdac_audio_ctl *ctl; - int i, j, k; - - /* Disable unassosiated widgets. */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->bindas == -1) { - w->enable = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling unassociated nid %d.\n", - w->nid); - ); - } - } - /* Disable input connections on input pin and - * output on output. */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - if (w->bindas < 0) - continue; - if (as[w->bindas].dir == HDA_CTL_IN) { - for (j = 0; j < w->nconns; j++) { - if (w->connsenable[j] == 0) - continue; - w->connsenable[j] = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling connection to input pin " - "nid %d conn %d.\n", - i, j); - ); - } - ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, - HDA_CTL_IN, -1, 1); - if (ctl && ctl->enable) { - ctl->forcemute = 1; - ctl->muted = HDA_AMP_MUTE_ALL; - ctl->left = 0; - ctl->right = 0; - ctl->enable = 0; - } - } else { - ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, - HDA_CTL_OUT, -1, 1); - if (ctl && ctl->enable) { - ctl->forcemute = 1; - ctl->muted = HDA_AMP_MUTE_ALL; - ctl->left = 0; - ctl->right = 0; - ctl->enable = 0; - } - for (k = devinfo->startnode; k < devinfo->endnode; k++) { - cw = hdac_widget_get(devinfo, k); - if (cw == NULL || cw->enable == 0) - continue; - for (j = 0; j < cw->nconns; j++) { - if (cw->connsenable[j] && cw->conns[j] == i) { - cw->connsenable[j] = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling connection from output pin " - "nid %d conn %d cnid %d.\n", - k, j, i); - ); - if (cw->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && - cw->nconns > 1) - continue; - ctl = hdac_audio_ctl_amp_get(devinfo, k, - HDA_CTL_IN, j, 1); - if (ctl && ctl->enable) { - ctl->forcemute = 1; - ctl->muted = HDA_AMP_MUTE_ALL; - ctl->left = 0; - ctl->right = 0; - ctl->enable = 0; - } - } - } - } - } - } -} - -static void -hdac_audio_disable_notselected(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_widget *w; - int i, j; - - /* On playback path we can safely disable all unseleted inputs. */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->nconns <= 1) - continue; - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) - continue; - if (w->bindas < 0 || as[w->bindas].dir == HDA_CTL_IN) - continue; - for (j = 0; j < w->nconns; j++) { - if (w->connsenable[j] == 0) - continue; - if (w->selconn < 0 || w->selconn == j) - continue; - w->connsenable[j] = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling unselected connection " - "nid %d conn %d.\n", - i, j); - ); - } - } -} - -static void -hdac_audio_disable_crossas(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_as *ases = devinfo->function.audio.as; - struct hdac_widget *w, *cw; - struct hdac_audio_ctl *ctl; - int i, j; - - /* Disable crossassociatement and unwanted crosschannel connections. */ - /* ... using selectors */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->nconns <= 1) - continue; - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) - continue; - if (w->bindas == -2) - continue; - for (j = 0; j < w->nconns; j++) { - if (w->connsenable[j] == 0) - continue; - cw = hdac_widget_get(devinfo, w->conns[j]); - if (cw == NULL || w->enable == 0) - continue; - if (cw->bindas == -2 || - ((w->pflags & HDA_ADC_MONITOR) && - cw->bindas >= 0 && - ases[cw->bindas].dir == HDA_CTL_IN)) - continue; - if (w->bindas == cw->bindas && - (w->bindseqmask & cw->bindseqmask) != 0) - continue; - w->connsenable[j] = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling crossassociatement connection " - "nid %d conn %d cnid %d.\n", - i, j, cw->nid); - ); - } - } - /* ... using controls */ - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0 || ctl->childwidget == NULL) - continue; - if (ctl->widget->bindas == -2) - continue; - if (ctl->childwidget->bindas == -2 || - ((ctl->widget->pflags & HDA_ADC_MONITOR) && - ctl->childwidget->bindas >= 0 && - ases[ctl->childwidget->bindas].dir == HDA_CTL_IN)) - continue; - if (ctl->widget->bindas != ctl->childwidget->bindas || - (ctl->widget->bindseqmask & ctl->childwidget->bindseqmask) == 0) { - ctl->forcemute = 1; - ctl->muted = HDA_AMP_MUTE_ALL; - ctl->left = 0; - ctl->right = 0; - ctl->enable = 0; - if (ctl->ndir == HDA_CTL_IN) - ctl->widget->connsenable[ctl->index] = 0; - HDA_BOOTHVERBOSE( - device_printf(devinfo->codec->sc->dev, - " Disabling crossassociatement connection " - "ctl %d nid %d cnid %d.\n", i, - ctl->widget->nid, - ctl->childwidget->nid); - ); - } } + if ((err = device_get_children(dev, &devlist, &devcount)) != 0) + return (err); + hdac_lock(sc); + for (i = 0; i < devcount; i++) + HDAC_PINDUMP(devlist[i]); + hdac_unlock(sc); + free(devlist, M_TEMP); + return (0); } -#define HDA_CTL_GIVE(ctl) ((ctl)->step?1:0) - -/* - * Find controls to control amplification for source. - */ static int -hdac_audio_ctl_source_amp(struct hdac_devinfo *devinfo, nid_t nid, int index, - int ossdev, int ctlable, int depth, int need) +hdac_data_rate(uint16_t fmt) { - struct hdac_widget *w, *wc; - struct hdac_audio_ctl *ctl; - int i, j, conns = 0, rneed; - - if (depth > HDA_PARSE_MAXDEPTH) - return (need); - - w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0) - return (need); - - /* Count number of active inputs. */ - if (depth > 0) { - for (j = 0; j < w->nconns; j++) { - if (w->connsenable[j]) - conns++; - } - } + static const int bits[8] = { 8, 16, 20, 24, 32, 32, 32, 32 }; + int rate; - /* If this is not a first step - use input mixer. - Pins have common input ctl so care must be taken. */ - if (depth > 0 && ctlable && (conns == 1 || - w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX)) { - ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_IN, - index, 1); - if (ctl) { - if (HDA_CTL_GIVE(ctl) & need) - ctl->ossmask |= (1 << ossdev); - else - ctl->possmask |= (1 << ossdev); - need &= ~HDA_CTL_GIVE(ctl); - } - } - - /* If widget has own ossdev - not traverse it. - It will be traversed on it's own. */ - if (w->ossdev >= 0 && depth > 0) - return (need); - - /* We must not traverse pin */ - if ((w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT || - w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) && - depth > 0) - return (need); - - /* record that this widget exports such signal, */ - w->ossmask |= (1 << ossdev); - - /* If signals mixed, we can't assign controls farther. - * Ignore this on depth zero. Caller must knows why. - * Ignore this for static selectors if this input selected. - */ - if (conns > 1) - ctlable = 0; - - if (ctlable) { - ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, HDA_CTL_OUT, -1, 1); - if (ctl) { - if (HDA_CTL_GIVE(ctl) & need) - ctl->ossmask |= (1 << ossdev); - else - ctl->possmask |= (1 << ossdev); - need &= ~HDA_CTL_GIVE(ctl); - } - } - - rneed = 0; - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - wc = hdac_widget_get(devinfo, i); - if (wc == NULL || wc->enable == 0) - continue; - for (j = 0; j < wc->nconns; j++) { - if (wc->connsenable[j] && wc->conns[j] == nid) { - rneed |= hdac_audio_ctl_source_amp(devinfo, - wc->nid, j, ossdev, ctlable, depth + 1, need); - } - } - } - rneed &= need; - - return (rneed); + if (fmt & (1 << 14)) + rate = 44100; + else + rate = 48000; + rate *= ((fmt >> 11) & 0x07) + 1; + rate /= ((fmt >> 8) & 0x07) + 1; + rate *= ((bits[(fmt >> 4) & 0x03]) * ((fmt & 0x0f) + 1) + 7) / 8; + return (rate); } -/* - * Find controls to control amplification for destination. - */ static void -hdac_audio_ctl_dest_amp(struct hdac_devinfo *devinfo, nid_t nid, int index, - int ossdev, int depth, int need) +hdac_poll_reinit(struct hdac_softc *sc) { - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_widget *w, *wc; - struct hdac_audio_ctl *ctl; - int i, j, consumers; - - if (depth > HDA_PARSE_MAXDEPTH) - return; - - w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0) - return; - - if (depth > 0) { - /* If this node produce output for several consumers, - we can't touch it. */ - consumers = 0; - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - wc = hdac_widget_get(devinfo, i); - if (wc == NULL || wc->enable == 0) - continue; - for (j = 0; j < wc->nconns; j++) { - if (wc->connsenable[j] && wc->conns[j] == nid) - consumers++; - } - } - /* The only exception is if real HP redirection is configured - and this is a duplication point. - XXX: Actually exception is not completely correct. - XXX: Duplication point check is not perfect. */ - if ((consumers == 2 && (w->bindas < 0 || - as[w->bindas].hpredir < 0 || as[w->bindas].fakeredir || - (w->bindseqmask & (1 << 15)) == 0)) || - consumers > 2) - return; + int i, pollticks, min = 1000000; + struct hdac_stream *s; - /* Else use it's output mixer. */ - ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, - HDA_CTL_OUT, -1, 1); - if (ctl) { - if (HDA_CTL_GIVE(ctl) & need) - ctl->ossmask |= (1 << ossdev); - else - ctl->possmask |= (1 << ossdev); - need &= ~HDA_CTL_GIVE(ctl); - } - } - - /* We must not traverse pin */ - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && - depth > 0) + if (sc->polling == 0) return; - - for (i = 0; i < w->nconns; i++) { - int tneed = need; - if (w->connsenable[i] == 0) - continue; - if (index >= 0 && i != index) - continue; - ctl = hdac_audio_ctl_amp_get(devinfo, w->nid, - HDA_CTL_IN, i, 1); - if (ctl) { - if (HDA_CTL_GIVE(ctl) & tneed) - ctl->ossmask |= (1 << ossdev); - else - ctl->possmask |= (1 << ossdev); - tneed &= ~HDA_CTL_GIVE(ctl); - } - hdac_audio_ctl_dest_amp(devinfo, w->conns[i], -1, ossdev, - depth + 1, tneed); - } -} - -/* - * Assign OSS names to sound sources - */ -static void -hdac_audio_assign_names(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_widget *w; - int i, j; - int type = -1, use, used = 0; - static const int types[7][13] = { - { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2, - SOUND_MIXER_LINE3, -1 }, /* line */ - { SOUND_MIXER_MONITOR, SOUND_MIXER_MIC, -1 }, /* int mic */ - { SOUND_MIXER_MIC, SOUND_MIXER_MONITOR, -1 }, /* ext mic */ - { SOUND_MIXER_CD, -1 }, /* cd */ - { SOUND_MIXER_SPEAKER, -1 }, /* speaker */ - { SOUND_MIXER_DIGITAL1, SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3, - -1 }, /* digital */ - { SOUND_MIXER_LINE, SOUND_MIXER_LINE1, SOUND_MIXER_LINE2, - SOUND_MIXER_LINE3, SOUND_MIXER_PHONEIN, SOUND_MIXER_PHONEOUT, - SOUND_MIXER_VIDEO, SOUND_MIXER_RADIO, SOUND_MIXER_DIGITAL1, - SOUND_MIXER_DIGITAL2, SOUND_MIXER_DIGITAL3, SOUND_MIXER_MONITOR, - -1 } /* others */ - }; - - /* Surely known names */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->bindas == -1) - continue; - use = -1; - switch (w->type) { - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: - if (as[w->bindas].dir == HDA_CTL_OUT) - break; - type = -1; - switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { - case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN: - type = 0; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: - if ((w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) - == HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK) - break; - type = 1; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_CD: - type = 3; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: - type = 4; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_IN: - case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_IN: - type = 5; - break; - } - if (type == -1) - break; - j = 0; - while (types[type][j] >= 0 && - (used & (1 << types[type][j])) != 0) { - j++; - } - if (types[type][j] >= 0) - use = types[type][j]; - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: - use = SOUND_MIXER_PCM; - break; - case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET: - use = SOUND_MIXER_SPEAKER; - break; - default: - break; - } - if (use >= 0) { - w->ossdev = use; - used |= (1 << use); - } - } - /* Semi-known names */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->ossdev >= 0) - continue; - if (w->bindas == -1) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - if (as[w->bindas].dir == HDA_CTL_OUT) - continue; - type = -1; - switch (w->wclass.pin.config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { - case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT: - case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: - case HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT: - case HDA_CONFIG_DEFAULTCONF_DEVICE_AUX: - type = 0; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: - type = 2; - break; - case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT: - case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT: - type = 5; - break; - } - if (type == -1) - break; - j = 0; - while (types[type][j] >= 0 && - (used & (1 << types[type][j])) != 0) { - j++; - } - if (types[type][j] >= 0) { - w->ossdev = types[type][j]; - used |= (1 << types[type][j]); - } - } - /* Others */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->ossdev >= 0) - continue; - if (w->bindas == -1) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - if (as[w->bindas].dir == HDA_CTL_OUT) - continue; - j = 0; - while (types[6][j] >= 0 && - (used & (1 << types[6][j])) != 0) { - j++; - } - if (types[6][j] >= 0) { - w->ossdev = types[6][j]; - used |= (1 << types[6][j]); - } - } -} - -static void -hdac_audio_build_tree(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_as *as = devinfo->function.audio.as; - int j, res; - - /* Trace all associations in order of their numbers, */ - for (j = 0; j < devinfo->function.audio.ascnt; j++) { - if (as[j].enable == 0) - continue; - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - "Tracing association %d (%d)\n", j, as[j].index); - ); - if (as[j].dir == HDA_CTL_OUT) { -retry: - res = hdac_audio_trace_as_out(devinfo, j, 0); - if (res == 0 && as[j].hpredir >= 0 && - as[j].fakeredir == 0) { - /* If codec can't do analog HP redirection - try to make it using one more DAC. */ - as[j].fakeredir = 1; - goto retry; - } - } else { - res = hdac_audio_trace_as_in(devinfo, j); - } - if (res) { - HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - "Association %d (%d) trace succeeded\n", - j, as[j].index); - ); - } else { + if (sc->unsol_registered > 0) + min = hz / 2; + for (i = 0; i < sc->num_ss; i++) { + s = &sc->streams[i]; + if (s->running == 0) + continue; + pollticks = ((uint64_t)hz * s->blksz) / + hdac_data_rate(s->format); + pollticks >>= 1; + if (pollticks > hz) + pollticks = hz; + if (pollticks < 1) { HDA_BOOTVERBOSE( - device_printf(devinfo->codec->sc->dev, - "Association %d (%d) trace failed\n", - j, as[j].index); + device_printf(sc->dev, + "poll interval < 1 tick !\n"); ); - as[j].enable = 0; - } - } - - /* Trace mixer and beeper pseudo associations. */ - hdac_audio_trace_as_extra(devinfo); -} - -static void -hdac_audio_assign_mixers(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_audio_ctl *ctl; - struct hdac_widget *w, *cw; - int i, j; - - /* Assign mixers to the tree. */ - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || - w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET || - (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && - as[w->bindas].dir == HDA_CTL_IN)) { - if (w->ossdev < 0) - continue; - hdac_audio_ctl_source_amp(devinfo, w->nid, -1, - w->ossdev, 1, 0, 1); - } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { - hdac_audio_ctl_dest_amp(devinfo, w->nid, -1, - SOUND_MIXER_RECLEV, 0, 1); - } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && - as[w->bindas].dir == HDA_CTL_OUT) { - hdac_audio_ctl_dest_amp(devinfo, w->nid, -1, - SOUND_MIXER_VOLUME, 0, 1); - } - if (w->ossdev == SOUND_MIXER_IMIX) { - if (hdac_audio_ctl_source_amp(devinfo, w->nid, -1, - w->ossdev, 1, 0, 1)) { - /* If we are unable to control input monitor - as source - try to control it as destination. */ - hdac_audio_ctl_dest_amp(devinfo, w->nid, -1, - w->ossdev, 0, 1); - } - } - if (w->pflags & HDA_ADC_MONITOR) { - for (j = 0; j < w->nconns; j++) { - if (!w->connsenable[j]) - continue; - cw = hdac_widget_get(devinfo, w->conns[j]); - if (cw == NULL || cw->enable == 0) - continue; - if (cw->bindas == -1) - continue; - if (cw->bindas >= 0 && - as[cw->bindas].dir != HDA_CTL_IN) - continue; - hdac_audio_ctl_dest_amp(devinfo, - w->nid, j, SOUND_MIXER_IGAIN, 0, 1); - } - } - } - /* Treat unrequired as possible. */ - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->ossmask == 0) - ctl->ossmask = ctl->possmask; - } -} - -static void -hdac_audio_prepare_pin_ctrl(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_widget *w; - uint32_t pincap; - int i; - - for (i = 0; i < devinfo->nodecnt; i++) { - w = &devinfo->widget[i]; - if (w == NULL) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - - pincap = w->wclass.pin.cap; - - /* Disable everything. */ - w->wclass.pin.ctrl &= ~( - HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE | - HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | - HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE | - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK); - - if (w->enable == 0 || - w->bindas < 0 || as[w->bindas].enable == 0) { - /* Pin is unused so left it disabled. */ - continue; - } else if (as[w->bindas].dir == HDA_CTL_IN) { - /* Input pin, configure for input. */ - if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE; - - if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF100) && - HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); - else if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF80) && - HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); - else if ((devinfo->function.audio.quirks & HDA_QUIRK_IVREF50) && - HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); - } else { - /* Output pin, configure for output. */ - if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; - - if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap) && - (w->wclass.pin.config & - HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) == - HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE; - - if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF100) && - HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100); - else if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF80) && - HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80); - else if ((devinfo->function.audio.quirks & HDA_QUIRK_OVREF50) && - HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) - w->wclass.pin.ctrl |= - HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE( - HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50); - } - } -} - -static void -hdac_audio_ctl_commit(struct hdac_devinfo *devinfo) -{ - struct hdac_audio_ctl *ctl; - int i, z; - - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0 || ctl->ossmask != 0) { - /* Mute disabled and mixer controllable controls. - * Last will be initialized by mixer_init(). - * This expected to reduce click on startup. */ - hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_ALL, 0, 0); - continue; - } - /* Init fixed controls to 0dB amplification. */ - z = ctl->offset; - if (z > ctl->step) - z = ctl->step; - hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_NONE, z, z); - } -} - -static void -hdac_audio_commit(struct hdac_devinfo *devinfo) -{ - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_widget *w; - nid_t cad; - uint32_t id, gdata, gmask, gdir; - int commitgpio, numgpio; - int i; - - cad = devinfo->codec->cad; - - if (sc->pci_subvendor == APPLE_INTEL_MAC) - hdac_command(sc, HDA_CMD_12BIT(cad, devinfo->nid, - 0x7e7, 0), cad); - id = hdac_codec_id(devinfo->codec); - switch (id) { - case HDA_CODEC_VT1708S_0: - case HDA_CODEC_VT1708S_1: - case HDA_CODEC_VT1708S_2: - case HDA_CODEC_VT1708S_3: - case HDA_CODEC_VT1708S_4: - case HDA_CODEC_VT1708S_5: - case HDA_CODEC_VT1708S_6: - case HDA_CODEC_VT1708S_7: - /* Enable Mic Boost Volume controls. */ - hdac_command(sc, HDA_CMD_12BIT(cad, devinfo->nid, - 0xf98, 0x01), cad); - /* Don't bypass mixer. */ - hdac_command(sc, HDA_CMD_12BIT(cad, devinfo->nid, - 0xf88, 0xc0), cad); - break; - } - - /* Commit controls. */ - hdac_audio_ctl_commit(devinfo); - - /* Commit selectors, pins and EAPD. */ - for (i = 0; i < devinfo->nodecnt; i++) { - w = &devinfo->widget[i]; - if (w == NULL) - continue; - if (w->selconn == -1) - w->selconn = 0; - if (w->nconns > 0) - hdac_widget_connection_select(w, w->selconn); - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { - hdac_command(sc, - HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, - w->wclass.pin.ctrl), cad); - } - if (w->param.eapdbtl != HDAC_INVALID) { - uint32_t val; - - val = w->param.eapdbtl; - if (devinfo->function.audio.quirks & - HDA_QUIRK_EAPDINV) - val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; - hdac_command(sc, - HDA_CMD_SET_EAPD_BTL_ENABLE(cad, w->nid, - val), cad); - } - } - - /* Commit GPIOs. */ - gdata = 0; - gmask = 0; - gdir = 0; - commitgpio = 0; - numgpio = HDA_PARAM_GPIO_COUNT_NUM_GPIO( - devinfo->function.audio.gpio); - - if (devinfo->function.audio.quirks & HDA_QUIRK_GPIOFLUSH) - commitgpio = (numgpio > 0) ? 1 : 0; - else { - for (i = 0; i < numgpio && i < HDA_GPIO_MAX; i++) { - if (!(devinfo->function.audio.quirks & - (1 << i))) - continue; - if (commitgpio == 0) { - commitgpio = 1; - HDA_BOOTVERBOSE( - gdata = hdac_command(sc, - HDA_CMD_GET_GPIO_DATA(cad, - devinfo->nid), cad); - gmask = hdac_command(sc, - HDA_CMD_GET_GPIO_ENABLE_MASK(cad, - devinfo->nid), cad); - gdir = hdac_command(sc, - HDA_CMD_GET_GPIO_DIRECTION(cad, - devinfo->nid), cad); - device_printf(sc->dev, - "GPIO init: data=0x%08x " - "mask=0x%08x dir=0x%08x\n", - gdata, gmask, gdir); - gdata = 0; - gmask = 0; - gdir = 0; - ); - } - gdata |= 1 << i; - gmask |= 1 << i; - gdir |= 1 << i; - } - } - - if (commitgpio != 0) { - HDA_BOOTVERBOSE( - device_printf(sc->dev, - "GPIO commit: data=0x%08x mask=0x%08x " - "dir=0x%08x\n", - gdata, gmask, gdir); - ); - hdac_command(sc, - HDA_CMD_SET_GPIO_ENABLE_MASK(cad, devinfo->nid, - gmask), cad); - hdac_command(sc, - HDA_CMD_SET_GPIO_DIRECTION(cad, devinfo->nid, - gdir), cad); - hdac_command(sc, - HDA_CMD_SET_GPIO_DATA(cad, devinfo->nid, - gdata), cad); - } -} - -static void -hdac_powerup(struct hdac_devinfo *devinfo) -{ - struct hdac_softc *sc = devinfo->codec->sc; - nid_t cad = devinfo->codec->cad; - int i; - - hdac_command(sc, - HDA_CMD_SET_POWER_STATE(cad, - devinfo->nid, HDA_CMD_POWER_STATE_D0), - cad); - DELAY(100); - - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - hdac_command(sc, - HDA_CMD_SET_POWER_STATE(cad, - i, HDA_CMD_POWER_STATE_D0), - cad); - } - DELAY(1000); -} - -static int -hdac_pcmchannel_setup(struct hdac_chan *ch) -{ - struct hdac_devinfo *devinfo = ch->devinfo; - struct hdac_audio_as *as = devinfo->function.audio.as; - struct hdac_widget *w; - uint32_t cap, fmtcap, pcmcap; - int i, j, ret, channels, onlystereo; - uint16_t pinset; - - ch->caps = hdac_caps; - ch->caps.fmtlist = ch->fmtlist; - ch->bit16 = 1; - ch->bit32 = 0; - ch->pcmrates[0] = 48000; - ch->pcmrates[1] = 0; - - ret = 0; - channels = 0; - onlystereo = 1; - pinset = 0; - fmtcap = devinfo->function.audio.supp_stream_formats; - pcmcap = devinfo->function.audio.supp_pcm_size_rate; - - for (i = 0; i < 16; i++) { - /* Check as is correct */ - if (ch->as < 0) - break; - /* Cound only present DACs */ - if (as[ch->as].dacs[i] <= 0) - continue; - /* Ignore duplicates */ - for (j = 0; j < ret; j++) { - if (ch->io[j] == as[ch->as].dacs[i]) - break; - } - if (j < ret) - continue; - - w = hdac_widget_get(devinfo, as[ch->as].dacs[i]); - if (w == NULL || w->enable == 0) - continue; - cap = w->param.supp_stream_formats; - if (!HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap) && - !HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) - continue; - /* Many CODECs does not declare AC3 support on SPDIF. - I don't beleave that they doesn't support it! */ - if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) - cap |= HDA_PARAM_SUPP_STREAM_FORMATS_AC3_MASK; - if (ret == 0) { - fmtcap = cap; - pcmcap = w->param.supp_pcm_size_rate; - } else { - fmtcap &= cap; - pcmcap &= w->param.supp_pcm_size_rate; - } - ch->io[ret++] = as[ch->as].dacs[i]; - /* Do not count redirection pin/dac channels. */ - if (i == 15 && as[ch->as].hpredir >= 0) - continue; - channels += HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) + 1; - if (HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap) != 1) - onlystereo = 0; - pinset |= (1 << i); - } - ch->io[ret] = -1; - - if (as[ch->as].fakeredir) - ret--; - /* Standard speaks only about stereo pins and playback, ... */ - if ((!onlystereo) || as[ch->as].dir != HDA_CTL_OUT) - pinset = 0; - /* ..., but there it gives us info about speakers layout. */ - as[ch->as].pinset = pinset; - - ch->supp_stream_formats = fmtcap; - ch->supp_pcm_size_rate = pcmcap; - - /* - * 8bit = 0 - * 16bit = 1 - * 20bit = 2 - * 24bit = 3 - * 32bit = 4 - */ - if (ret > 0) { - i = 0; - if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(fmtcap)) { - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(pcmcap)) - ch->bit16 = 1; - else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(pcmcap)) - ch->bit16 = 0; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(pcmcap)) - ch->bit32 = 4; - else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(pcmcap)) - ch->bit32 = 3; - else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(pcmcap)) - ch->bit32 = 2; - if (!(devinfo->function.audio.quirks & HDA_QUIRK_FORCESTEREO)) { - ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 1, 0); - if (ch->bit32) - ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 1, 0); - } - if (channels >= 2) { - ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 2, 0); - if (ch->bit32) - ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 2, 0); - } - if (channels == 4 || /* Any 4-channel */ - pinset == 0x0007 || /* 5.1 */ - pinset == 0x0013 || /* 5.1 */ - pinset == 0x0017) { /* 7.1 */ - ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 4, 0); - if (ch->bit32) - ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 4, 0); - } - if (channels == 6 || /* Any 6-channel */ - pinset == 0x0017) { /* 7.1 */ - ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 6, 1); - if (ch->bit32) - ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 6, 1); - } - if (channels == 8) { /* Any 8-channel */ - ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 8, 1); - if (ch->bit32) - ch->fmtlist[i++] = SND_FORMAT(AFMT_S32_LE, 8, 1); - } - } - if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(fmtcap)) { - ch->fmtlist[i++] = SND_FORMAT(AFMT_AC3, 2, 0); - } - ch->fmtlist[i] = 0; - i = 0; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(pcmcap)) - ch->pcmrates[i++] = 8000; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(pcmcap)) - ch->pcmrates[i++] = 11025; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(pcmcap)) - ch->pcmrates[i++] = 16000; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(pcmcap)) - ch->pcmrates[i++] = 22050; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(pcmcap)) - ch->pcmrates[i++] = 32000; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(pcmcap)) - ch->pcmrates[i++] = 44100; - /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ(pcmcap)) */ - ch->pcmrates[i++] = 48000; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(pcmcap)) - ch->pcmrates[i++] = 88200; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(pcmcap)) - ch->pcmrates[i++] = 96000; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(pcmcap)) - ch->pcmrates[i++] = 176400; - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(pcmcap)) - ch->pcmrates[i++] = 192000; - /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ(pcmcap)) */ - ch->pcmrates[i] = 0; - if (i > 0) { - ch->caps.minspeed = ch->pcmrates[0]; - ch->caps.maxspeed = ch->pcmrates[i - 1]; - } - } - - return (ret); -} - -static void -hdac_create_pcms(struct hdac_devinfo *devinfo) -{ - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_audio_as *as = devinfo->function.audio.as; - int i, j, apdev = 0, ardev = 0, dpdev = 0, drdev = 0; - - for (i = 0; i < devinfo->function.audio.ascnt; i++) { - if (as[i].enable == 0) - continue; - if (as[i].dir == HDA_CTL_IN) { - if (as[i].digital) - drdev++; - else - ardev++; - } else { - if (as[i].digital) - dpdev++; - else - apdev++; - } - } - devinfo->function.audio.num_devs = - max(ardev, apdev) + max(drdev, dpdev); - devinfo->function.audio.devs = - (struct hdac_pcm_devinfo *)malloc( - devinfo->function.audio.num_devs * sizeof(struct hdac_pcm_devinfo), - M_HDAC, M_ZERO | M_NOWAIT); - if (devinfo->function.audio.devs == NULL) { - device_printf(sc->dev, - "Unable to allocate memory for devices\n"); - return; - } - for (i = 0; i < devinfo->function.audio.num_devs; i++) { - devinfo->function.audio.devs[i].index = i; - devinfo->function.audio.devs[i].devinfo = devinfo; - devinfo->function.audio.devs[i].play = -1; - devinfo->function.audio.devs[i].rec = -1; - devinfo->function.audio.devs[i].digital = 255; - } - for (i = 0; i < devinfo->function.audio.ascnt; i++) { - if (as[i].enable == 0) - continue; - for (j = 0; j < devinfo->function.audio.num_devs; j++) { - if (devinfo->function.audio.devs[j].digital != 255 && - (!devinfo->function.audio.devs[j].digital) != - (!as[i].digital)) - continue; - if (as[i].dir == HDA_CTL_IN) { - if (devinfo->function.audio.devs[j].rec >= 0) - continue; - devinfo->function.audio.devs[j].rec - = as[i].chan; - } else { - if (devinfo->function.audio.devs[j].play >= 0) - continue; - devinfo->function.audio.devs[j].play - = as[i].chan; - } - sc->chans[as[i].chan].pdevinfo = - &devinfo->function.audio.devs[j]; - devinfo->function.audio.devs[j].digital = - as[i].digital; - break; - } - } - for (i = 0; i < devinfo->function.audio.num_devs; i++) { - struct hdac_pcm_devinfo *pdevinfo = - &devinfo->function.audio.devs[i]; - pdevinfo->dev = - device_add_child(sc->dev, "pcm", -1); - device_set_ivars(pdevinfo->dev, - (void *)pdevinfo); - } -} - -static void -hdac_dump_ctls(struct hdac_pcm_devinfo *pdevinfo, const char *banner, uint32_t flag) -{ - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_audio_ctl *ctl; - struct hdac_softc *sc = devinfo->codec->sc; - char buf[64]; - int i, j, printed; - - if (flag == 0) { - flag = ~(SOUND_MASK_VOLUME | SOUND_MASK_PCM | - SOUND_MASK_CD | SOUND_MASK_LINE | SOUND_MASK_RECLEV | - SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IGAIN | - SOUND_MASK_OGAIN | SOUND_MASK_IMIX | SOUND_MASK_MONITOR); - } - - for (j = 0; j < SOUND_MIXER_NRDEVICES; j++) { - if ((flag & (1 << j)) == 0) - continue; - i = 0; - printed = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - if (ctl->enable == 0 || - ctl->widget->enable == 0) - continue; - if (!((pdevinfo->play >= 0 && - ctl->widget->bindas == sc->chans[pdevinfo->play].as) || - (pdevinfo->rec >= 0 && - ctl->widget->bindas == sc->chans[pdevinfo->rec].as) || - (ctl->widget->bindas == -2 && pdevinfo->index == 0))) - continue; - if ((ctl->ossmask & (1 << j)) == 0) - continue; - - if (printed == 0) { - device_printf(pdevinfo->dev, "\n"); - if (banner != NULL) { - device_printf(pdevinfo->dev, "%s", banner); - } else { - device_printf(pdevinfo->dev, "Unknown Ctl"); - } - printf(" (OSS: %s)\n", - hdac_audio_ctl_ossmixer_mask2allname(1 << j, - buf, sizeof(buf))); - device_printf(pdevinfo->dev, " |\n"); - printed = 1; - } - device_printf(pdevinfo->dev, " +- ctl %2d (nid %3d %s", i, - ctl->widget->nid, - (ctl->ndir == HDA_CTL_IN)?"in ":"out"); - if (ctl->ndir == HDA_CTL_IN && ctl->ndir == ctl->dir) - printf(" %2d): ", ctl->index); - else - printf("): "); - if (ctl->step > 0) { - printf("%+d/%+ddB (%d steps)%s\n", - (0 - ctl->offset) * (ctl->size + 1) / 4, - (ctl->step - ctl->offset) * (ctl->size + 1) / 4, - ctl->step + 1, - ctl->mute?" + mute":""); - } else - printf("%s\n", ctl->mute?"mute":""); - } - } -} - -static void -hdac_dump_audio_formats(device_t dev, uint32_t fcap, uint32_t pcmcap) -{ - uint32_t cap; - - cap = fcap; - if (cap != 0) { - device_printf(dev, " Stream cap: 0x%08x\n", cap); - device_printf(dev, " "); - if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) - printf(" AC3"); - if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap)) - printf(" FLOAT32"); - if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap)) - printf(" PCM"); - printf("\n"); - } - cap = pcmcap; - if (cap != 0) { - device_printf(dev, " PCM cap: 0x%08x\n", cap); - device_printf(dev, " "); - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(cap)) - printf(" 8"); - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(cap)) - printf(" 16"); - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(cap)) - printf(" 20"); - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(cap)) - printf(" 24"); - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(cap)) - printf(" 32"); - printf(" bits,"); - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(cap)) - printf(" 8"); - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(cap)) - printf(" 11"); - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(cap)) - printf(" 16"); - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(cap)) - printf(" 22"); - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(cap)) - printf(" 32"); - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(cap)) - printf(" 44"); - printf(" 48"); - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(cap)) - printf(" 88"); - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(cap)) - printf(" 96"); - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(cap)) - printf(" 176"); - if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(cap)) - printf(" 192"); - printf(" KHz\n"); - } -} - -static void -hdac_dump_pin(struct hdac_softc *sc, struct hdac_widget *w) -{ - uint32_t pincap; - - pincap = w->wclass.pin.cap; - - device_printf(sc->dev, " Pin cap: 0x%08x\n", pincap); - device_printf(sc->dev, " "); - if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap)) - printf(" ISC"); - if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) - printf(" TRQD"); - if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) - printf(" PDC"); - if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)) - printf(" HP"); - if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)) - printf(" OUT"); - if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) - printf(" IN"); - if (HDA_PARAM_PIN_CAP_BALANCED_IO_PINS(pincap)) - printf(" BAL"); - if (HDA_PARAM_PIN_CAP_HDMI(pincap)) - printf(" HDMI"); - if (HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)) { - printf(" VREF["); - if (HDA_PARAM_PIN_CAP_VREF_CTRL_50(pincap)) - printf(" 50"); - if (HDA_PARAM_PIN_CAP_VREF_CTRL_80(pincap)) - printf(" 80"); - if (HDA_PARAM_PIN_CAP_VREF_CTRL_100(pincap)) - printf(" 100"); - if (HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND(pincap)) - printf(" GROUND"); - if (HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ(pincap)) - printf(" HIZ"); - printf(" ]"); - } - if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) - printf(" EAPD"); - if (HDA_PARAM_PIN_CAP_DP(pincap)) - printf(" DP"); - if (HDA_PARAM_PIN_CAP_HBR(pincap)) - printf(" HBR"); - printf("\n"); - device_printf(sc->dev, " Pin config: 0x%08x\n", - w->wclass.pin.config); - device_printf(sc->dev, " Pin control: 0x%08x", w->wclass.pin.ctrl); - if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE) - printf(" HP"); - if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE) - printf(" IN"); - if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE) - printf(" OUT"); - if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) - printf(" VREFs"); - printf("\n"); -} - -static void -hdac_dump_pin_config(struct hdac_widget *w, uint32_t conf) -{ - struct hdac_softc *sc = w->devinfo->codec->sc; - - device_printf(sc->dev, " nid %d 0x%08x as %2d seq %2d %13s %5s " - "jack %2d loc %2d color %7s misc %d%s\n", - w->nid, conf, - HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf), - HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf), - HDA_DEVS[HDA_CONFIG_DEFAULTCONF_DEVICE(conf)], - HDA_CONNS[HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf)], - HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf), - HDA_CONFIG_DEFAULTCONF_LOCATION(conf), - HDA_COLORS[HDA_CONFIG_DEFAULTCONF_COLOR(conf)], - HDA_CONFIG_DEFAULTCONF_MISC(conf), - (w->enable == 0)?" [DISABLED]":""); -} - -static void -hdac_dump_pin_configs(struct hdac_devinfo *devinfo) -{ - struct hdac_widget *w; - int i; - - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - hdac_dump_pin_config(w, w->wclass.pin.config); - } -} - -static void -hdac_dump_amp(struct hdac_softc *sc, uint32_t cap, char *banner) -{ - device_printf(sc->dev, " %s amp: 0x%08x\n", banner, cap); - device_printf(sc->dev, " " - "mute=%d step=%d size=%d offset=%d\n", - HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(cap), - HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(cap), - HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(cap), - HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(cap)); -} - -static void -hdac_dump_nodes(struct hdac_devinfo *devinfo) -{ - struct hdac_softc *sc = devinfo->codec->sc; - static char *ossname[] = SOUND_DEVICE_NAMES; - struct hdac_widget *w, *cw; - char buf[64]; - int i, j; - - device_printf(sc->dev, "\n"); - device_printf(sc->dev, "Default Parameter\n"); - device_printf(sc->dev, "-----------------\n"); - hdac_dump_audio_formats(sc->dev, - devinfo->function.audio.supp_stream_formats, - devinfo->function.audio.supp_pcm_size_rate); - device_printf(sc->dev, " IN amp: 0x%08x\n", - devinfo->function.audio.inamp_cap); - device_printf(sc->dev, " OUT amp: 0x%08x\n", - devinfo->function.audio.outamp_cap); - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL) { - device_printf(sc->dev, "Ghost widget nid=%d\n", i); - continue; - } - device_printf(sc->dev, "\n"); - device_printf(sc->dev, " nid: %d%s\n", w->nid, - (w->enable == 0) ? " [DISABLED]" : ""); - device_printf(sc->dev, " Name: %s\n", w->name); - device_printf(sc->dev, " Widget cap: 0x%08x\n", - w->param.widget_cap); - if (w->param.widget_cap & 0x0ee1) { - device_printf(sc->dev, " "); - if (HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP(w->param.widget_cap)) - printf(" LRSWAP"); - if (HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(w->param.widget_cap)) - printf(" PWR"); - if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) - printf(" DIGITAL"); - if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) - printf(" UNSOL"); - if (HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET(w->param.widget_cap)) - printf(" PROC"); - if (HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(w->param.widget_cap)) - printf(" STRIPE"); - j = HDA_PARAM_AUDIO_WIDGET_CAP_CC(w->param.widget_cap); - if (j == 1) - printf(" STEREO"); - else if (j > 1) - printf(" %dCH", j + 1); - printf("\n"); - } - if (w->bindas != -1) { - device_printf(sc->dev, " Association: %d (0x%08x)\n", - w->bindas, w->bindseqmask); - } - if (w->ossmask != 0 || w->ossdev >= 0) { - device_printf(sc->dev, " OSS: %s", - hdac_audio_ctl_ossmixer_mask2allname(w->ossmask, buf, sizeof(buf))); - if (w->ossdev >= 0) - printf(" (%s)", ossname[w->ossdev]); - printf("\n"); - } - if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || - w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { - hdac_dump_audio_formats(sc->dev, - w->param.supp_stream_formats, - w->param.supp_pcm_size_rate); - } else if (w->type == - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - hdac_dump_pin(sc, w); - if (w->param.eapdbtl != HDAC_INVALID) - device_printf(sc->dev, " EAPD: 0x%08x\n", - w->param.eapdbtl); - if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(w->param.widget_cap) && - w->param.outamp_cap != 0) - hdac_dump_amp(sc, w->param.outamp_cap, "Output"); - if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(w->param.widget_cap) && - w->param.inamp_cap != 0) - hdac_dump_amp(sc, w->param.inamp_cap, " Input"); - if (w->nconns > 0) { - device_printf(sc->dev, " connections: %d\n", w->nconns); - device_printf(sc->dev, " |\n"); - } - for (j = 0; j < w->nconns; j++) { - cw = hdac_widget_get(devinfo, w->conns[j]); - device_printf(sc->dev, " + %s<- nid=%d [%s]", - (w->connsenable[j] == 0)?"[DISABLED] ":"", - w->conns[j], (cw == NULL) ? "GHOST!" : cw->name); - if (cw == NULL) - printf(" [UNKNOWN]"); - else if (cw->enable == 0) - printf(" [DISABLED]"); - if (w->nconns > 1 && w->selconn == j && w->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) - printf(" (selected)"); - printf("\n"); - } - } - -} - -static void -hdac_dump_dst_nid(struct hdac_pcm_devinfo *pdevinfo, nid_t nid, int depth) -{ - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_widget *w, *cw; - char buf[64]; - int i, printed = 0; - - if (depth > HDA_PARSE_MAXDEPTH) - return; - - w = hdac_widget_get(devinfo, nid); - if (w == NULL || w->enable == 0) - return; - - if (depth == 0) - device_printf(pdevinfo->dev, "%*s", 4, ""); - else - device_printf(pdevinfo->dev, "%*s + <- ", 4 + (depth - 1) * 7, ""); - printf("nid=%d [%s]", w->nid, w->name); - - if (depth > 0) { - if (w->ossmask == 0) { - printf("\n"); - return; - } - printf(" [src: %s]", - hdac_audio_ctl_ossmixer_mask2allname( - w->ossmask, buf, sizeof(buf))); - if (w->ossdev >= 0) { - printf("\n"); - return; - } - } - printf("\n"); - - for (i = 0; i < w->nconns; i++) { - if (w->connsenable[i] == 0) - continue; - cw = hdac_widget_get(devinfo, w->conns[i]); - if (cw == NULL || cw->enable == 0 || cw->bindas == -1) - continue; - if (printed == 0) { - device_printf(pdevinfo->dev, "%*s |\n", 4 + (depth) * 7, ""); - printed = 1; - } - hdac_dump_dst_nid(pdevinfo, w->conns[i], depth + 1); - } - -} - -static void -hdac_dump_dac(struct hdac_pcm_devinfo *pdevinfo) -{ - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_audio_as *as; - struct hdac_widget *w; - int i, printed = 0; - - if (pdevinfo->play < 0) - return; - - as = &devinfo->function.audio.as[sc->chans[pdevinfo->play].as]; - for (i = 0; i < 16; i++) { - if (as->pins[i] <= 0) - continue; - w = hdac_widget_get(devinfo, as->pins[i]); - if (w == NULL || w->enable == 0) - continue; - if (printed == 0) { - printed = 1; - device_printf(pdevinfo->dev, "\n"); - device_printf(pdevinfo->dev, "Playback:\n"); - } - device_printf(pdevinfo->dev, "\n"); - hdac_dump_dst_nid(pdevinfo, as->pins[i], 0); - } -} - -static void -hdac_dump_adc(struct hdac_pcm_devinfo *pdevinfo) -{ - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_softc *sc = devinfo->codec->sc; - struct hdac_widget *w; - int i; - int printed = 0; - - if (pdevinfo->rec < 0) - return; - - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) - continue; - if (w->bindas != sc->chans[pdevinfo->rec].as) - continue; - if (printed == 0) { - printed = 1; - device_printf(pdevinfo->dev, "\n"); - device_printf(pdevinfo->dev, "Record:\n"); - } - device_printf(pdevinfo->dev, "\n"); - hdac_dump_dst_nid(pdevinfo, i, 0); - } -} - -static void -hdac_dump_mix(struct hdac_pcm_devinfo *pdevinfo) -{ - struct hdac_devinfo *devinfo = pdevinfo->devinfo; - struct hdac_widget *w; - int i; - int printed = 0; - - if (pdevinfo->index != 0) - return; - - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->enable == 0) - continue; - if (w->ossdev != SOUND_MIXER_IMIX) - continue; - if (printed == 0) { - printed = 1; - device_printf(pdevinfo->dev, "\n"); - device_printf(pdevinfo->dev, "Input Mix:\n"); - } - device_printf(pdevinfo->dev, "\n"); - hdac_dump_dst_nid(pdevinfo, i, 0); - } -} - -static void -hdac_dump_pcmchannels(struct hdac_pcm_devinfo *pdevinfo) -{ - struct hdac_softc *sc = pdevinfo->devinfo->codec->sc; - nid_t *nids; - int i; - - if (pdevinfo->play >= 0) { - i = pdevinfo->play; - device_printf(pdevinfo->dev, "\n"); - device_printf(pdevinfo->dev, "Playback:\n"); - device_printf(pdevinfo->dev, "\n"); - hdac_dump_audio_formats(pdevinfo->dev, sc->chans[i].supp_stream_formats, - sc->chans[i].supp_pcm_size_rate); - device_printf(pdevinfo->dev, " DAC:"); - for (nids = sc->chans[i].io; *nids != -1; nids++) - printf(" %d", *nids); - printf("\n"); - } - if (pdevinfo->rec >= 0) { - i = pdevinfo->rec; - device_printf(pdevinfo->dev, "\n"); - device_printf(pdevinfo->dev, "Record:\n"); - device_printf(pdevinfo->dev, "\n"); - hdac_dump_audio_formats(pdevinfo->dev, sc->chans[i].supp_stream_formats, - sc->chans[i].supp_pcm_size_rate); - device_printf(pdevinfo->dev, " ADC:"); - for (nids = sc->chans[i].io; *nids != -1; nids++) - printf(" %d", *nids); - printf("\n"); - } -} - -static void -hdac_release_resources(struct hdac_softc *sc) -{ - int i, j; - - if (sc == NULL) - return; - - hdac_lock(sc); - sc->polling = 0; - sc->poll_ival = 0; - callout_stop(&sc->poll_hda); - callout_stop(&sc->poll_hdac); - callout_stop(&sc->poll_jack); - hdac_reset(sc, 0); - hdac_unlock(sc); - taskqueue_drain(taskqueue_thread, &sc->unsolq_task); - callout_drain(&sc->poll_hda); - callout_drain(&sc->poll_hdac); - callout_drain(&sc->poll_jack); - - hdac_irq_free(sc); - - for (i = 0; i < HDAC_CODEC_MAX; i++) { - if (sc->codecs[i] == NULL) - continue; - for (j = 0; j < sc->codecs[i]->num_fgs; j++) { - free(sc->codecs[i]->fgs[j].widget, M_HDAC); - if (sc->codecs[i]->fgs[j].node_type == - HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { - free(sc->codecs[i]->fgs[j].function.audio.ctl, - M_HDAC); - free(sc->codecs[i]->fgs[j].function.audio.as, - M_HDAC); - free(sc->codecs[i]->fgs[j].function.audio.devs, - M_HDAC); - } + pollticks = 1; } - free(sc->codecs[i]->fgs, M_HDAC); - free(sc->codecs[i], M_HDAC); - sc->codecs[i] = NULL; - } - - hdac_dma_free(sc, &sc->pos_dma); - hdac_dma_free(sc, &sc->rirb_dma); - hdac_dma_free(sc, &sc->corb_dma); - for (i = 0; i < sc->num_chans; i++) { - if (sc->chans[i].blkcnt > 0) - hdac_dma_free(sc, &sc->chans[i].bdl_dma); - } - free(sc->chans, M_HDAC); - if (sc->chan_dmat != NULL) { - bus_dma_tag_destroy(sc->chan_dmat); - sc->chan_dmat = NULL; + if (min > pollticks) + min = pollticks; } - hdac_mem_free(sc); - snd_mtxfree(sc->lock); -} - -/* This function surely going to make its way into upper level someday. */ -static void -hdac_config_fetch(struct hdac_softc *sc, uint32_t *on, uint32_t *off) -{ - const char *res = NULL; - int i = 0, j, k, len, inv; - - if (on != NULL) - *on = 0; - if (off != NULL) - *off = 0; - if (sc == NULL) - return; - if (resource_string_value(device_get_name(sc->dev), - device_get_unit(sc->dev), "config", &res) != 0) - return; - if (!(res != NULL && strlen(res) > 0)) - return; HDA_BOOTVERBOSE( - device_printf(sc->dev, "HDA Config:"); + device_printf(sc->dev, + "poll interval %d -> %d ticks\n", + sc->poll_ival, min); ); - for (;;) { - while (res[i] != '\0' && - (res[i] == ',' || isspace(res[i]) != 0)) - i++; - if (res[i] == '\0') { - HDA_BOOTVERBOSE( - printf("\n"); - ); - return; - } - j = i; - while (res[j] != '\0' && - !(res[j] == ',' || isspace(res[j]) != 0)) - j++; - len = j - i; - if (len > 2 && strncmp(res + i, "no", 2) == 0) - inv = 2; - else - inv = 0; - for (k = 0; len > inv && k < HDAC_QUIRKS_TAB_LEN; k++) { - if (strncmp(res + i + inv, - hdac_quirks_tab[k].key, len - inv) != 0) - continue; - if (len - inv != strlen(hdac_quirks_tab[k].key)) - continue; - HDA_BOOTVERBOSE( - printf(" %s%s", (inv != 0) ? "no" : "", - hdac_quirks_tab[k].key); - ); - if (inv == 0 && on != NULL) - *on |= hdac_quirks_tab[k].value; - else if (inv != 0 && off != NULL) - *off |= hdac_quirks_tab[k].value; - break; - } - i = j; - } + sc->poll_ival = min; + if (min == 1000000) + callout_stop(&sc->poll_callout); + else + callout_reset(&sc->poll_callout, 1, hdac_poll_callback, sc); } static int @@ -7512,11 +1409,9 @@ sysctl_hdac_polling(SYSCTL_HANDLER_ARGS) hdac_lock(sc); if (val != sc->polling) { if (val == 0) { - callout_stop(&sc->poll_hda); - callout_stop(&sc->poll_hdac); + callout_stop(&sc->poll_callout); hdac_unlock(sc); - callout_drain(&sc->poll_hda); - callout_drain(&sc->poll_hdac); + callout_drain(&sc->poll_callout); hdac_lock(sc); sc->polling = 0; ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); @@ -7526,12 +1421,8 @@ sysctl_hdac_polling(SYSCTL_HANDLER_ARGS) ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); ctl &= ~HDAC_INTCTL_GIE; HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); - hdac_unlock(sc); - taskqueue_drain(taskqueue_thread, &sc->unsolq_task); - hdac_lock(sc); sc->polling = 1; hdac_poll_reinit(sc); - callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); } } hdac_unlock(sc); @@ -7539,202 +1430,17 @@ sysctl_hdac_polling(SYSCTL_HANDLER_ARGS) return (err); } -static int -sysctl_hdac_polling_interval(SYSCTL_HANDLER_ARGS) -{ - struct hdac_softc *sc; - device_t dev; - int err, val; - - dev = oidp->oid_arg1; - sc = device_get_softc(dev); - if (sc == NULL) - return (EINVAL); - hdac_lock(sc); - val = ((uint64_t)sc->poll_ival * 1000) / hz; - hdac_unlock(sc); - err = sysctl_handle_int(oidp, &val, 0, req); - - if (err != 0 || req->newptr == NULL) - return (err); - - if (val < 1) - val = 1; - if (val > 5000) - val = 5000; - val = ((uint64_t)val * hz) / 1000; - if (val < 1) - val = 1; - if (val > (hz * 5)) - val = hz * 5; - - hdac_lock(sc); - sc->poll_ival = val; - hdac_unlock(sc); - - return (err); -} - -static int -sysctl_hdac_pindump(SYSCTL_HANDLER_ARGS) -{ - struct hdac_softc *sc; - struct hdac_codec *codec; - struct hdac_devinfo *devinfo; - struct hdac_widget *w; - device_t dev; - uint32_t res, pincap, delay; - int codec_index, fg_index; - int i, err, val; - nid_t cad; - - dev = oidp->oid_arg1; - sc = device_get_softc(dev); - if (sc == NULL) - return (EINVAL); - val = 0; - err = sysctl_handle_int(oidp, &val, 0, req); - if (err != 0 || req->newptr == NULL || val == 0) - return (err); - - /* XXX: Temporary. For debugging. */ - if (val == 100) { - hdac_suspend(dev); - return (0); - } else if (val == 101) { - hdac_resume(dev); - return (0); - } - - hdac_lock(sc); - for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { - codec = sc->codecs[codec_index]; - if (codec == NULL) - continue; - cad = codec->cad; - for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { - devinfo = &codec->fgs[fg_index]; - if (devinfo->node_type != - HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) - continue; - - device_printf(dev, "Dumping AFG cad=%d nid=%d pins:\n", - codec_index, devinfo->nid); - for (i = devinfo->startnode; i < devinfo->endnode; i++) { - w = hdac_widget_get(devinfo, i); - if (w == NULL || w->type != - HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) - continue; - hdac_dump_pin_config(w, w->wclass.pin.config); - pincap = w->wclass.pin.cap; - device_printf(dev, " Caps: %2s %3s %2s %4s %4s", - HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)?"IN":"", - HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)?"OUT":"", - HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)?"HP":"", - HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)?"EAPD":"", - HDA_PARAM_PIN_CAP_VREF_CTRL(pincap)?"VREF":""); - if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap) || - HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) { - if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) { - delay = 0; - hdac_command(sc, - HDA_CMD_SET_PIN_SENSE(cad, w->nid, 0), cad); - do { - res = hdac_command(sc, - HDA_CMD_GET_PIN_SENSE(cad, w->nid), cad); - if (res != 0x7fffffff && res != 0xffffffff) - break; - DELAY(10); - } while (++delay < 10000); - } else { - delay = 0; - res = hdac_command(sc, HDA_CMD_GET_PIN_SENSE(cad, - w->nid), cad); - } - printf(" Sense: 0x%08x", res); - if (delay > 0) - printf(" delay %dus", delay * 10); - } - printf("\n"); - } - device_printf(dev, - "NumGPIO=%d NumGPO=%d NumGPI=%d GPIWake=%d GPIUnsol=%d\n", - HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_GPI_WAKE(devinfo->function.audio.gpio), - HDA_PARAM_GPIO_COUNT_GPI_UNSOL(devinfo->function.audio.gpio)); - if (HDA_PARAM_GPIO_COUNT_NUM_GPI(devinfo->function.audio.gpio) > 0) { - device_printf(dev, " GPI:"); - res = hdac_command(sc, - HDA_CMD_GET_GPI_DATA(cad, devinfo->nid), cad); - printf(" data=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPI_WAKE_ENABLE_MASK(cad, devinfo->nid), - cad); - printf(" wake=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPI_UNSOLICITED_ENABLE_MASK(cad, devinfo->nid), - cad); - printf(" unsol=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPI_STICKY_MASK(cad, devinfo->nid), cad); - printf(" sticky=0x%08x\n", res); - } - if (HDA_PARAM_GPIO_COUNT_NUM_GPO(devinfo->function.audio.gpio) > 0) { - device_printf(dev, " GPO:"); - res = hdac_command(sc, - HDA_CMD_GET_GPO_DATA(cad, devinfo->nid), cad); - printf(" data=0x%08x\n", res); - } - if (HDA_PARAM_GPIO_COUNT_NUM_GPIO(devinfo->function.audio.gpio) > 0) { - device_printf(dev, "GPIO:"); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_DATA(cad, devinfo->nid), cad); - printf(" data=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_ENABLE_MASK(cad, devinfo->nid), cad); - printf(" enable=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_DIRECTION(cad, devinfo->nid), cad); - printf(" direction=0x%08x\n", res); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_WAKE_ENABLE_MASK(cad, devinfo->nid), cad); - device_printf(dev, " wake=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_UNSOLICITED_ENABLE_MASK(cad, devinfo->nid), - cad); - printf(" unsol=0x%08x", res); - res = hdac_command(sc, - HDA_CMD_GET_GPIO_STICKY_MASK(cad, devinfo->nid), cad); - printf(" sticky=0x%08x\n", res); - } - } - } - hdac_unlock(sc); - return (0); -} - static void hdac_attach2(void *arg) { - struct hdac_codec *codec; struct hdac_softc *sc; - struct hdac_audio_ctl *ctl; - uint32_t quirks_on, quirks_off; - int codec_index, fg_index; - int i, dmaalloc = 0; - struct hdac_devinfo *devinfo; + device_t child; + uint32_t vendorid, revisionid; + int i; + uint16_t statests; sc = (struct hdac_softc *)arg; - hdac_config_fetch(sc, &quirks_on, &quirks_off); - - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "HDA Config: on=0x%08x off=0x%08x\n", - quirks_on, quirks_off); - ); - hdac_lock(sc); /* Remove ourselves from the config hooks */ @@ -7743,7 +1449,6 @@ hdac_attach2(void *arg) sc->intrhook.ich_func = NULL; } - /* Start the corb and rirb engines */ HDA_BOOTHVERBOSE( device_printf(sc->dev, "Starting CORB Engine...\n"); ); @@ -7752,7 +1457,6 @@ hdac_attach2(void *arg) device_printf(sc->dev, "Starting RIRB Engine...\n"); ); hdac_rirb_start(sc); - HDA_BOOTHVERBOSE( device_printf(sc->dev, "Enabling controller interrupt...\n"); @@ -7762,218 +1466,60 @@ hdac_attach2(void *arg) if (sc->polling == 0) { HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); - } else { - callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); } DELAY(1000); HDA_BOOTHVERBOSE( - device_printf(sc->dev, - "Scanning HDA codecs ...\n"); + device_printf(sc->dev, "Scanning HDA codecs ...\n"); ); - hdac_scan_codecs(sc); - - for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { - codec = sc->codecs[codec_index]; - if (codec == NULL) - continue; - for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { - devinfo = &codec->fgs[fg_index]; - HDA_BOOTVERBOSE( - device_printf(sc->dev, "\n"); + statests = HDAC_READ_2(&sc->mem, HDAC_STATESTS); + hdac_unlock(sc); + for (i = 0; i < HDAC_CODEC_MAX; i++) { + if (HDAC_STATESTS_SDIWAKE(statests, i)) { + HDA_BOOTHVERBOSE( device_printf(sc->dev, - "Processing %s FG cad=%d nid=%d...\n", - (devinfo->node_type == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) ? "audio": - (devinfo->node_type == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MODEM) ? "modem": - "unknown", - devinfo->codec->cad, devinfo->nid); + "Found CODEC at address %d\n", i); ); - if (devinfo->node_type != - HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { - HDA_BOOTHVERBOSE( - device_printf(sc->dev, - "Powering down...\n"); - ); - hdac_command(sc, - HDA_CMD_SET_POWER_STATE(codec->cad, - devinfo->nid, HDA_CMD_POWER_STATE_D3), - codec->cad); + hdac_lock(sc); + vendorid = hdac_send_command(sc, i, + HDA_CMD_GET_PARAMETER(0, 0x0, HDA_PARAM_VENDOR_ID)); + revisionid = hdac_send_command(sc, i, + HDA_CMD_GET_PARAMETER(0, 0x0, HDA_PARAM_REVISION_ID)); + hdac_unlock(sc); + if (vendorid == HDA_INVALID && + revisionid == HDA_INVALID) { + device_printf(sc->dev, + "CODEC is not responding!\n"); continue; } - - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Powering up...\n"); - ); - hdac_powerup(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Parsing audio FG...\n"); - ); - hdac_audio_parse(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Parsing vendor patch...\n"); - ); - hdac_vendor_patch_parse(devinfo); - devinfo->function.audio.quirks |= quirks_on; - devinfo->function.audio.quirks &= ~quirks_off; - - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Parsing Ctls...\n"); - ); - hdac_audio_ctl_parse(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Disabling nonaudio...\n"); - ); - hdac_audio_disable_nonaudio(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Disabling useless...\n"); - ); - hdac_audio_disable_useless(devinfo); - HDA_BOOTVERBOSE( - device_printf(sc->dev, "Patched pins configuration:\n"); - hdac_dump_pin_configs(devinfo); - ); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Parsing pin associations...\n"); - ); - hdac_audio_as_parse(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Building AFG tree...\n"); - ); - hdac_audio_build_tree(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Disabling unassociated " - "widgets...\n"); - ); - hdac_audio_disable_unas(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Disabling nonselected " - "inputs...\n"); - ); - hdac_audio_disable_notselected(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Disabling useless...\n"); - ); - hdac_audio_disable_useless(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Disabling " - "crossassociatement connections...\n"); - ); - hdac_audio_disable_crossas(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Disabling useless...\n"); - ); - hdac_audio_disable_useless(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Binding associations to channels...\n"); - ); - hdac_audio_bind_as(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Assigning names to signal sources...\n"); - ); - hdac_audio_assign_names(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Assigning mixers to the tree...\n"); - ); - hdac_audio_assign_mixers(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Preparing pin controls...\n"); - ); - hdac_audio_prepare_pin_ctrl(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "AFG commit...\n"); - ); - hdac_audio_commit(devinfo); - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "HP switch init...\n"); - ); - hdac_hp_switch_init(devinfo); - - if ((devinfo->function.audio.quirks & HDA_QUIRK_DMAPOS) && - dmaalloc == 0) { - if (hdac_dma_alloc(sc, &sc->pos_dma, - (sc->num_iss + sc->num_oss + sc->num_bss) * 8) != 0) { - HDA_BOOTVERBOSE( - device_printf(sc->dev, "Failed to " - "allocate DMA pos buffer " - "(non-fatal)\n"); - ); - } else - dmaalloc = 1; + sc->codecs[i].vendor_id = + HDA_PARAM_VENDOR_ID_VENDOR_ID(vendorid); + sc->codecs[i].device_id = + HDA_PARAM_VENDOR_ID_DEVICE_ID(vendorid); + sc->codecs[i].revision_id = + HDA_PARAM_REVISION_ID_REVISION_ID(revisionid); + sc->codecs[i].stepping_id = + HDA_PARAM_REVISION_ID_STEPPING_ID(revisionid); + child = device_add_child(sc->dev, "hdacc", -1); + if (child == NULL) { + device_printf(sc->dev, + "Failed to add CODEC device\n"); + continue; } - - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "Creating PCM devices...\n"); - ); - hdac_create_pcms(devinfo); - - HDA_BOOTVERBOSE( - if (devinfo->function.audio.quirks != 0) { - device_printf(sc->dev, "FG config/quirks:"); - for (i = 0; i < HDAC_QUIRKS_TAB_LEN; i++) { - if ((devinfo->function.audio.quirks & - hdac_quirks_tab[i].value) == - hdac_quirks_tab[i].value) - printf(" %s", hdac_quirks_tab[i].key); - } - printf("\n"); - } - - device_printf(sc->dev, "\n"); - device_printf(sc->dev, "+-------------------+\n"); - device_printf(sc->dev, "| DUMPING HDA NODES |\n"); - device_printf(sc->dev, "+-------------------+\n"); - hdac_dump_nodes(devinfo); - ); - - HDA_BOOTHVERBOSE( - device_printf(sc->dev, "\n"); - device_printf(sc->dev, "+------------------------+\n"); - device_printf(sc->dev, "| DUMPING HDA AMPLIFIERS |\n"); - device_printf(sc->dev, "+------------------------+\n"); - device_printf(sc->dev, "\n"); - i = 0; - while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { - device_printf(sc->dev, "%3d: nid %3d %s (%s) index %d", i, - (ctl->widget != NULL) ? ctl->widget->nid : -1, - (ctl->ndir == HDA_CTL_IN)?"in ":"out", - (ctl->dir == HDA_CTL_IN)?"in ":"out", - ctl->index); - if (ctl->childwidget != NULL) - printf(" cnid %3d", ctl->childwidget->nid); - else - printf(" "); - printf(" ossmask=0x%08x\n", - ctl->ossmask); - device_printf(sc->dev, - " mute: %d step: %3d size: %3d off: %3d%s\n", - ctl->mute, ctl->step, ctl->size, ctl->offset, - (ctl->enable == 0) ? " [DISABLED]" : - ((ctl->ossmask == 0) ? " [UNUSED]" : "")); - } - ); + device_set_ivars(child, (void *)(intptr_t)i); + sc->codecs[i].dev = child; } } - hdac_unlock(sc); - - HDA_BOOTVERBOSE( - device_printf(sc->dev, "\n"); - ); - bus_generic_attach(sc->dev); SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, - "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), - sysctl_hdac_polling, "I", "Enable polling mode"); - SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, - "polling_interval", CTLTYPE_INT | CTLFLAG_RW, sc->dev, - sizeof(sc->dev), sysctl_hdac_polling_interval, "I", - "Controller/Jack Sense polling interval (1-1000 ms)"); - SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), - SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "pindump", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), sysctl_hdac_pindump, "I", "Dump pin states/data"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, + "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), + sysctl_hdac_polling, "I", "Enable polling mode"); } /**************************************************************************** @@ -7984,64 +1530,23 @@ hdac_attach2(void *arg) static int hdac_suspend(device_t dev) { - struct hdac_softc *sc; - struct hdac_codec *codec; - struct hdac_devinfo *devinfo; - int codec_index, fg_index, i; + struct hdac_softc *sc = device_get_softc(dev); HDA_BOOTHVERBOSE( device_printf(dev, "Suspend...\n"); ); + bus_generic_suspend(dev); - sc = device_get_softc(dev); hdac_lock(sc); - - HDA_BOOTHVERBOSE( - device_printf(dev, "Stop streams...\n"); - ); - for (i = 0; i < sc->num_chans; i++) { - if (sc->chans[i].flags & HDAC_CHN_RUNNING) { - sc->chans[i].flags |= HDAC_CHN_SUSPEND; - hdac_channel_stop(sc, &sc->chans[i]); - } - } - - for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { - codec = sc->codecs[codec_index]; - if (codec == NULL) - continue; - for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { - devinfo = &codec->fgs[fg_index]; - HDA_BOOTHVERBOSE( - device_printf(dev, - "Power down FG" - " cad=%d nid=%d to the D3 state...\n", - codec->cad, devinfo->nid); - ); - hdac_command(sc, - HDA_CMD_SET_POWER_STATE(codec->cad, - devinfo->nid, HDA_CMD_POWER_STATE_D3), - codec->cad); - } - } - HDA_BOOTHVERBOSE( device_printf(dev, "Reset controller...\n"); ); - callout_stop(&sc->poll_hda); - callout_stop(&sc->poll_hdac); - callout_stop(&sc->poll_jack); hdac_reset(sc, 0); hdac_unlock(sc); taskqueue_drain(taskqueue_thread, &sc->unsolq_task); - callout_drain(&sc->poll_hda); - callout_drain(&sc->poll_hdac); - callout_drain(&sc->poll_jack); - HDA_BOOTHVERBOSE( device_printf(dev, "Suspend done\n"); ); - return (0); } @@ -8053,16 +1558,12 @@ hdac_suspend(device_t dev) static int hdac_resume(device_t dev) { - struct hdac_softc *sc; - struct hdac_codec *codec; - struct hdac_devinfo *devinfo; - int codec_index, fg_index, i; + struct hdac_softc *sc = device_get_softc(dev); + int error; HDA_BOOTHVERBOSE( device_printf(dev, "Resume...\n"); ); - - sc = device_get_softc(dev); hdac_lock(sc); /* Quiesce everything */ @@ -8075,7 +1576,6 @@ hdac_resume(device_t dev) hdac_corb_init(sc); hdac_rirb_init(sc); - /* Start the corb and rirb engines */ HDA_BOOTHVERBOSE( device_printf(dev, "Starting CORB Engine...\n"); ); @@ -8084,91 +1584,22 @@ hdac_resume(device_t dev) device_printf(dev, "Starting RIRB Engine...\n"); ); hdac_rirb_start(sc); - HDA_BOOTHVERBOSE( - device_printf(dev, - "Enabling controller interrupt...\n"); + device_printf(dev, "Enabling controller interrupt...\n"); ); HDAC_WRITE_4(&sc->mem, HDAC_GCTL, HDAC_READ_4(&sc->mem, HDAC_GCTL) | HDAC_GCTL_UNSOL); - if (sc->polling == 0) { - HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, - HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); - } else { - callout_reset(&sc->poll_hdac, 1, hdac_poll_callback, sc); - } + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); DELAY(1000); - - for (codec_index = 0; codec_index < HDAC_CODEC_MAX; codec_index++) { - codec = sc->codecs[codec_index]; - if (codec == NULL) - continue; - for (fg_index = 0; fg_index < codec->num_fgs; fg_index++) { - devinfo = &codec->fgs[fg_index]; - if (devinfo->node_type != - HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { - HDA_BOOTHVERBOSE( - device_printf(dev, - "Power down unsupported non-audio FG" - " cad=%d nid=%d to the D3 state...\n", - codec->cad, devinfo->nid); - ); - hdac_command(sc, - HDA_CMD_SET_POWER_STATE(codec->cad, - devinfo->nid, HDA_CMD_POWER_STATE_D3), - codec->cad); - continue; - } - - HDA_BOOTHVERBOSE( - device_printf(dev, - "Power up audio FG cad=%d nid=%d...\n", - devinfo->codec->cad, devinfo->nid); - ); - hdac_powerup(devinfo); - HDA_BOOTHVERBOSE( - device_printf(dev, "AFG commit...\n"); - ); - hdac_audio_commit(devinfo); - HDA_BOOTHVERBOSE( - device_printf(dev, "HP switch init...\n"); - ); - hdac_hp_switch_init(devinfo); - - hdac_unlock(sc); - for (i = 0; i < devinfo->function.audio.num_devs; i++) { - struct hdac_pcm_devinfo *pdevinfo = - &devinfo->function.audio.devs[i]; - HDA_BOOTHVERBOSE( - device_printf(pdevinfo->dev, - "OSS mixer reinitialization...\n"); - ); - if (mixer_reinit(pdevinfo->dev) == -1) - device_printf(pdevinfo->dev, - "unable to reinitialize the mixer\n"); - } - hdac_lock(sc); - } - } - - HDA_BOOTHVERBOSE( - device_printf(dev, "Start streams...\n"); - ); - for (i = 0; i < sc->num_chans; i++) { - if (sc->chans[i].flags & HDAC_CHN_SUSPEND) { - sc->chans[i].flags &= ~HDAC_CHN_SUSPEND; - hdac_channel_start(sc, &sc->chans[i]); - } - } - hdac_unlock(sc); + error = bus_generic_resume(dev); HDA_BOOTHVERBOSE( device_printf(dev, "Resume done\n"); ); - - return (0); + return (error); } + /**************************************************************************** * int hdac_detach(device_t) * @@ -8177,204 +1608,409 @@ hdac_resume(device_t dev) static int hdac_detach(device_t dev) { - struct hdac_softc *sc; + struct hdac_softc *sc = device_get_softc(dev); device_t *devlist; - int i, devcount, error; + int cad, i, devcount, error; if ((error = device_get_children(dev, &devlist, &devcount)) != 0) return (error); for (i = 0; i < devcount; i++) { + cad = (intptr_t)device_get_ivars(devlist[i]); if ((error = device_delete_child(dev, devlist[i])) != 0) { free(devlist, M_TEMP); return (error); } + sc->codecs[cad].dev = NULL; } free(devlist, M_TEMP); - sc = device_get_softc(dev); - hdac_release_resources(sc); + hdac_lock(sc); + hdac_reset(sc, 0); + hdac_unlock(sc); + taskqueue_drain(taskqueue_thread, &sc->unsolq_task); + hdac_irq_free(sc); + for (i = 0; i < sc->num_ss; i++) + hdac_dma_free(sc, &sc->streams[i].bdl); + free(sc->streams, M_HDAC); + hdac_dma_free(sc, &sc->pos_dma); + hdac_dma_free(sc, &sc->rirb_dma); + hdac_dma_free(sc, &sc->corb_dma); + if (sc->chan_dmat != NULL) { + bus_dma_tag_destroy(sc->chan_dmat); + sc->chan_dmat = NULL; + } + hdac_mem_free(sc); + snd_mtxfree(sc->lock); return (0); } +static bus_dma_tag_t +hdac_get_dma_tag(device_t dev, device_t child) +{ + struct hdac_softc *sc = device_get_softc(dev); + + return (sc->chan_dmat); +} + static int hdac_print_child(device_t dev, device_t child) { - struct hdac_pcm_devinfo *pdevinfo = - (struct hdac_pcm_devinfo *)device_get_ivars(child); int retval; retval = bus_print_child_header(dev, child); - retval += printf(" at cad %d nid %d", - pdevinfo->devinfo->codec->cad, pdevinfo->devinfo->nid); + retval += printf(" at cad %d", + (int)(intptr_t)device_get_ivars(child)); retval += bus_print_child_footer(dev, child); return (retval); } -static device_method_t hdac_methods[] = { - /* device interface */ - DEVMETHOD(device_probe, hdac_probe), - DEVMETHOD(device_attach, hdac_attach), - DEVMETHOD(device_detach, hdac_detach), - DEVMETHOD(device_suspend, hdac_suspend), - DEVMETHOD(device_resume, hdac_resume), - /* Bus interface */ - DEVMETHOD(bus_print_child, hdac_print_child), - { 0, 0 } -}; +static int +hdac_child_location_str(device_t dev, device_t child, char *buf, + size_t buflen) +{ -static driver_t hdac_driver = { - "hdac", - hdac_methods, - sizeof(struct hdac_softc), -}; + snprintf(buf, buflen, "cad=%d", + (int)(intptr_t)device_get_ivars(child)); + return (0); +} -static devclass_t hdac_devclass; +static int +hdac_child_pnpinfo_str_method(device_t dev, device_t child, char *buf, + size_t buflen) +{ + struct hdac_softc *sc = device_get_softc(dev); + nid_t cad = (uintptr_t)device_get_ivars(child); -DRIVER_MODULE(snd_hda, pci, hdac_driver, hdac_devclass, 0, 0); -MODULE_DEPEND(snd_hda, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); -MODULE_VERSION(snd_hda, 1); + snprintf(buf, buflen, "vendor=0x%04x device=0x%04x revision=0x%02x " + "stepping=0x%02x", + sc->codecs[cad].vendor_id, sc->codecs[cad].device_id, + sc->codecs[cad].revision_id, sc->codecs[cad].stepping_id); + return (0); +} static int -hdac_pcm_probe(device_t dev) +hdac_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { - struct hdac_pcm_devinfo *pdevinfo = - (struct hdac_pcm_devinfo *)device_get_ivars(dev); - char buf[128]; - - snprintf(buf, sizeof(buf), "HDA %s PCM #%d %s", - hdac_codec_name(pdevinfo->devinfo->codec), - pdevinfo->index, - (pdevinfo->digital == 3)?"DisplayPort": - ((pdevinfo->digital == 2)?"HDMI": - ((pdevinfo->digital)?"Digital":"Analog"))); - device_set_desc_copy(dev, buf); + struct hdac_softc *sc = device_get_softc(dev); + nid_t cad = (uintptr_t)device_get_ivars(child); + + switch (which) { + case HDA_IVAR_CODEC_ID: + *result = cad; + break; + case HDA_IVAR_VENDOR_ID: + *result = sc->codecs[cad].vendor_id; + break; + case HDA_IVAR_DEVICE_ID: + *result = sc->codecs[cad].device_id; + break; + case HDA_IVAR_REVISION_ID: + *result = sc->codecs[cad].revision_id; + break; + case HDA_IVAR_STEPPING_ID: + *result = sc->codecs[cad].stepping_id; + break; + case HDA_IVAR_SUBVENDOR_ID: + *result = pci_get_subvendor(dev); + break; + case HDA_IVAR_SUBDEVICE_ID: + *result = pci_get_subdevice(dev); + break; + case HDA_IVAR_DMA_NOCACHE: + *result = (sc->flags & HDAC_F_DMA_NOCACHE) != 0; + break; + default: + return (ENOENT); + } return (0); } -static int -hdac_pcm_attach(device_t dev) +static struct mtx * +hdac_get_mtx(device_t dev, device_t child) { - struct hdac_pcm_devinfo *pdevinfo = - (struct hdac_pcm_devinfo *)device_get_ivars(dev); - struct hdac_softc *sc = pdevinfo->devinfo->codec->sc; - char status[SND_STATUSLEN]; - int i; + struct hdac_softc *sc = device_get_softc(dev); - pdevinfo->chan_size = pcm_getbuffersize(dev, - HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_MAX); + return (sc->lock); +} - HDA_BOOTVERBOSE( - device_printf(dev, "+--------------------------------------+\n"); - device_printf(dev, "| DUMPING PCM Playback/Record Channels |\n"); - device_printf(dev, "+--------------------------------------+\n"); - hdac_dump_pcmchannels(pdevinfo); - device_printf(dev, "\n"); - device_printf(dev, "+-------------------------------+\n"); - device_printf(dev, "| DUMPING Playback/Record Paths |\n"); - device_printf(dev, "+-------------------------------+\n"); - hdac_dump_dac(pdevinfo); - hdac_dump_adc(pdevinfo); - hdac_dump_mix(pdevinfo); - device_printf(dev, "\n"); - device_printf(dev, "+-------------------------+\n"); - device_printf(dev, "| DUMPING Volume Controls |\n"); - device_printf(dev, "+-------------------------+\n"); - hdac_dump_ctls(pdevinfo, "Master Volume", SOUND_MASK_VOLUME); - hdac_dump_ctls(pdevinfo, "PCM Volume", SOUND_MASK_PCM); - hdac_dump_ctls(pdevinfo, "CD Volume", SOUND_MASK_CD); - hdac_dump_ctls(pdevinfo, "Microphone Volume", SOUND_MASK_MIC); - hdac_dump_ctls(pdevinfo, "Microphone2 Volume", SOUND_MASK_MONITOR); - hdac_dump_ctls(pdevinfo, "Line-in Volume", SOUND_MASK_LINE); - hdac_dump_ctls(pdevinfo, "Speaker/Beep Volume", SOUND_MASK_SPEAKER); - hdac_dump_ctls(pdevinfo, "Recording Level", SOUND_MASK_RECLEV); - hdac_dump_ctls(pdevinfo, "Input Mix Level", SOUND_MASK_IMIX); - hdac_dump_ctls(pdevinfo, "Input Monitoring Level", SOUND_MASK_IGAIN); - hdac_dump_ctls(pdevinfo, NULL, 0); - device_printf(dev, "\n"); - ); +static uint32_t +hdac_codec_command(device_t dev, device_t child, uint32_t verb) +{ - if (resource_int_value(device_get_name(dev), - device_get_unit(dev), "blocksize", &i) == 0 && i > 0) { - i &= HDA_BLK_ALIGN; - if (i < HDA_BLK_MIN) - i = HDA_BLK_MIN; - pdevinfo->chan_blkcnt = pdevinfo->chan_size / i; - i = 0; - while (pdevinfo->chan_blkcnt >> i) - i++; - pdevinfo->chan_blkcnt = 1 << (i - 1); - if (pdevinfo->chan_blkcnt < HDA_BDL_MIN) - pdevinfo->chan_blkcnt = HDA_BDL_MIN; - else if (pdevinfo->chan_blkcnt > HDA_BDL_MAX) - pdevinfo->chan_blkcnt = HDA_BDL_MAX; - } else - pdevinfo->chan_blkcnt = HDA_BDL_DEFAULT; - - /* - * We don't register interrupt handler with snd_setup_intr - * in pcm device. Mark pcm device as MPSAFE manually. - */ - pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); + return (hdac_send_command(device_get_softc(dev), + (intptr_t)device_get_ivars(child), verb)); +} - HDA_BOOTHVERBOSE( - device_printf(dev, "OSS mixer initialization...\n"); - ); - if (mixer_init(dev, &hdac_audio_ctl_ossmixer_class, pdevinfo) != 0) - device_printf(dev, "Can't register mixer\n"); +static int +hdac_find_stream(struct hdac_softc *sc, int dir, int stream) +{ + int i, ss; - HDA_BOOTHVERBOSE( - device_printf(dev, "Registering PCM channels...\n"); - ); - if (pcm_register(dev, pdevinfo, (pdevinfo->play >= 0)?1:0, - (pdevinfo->rec >= 0)?1:0) != 0) - device_printf(dev, "Can't register PCM\n"); + ss = -1; + /* Allocate ISS/BSS first. */ + if (dir == 0) { + for (i = 0; i < sc->num_iss; i++) { + if (sc->streams[i].stream == stream) { + ss = i; + break; + } + } + } else { + for (i = 0; i < sc->num_oss; i++) { + if (sc->streams[i + sc->num_iss].stream == stream) { + ss = i + sc->num_iss; + break; + } + } + } + /* Fallback to BSS. */ + if (ss == -1) { + for (i = 0; i < sc->num_bss; i++) { + if (sc->streams[i + sc->num_iss + sc->num_oss].stream + == stream) { + ss = i + sc->num_iss + sc->num_oss; + break; + } + } + } + return (ss); +} - pdevinfo->registered++; +static int +hdac_stream_alloc(device_t dev, device_t child, int dir, int format, + uint32_t **dmapos) +{ + struct hdac_softc *sc = device_get_softc(dev); + int stream, ss; - if (pdevinfo->play >= 0) - pcm_addchan(dev, PCMDIR_PLAY, &hdac_channel_class, pdevinfo); - if (pdevinfo->rec >= 0) - pcm_addchan(dev, PCMDIR_REC, &hdac_channel_class, pdevinfo); + /* Look for empty stream. */ + ss = hdac_find_stream(sc, dir, 0); - snprintf(status, SND_STATUSLEN, "at cad %d nid %d on %s %s", - pdevinfo->devinfo->codec->cad, pdevinfo->devinfo->nid, - device_get_nameunit(sc->dev), PCM_KLDSTRING(snd_hda)); - pcm_setstatus(dev, status); + /* Return if found nothing. */ + if (ss < 0) + return (0); - return (0); + /* Allocate stream number */ + if (ss >= sc->num_iss + sc->num_oss) + stream = 15 - (ss - sc->num_iss + sc->num_oss); + else if (ss >= sc->num_iss) + stream = ss - sc->num_iss + 1; + else + stream = ss + 1; + + sc->streams[ss].dev = child; + sc->streams[ss].dir = dir; + sc->streams[ss].stream = stream; + sc->streams[ss].format = format; + if (dmapos != NULL) { + if (sc->pos_dma.dma_vaddr != NULL) + *dmapos = (uint32_t *)(sc->pos_dma.dma_vaddr + ss * 8); + else + *dmapos = NULL; + } + return (stream); +} + +static void +hdac_stream_free(device_t dev, device_t child, int dir, int stream) +{ + struct hdac_softc *sc = device_get_softc(dev); + int ss; + + ss = hdac_find_stream(sc, dir, stream); + KASSERT(ss >= 0, + ("Free for not allocated stream (%d/%d)\n", dir, stream)); + sc->streams[ss].stream = 0; + sc->streams[ss].dev = NULL; } static int -hdac_pcm_detach(device_t dev) +hdac_stream_start(device_t dev, device_t child, + int dir, int stream, bus_addr_t buf, int blksz, int blkcnt) { - struct hdac_pcm_devinfo *pdevinfo = - (struct hdac_pcm_devinfo *)device_get_ivars(dev); - int err; - - if (pdevinfo->registered > 0) { - err = pcm_unregister(dev); - if (err != 0) - return (err); + struct hdac_softc *sc = device_get_softc(dev); + struct hdac_bdle *bdle; + uint64_t addr; + int i, ss, off; + uint32_t ctl; + + ss = hdac_find_stream(sc, dir, stream); + KASSERT(ss >= 0, + ("Start for not allocated stream (%d/%d)\n", dir, stream)); + + addr = (uint64_t)buf; + bdle = (struct hdac_bdle *)sc->streams[ss].bdl.dma_vaddr; + for (i = 0; i < blkcnt; i++, bdle++) { + bdle->addrl = (uint32_t)addr; + bdle->addrh = (uint32_t)(addr >> 32); + bdle->len = blksz; + bdle->ioc = 1; + addr += blksz; } + off = ss << 5; + HDAC_WRITE_4(&sc->mem, off + HDAC_SDCBL, blksz * blkcnt); + HDAC_WRITE_2(&sc->mem, off + HDAC_SDLVI, blkcnt - 1); + addr = sc->streams[ss].bdl.dma_paddr; + HDAC_WRITE_4(&sc->mem, off + HDAC_SDBDPL, (uint32_t)addr); + HDAC_WRITE_4(&sc->mem, off + HDAC_SDBDPU, (uint32_t)(addr >> 32)); + + ctl = HDAC_READ_1(&sc->mem, off + HDAC_SDCTL2); + if (dir) + ctl |= HDAC_SDCTL2_DIR; + else + ctl &= ~HDAC_SDCTL2_DIR; + ctl &= ~HDAC_SDCTL2_STRM_MASK; + ctl |= stream << HDAC_SDCTL2_STRM_SHIFT; + HDAC_WRITE_1(&sc->mem, off + HDAC_SDCTL2, ctl); + + HDAC_WRITE_2(&sc->mem, off + HDAC_SDFMT, sc->streams[ss].format); + + ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); + ctl |= 1 << ss; + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); + + ctl = HDAC_READ_1(&sc->mem, off + HDAC_SDCTL0); + ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | + HDAC_SDCTL_RUN; + HDAC_WRITE_1(&sc->mem, off + HDAC_SDCTL0, ctl); + + sc->streams[ss].blksz = blksz; + sc->streams[ss].running = 1; + hdac_poll_reinit(sc); return (0); } -static device_method_t hdac_pcm_methods[] = { +static void +hdac_stream_stop(device_t dev, device_t child, int dir, int stream) +{ + struct hdac_softc *sc = device_get_softc(dev); + int ss, off; + uint32_t ctl; + + ss = hdac_find_stream(sc, dir, stream); + KASSERT(ss >= 0, + ("Stop for not allocated stream (%d/%d)\n", dir, stream)); + + off = ss << 5; + ctl = HDAC_READ_1(&sc->mem, off + HDAC_SDCTL0); + ctl &= ~(HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | + HDAC_SDCTL_RUN); + HDAC_WRITE_1(&sc->mem, off + HDAC_SDCTL0, ctl); + + ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); + ctl &= ~(1 << ss); + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); + + sc->streams[ss].running = 0; + hdac_poll_reinit(sc); +} + +static void +hdac_stream_reset(device_t dev, device_t child, int dir, int stream) +{ + struct hdac_softc *sc = device_get_softc(dev); + int timeout = 1000; + int to = timeout; + int ss, off; + uint32_t ctl; + + ss = hdac_find_stream(sc, dir, stream); + KASSERT(ss >= 0, + ("Reset for not allocated stream (%d/%d)\n", dir, stream)); + + off = ss << 5; + ctl = HDAC_READ_1(&sc->mem, off + HDAC_SDCTL0); + ctl |= HDAC_SDCTL_SRST; + HDAC_WRITE_1(&sc->mem, off + HDAC_SDCTL0, ctl); + do { + ctl = HDAC_READ_1(&sc->mem, off + HDAC_SDCTL0); + if (ctl & HDAC_SDCTL_SRST) + break; + DELAY(10); + } while (--to); + if (!(ctl & HDAC_SDCTL_SRST)) + device_printf(dev, "Reset setting timeout\n"); + ctl &= ~HDAC_SDCTL_SRST; + HDAC_WRITE_1(&sc->mem, off + HDAC_SDCTL0, ctl); + to = timeout; + do { + ctl = HDAC_READ_1(&sc->mem, off + HDAC_SDCTL0); + if (!(ctl & HDAC_SDCTL_SRST)) + break; + DELAY(10); + } while (--to); + if (ctl & HDAC_SDCTL_SRST) + device_printf(dev, "Reset timeout!\n"); +} + +static uint32_t +hdac_stream_getptr(device_t dev, device_t child, int dir, int stream) +{ + struct hdac_softc *sc = device_get_softc(dev); + int ss, off; + + ss = hdac_find_stream(sc, dir, stream); + KASSERT(ss >= 0, + ("Reset for not allocated stream (%d/%d)\n", dir, stream)); + + off = ss << 5; + return (HDAC_READ_4(&sc->mem, off + HDAC_SDLPIB)); +} + +static int +hdac_unsol_alloc(device_t dev, device_t child, int tag) +{ + struct hdac_softc *sc = device_get_softc(dev); + + sc->unsol_registered++; + hdac_poll_reinit(sc); + return (tag); +} + +static void +hdac_unsol_free(device_t dev, device_t child, int tag) +{ + struct hdac_softc *sc = device_get_softc(dev); + + sc->unsol_registered--; + hdac_poll_reinit(sc); +} + +static device_method_t hdac_methods[] = { /* device interface */ - DEVMETHOD(device_probe, hdac_pcm_probe), - DEVMETHOD(device_attach, hdac_pcm_attach), - DEVMETHOD(device_detach, hdac_pcm_detach), + DEVMETHOD(device_probe, hdac_probe), + DEVMETHOD(device_attach, hdac_attach), + DEVMETHOD(device_detach, hdac_detach), + DEVMETHOD(device_suspend, hdac_suspend), + DEVMETHOD(device_resume, hdac_resume), + /* Bus interface */ + DEVMETHOD(bus_get_dma_tag, hdac_get_dma_tag), + DEVMETHOD(bus_print_child, hdac_print_child), + DEVMETHOD(bus_child_location_str, hdac_child_location_str), + DEVMETHOD(bus_child_pnpinfo_str, hdac_child_pnpinfo_str_method), + DEVMETHOD(bus_read_ivar, hdac_read_ivar), + DEVMETHOD(hdac_get_mtx, hdac_get_mtx), + DEVMETHOD(hdac_codec_command, hdac_codec_command), + DEVMETHOD(hdac_stream_alloc, hdac_stream_alloc), + DEVMETHOD(hdac_stream_free, hdac_stream_free), + DEVMETHOD(hdac_stream_start, hdac_stream_start), + DEVMETHOD(hdac_stream_stop, hdac_stream_stop), + DEVMETHOD(hdac_stream_reset, hdac_stream_reset), + DEVMETHOD(hdac_stream_getptr, hdac_stream_getptr), + DEVMETHOD(hdac_unsol_alloc, hdac_unsol_alloc), + DEVMETHOD(hdac_unsol_free, hdac_unsol_free), { 0, 0 } }; -static driver_t hdac_pcm_driver = { - "pcm", - hdac_pcm_methods, - PCM_SOFTC_SIZE, +static driver_t hdac_driver = { + "hdac", + hdac_methods, + sizeof(struct hdac_softc), }; -DRIVER_MODULE(snd_hda_pcm, hdac, hdac_pcm_driver, pcm_devclass, 0, 0); +static devclass_t hdac_devclass; +DRIVER_MODULE(snd_hda, pci, hdac_driver, hdac_devclass, 0, 0); diff --git a/sys/dev/sound/pci/hda/hdac.h b/sys/dev/sound/pci/hda/hdac.h index 298afde..fd2390f 100644 --- a/sys/dev/sound/pci/hda/hdac.h +++ b/sys/dev/sound/pci/hda/hdac.h @@ -29,40 +29,546 @@ #ifndef _HDAC_H_ #define _HDAC_H_ +#include "hdac_if.h" -#if 0 /**************************************************************************** * Miscellanious defines ****************************************************************************/ +/* Controller models */ +#define HDA_MODEL_CONSTRUCT(vendor, model) \ + (((uint32_t)(model) << 16) | ((vendor##_VENDORID) & 0xffff)) + +/* Intel */ +#define INTEL_VENDORID 0x8086 +#define HDA_INTEL_CPT HDA_MODEL_CONSTRUCT(INTEL, 0x1c20) +#define HDA_INTEL_PATSBURG HDA_MODEL_CONSTRUCT(INTEL, 0x1d20) +#define HDA_INTEL_PPT1 HDA_MODEL_CONSTRUCT(INTEL, 0x1e20) +#define HDA_INTEL_82801F HDA_MODEL_CONSTRUCT(INTEL, 0x2668) +#define HDA_INTEL_63XXESB HDA_MODEL_CONSTRUCT(INTEL, 0x269a) +#define HDA_INTEL_82801G HDA_MODEL_CONSTRUCT(INTEL, 0x27d8) +#define HDA_INTEL_82801H HDA_MODEL_CONSTRUCT(INTEL, 0x284b) +#define HDA_INTEL_82801I HDA_MODEL_CONSTRUCT(INTEL, 0x293e) +#define HDA_INTEL_82801JI HDA_MODEL_CONSTRUCT(INTEL, 0x3a3e) +#define HDA_INTEL_82801JD HDA_MODEL_CONSTRUCT(INTEL, 0x3a6e) +#define HDA_INTEL_PCH HDA_MODEL_CONSTRUCT(INTEL, 0x3b56) +#define HDA_INTEL_PCH2 HDA_MODEL_CONSTRUCT(INTEL, 0x3b57) +#define HDA_INTEL_SCH HDA_MODEL_CONSTRUCT(INTEL, 0x811b) +#define HDA_INTEL_ALL HDA_MODEL_CONSTRUCT(INTEL, 0xffff) + +/* Nvidia */ +#define NVIDIA_VENDORID 0x10de +#define HDA_NVIDIA_MCP51 HDA_MODEL_CONSTRUCT(NVIDIA, 0x026c) +#define HDA_NVIDIA_MCP55 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0371) +#define HDA_NVIDIA_MCP61_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x03e4) +#define HDA_NVIDIA_MCP61_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x03f0) +#define HDA_NVIDIA_MCP65_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x044a) +#define HDA_NVIDIA_MCP65_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x044b) +#define HDA_NVIDIA_MCP67_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x055c) +#define HDA_NVIDIA_MCP67_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x055d) +#define HDA_NVIDIA_MCP78_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0774) +#define HDA_NVIDIA_MCP78_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0775) +#define HDA_NVIDIA_MCP78_3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0776) +#define HDA_NVIDIA_MCP78_4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0777) +#define HDA_NVIDIA_MCP73_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x07fc) +#define HDA_NVIDIA_MCP73_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x07fd) +#define HDA_NVIDIA_MCP79_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac0) +#define HDA_NVIDIA_MCP79_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac1) +#define HDA_NVIDIA_MCP79_3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac2) +#define HDA_NVIDIA_MCP79_4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0ac3) +#define HDA_NVIDIA_MCP89_1 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d94) +#define HDA_NVIDIA_MCP89_2 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d95) +#define HDA_NVIDIA_MCP89_3 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d96) +#define HDA_NVIDIA_MCP89_4 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0d97) +#define HDA_NVIDIA_ALL HDA_MODEL_CONSTRUCT(NVIDIA, 0xffff) + +/* ATI */ +#define ATI_VENDORID 0x1002 +#define HDA_ATI_SB450 HDA_MODEL_CONSTRUCT(ATI, 0x437b) +#define HDA_ATI_SB600 HDA_MODEL_CONSTRUCT(ATI, 0x4383) +#define HDA_ATI_RS600 HDA_MODEL_CONSTRUCT(ATI, 0x793b) +#define HDA_ATI_RS690 HDA_MODEL_CONSTRUCT(ATI, 0x7919) +#define HDA_ATI_RS780 HDA_MODEL_CONSTRUCT(ATI, 0x960f) +#define HDA_ATI_R600 HDA_MODEL_CONSTRUCT(ATI, 0xaa00) +#define HDA_ATI_RV630 HDA_MODEL_CONSTRUCT(ATI, 0xaa08) +#define HDA_ATI_RV610 HDA_MODEL_CONSTRUCT(ATI, 0xaa10) +#define HDA_ATI_RV670 HDA_MODEL_CONSTRUCT(ATI, 0xaa18) +#define HDA_ATI_RV635 HDA_MODEL_CONSTRUCT(ATI, 0xaa20) +#define HDA_ATI_RV620 HDA_MODEL_CONSTRUCT(ATI, 0xaa28) +#define HDA_ATI_RV770 HDA_MODEL_CONSTRUCT(ATI, 0xaa30) +#define HDA_ATI_RV730 HDA_MODEL_CONSTRUCT(ATI, 0xaa38) +#define HDA_ATI_RV710 HDA_MODEL_CONSTRUCT(ATI, 0xaa40) +#define HDA_ATI_RV740 HDA_MODEL_CONSTRUCT(ATI, 0xaa48) +#define HDA_ATI_ALL HDA_MODEL_CONSTRUCT(ATI, 0xffff) + +/* RDC */ +#define RDC_VENDORID 0x17f3 +#define HDA_RDC_M3010 HDA_MODEL_CONSTRUCT(RDC, 0x3010) + +/* VIA */ +#define VIA_VENDORID 0x1106 +#define HDA_VIA_VT82XX HDA_MODEL_CONSTRUCT(VIA, 0x3288) +#define HDA_VIA_ALL HDA_MODEL_CONSTRUCT(VIA, 0xffff) + +/* SiS */ +#define SIS_VENDORID 0x1039 +#define HDA_SIS_966 HDA_MODEL_CONSTRUCT(SIS, 0x7502) +#define HDA_SIS_ALL HDA_MODEL_CONSTRUCT(SIS, 0xffff) + +/* ULI */ +#define ULI_VENDORID 0x10b9 +#define HDA_ULI_M5461 HDA_MODEL_CONSTRUCT(ULI, 0x5461) +#define HDA_ULI_ALL HDA_MODEL_CONSTRUCT(ULI, 0xffff) + +/* OEM/subvendors */ + +/* Intel */ +#define INTEL_D101GGC_SUBVENDOR HDA_MODEL_CONSTRUCT(INTEL, 0xd600) + +/* HP/Compaq */ +#define HP_VENDORID 0x103c +#define HP_V3000_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30b5) +#define HP_NX7400_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30a2) +#define HP_NX6310_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30aa) +#define HP_NX6325_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30b0) +#define HP_XW4300_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x3013) +#define HP_3010_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x3010) +#define HP_DV5000_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30a5) +#define HP_DC7700S_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x2801) +#define HP_DC7700_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x2802) +#define HP_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0xffff) +/* What is wrong with XN 2563 anyway? (Got the picture ?) */ +#define HP_NX6325_SUBVENDORX 0x103c30b0 + +/* Dell */ +#define DELL_VENDORID 0x1028 +#define DELL_D630_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01f9) +#define DELL_D820_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01cc) +#define DELL_V1400_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x0227) +#define DELL_V1500_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x0228) +#define DELL_I1300_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01c9) +#define DELL_XPSM1210_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01d7) +#define DELL_OPLX745_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01da) +#define DELL_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0xffff) + +/* Clevo */ +#define CLEVO_VENDORID 0x1558 +#define CLEVO_D900T_SUBVENDOR HDA_MODEL_CONSTRUCT(CLEVO, 0x0900) +#define CLEVO_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(CLEVO, 0xffff) + +/* Acer */ +#define ACER_VENDORID 0x1025 +#define ACER_A5050_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x010f) +#define ACER_A4520_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0127) +#define ACER_A4710_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x012f) +#define ACER_A4715_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0133) +#define ACER_3681WXM_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x0110) +#define ACER_T6292_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x011b) +#define ACER_T5320_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0x011f) +#define ACER_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0xffff) + +/* Asus */ +#define ASUS_VENDORID 0x1043 +#define ASUS_A8X_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1153) +#define ASUS_U5F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) +#define ASUS_W6F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) +#define ASUS_A7M_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1323) +#define ASUS_F3JC_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1338) +#define ASUS_G2K_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1339) +#define ASUS_A7T_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x13c2) +#define ASUS_W2J_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1971) +#define ASUS_M5200_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1993) +#define ASUS_P5PL2_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x817f) +#define ASUS_P1AH2_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb) +#define ASUS_M2NPVMX_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81cb) +#define ASUS_M2V_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81e7) +#define ASUS_P5BWD_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x81ec) +#define ASUS_M2N_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x8234) +#define ASUS_A8NVMCSM_SUBVENDOR HDA_MODEL_CONSTRUCT(NVIDIA, 0xcb84) +#define ASUS_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0xffff) + +/* IBM / Lenovo */ +#define IBM_VENDORID 0x1014 +#define IBM_M52_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0x02f6) +#define IBM_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0xffff) + +/* Lenovo */ +#define LENOVO_VENDORID 0x17aa +#define LENOVO_3KN100_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x2066) +#define LENOVO_3KN200_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x384e) +#define LENOVO_B450_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x3a0d) +#define LENOVO_TCA55_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0x1015) +#define LENOVO_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LENOVO, 0xffff) + +/* Samsung */ +#define SAMSUNG_VENDORID 0x144d +#define SAMSUNG_Q1_SUBVENDOR HDA_MODEL_CONSTRUCT(SAMSUNG, 0xc027) +#define SAMSUNG_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(SAMSUNG, 0xffff) + +/* Medion ? */ +#define MEDION_VENDORID 0x161f +#define MEDION_MD95257_SUBVENDOR HDA_MODEL_CONSTRUCT(MEDION, 0x203d) +#define MEDION_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(MEDION, 0xffff) + +/* Apple Computer Inc. */ +#define APPLE_VENDORID 0x106b +#define APPLE_MB3_SUBVENDOR HDA_MODEL_CONSTRUCT(APPLE, 0x00a1) + +/* Sony */ +#define SONY_VENDORID 0x104d +#define SONY_S5_SUBVENDOR HDA_MODEL_CONSTRUCT(SONY, 0x81cc) +#define SONY_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(SONY, 0xffff) + +/* + * Apple Intel MacXXXX seems using Sigmatel codec/vendor id + * instead of their own, which is beyond my comprehension + * (see HDA_CODEC_STAC9221 below). + */ +#define APPLE_INTEL_MAC 0x76808384 +#define APPLE_MACBOOKPRO55 0xcb7910de + +/* LG Electronics */ +#define LG_VENDORID 0x1854 +#define LG_LW20_SUBVENDOR HDA_MODEL_CONSTRUCT(LG, 0x0018) +#define LG_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(LG, 0xffff) + +/* Fujitsu Siemens */ +#define FS_VENDORID 0x1734 +#define FS_PA1510_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0x10b8) +#define FS_SI1848_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0x10cd) +#define FS_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(FS, 0xffff) + +/* Fujitsu Limited */ +#define FL_VENDORID 0x10cf +#define FL_S7020D_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0x1326) +#define FL_U1010_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0x142d) +#define FL_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(FL, 0xffff) + +/* Toshiba */ +#define TOSHIBA_VENDORID 0x1179 +#define TOSHIBA_U200_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0x0001) +#define TOSHIBA_A135_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0xff01) +#define TOSHIBA_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(TOSHIBA, 0xffff) + +/* Micro-Star International (MSI) */ +#define MSI_VENDORID 0x1462 +#define MSI_MS1034_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0x0349) +#define MSI_MS034A_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0x034a) +#define MSI_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(MSI, 0xffff) + +/* Giga-Byte Technology */ +#define GB_VENDORID 0x1458 +#define GB_G33S2H_SUBVENDOR HDA_MODEL_CONSTRUCT(GB, 0xa022) +#define GP_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(GB, 0xffff) + +/* Uniwill ? */ +#define UNIWILL_VENDORID 0x1584 +#define UNIWILL_9075_SUBVENDOR HDA_MODEL_CONSTRUCT(UNIWILL, 0x9075) +#define UNIWILL_9080_SUBVENDOR HDA_MODEL_CONSTRUCT(UNIWILL, 0x9080) + +/* All codecs you can eat... */ +#define HDA_CODEC_CONSTRUCT(vendor, id) \ + (((uint32_t)(vendor##_VENDORID) << 16) | ((id) & 0xffff)) + +/* Cirrus Logic */ +#define CIRRUSLOGIC_VENDORID 0x1013 +#define HDA_CODEC_CS4206 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4206) +#define HDA_CODEC_CS4207 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4207) +#define HDA_CODEC_CS4210 HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0x4210) +#define HDA_CODEC_CSXXXX HDA_CODEC_CONSTRUCT(CIRRUSLOGIC, 0xffff) + +/* Realtek */ +#define REALTEK_VENDORID 0x10ec +#define HDA_CODEC_ALC221 HDA_CODEC_CONSTRUCT(REALTEK, 0x0221) +#define HDA_CODEC_ALC260 HDA_CODEC_CONSTRUCT(REALTEK, 0x0260) +#define HDA_CODEC_ALC262 HDA_CODEC_CONSTRUCT(REALTEK, 0x0262) +#define HDA_CODEC_ALC267 HDA_CODEC_CONSTRUCT(REALTEK, 0x0267) +#define HDA_CODEC_ALC268 HDA_CODEC_CONSTRUCT(REALTEK, 0x0268) +#define HDA_CODEC_ALC269 HDA_CODEC_CONSTRUCT(REALTEK, 0x0269) +#define HDA_CODEC_ALC270 HDA_CODEC_CONSTRUCT(REALTEK, 0x0270) +#define HDA_CODEC_ALC272 HDA_CODEC_CONSTRUCT(REALTEK, 0x0272) +#define HDA_CODEC_ALC273 HDA_CODEC_CONSTRUCT(REALTEK, 0x0273) +#define HDA_CODEC_ALC275 HDA_CODEC_CONSTRUCT(REALTEK, 0x0275) +#define HDA_CODEC_ALC276 HDA_CODEC_CONSTRUCT(REALTEK, 0x0276) +#define HDA_CODEC_ALC660 HDA_CODEC_CONSTRUCT(REALTEK, 0x0660) +#define HDA_CODEC_ALC662 HDA_CODEC_CONSTRUCT(REALTEK, 0x0662) +#define HDA_CODEC_ALC663 HDA_CODEC_CONSTRUCT(REALTEK, 0x0663) +#define HDA_CODEC_ALC665 HDA_CODEC_CONSTRUCT(REALTEK, 0x0665) +#define HDA_CODEC_ALC861 HDA_CODEC_CONSTRUCT(REALTEK, 0x0861) +#define HDA_CODEC_ALC861VD HDA_CODEC_CONSTRUCT(REALTEK, 0x0862) +#define HDA_CODEC_ALC880 HDA_CODEC_CONSTRUCT(REALTEK, 0x0880) +#define HDA_CODEC_ALC882 HDA_CODEC_CONSTRUCT(REALTEK, 0x0882) +#define HDA_CODEC_ALC883 HDA_CODEC_CONSTRUCT(REALTEK, 0x0883) +#define HDA_CODEC_ALC885 HDA_CODEC_CONSTRUCT(REALTEK, 0x0885) +#define HDA_CODEC_ALC887 HDA_CODEC_CONSTRUCT(REALTEK, 0x0887) +#define HDA_CODEC_ALC888 HDA_CODEC_CONSTRUCT(REALTEK, 0x0888) +#define HDA_CODEC_ALC889 HDA_CODEC_CONSTRUCT(REALTEK, 0x0889) +#define HDA_CODEC_ALC892 HDA_CODEC_CONSTRUCT(REALTEK, 0x0892) +#define HDA_CODEC_ALC899 HDA_CODEC_CONSTRUCT(REALTEK, 0x0899) +#define HDA_CODEC_ALCXXXX HDA_CODEC_CONSTRUCT(REALTEK, 0xffff) + +/* Analog Devices */ +#define ANALOGDEVICES_VENDORID 0x11d4 +#define HDA_CODEC_AD1884A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x184a) +#define HDA_CODEC_AD1882 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1882) +#define HDA_CODEC_AD1883 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1883) +#define HDA_CODEC_AD1884 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1884) +#define HDA_CODEC_AD1984A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x194a) +#define HDA_CODEC_AD1984B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x194b) +#define HDA_CODEC_AD1981HD HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1981) +#define HDA_CODEC_AD1983 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1983) +#define HDA_CODEC_AD1984 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1984) +#define HDA_CODEC_AD1986A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1986) +#define HDA_CODEC_AD1987 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1987) +#define HDA_CODEC_AD1988 HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x1988) +#define HDA_CODEC_AD1988B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x198b) +#define HDA_CODEC_AD1882A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x882a) +#define HDA_CODEC_AD1989A HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x989a) +#define HDA_CODEC_AD1989B HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0x989b) +#define HDA_CODEC_ADXXXX HDA_CODEC_CONSTRUCT(ANALOGDEVICES, 0xffff) + +/* CMedia */ +#define CMEDIA_VENDORID 0x434d +#define HDA_CODEC_CMI9880 HDA_CODEC_CONSTRUCT(CMEDIA, 0x4980) +#define HDA_CODEC_CMIXXXX HDA_CODEC_CONSTRUCT(CMEDIA, 0xffff) + +/* Sigmatel */ +#define SIGMATEL_VENDORID 0x8384 +#define HDA_CODEC_STAC9230X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7612) +#define HDA_CODEC_STAC9230D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7613) +#define HDA_CODEC_STAC9229X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7614) +#define HDA_CODEC_STAC9229D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7615) +#define HDA_CODEC_STAC9228X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7616) +#define HDA_CODEC_STAC9228D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7617) +#define HDA_CODEC_STAC9227X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7618) +#define HDA_CODEC_STAC9227D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7619) +#define HDA_CODEC_STAC9274 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7620) +#define HDA_CODEC_STAC9274D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7621) +#define HDA_CODEC_STAC9273X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7622) +#define HDA_CODEC_STAC9273D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7623) +#define HDA_CODEC_STAC9272X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7624) +#define HDA_CODEC_STAC9272D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7625) +#define HDA_CODEC_STAC9271X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7626) +#define HDA_CODEC_STAC9271D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7627) +#define HDA_CODEC_STAC9274X5NH HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7628) +#define HDA_CODEC_STAC9274D5NH HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7629) +#define HDA_CODEC_STAC9250 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7634) +#define HDA_CODEC_STAC9251 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7636) +#define HDA_CODEC_IDT92HD700X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7638) +#define HDA_CODEC_IDT92HD700D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7639) +#define HDA_CODEC_IDT92HD206X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7645) +#define HDA_CODEC_IDT92HD206D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7646) +#define HDA_CODEC_CXD9872RDK HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7661) +#define HDA_CODEC_STAC9872AK HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7662) +#define HDA_CODEC_CXD9872AKD HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7664) +#define HDA_CODEC_STAC9221 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7680) +#define HDA_CODEC_STAC922XD HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7681) +#define HDA_CODEC_STAC9221_A2 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7682) +#define HDA_CODEC_STAC9221D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7683) +#define HDA_CODEC_STAC9220 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7690) +#define HDA_CODEC_STAC9200D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7691) +#define HDA_CODEC_IDT92HD005 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7698) +#define HDA_CODEC_IDT92HD005D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7699) +#define HDA_CODEC_STAC9205X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a0) +#define HDA_CODEC_STAC9205D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a1) +#define HDA_CODEC_STAC9204X HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a2) +#define HDA_CODEC_STAC9204D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x76a3) +#define HDA_CODEC_STAC9220_A2 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7880) +#define HDA_CODEC_STAC9220_A1 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7882) +#define HDA_CODEC_STACXXXX HDA_CODEC_CONSTRUCT(SIGMATEL, 0xffff) + +/* IDT */ +#define IDT_VENDORID 0x111d +#define HDA_CODEC_IDT92HD75BX HDA_CODEC_CONSTRUCT(IDT, 0x7603) +#define HDA_CODEC_IDT92HD83C1X HDA_CODEC_CONSTRUCT(IDT, 0x7604) +#define HDA_CODEC_IDT92HD81B1X HDA_CODEC_CONSTRUCT(IDT, 0x7605) +#define HDA_CODEC_IDT92HD75B3 HDA_CODEC_CONSTRUCT(IDT, 0x7608) +#define HDA_CODEC_IDT92HD73D1 HDA_CODEC_CONSTRUCT(IDT, 0x7674) +#define HDA_CODEC_IDT92HD73C1 HDA_CODEC_CONSTRUCT(IDT, 0x7675) +#define HDA_CODEC_IDT92HD73E1 HDA_CODEC_CONSTRUCT(IDT, 0x7676) +#define HDA_CODEC_IDT92HD71B8 HDA_CODEC_CONSTRUCT(IDT, 0x76b0) +#define HDA_CODEC_IDT92HD71B7 HDA_CODEC_CONSTRUCT(IDT, 0x76b2) +#define HDA_CODEC_IDT92HD71B5 HDA_CODEC_CONSTRUCT(IDT, 0x76b6) +#define HDA_CODEC_IDT92HD83C1C HDA_CODEC_CONSTRUCT(IDT, 0x76d4) +#define HDA_CODEC_IDT92HD81B1C HDA_CODEC_CONSTRUCT(IDT, 0x76d5) +#define HDA_CODEC_IDTXXXX HDA_CODEC_CONSTRUCT(IDT, 0xffff) + +/* Silicon Image */ +#define SII_VENDORID 0x1095 +#define HDA_CODEC_SII1390 HDA_CODEC_CONSTRUCT(SII, 0x1390) +#define HDA_CODEC_SII1392 HDA_CODEC_CONSTRUCT(SII, 0x1392) +#define HDA_CODEC_SIIXXXX HDA_CODEC_CONSTRUCT(SII, 0xffff) + +/* Lucent/Agere */ +#define AGERE_VENDORID 0x11c1 +#define HDA_CODEC_AGEREXXXX HDA_CODEC_CONSTRUCT(AGERE, 0xffff) + +/* Conexant */ +#define CONEXANT_VENDORID 0x14f1 +#define HDA_CODEC_CX20549 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5045) +#define HDA_CODEC_CX20551 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5047) +#define HDA_CODEC_CX20561 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5051) +#define HDA_CODEC_CX20582 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5066) +#define HDA_CODEC_CX20583 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5067) +#define HDA_CODEC_CX20584 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5068) +#define HDA_CODEC_CX20585 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5069) +#define HDA_CODEC_CX20588 HDA_CODEC_CONSTRUCT(CONEXANT, 0x506c) +#define HDA_CODEC_CX20590 HDA_CODEC_CONSTRUCT(CONEXANT, 0x506e) +#define HDA_CODEC_CX20631 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5097) +#define HDA_CODEC_CX20632 HDA_CODEC_CONSTRUCT(CONEXANT, 0x5098) +#define HDA_CODEC_CX20641 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50a1) +#define HDA_CODEC_CX20642 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50a2) +#define HDA_CODEC_CX20651 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50ab) +#define HDA_CODEC_CX20652 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50ac) +#define HDA_CODEC_CX20664 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50b8) +#define HDA_CODEC_CX20665 HDA_CODEC_CONSTRUCT(CONEXANT, 0x50b9) +#define HDA_CODEC_CXXXXX HDA_CODEC_CONSTRUCT(CONEXANT, 0xffff) + +/* VIA */ +#define HDA_CODEC_VT1708_8 HDA_CODEC_CONSTRUCT(VIA, 0x1708) +#define HDA_CODEC_VT1708_9 HDA_CODEC_CONSTRUCT(VIA, 0x1709) +#define HDA_CODEC_VT1708_A HDA_CODEC_CONSTRUCT(VIA, 0x170a) +#define HDA_CODEC_VT1708_B HDA_CODEC_CONSTRUCT(VIA, 0x170b) +#define HDA_CODEC_VT1709_0 HDA_CODEC_CONSTRUCT(VIA, 0xe710) +#define HDA_CODEC_VT1709_1 HDA_CODEC_CONSTRUCT(VIA, 0xe711) +#define HDA_CODEC_VT1709_2 HDA_CODEC_CONSTRUCT(VIA, 0xe712) +#define HDA_CODEC_VT1709_3 HDA_CODEC_CONSTRUCT(VIA, 0xe713) +#define HDA_CODEC_VT1709_4 HDA_CODEC_CONSTRUCT(VIA, 0xe714) +#define HDA_CODEC_VT1709_5 HDA_CODEC_CONSTRUCT(VIA, 0xe715) +#define HDA_CODEC_VT1709_6 HDA_CODEC_CONSTRUCT(VIA, 0xe716) +#define HDA_CODEC_VT1709_7 HDA_CODEC_CONSTRUCT(VIA, 0xe717) +#define HDA_CODEC_VT1708B_0 HDA_CODEC_CONSTRUCT(VIA, 0xe720) +#define HDA_CODEC_VT1708B_1 HDA_CODEC_CONSTRUCT(VIA, 0xe721) +#define HDA_CODEC_VT1708B_2 HDA_CODEC_CONSTRUCT(VIA, 0xe722) +#define HDA_CODEC_VT1708B_3 HDA_CODEC_CONSTRUCT(VIA, 0xe723) +#define HDA_CODEC_VT1708B_4 HDA_CODEC_CONSTRUCT(VIA, 0xe724) +#define HDA_CODEC_VT1708B_5 HDA_CODEC_CONSTRUCT(VIA, 0xe725) +#define HDA_CODEC_VT1708B_6 HDA_CODEC_CONSTRUCT(VIA, 0xe726) +#define HDA_CODEC_VT1708B_7 HDA_CODEC_CONSTRUCT(VIA, 0xe727) +#define HDA_CODEC_VT1708S_0 HDA_CODEC_CONSTRUCT(VIA, 0x0397) +#define HDA_CODEC_VT1708S_1 HDA_CODEC_CONSTRUCT(VIA, 0x1397) +#define HDA_CODEC_VT1708S_2 HDA_CODEC_CONSTRUCT(VIA, 0x2397) +#define HDA_CODEC_VT1708S_3 HDA_CODEC_CONSTRUCT(VIA, 0x3397) +#define HDA_CODEC_VT1708S_4 HDA_CODEC_CONSTRUCT(VIA, 0x4397) +#define HDA_CODEC_VT1708S_5 HDA_CODEC_CONSTRUCT(VIA, 0x5397) +#define HDA_CODEC_VT1708S_6 HDA_CODEC_CONSTRUCT(VIA, 0x6397) +#define HDA_CODEC_VT1708S_7 HDA_CODEC_CONSTRUCT(VIA, 0x7397) +#define HDA_CODEC_VT1702_0 HDA_CODEC_CONSTRUCT(VIA, 0x0398) +#define HDA_CODEC_VT1702_1 HDA_CODEC_CONSTRUCT(VIA, 0x1398) +#define HDA_CODEC_VT1702_2 HDA_CODEC_CONSTRUCT(VIA, 0x2398) +#define HDA_CODEC_VT1702_3 HDA_CODEC_CONSTRUCT(VIA, 0x3398) +#define HDA_CODEC_VT1702_4 HDA_CODEC_CONSTRUCT(VIA, 0x4398) +#define HDA_CODEC_VT1702_5 HDA_CODEC_CONSTRUCT(VIA, 0x5398) +#define HDA_CODEC_VT1702_6 HDA_CODEC_CONSTRUCT(VIA, 0x6398) +#define HDA_CODEC_VT1702_7 HDA_CODEC_CONSTRUCT(VIA, 0x7398) +#define HDA_CODEC_VT1716S_0 HDA_CODEC_CONSTRUCT(VIA, 0x0433) +#define HDA_CODEC_VT1716S_1 HDA_CODEC_CONSTRUCT(VIA, 0xa721) +#define HDA_CODEC_VT1718S_0 HDA_CODEC_CONSTRUCT(VIA, 0x0428) +#define HDA_CODEC_VT1718S_1 HDA_CODEC_CONSTRUCT(VIA, 0x4428) +#define HDA_CODEC_VT1802_0 HDA_CODEC_CONSTRUCT(VIA, 0x0446) +#define HDA_CODEC_VT1802_1 HDA_CODEC_CONSTRUCT(VIA, 0x8446) +#define HDA_CODEC_VT1812 HDA_CODEC_CONSTRUCT(VIA, 0x0448) +#define HDA_CODEC_VT1818S HDA_CODEC_CONSTRUCT(VIA, 0x0440) +#define HDA_CODEC_VT1828S HDA_CODEC_CONSTRUCT(VIA, 0x4441) +#define HDA_CODEC_VT2002P_0 HDA_CODEC_CONSTRUCT(VIA, 0x0438) +#define HDA_CODEC_VT2002P_1 HDA_CODEC_CONSTRUCT(VIA, 0x4438) +#define HDA_CODEC_VT2020 HDA_CODEC_CONSTRUCT(VIA, 0x0441) +#define HDA_CODEC_VTXXXX HDA_CODEC_CONSTRUCT(VIA, 0xffff) + +/* ATI */ +#define HDA_CODEC_ATIRS600_1 HDA_CODEC_CONSTRUCT(ATI, 0x793c) +#define HDA_CODEC_ATIRS600_2 HDA_CODEC_CONSTRUCT(ATI, 0x7919) +#define HDA_CODEC_ATIRS690 HDA_CODEC_CONSTRUCT(ATI, 0x791a) +#define HDA_CODEC_ATIR6XX HDA_CODEC_CONSTRUCT(ATI, 0xaa01) +#define HDA_CODEC_ATIXXXX HDA_CODEC_CONSTRUCT(ATI, 0xffff) + +/* NVIDIA */ +#define HDA_CODEC_NVIDIAMCP78 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0002) +#define HDA_CODEC_NVIDIAMCP78_2 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0006) +#define HDA_CODEC_NVIDIAMCP7A HDA_CODEC_CONSTRUCT(NVIDIA, 0x0007) +#define HDA_CODEC_NVIDIAGT220 HDA_CODEC_CONSTRUCT(NVIDIA, 0x000a) +#define HDA_CODEC_NVIDIAGT21X HDA_CODEC_CONSTRUCT(NVIDIA, 0x000b) +#define HDA_CODEC_NVIDIAMCP89 HDA_CODEC_CONSTRUCT(NVIDIA, 0x000c) +#define HDA_CODEC_NVIDIAGT240 HDA_CODEC_CONSTRUCT(NVIDIA, 0x000d) +#define HDA_CODEC_NVIDIAMCP67 HDA_CODEC_CONSTRUCT(NVIDIA, 0x0067) +#define HDA_CODEC_NVIDIAMCP73 HDA_CODEC_CONSTRUCT(NVIDIA, 0x8001) +#define HDA_CODEC_NVIDIAXXXX HDA_CODEC_CONSTRUCT(NVIDIA, 0xffff) + +/* INTEL */ +#define HDA_CODEC_INTELIP HDA_CODEC_CONSTRUCT(INTEL, 0x0054) +#define HDA_CODEC_INTELBL HDA_CODEC_CONSTRUCT(INTEL, 0x2801) +#define HDA_CODEC_INTELCA HDA_CODEC_CONSTRUCT(INTEL, 0x2802) +#define HDA_CODEC_INTELEL HDA_CODEC_CONSTRUCT(INTEL, 0x2803) +#define HDA_CODEC_INTELIP2 HDA_CODEC_CONSTRUCT(INTEL, 0x2804) +#define HDA_CODEC_INTELCPT HDA_CODEC_CONSTRUCT(INTEL, 0x2805) +#define HDA_CODEC_INTELPPT HDA_CODEC_CONSTRUCT(INTEL, 0x2806) +#define HDA_CODEC_INTELCL HDA_CODEC_CONSTRUCT(INTEL, 0x29fb) +#define HDA_CODEC_INTELXXXX HDA_CODEC_CONSTRUCT(INTEL, 0xffff) + /**************************************************************************** * Helper Macros ****************************************************************************/ +#define HDA_DMA_ALIGNMENT 128 +#define HDA_GPIO_MAX 8 + +#define HDA_DEV_MATCH(fl, v) ((fl) == (v) || \ + (fl) == 0xffffffff || \ + (((fl) & 0xffff0000) == 0xffff0000 && \ + ((fl) & 0x0000ffff) == ((v) & 0x0000ffff)) || \ + (((fl) & 0x0000ffff) == 0x0000ffff && \ + ((fl) & 0xffff0000) == ((v) & 0xffff0000))) +#define HDA_MATCH_ALL 0xffffffff +#define HDA_INVALID 0xffffffff + +#define HDA_BOOTVERBOSE(stmt) do { \ + if (bootverbose != 0 || snd_verbose > 3) { \ + stmt \ + } \ +} while (0) + +#define HDA_BOOTHVERBOSE(stmt) do { \ + if (snd_verbose > 3) { \ + stmt \ + } \ +} while (0) + +#define hda_command(dev, verb) \ + HDAC_CODEC_COMMAND(device_get_parent(dev), (dev), (verb)) + +typedef int nid_t; + /**************************************************************************** * Simplified Accessors for HDA devices ****************************************************************************/ + enum hdac_device_ivars { - HDAC_IVAR_CODEC_ID, - HDAC_IVAR_NODE_ID, - HDAC_IVAR_VENDOR_ID, - HDAC_IVAR_DEVICE_ID, - HDAC_IVAR_REVISION_ID, - HDAC_IVAR_STEPPING_ID, - HDAC_IVAR_NODE_TYPE, + HDA_IVAR_CODEC_ID, + HDA_IVAR_NODE_ID, + HDA_IVAR_VENDOR_ID, + HDA_IVAR_DEVICE_ID, + HDA_IVAR_REVISION_ID, + HDA_IVAR_STEPPING_ID, + HDA_IVAR_SUBVENDOR_ID, + HDA_IVAR_SUBDEVICE_ID, + HDA_IVAR_SUBSYSTEM_ID, + HDA_IVAR_NODE_TYPE, + HDA_IVAR_DMA_NOCACHE, }; -#define HDAC_ACCESSOR(var, ivar, type) \ - __BUS_ACCESSOR(hdac, var, HDAC, ivar, type) +#define HDA_ACCESSOR(var, ivar, type) \ + __BUS_ACCESSOR(hda, var, HDA, ivar, type) -HDAC_ACCESSOR(codec_id, CODEC_ID, uint8_t); -HDAC_ACCESSOR(node_id, NODE_ID, uint8_t); -HDAC_ACCESSOR(vendor_id, VENDOR_ID, uint16_t); -HDAC_ACCESSOR(device_id, DEVICE_ID, uint16_t); -HDAC_ACCESSOR(revision_id, REVISION_ID, uint8_t); -HDAC_ACCESSOR(stepping_id, STEPPING_ID, uint8_t); -HDAC_ACCESSOR(node_type, NODE_TYPE, uint8_t); -#endif +HDA_ACCESSOR(codec_id, CODEC_ID, uint8_t); +HDA_ACCESSOR(node_id, NODE_ID, uint8_t); +HDA_ACCESSOR(vendor_id, VENDOR_ID, uint16_t); +HDA_ACCESSOR(device_id, DEVICE_ID, uint16_t); +HDA_ACCESSOR(revision_id, REVISION_ID, uint8_t); +HDA_ACCESSOR(stepping_id, STEPPING_ID, uint8_t); +HDA_ACCESSOR(subvendor_id, SUBVENDOR_ID, uint16_t); +HDA_ACCESSOR(subdevice_id, SUBDEVICE_ID, uint16_t); +HDA_ACCESSOR(node_type, NODE_TYPE, uint8_t); +HDA_ACCESSOR(dma_nocache, DMA_NOCACHE, uint8_t); #define PCIS_MULTIMEDIA_HDA 0x03 diff --git a/sys/dev/sound/pci/hda/hdac_if.m b/sys/dev/sound/pci/hda/hdac_if.m new file mode 100644 index 0000000..4fb77bd --- /dev/null +++ b/sys/dev/sound/pci/hda/hdac_if.m @@ -0,0 +1,114 @@ +# Copyright (c) 2012 Alexander Motin <mav@FreeBSD.org> +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer, +# without modification, immediately at the beginning of the file. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# $FreeBSD$ + +#include <sys/rman.h> + +INTERFACE hdac; + +METHOD struct mtx * get_mtx { + device_t dev; + device_t child; +}; + +METHOD uint32_t codec_command { + device_t dev; + device_t child; + uint32_t verb; +}; + +METHOD int stream_alloc { + device_t dev; + device_t child; + int dir; + int format; + uint32_t **dmapos; +}; + +METHOD void stream_free { + device_t dev; + device_t child; + int dir; + int stream; +}; + +METHOD int stream_start { + device_t dev; + device_t child; + int dir; + int stream; + bus_addr_t buf; + int blksz; + int blkcnt; +}; + +METHOD void stream_stop { + device_t dev; + device_t child; + int dir; + int stream; +}; + +METHOD void stream_reset { + device_t dev; + device_t child; + int dir; + int stream; +}; + +METHOD uint32_t stream_getptr { + device_t dev; + device_t child; + int dir; + int stream; +}; + +METHOD void stream_intr { + device_t dev; + int dir; + int stream; +}; + +METHOD int unsol_alloc { + device_t dev; + device_t child; + int wanted; +}; + +METHOD void unsol_free { + device_t dev; + device_t child; + int tag; +}; + +METHOD void unsol_intr { + device_t dev; + uint32_t resp; +}; + +METHOD void pindump { + device_t dev; +}; + diff --git a/sys/dev/sound/pci/hda/hdac_private.h b/sys/dev/sound/pci/hda/hdac_private.h index 24beed7..8532f83 100644 --- a/sys/dev/sound/pci/hda/hdac_private.h +++ b/sys/dev/sound/pci/hda/hdac_private.h @@ -1,5 +1,6 @@ /*- * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca> + * Copyright (c) 2008-2012 Alexander Motin <mav@FreeBSD.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,15 +30,11 @@ #ifndef _HDAC_PRIVATE_H_ #define _HDAC_PRIVATE_H_ - /**************************************************************************** * Miscellaneous defines ****************************************************************************/ -#define HDAC_DMA_ALIGNMENT 128 #define HDAC_CODEC_MAX 16 -#define HDAC_MTX_NAME "hdac driver mutex" - /**************************************************************************** * Helper Macros ****************************************************************************/ @@ -82,7 +79,6 @@ #define HDAC_BSDBDPL(sc, n) (_HDAC_BSDBDPL((n), (sc)->num_iss, (sc)->num_oss)) #define HDAC_BSDBDPU(sc, n) (_HDAC_BSDBDPU((n), (sc)->num_iss, (sc)->num_oss)) - /**************************************************************************** * Custom hdac malloc type ****************************************************************************/ @@ -145,22 +141,6 @@ struct hdac_rirb { (((response_ex) & HDAC_RIRB_RESPONSE_EX_SDATA_IN_MASK) >> \ HDAC_RIRB_RESPONSE_EX_SDATA_IN_OFFSET) -/**************************************************************************** - * struct hdac_command_list - * - * This structure holds the list of verbs that are to be sent to the codec - * via the corb and the responses received via the rirb. It's allocated by - * the codec driver and is owned by it. - ****************************************************************************/ -struct hdac_command_list { - int num_commands; - uint32_t *verbs; - uint32_t *responses; -}; - -typedef int nid_t; - -struct hdac_softc; struct hdac_bdle { volatile uint32_t addrl; volatile uint32_t addrh; @@ -168,181 +148,36 @@ struct hdac_bdle { volatile uint32_t ioc; } __packed; -#define HDA_MAX_CONNS 32 -#define HDA_MAX_NAMELEN 32 - -struct hdac_widget { - nid_t nid; - int type; - int enable; - int nconns, selconn; - int waspin; - uint32_t pflags; - int bindas; - int bindseqmask; - int ossdev; - uint32_t ossmask; - nid_t conns[HDA_MAX_CONNS]; - u_char connsenable[HDA_MAX_CONNS]; - char name[HDA_MAX_NAMELEN]; - struct hdac_devinfo *devinfo; - struct { - uint32_t widget_cap; - uint32_t outamp_cap; - uint32_t inamp_cap; - uint32_t supp_stream_formats; - uint32_t supp_pcm_size_rate; - uint32_t eapdbtl; - } param; - union { - struct { - uint32_t config; - uint32_t cap; - uint32_t ctrl; - } pin; - } wclass; -}; - -struct hdac_audio_ctl { - struct hdac_widget *widget, *childwidget; - int enable; - int index, dir, ndir; - int mute, step, size, offset; - int left, right, forcemute; - uint32_t muted; - uint32_t ossmask, possmask; -}; - -/* Association is a group of pins bound for some special function. */ -struct hdac_audio_as { - u_char enable; - u_char index; - u_char dir; - u_char pincnt; - u_char fakeredir; - u_char digital; - uint16_t pinset; - nid_t hpredir; - nid_t pins[16]; - nid_t dacs[16]; - int chan; -}; - -struct hdac_pcm_devinfo { - device_t dev; - struct hdac_devinfo *devinfo; - int index; - int registered; - int play, rec; - u_char left[SOUND_MIXER_NRDEVICES]; - u_char right[SOUND_MIXER_NRDEVICES]; - int chan_size; - int chan_blkcnt; - u_char digital; -}; - -/**************************************************************************** - * struct hdac_devinfo - * - * Holds all the parameters of a given codec function group. This is stored - * in the ivar of each child of the hdac bus - ****************************************************************************/ -struct hdac_devinfo { - uint8_t node_type; - nid_t nid; - nid_t startnode, endnode; - int nodecnt; - struct hdac_codec *codec; - struct hdac_widget *widget; - union { - struct { - uint32_t outamp_cap; - uint32_t inamp_cap; - uint32_t supp_stream_formats; - uint32_t supp_pcm_size_rate; - int ctlcnt, ascnt; - struct hdac_audio_ctl *ctl; - struct hdac_audio_as *as; - uint32_t quirks; - uint32_t gpio; - struct hdac_pcm_devinfo *devs; - int num_devs; - } audio; - /* XXX undefined: modem, hdmi. */ - } function; -}; - -#define HDAC_CHN_RUNNING 0x00000001 -#define HDAC_CHN_SUSPEND 0x00000002 - -struct hdac_chan { - struct snd_dbuf *b; - struct pcm_channel *c; - struct pcmchan_caps caps; - struct hdac_devinfo *devinfo; - struct hdac_pcm_devinfo *pdevinfo; - struct hdac_dma bdl_dma; - uint32_t spd, fmt, fmtlist[16], pcmrates[16]; - uint32_t supp_stream_formats, supp_pcm_size_rate; - uint32_t ptr, prevptr, blkcnt, blksz; - uint32_t *dmapos; - uint32_t flags; - int dir; - int off; - int sid; - int bit16, bit32; - int as; - nid_t io[16]; -}; - -/**************************************************************************** - * struct hdac_codec - * - ****************************************************************************/ -struct hdac_codec { - int verbs_sent; - int responses_received; - nid_t cad; - uint16_t vendor_id; - uint16_t device_id; - uint8_t revision_id; - uint8_t stepping_id; - struct hdac_command_list *commands; - struct hdac_softc *sc; - struct hdac_devinfo *fgs; - int num_fgs; +struct hdac_stream { + device_t dev; + struct hdac_dma bdl; + int dir; + int stream; + int blksz; + int running; + uint16_t format; }; -/**************************************************************************** - * struct hdac_softc - * - * This structure holds the current state of the hdac driver. - ****************************************************************************/ - -#define HDAC_F_DMA_NOCACHE 0x00000001 -#define HDAC_F_MSI 0x00000002 - struct hdac_softc { device_t dev; - device_t hdabus; struct mtx *lock; struct intr_config_hook intrhook; struct hdac_mem mem; struct hdac_irq irq; - uint32_t pci_subvendor; + uint32_t quirks_on; + uint32_t quirks_off; uint32_t flags; +#define HDAC_F_DMA_NOCACHE 0x00000001 - struct hdac_chan *chans; - int num_chans; int num_iss; int num_oss; int num_bss; + int num_ss; int num_sdo; int support_64bit; - int streamcnt; int corb_size; struct hdac_dma corb_dma; @@ -356,18 +191,13 @@ struct hdac_softc { bus_dma_tag_t chan_dmat; - /* - * Polling - */ + /* Polling */ int polling; - int poll_ticks; int poll_ival; - struct callout poll_hda; - struct callout poll_hdac; - struct callout poll_jack; + struct callout poll_callout; + int unsol_registered; struct task unsolq_task; - #define HDAC_UNSOLQ_MAX 64 #define HDAC_UNSOLQ_READY 0 #define HDAC_UNSOLQ_BUSY 1 @@ -376,13 +206,17 @@ struct hdac_softc { int unsolq_st; uint32_t unsolq[HDAC_UNSOLQ_MAX]; - struct hdac_codec *codecs[HDAC_CODEC_MAX]; -}; + struct hdac_stream *streams; -/**************************************************************************** - * struct hdac_command flags - ****************************************************************************/ -#define HDAC_COMMAND_FLAG_WAITOK 0x0000 -#define HDAC_COMMAND_FLAG_NOWAIT 0x0001 + struct { + device_t dev; + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision_id; + uint8_t stepping_id; + int pending; + uint32_t response; + } codecs[HDAC_CODEC_MAX]; +}; #endif diff --git a/sys/dev/sound/pci/hda/hdac_reg.h b/sys/dev/sound/pci/hda/hdac_reg.h index 813af72..893e3d2 100644 --- a/sys/dev/sound/pci/hda/hdac_reg.h +++ b/sys/dev/sound/pci/hda/hdac_reg.h @@ -254,10 +254,10 @@ #define HDAC_SDCTL_IOCE 0x000004 #define HDAC_SDCTL_FEIE 0x000008 #define HDAC_SDCTL_DEIE 0x000010 -#define HDAC_SDCTL_STRIPE_MASK 0x030000 -#define HDAC_SDCTL_STRIPE_SHIFT 16 -#define HDAC_SDCTL_TP 0x040000 -#define HDAC_SDCTL_DIR 0x080000 +#define HDAC_SDCTL2_STRIPE_MASK 0x03 +#define HDAC_SDCTL2_STRIPE_SHIFT 0 +#define HDAC_SDCTL2_TP 0x04 +#define HDAC_SDCTL2_DIR 0x08 #define HDAC_SDCTL2_STRM_MASK 0xf0 #define HDAC_SDCTL2_STRM_SHIFT 4 diff --git a/sys/dev/sound/pci/hda/hdacc.c b/sys/dev/sound/pci/hda/hdacc.c new file mode 100644 index 0000000..47feb1b --- /dev/null +++ b/sys/dev/sound/pci/hda/hdacc.c @@ -0,0 +1,662 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca> + * Copyright (c) 2006 Ariff Abdullah <ariff@FreeBSD.org> + * Copyright (c) 2008-2012 Alexander Motin <mav@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Intel High Definition Audio (CODEC) driver for FreeBSD. + */ + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_snd.h" +#endif + +#include <dev/sound/pcm/sound.h> + +#include <sys/ctype.h> + +#include <dev/sound/pci/hda/hda_reg.h> +#include <dev/sound/pci/hda/hdac.h> + +SND_DECLARE_FILE("$FreeBSD$"); + +struct hdacc_fg { + device_t dev; + nid_t nid; + uint8_t type; + uint32_t subsystem_id; +}; + +struct hdacc_softc { + device_t dev; + struct mtx *lock; + nid_t cad; + device_t streams[2][16]; + device_t tags[64]; + int fgcnt; + struct hdacc_fg *fgs; +}; + +#define hdacc_lock(codec) snd_mtxlock((codec)->lock) +#define hdacc_unlock(codec) snd_mtxunlock((codec)->lock) +#define hdacc_lockassert(codec) snd_mtxassert((codec)->lock) +#define hdacc_lockowned(codec) mtx_owned((codec)->lock) + +MALLOC_DEFINE(M_HDACC, "hdacc", "HDA CODEC"); + +/* CODECs */ +static const struct { + uint32_t id; + uint16_t revid; + char *name; +} hdacc_codecs[] = { + { HDA_CODEC_CS4206, 0, "Cirrus Logic CS4206" }, + { HDA_CODEC_CS4207, 0, "Cirrus Logic CS4207" }, + { HDA_CODEC_CS4210, 0, "Cirrus Logic CS4210" }, + { HDA_CODEC_ALC221, 0, "Realtek ALC221" }, + { HDA_CODEC_ALC260, 0, "Realtek ALC260" }, + { HDA_CODEC_ALC262, 0, "Realtek ALC262" }, + { HDA_CODEC_ALC267, 0, "Realtek ALC267" }, + { HDA_CODEC_ALC268, 0, "Realtek ALC268" }, + { HDA_CODEC_ALC269, 0, "Realtek ALC269" }, + { HDA_CODEC_ALC270, 0, "Realtek ALC270" }, + { HDA_CODEC_ALC272, 0, "Realtek ALC272" }, + { HDA_CODEC_ALC273, 0, "Realtek ALC273" }, + { HDA_CODEC_ALC275, 0, "Realtek ALC275" }, + { HDA_CODEC_ALC276, 0, "Realtek ALC276" }, + { HDA_CODEC_ALC660, 0, "Realtek ALC660-VD" }, + { HDA_CODEC_ALC662, 0x0002, "Realtek ALC662 rev2" }, + { HDA_CODEC_ALC662, 0, "Realtek ALC662" }, + { HDA_CODEC_ALC663, 0, "Realtek ALC663" }, + { HDA_CODEC_ALC665, 0, "Realtek ALC665" }, + { HDA_CODEC_ALC861, 0x0340, "Realtek ALC660" }, + { HDA_CODEC_ALC861, 0, "Realtek ALC861" }, + { HDA_CODEC_ALC861VD, 0, "Realtek ALC861-VD" }, + { HDA_CODEC_ALC880, 0, "Realtek ALC880" }, + { HDA_CODEC_ALC882, 0, "Realtek ALC882" }, + { HDA_CODEC_ALC883, 0, "Realtek ALC883" }, + { HDA_CODEC_ALC885, 0x0101, "Realtek ALC889A" }, + { HDA_CODEC_ALC885, 0x0103, "Realtek ALC889A" }, + { HDA_CODEC_ALC885, 0, "Realtek ALC885" }, + { HDA_CODEC_ALC887, 0, "Realtek ALC887" }, + { HDA_CODEC_ALC888, 0x0101, "Realtek ALC1200" }, + { HDA_CODEC_ALC888, 0, "Realtek ALC888" }, + { HDA_CODEC_ALC889, 0, "Realtek ALC889" }, + { HDA_CODEC_ALC892, 0, "Realtek ALC892" }, + { HDA_CODEC_ALC899, 0, "Realtek ALC899" }, + { HDA_CODEC_AD1882, 0, "Analog Devices AD1882" }, + { HDA_CODEC_AD1882A, 0, "Analog Devices AD1882A" }, + { HDA_CODEC_AD1883, 0, "Analog Devices AD1883" }, + { HDA_CODEC_AD1884, 0, "Analog Devices AD1884" }, + { HDA_CODEC_AD1884A, 0, "Analog Devices AD1884A" }, + { HDA_CODEC_AD1981HD, 0, "Analog Devices AD1981HD" }, + { HDA_CODEC_AD1983, 0, "Analog Devices AD1983" }, + { HDA_CODEC_AD1984, 0, "Analog Devices AD1984" }, + { HDA_CODEC_AD1984A, 0, "Analog Devices AD1984A" }, + { HDA_CODEC_AD1984B, 0, "Analog Devices AD1984B" }, + { HDA_CODEC_AD1986A, 0, "Analog Devices AD1986A" }, + { HDA_CODEC_AD1987, 0, "Analog Devices AD1987" }, + { HDA_CODEC_AD1988, 0, "Analog Devices AD1988A" }, + { HDA_CODEC_AD1988B, 0, "Analog Devices AD1988B" }, + { HDA_CODEC_AD1989A, 0, "Analog Devices AD1989A" }, + { HDA_CODEC_AD1989B, 0, "Analog Devices AD1989B" }, + { HDA_CODEC_CMI9880, 0, "CMedia CMI9880" }, + { HDA_CODEC_CXD9872RDK, 0, "Sigmatel CXD9872RD/K" }, + { HDA_CODEC_CXD9872AKD, 0, "Sigmatel CXD9872AKD" }, + { HDA_CODEC_STAC9200D, 0, "Sigmatel STAC9200D" }, + { HDA_CODEC_STAC9204X, 0, "Sigmatel STAC9204X" }, + { HDA_CODEC_STAC9204D, 0, "Sigmatel STAC9204D" }, + { HDA_CODEC_STAC9205X, 0, "Sigmatel STAC9205X" }, + { HDA_CODEC_STAC9205D, 0, "Sigmatel STAC9205D" }, + { HDA_CODEC_STAC9220, 0, "Sigmatel STAC9220" }, + { HDA_CODEC_STAC9220_A1, 0, "Sigmatel STAC9220_A1" }, + { HDA_CODEC_STAC9220_A2, 0, "Sigmatel STAC9220_A2" }, + { HDA_CODEC_STAC9221, 0, "Sigmatel STAC9221" }, + { HDA_CODEC_STAC9221_A2, 0, "Sigmatel STAC9221_A2" }, + { HDA_CODEC_STAC9221D, 0, "Sigmatel STAC9221D" }, + { HDA_CODEC_STAC922XD, 0, "Sigmatel STAC9220D/9223D" }, + { HDA_CODEC_STAC9227X, 0, "Sigmatel STAC9227X" }, + { HDA_CODEC_STAC9227D, 0, "Sigmatel STAC9227D" }, + { HDA_CODEC_STAC9228X, 0, "Sigmatel STAC9228X" }, + { HDA_CODEC_STAC9228D, 0, "Sigmatel STAC9228D" }, + { HDA_CODEC_STAC9229X, 0, "Sigmatel STAC9229X" }, + { HDA_CODEC_STAC9229D, 0, "Sigmatel STAC9229D" }, + { HDA_CODEC_STAC9230X, 0, "Sigmatel STAC9230X" }, + { HDA_CODEC_STAC9230D, 0, "Sigmatel STAC9230D" }, + { HDA_CODEC_STAC9250, 0, "Sigmatel STAC9250" }, + { HDA_CODEC_STAC9251, 0, "Sigmatel STAC9251" }, + { HDA_CODEC_STAC9271X, 0, "Sigmatel STAC9271X" }, + { HDA_CODEC_STAC9271D, 0, "Sigmatel STAC9271D" }, + { HDA_CODEC_STAC9272X, 0, "Sigmatel STAC9272X" }, + { HDA_CODEC_STAC9272D, 0, "Sigmatel STAC9272D" }, + { HDA_CODEC_STAC9273X, 0, "Sigmatel STAC9273X" }, + { HDA_CODEC_STAC9273D, 0, "Sigmatel STAC9273D" }, + { HDA_CODEC_STAC9274, 0, "Sigmatel STAC9274" }, + { HDA_CODEC_STAC9274D, 0, "Sigmatel STAC9274D" }, + { HDA_CODEC_STAC9274X5NH, 0, "Sigmatel STAC9274X5NH" }, + { HDA_CODEC_STAC9274D5NH, 0, "Sigmatel STAC9274D5NH" }, + { HDA_CODEC_STAC9872AK, 0, "Sigmatel STAC9872AK" }, + { HDA_CODEC_IDT92HD005, 0, "IDT 92HD005" }, + { HDA_CODEC_IDT92HD005D, 0, "IDT 92HD005D" }, + { HDA_CODEC_IDT92HD206X, 0, "IDT 92HD206X" }, + { HDA_CODEC_IDT92HD206D, 0, "IDT 92HD206D" }, + { HDA_CODEC_IDT92HD700X, 0, "IDT 92HD700X" }, + { HDA_CODEC_IDT92HD700D, 0, "IDT 92HD700D" }, + { HDA_CODEC_IDT92HD71B5, 0, "IDT 92HD71B5" }, + { HDA_CODEC_IDT92HD71B7, 0, "IDT 92HD71B7" }, + { HDA_CODEC_IDT92HD71B8, 0, "IDT 92HD71B8" }, + { HDA_CODEC_IDT92HD73C1, 0, "IDT 92HD73C1" }, + { HDA_CODEC_IDT92HD73D1, 0, "IDT 92HD73D1" }, + { HDA_CODEC_IDT92HD73E1, 0, "IDT 92HD73E1" }, + { HDA_CODEC_IDT92HD75B3, 0, "IDT 92HD75B3" }, + { HDA_CODEC_IDT92HD75BX, 0, "IDT 92HD75BX" }, + { HDA_CODEC_IDT92HD81B1C, 0, "IDT 92HD81B1C" }, + { HDA_CODEC_IDT92HD81B1X, 0, "IDT 92HD81B1X" }, + { HDA_CODEC_IDT92HD83C1C, 0, "IDT 92HD83C1C" }, + { HDA_CODEC_IDT92HD83C1X, 0, "IDT 92HD83C1X" }, + { HDA_CODEC_CX20549, 0, "Conexant CX20549 (Venice)" }, + { HDA_CODEC_CX20551, 0, "Conexant CX20551 (Waikiki)" }, + { HDA_CODEC_CX20561, 0, "Conexant CX20561 (Hermosa)" }, + { HDA_CODEC_CX20582, 0, "Conexant CX20582 (Pebble)" }, + { HDA_CODEC_CX20583, 0, "Conexant CX20583 (Pebble HSF)" }, + { HDA_CODEC_CX20584, 0, "Conexant CX20584" }, + { HDA_CODEC_CX20585, 0, "Conexant CX20585" }, + { HDA_CODEC_CX20588, 0, "Conexant CX20588" }, + { HDA_CODEC_CX20590, 0, "Conexant CX20590" }, + { HDA_CODEC_CX20631, 0, "Conexant CX20631" }, + { HDA_CODEC_CX20632, 0, "Conexant CX20632" }, + { HDA_CODEC_CX20641, 0, "Conexant CX20641" }, + { HDA_CODEC_CX20642, 0, "Conexant CX20642" }, + { HDA_CODEC_CX20651, 0, "Conexant CX20651" }, + { HDA_CODEC_CX20652, 0, "Conexant CX20652" }, + { HDA_CODEC_CX20664, 0, "Conexant CX20664" }, + { HDA_CODEC_CX20665, 0, "Conexant CX20665" }, + { HDA_CODEC_VT1708_8, 0, "VIA VT1708_8" }, + { HDA_CODEC_VT1708_9, 0, "VIA VT1708_9" }, + { HDA_CODEC_VT1708_A, 0, "VIA VT1708_A" }, + { HDA_CODEC_VT1708_B, 0, "VIA VT1708_B" }, + { HDA_CODEC_VT1709_0, 0, "VIA VT1709_0" }, + { HDA_CODEC_VT1709_1, 0, "VIA VT1709_1" }, + { HDA_CODEC_VT1709_2, 0, "VIA VT1709_2" }, + { HDA_CODEC_VT1709_3, 0, "VIA VT1709_3" }, + { HDA_CODEC_VT1709_4, 0, "VIA VT1709_4" }, + { HDA_CODEC_VT1709_5, 0, "VIA VT1709_5" }, + { HDA_CODEC_VT1709_6, 0, "VIA VT1709_6" }, + { HDA_CODEC_VT1709_7, 0, "VIA VT1709_7" }, + { HDA_CODEC_VT1708B_0, 0, "VIA VT1708B_0" }, + { HDA_CODEC_VT1708B_1, 0, "VIA VT1708B_1" }, + { HDA_CODEC_VT1708B_2, 0, "VIA VT1708B_2" }, + { HDA_CODEC_VT1708B_3, 0, "VIA VT1708B_3" }, + { HDA_CODEC_VT1708B_4, 0, "VIA VT1708B_4" }, + { HDA_CODEC_VT1708B_5, 0, "VIA VT1708B_5" }, + { HDA_CODEC_VT1708B_6, 0, "VIA VT1708B_6" }, + { HDA_CODEC_VT1708B_7, 0, "VIA VT1708B_7" }, + { HDA_CODEC_VT1708S_0, 0, "VIA VT1708S_0" }, + { HDA_CODEC_VT1708S_1, 0, "VIA VT1708S_1" }, + { HDA_CODEC_VT1708S_2, 0, "VIA VT1708S_2" }, + { HDA_CODEC_VT1708S_3, 0, "VIA VT1708S_3" }, + { HDA_CODEC_VT1708S_4, 0, "VIA VT1708S_4" }, + { HDA_CODEC_VT1708S_5, 0, "VIA VT1708S_5" }, + { HDA_CODEC_VT1708S_6, 0, "VIA VT1708S_6" }, + { HDA_CODEC_VT1708S_7, 0, "VIA VT1708S_7" }, + { HDA_CODEC_VT1702_0, 0, "VIA VT1702_0" }, + { HDA_CODEC_VT1702_1, 0, "VIA VT1702_1" }, + { HDA_CODEC_VT1702_2, 0, "VIA VT1702_2" }, + { HDA_CODEC_VT1702_3, 0, "VIA VT1702_3" }, + { HDA_CODEC_VT1702_4, 0, "VIA VT1702_4" }, + { HDA_CODEC_VT1702_5, 0, "VIA VT1702_5" }, + { HDA_CODEC_VT1702_6, 0, "VIA VT1702_6" }, + { HDA_CODEC_VT1702_7, 0, "VIA VT1702_7" }, + { HDA_CODEC_VT1716S_0, 0, "VIA VT1716S_0" }, + { HDA_CODEC_VT1716S_1, 0, "VIA VT1716S_1" }, + { HDA_CODEC_VT1718S_0, 0, "VIA VT1718S_0" }, + { HDA_CODEC_VT1718S_1, 0, "VIA VT1718S_1" }, + { HDA_CODEC_VT1802_0, 0, "VIA VT1802_0" }, + { HDA_CODEC_VT1802_1, 0, "VIA VT1802_1" }, + { HDA_CODEC_VT1812, 0, "VIA VT1812" }, + { HDA_CODEC_VT1818S, 0, "VIA VT1818S" }, + { HDA_CODEC_VT1828S, 0, "VIA VT1828S" }, + { HDA_CODEC_VT2002P_0, 0, "VIA VT2002P_0" }, + { HDA_CODEC_VT2002P_1, 0, "VIA VT2002P_1" }, + { HDA_CODEC_VT2020, 0, "VIA VT2020" }, + { HDA_CODEC_ATIRS600_1, 0, "ATI RS600" }, + { HDA_CODEC_ATIRS600_2, 0, "ATI RS600" }, + { HDA_CODEC_ATIRS690, 0, "ATI RS690/780" }, + { HDA_CODEC_ATIR6XX, 0, "ATI R6xx" }, + { HDA_CODEC_NVIDIAMCP67, 0, "NVIDIA MCP67" }, + { HDA_CODEC_NVIDIAMCP73, 0, "NVIDIA MCP73" }, + { HDA_CODEC_NVIDIAMCP78, 0, "NVIDIA MCP78" }, + { HDA_CODEC_NVIDIAMCP78_2, 0, "NVIDIA MCP78" }, + { HDA_CODEC_NVIDIAMCP7A, 0, "NVIDIA MCP7A" }, + { HDA_CODEC_NVIDIAGT220, 0, "NVIDIA GT220" }, + { HDA_CODEC_NVIDIAGT21X, 0, "NVIDIA GT21x" }, + { HDA_CODEC_NVIDIAMCP89, 0, "NVIDIA MCP89" }, + { HDA_CODEC_NVIDIAGT240, 0, "NVIDIA GT240" }, + { HDA_CODEC_INTELIP, 0, "Intel Ibex Peak" }, + { HDA_CODEC_INTELBL, 0, "Intel Bearlake" }, + { HDA_CODEC_INTELCA, 0, "Intel Cantiga" }, + { HDA_CODEC_INTELEL, 0, "Intel Eaglelake" }, + { HDA_CODEC_INTELIP2, 0, "Intel Ibex Peak" }, + { HDA_CODEC_INTELCPT, 0, "Intel Cougar Point" }, + { HDA_CODEC_INTELPPT, 0, "Intel Panther Point" }, + { HDA_CODEC_INTELCL, 0, "Intel Crestline" }, + { HDA_CODEC_SII1390, 0, "Silicon Image SiI1390" }, + { HDA_CODEC_SII1392, 0, "Silicon Image SiI1392" }, + /* Unknown CODECs */ + { HDA_CODEC_ALCXXXX, 0, "Realtek (Unknown)" }, + { HDA_CODEC_ADXXXX, 0, "Analog Devices (Unknown)" }, + { HDA_CODEC_CSXXXX, 0, "Cirrus Logic (Unknown)" }, + { HDA_CODEC_CMIXXXX, 0, "CMedia (Unknown)" }, + { HDA_CODEC_STACXXXX, 0, "Sigmatel (Unknown)" }, + { HDA_CODEC_SIIXXXX, 0, "Silicon Image (Unknown)" }, + { HDA_CODEC_AGEREXXXX, 0, "Lucent/Agere Systems (Unknown)" }, + { HDA_CODEC_CXXXXX, 0, "Conexant (Unknown)" }, + { HDA_CODEC_VTXXXX, 0, "VIA (Unknown)" }, + { HDA_CODEC_ATIXXXX, 0, "ATI (Unknown)" }, + { HDA_CODEC_NVIDIAXXXX, 0, "NVIDIA (Unknown)" }, + { HDA_CODEC_INTELXXXX, 0, "Intel (Unknown)" }, + { HDA_CODEC_IDTXXXX, 0, "IDT (Unknown)" }, +}; +#define HDACC_CODECS_LEN (sizeof(hdacc_codecs) / sizeof(hdacc_codecs[0])) + + +/**************************************************************************** + * Function prototypes + ****************************************************************************/ + +static char * +hdacc_codec_name(uint32_t id, uint16_t revid) +{ + int i; + + for (i = 0; i < HDACC_CODECS_LEN; i++) { + if (!HDA_DEV_MATCH(hdacc_codecs[i].id, id)) + continue; + if (hdacc_codecs[i].revid != 0 && + hdacc_codecs[i].revid != revid) + continue; + return (hdacc_codecs[i].name); + } + + return ((id == 0x00000000) ? "NULL CODEC" : "Unknown CODEC"); +} + +static int +hdacc_suspend(device_t dev) +{ + + HDA_BOOTHVERBOSE( + device_printf(dev, "Suspend...\n"); + ); + bus_generic_suspend(dev); + HDA_BOOTHVERBOSE( + device_printf(dev, "Suspend done\n"); + ); + return (0); +} + +static int +hdacc_resume(device_t dev) +{ + + HDA_BOOTHVERBOSE( + device_printf(dev, "Resume...\n"); + ); + bus_generic_resume(dev); + HDA_BOOTHVERBOSE( + device_printf(dev, "Resume done\n"); + ); + return (0); +} + +static int +hdacc_probe(device_t dev) +{ + uint32_t id, revid; + char buf[128]; + + id = ((uint32_t)hda_get_vendor_id(dev) << 16) + hda_get_device_id(dev); + revid = ((uint32_t)hda_get_revision_id(dev) << 8) + hda_get_stepping_id(dev); + snprintf(buf, sizeof(buf), "%s HDA CODEC", hdacc_codec_name(id, revid)); + device_set_desc_copy(dev, buf); + return (BUS_PROBE_DEFAULT); +} + +static int +hdacc_attach(device_t dev) +{ + struct hdacc_softc *codec = device_get_softc(dev); + device_t child; + int cad = (intptr_t)device_get_ivars(dev); + uint32_t subnode; + int startnode; + int endnode; + int i, n; + + codec->lock = HDAC_GET_MTX(device_get_parent(dev), dev); + codec->dev = dev; + codec->cad = cad; + + hdacc_lock(codec); + subnode = hda_command(dev, + HDA_CMD_GET_PARAMETER(0, 0x0, HDA_PARAM_SUB_NODE_COUNT)); + hdacc_unlock(codec); + if (subnode == HDA_INVALID) + return (EIO); + codec->fgcnt = HDA_PARAM_SUB_NODE_COUNT_TOTAL(subnode); + startnode = HDA_PARAM_SUB_NODE_COUNT_START(subnode); + endnode = startnode + codec->fgcnt; + + HDA_BOOTVERBOSE( + device_printf(dev, + "Root Node at nid=0: %d subnodes %d-%d\n", + HDA_PARAM_SUB_NODE_COUNT_TOTAL(subnode), + startnode, endnode - 1); + ); + + codec->fgs = malloc(sizeof(struct hdacc_fg) * codec->fgcnt, + M_HDACC, M_ZERO | M_WAITOK); + for (i = startnode, n = 0; i < endnode; i++, n++) { + codec->fgs[n].nid = i; + hdacc_lock(codec); + codec->fgs[n].type = + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE(hda_command(dev, + HDA_CMD_GET_PARAMETER(0, i, HDA_PARAM_FCT_GRP_TYPE))); + codec->fgs[n].subsystem_id = hda_command(dev, + HDA_CMD_GET_SUBSYSTEM_ID(0, i)); + hdacc_unlock(codec); + codec->fgs[n].dev = child = device_add_child(dev, NULL, -1); + if (child == NULL) { + device_printf(dev, "Failed to add function device\n"); + continue; + } + device_set_ivars(child, &codec->fgs[n]); + } + + bus_generic_attach(dev); + + return (0); +} + +static int +hdacc_detach(device_t dev) +{ + + return (device_delete_children(dev)); +} + +static int +hdacc_child_location_str(device_t dev, device_t child, char *buf, + size_t buflen) +{ + struct hdacc_fg *fg = device_get_ivars(child); + + snprintf(buf, buflen, "nid=%d", fg->nid); + return (0); +} + +static int +hdacc_child_pnpinfo_str_method(device_t dev, device_t child, char *buf, + size_t buflen) +{ + struct hdacc_fg *fg = device_get_ivars(child); + + snprintf(buf, buflen, "type=0x%02x subsystem=0x%08x", + fg->type, fg->subsystem_id); + return (0); +} + +static int +hdacc_print_child(device_t dev, device_t child) +{ + struct hdacc_fg *fg = device_get_ivars(child); + int retval; + + retval = bus_print_child_header(dev, child); + retval += printf(" at nid %d", fg->nid); + retval += bus_print_child_footer(dev, child); + + return (retval); +} + +static void +hdacc_probe_nomatch(device_t dev, device_t child) +{ + struct hdacc_softc *codec = device_get_softc(dev); + struct hdacc_fg *fg = device_get_ivars(child); + + device_printf(child, "<%s %s Function Group> at nid %d on %s " + "(no driver attached)\n", + device_get_desc(dev), + fg->type == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO ? "Audio" : + (fg->type == HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MODEM ? "Modem" : + "Unknown"), fg->nid, device_get_nameunit(dev)); + HDA_BOOTHVERBOSE( + device_printf(dev, "Power down FG nid=%d to the D3 state...\n", + fg->nid); + ); + hdacc_lock(codec); + hda_command(dev, HDA_CMD_SET_POWER_STATE(0, + fg->nid, HDA_CMD_POWER_STATE_D3)); + hdacc_unlock(codec); +} + +static int +hdacc_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +{ + struct hdacc_fg *fg = device_get_ivars(child); + + switch (which) { + case HDA_IVAR_NODE_ID: + *result = fg->nid; + break; + case HDA_IVAR_NODE_TYPE: + *result = fg->type; + break; + case HDA_IVAR_SUBSYSTEM_ID: + *result = fg->subsystem_id; + break; + default: + return(BUS_READ_IVAR(device_get_parent(dev), dev, + which, result)); + } + return (0); +} + +static struct mtx * +hdacc_get_mtx(device_t dev, device_t child) +{ + struct hdacc_softc *codec = device_get_softc(dev); + + return (codec->lock); +} + +static uint32_t +hdacc_codec_command(device_t dev, device_t child, uint32_t verb) +{ + + return (HDAC_CODEC_COMMAND(device_get_parent(dev), dev, verb)); +} + +static int +hdacc_stream_alloc(device_t dev, device_t child, int dir, int format, + uint32_t **dmapos) +{ + struct hdacc_softc *codec = device_get_softc(dev); + int stream; + + stream = HDAC_STREAM_ALLOC(device_get_parent(dev), dev, + dir, format, dmapos); + if (stream > 0) + codec->streams[dir][stream] = child; + return (stream); +} + +static void +hdacc_stream_free(device_t dev, device_t child, int dir, int stream) +{ + struct hdacc_softc *codec = device_get_softc(dev); + + codec->streams[dir][stream] = NULL; + HDAC_STREAM_FREE(device_get_parent(dev), dev, dir, stream); +} + +static int +hdacc_stream_start(device_t dev, device_t child, + int dir, int stream, bus_addr_t buf, int blksz, int blkcnt) +{ + + return (HDAC_STREAM_START(device_get_parent(dev), dev, + dir, stream, buf, blksz, blkcnt)); +} + +static void +hdacc_stream_stop(device_t dev, device_t child, int dir, int stream) +{ + + HDAC_STREAM_STOP(device_get_parent(dev), dev, dir, stream); +} + +static void +hdacc_stream_reset(device_t dev, device_t child, int dir, int stream) +{ + + HDAC_STREAM_RESET(device_get_parent(dev), dev, dir, stream); +} + +static uint32_t +hdacc_stream_getptr(device_t dev, device_t child, int dir, int stream) +{ + + return (HDAC_STREAM_GETPTR(device_get_parent(dev), dev, dir, stream)); +} + +static void +hdacc_stream_intr(device_t dev, int dir, int stream) +{ + struct hdacc_softc *codec = device_get_softc(dev); + device_t child; + + if ((child = codec->streams[dir][stream]) != NULL); + HDAC_STREAM_INTR(child, dir, stream); +} + +static int +hdacc_unsol_alloc(device_t dev, device_t child, int wanted) +{ + struct hdacc_softc *codec = device_get_softc(dev); + int tag; + + wanted &= 0x3f; + tag = wanted; + do { + if (codec->tags[tag] == NULL) { + codec->tags[tag] = child; + HDAC_UNSOL_ALLOC(device_get_parent(dev), dev, tag); + return (tag); + } + tag++; + tag &= 0x3f; + } while (tag != wanted); + return (-1); +} + +static void +hdacc_unsol_free(device_t dev, device_t child, int tag) +{ + struct hdacc_softc *codec = device_get_softc(dev); + + KASSERT(tag >= 0 && tag <= 0x3f, ("Wrong tag value %d\n", tag)); + codec->tags[tag] = NULL; + HDAC_UNSOL_FREE(device_get_parent(dev), dev, tag); +} + +static void +hdacc_unsol_intr(device_t dev, uint32_t resp) +{ + struct hdacc_softc *codec = device_get_softc(dev); + device_t child; + int tag; + + tag = resp >> 26; + if ((child = codec->tags[tag]) != NULL) + HDAC_UNSOL_INTR(child, resp); + else + device_printf(codec->dev, "Unexpected unsolicited " + "response with tag %d: %08x\n", tag, resp); +} + +static void +hdacc_pindump(device_t dev) +{ + device_t *devlist; + int devcount, i; + + if (device_get_children(dev, &devlist, &devcount) != 0) + return; + for (i = 0; i < devcount; i++) + HDAC_PINDUMP(devlist[i]); + free(devlist, M_TEMP); +} + +static device_method_t hdacc_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, hdacc_probe), + DEVMETHOD(device_attach, hdacc_attach), + DEVMETHOD(device_detach, hdacc_detach), + DEVMETHOD(device_suspend, hdacc_suspend), + DEVMETHOD(device_resume, hdacc_resume), + /* Bus interface */ + DEVMETHOD(bus_child_location_str, hdacc_child_location_str), + DEVMETHOD(bus_child_pnpinfo_str, hdacc_child_pnpinfo_str_method), + DEVMETHOD(bus_print_child, hdacc_print_child), + DEVMETHOD(bus_probe_nomatch, hdacc_probe_nomatch), + DEVMETHOD(bus_read_ivar, hdacc_read_ivar), + DEVMETHOD(hdac_get_mtx, hdacc_get_mtx), + DEVMETHOD(hdac_codec_command, hdacc_codec_command), + DEVMETHOD(hdac_stream_alloc, hdacc_stream_alloc), + DEVMETHOD(hdac_stream_free, hdacc_stream_free), + DEVMETHOD(hdac_stream_start, hdacc_stream_start), + DEVMETHOD(hdac_stream_stop, hdacc_stream_stop), + DEVMETHOD(hdac_stream_reset, hdacc_stream_reset), + DEVMETHOD(hdac_stream_getptr, hdacc_stream_getptr), + DEVMETHOD(hdac_stream_intr, hdacc_stream_intr), + DEVMETHOD(hdac_unsol_alloc, hdacc_unsol_alloc), + DEVMETHOD(hdac_unsol_free, hdacc_unsol_free), + DEVMETHOD(hdac_unsol_intr, hdacc_unsol_intr), + DEVMETHOD(hdac_pindump, hdacc_pindump), + { 0, 0 } +}; + +static driver_t hdacc_driver = { + "hdacc", + hdacc_methods, + sizeof(struct hdacc_softc), +}; + +static devclass_t hdacc_devclass; + +DRIVER_MODULE(snd_hda, hdac, hdacc_driver, hdacc_devclass, 0, 0); diff --git a/sys/modules/sound/driver/hda/Makefile b/sys/modules/sound/driver/hda/Makefile index 8508b5d..f136936 100644 --- a/sys/modules/sound/driver/hda/Makefile +++ b/sys/modules/sound/driver/hda/Makefile @@ -3,7 +3,8 @@ .PATH: ${.CURDIR}/../../../../dev/sound/pci/hda KMOD= snd_hda -SRCS= device_if.h bus_if.h pci_if.h channel_if.h mixer_if.h -SRCS+= hdac.c hdac_private.h hdac_reg.h hda_reg.h hdac.h +SRCS= device_if.h bus_if.h pci_if.h channel_if.h mixer_if.h hdac_if.h +SRCS+= hdaa.c hdaa.h hdaa_patches.c hdac.c hdac_if.h hdac_if.c +SRCS+= hdacc.c hdac_private.h hdac_reg.h hda_reg.h hdac.h .include <bsd.kmod.mk> |