summaryrefslogtreecommitdiff
path: root/kernel/drv/oss_hdaudio
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/drv/oss_hdaudio')
-rw-r--r--kernel/drv/oss_hdaudio/.changelog7
-rw-r--r--kernel/drv/oss_hdaudio/.config1
-rw-r--r--kernel/drv/oss_hdaudio/.devices27
-rw-r--r--kernel/drv/oss_hdaudio/.name1
-rw-r--r--kernel/drv/oss_hdaudio/.params24
-rw-r--r--kernel/drv/oss_hdaudio/hdaudio.h168
-rw-r--r--kernel/drv/oss_hdaudio/hdaudio_abit_AA8.c347
-rw-r--r--kernel/drv/oss_hdaudio/hdaudio_asus_P4B_E.c395
-rw-r--r--kernel/drv/oss_hdaudio/hdaudio_asus_m9.c36
-rw-r--r--kernel/drv/oss_hdaudio/hdaudio_codec.c3580
-rw-r--r--kernel/drv/oss_hdaudio/hdaudio_codec.h379
-rw-r--r--kernel/drv/oss_hdaudio/hdaudio_codecids.h1154
-rw-r--r--kernel/drv/oss_hdaudio/hdaudio_dedicated.h74
-rw-r--r--kernel/drv/oss_hdaudio/hdaudio_eeepc.c293
-rw-r--r--kernel/drv/oss_hdaudio/hdaudio_ferrari5k.c303
-rw-r--r--kernel/drv/oss_hdaudio/hdaudio_generic.c868
-rw-r--r--kernel/drv/oss_hdaudio/hdaudio_gpio_handlers.c128
-rw-r--r--kernel/drv/oss_hdaudio/hdaudio_mixers.h20
-rw-r--r--kernel/drv/oss_hdaudio/hdaudio_scaleoP.c277
-rw-r--r--kernel/drv/oss_hdaudio/hdaudio_si3055.c206
-rwxr-xr-xkernel/drv/oss_hdaudio/hdaudio_thinkpad_r61.c301
-rw-r--r--kernel/drv/oss_hdaudio/hdaudio_vaio_vgn.c144
-rw-r--r--kernel/drv/oss_hdaudio/oss_hdaudio.c1996
-rw-r--r--kernel/drv/oss_hdaudio/oss_hdaudio.man77
24 files changed, 10806 insertions, 0 deletions
diff --git a/kernel/drv/oss_hdaudio/.changelog b/kernel/drv/oss_hdaudio/.changelog
new file mode 100644
index 0000000..d26ec7a
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/.changelog
@@ -0,0 +1,7 @@
+20080111 by Hannu: Fixed problem of several jack widgets getting named as "lineout"
+
+20080205 by Hannu: Improved support for systems with multiple codecs.
+
+20080221 by Hannu: Changed default volumes in hdaudio_generic.c to be louder. Earlier the volumes were 80% now 90%.
+
+
diff --git a/kernel/drv/oss_hdaudio/.config b/kernel/drv/oss_hdaudio/.config
new file mode 100644
index 0000000..5280084
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/.config
@@ -0,0 +1 @@
+platform=i86pc
diff --git a/kernel/drv/oss_hdaudio/.devices b/kernel/drv/oss_hdaudio/.devices
new file mode 100644
index 0000000..7ee6bb3
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/.devices
@@ -0,0 +1,27 @@
+oss_hdaudio pci8086,2668 Intel High Definition Audio (ICH6)
+oss_hdaudio pci8086,27d8 Intel High Definition Audio (ICH7)
+oss_hdaudio pci8086,269a Intel High Definition Audio (ESB2)
+oss_hdaudio pci8086,284b Intel High Definition Audio (ICH8)
+oss_hdaudio pci8086,293e Intel High Definition Audio (P35)
+oss_hdaudio pci8086,293f Intel High Definition Audio (ICH9)
+oss_hdaudio pci8086,3a3e Intel High Definition Audio (ICH10)
+oss_hdaudio pci8086,3a6e Intel High Definition Audio (ICH10)
+oss_hdaudio pci8086,3b56 Intel High Definition Audio (PCH)
+oss_hdaudio pci8086,3b57 Intel High Definition Audio (PCH)
+oss_hdaudio pci8086,1c20 Intel High Definition Audio (CPT)
+oss_hdaudio pci8086,811b Intel High Definition Audio (SCH)
+oss_hdaudio pci10de,26c Nvidia High Definition Audio (MCP51)
+oss_hdaudio pci10de,371 Nvidia High Definition Audio (MCP55)
+oss_hdaudio pci10de,3e4 Nvidia High Definition Audio (MCP61)
+oss_hdaudio pci10de,3f0 Nvidia High Definition Audio (MCP61)
+oss_hdaudio pci10de,44a Nvidia High Definition Audio (MCP65)
+oss_hdaudio pci10de,55c Nvidia High Definition Audio (MCP67)
+oss_hdaudio pci10de,7fc Nvidia High Definition Audio (MCP73)
+oss_hdaudio pci10de,774 Nvidia High Definition Audio (MCP78S)
+oss_hdaudio pci10de,ac0 Nvidia High Definition Audio (MCP79)
+oss_hdaudio pci1002,437b ATI High Definition Audio (SB450)
+oss_hdaudio pci1002,4383 ATI High Definition Audio (SB600)
+oss_hdaudio pci1106,3288 VIA High Definition Audio
+oss_hdaudio pci1039,7502 SiS High Definition Audio
+oss_hdaudio pci10b9,5461 ULI High Definition Audio
+oss_hdaudio pci1102,9 Creative Labs SB XFi Xtreme
diff --git a/kernel/drv/oss_hdaudio/.name b/kernel/drv/oss_hdaudio/.name
new file mode 100644
index 0000000..21af427
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/.name
@@ -0,0 +1 @@
+High Definition Audio (Azalia)
diff --git a/kernel/drv/oss_hdaudio/.params b/kernel/drv/oss_hdaudio/.params
new file mode 100644
index 0000000..916648f
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/.params
@@ -0,0 +1,24 @@
+int hdaudio_snoopy=0;
+/*
+ * hdaudio_snopy is reserved for diagnostic purposes and it must be 0
+ * in all situations. Other values may make the driver vulnerable to
+ * DoS attacks. For security reasons only root can use this diagnostic
+ * interface.
+ */
+int hdaudio_jacksense=0;
+/*
+ * Setting hdaudio_jacksense=1 enables jack sensing mode when the
+ * hdaudio driver is loaded. In this mode all I/O pin's that are not
+ * in use will be disabled as well as the mixer controls that are related
+ * with them. In this way the mixer/control panel will become more intuitive.
+ * However OSS will need to be restarted with soundoff;soundon every time
+ * new inputs or outputs are attached to the audio jacks.
+ *
+ * NOTE! hdaudio_jacksense=1 works only in some systems. Many laptops and
+ * motherboards don't support jack sensing.
+ */
+int hdaudio_noskip=0;
+/*
+ * Disable checks to skip unconnected jack. Values: 0-7, where value is a
+ * bitmask - every bit disables another check. Can override hdaudio_jacksense.
+ */
diff --git a/kernel/drv/oss_hdaudio/hdaudio.h b/kernel/drv/oss_hdaudio/hdaudio.h
new file mode 100644
index 0000000..964fd7f
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/hdaudio.h
@@ -0,0 +1,168 @@
+/*
+ * Purpose: Common definitions for the hdaudio driver files
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+#define HDA_GCAP 0x00 /* Global Capabilities */
+#define HDA_VMIN 0x02 /* Minor Version */
+#define HDA_VMAJ 0x03 /* Major Version */
+#define HDA_OUTPAY 0x04 /* Output Payload Capability */
+#define HDA_INPAY 0x06 /* Input Payload Capability */
+#define HDA_GCTL 0x08 /* Global Control */
+# define CRST 0x00000001 /* Controller reset */
+#define HDA_WAKEEN 0x0C /* Wake Enable */
+#define HDA_STATESTS 0x0E /* Wake Status */
+#define HDA_GSTST 0x10 /* Global Status */
+#define HDA_INTCTL 0x20 /* Interrupt Control */
+#define HDA_INTSTS 0x24 /* Interrupt Status */
+#define HDA_WCCNT 0x30 /* Wall Clock Counter */
+#define HDA_SSYNC 0x38 /* Stream Synchronization */
+#define HDA_CORBLBASE 0x40 /* CORB Lower Base Address */
+#define HDA_CORBUBASE 0x44 /* CORB Upper Base Address */
+#define HDA_CORBWP 0x48 /* CORB Write Pointer */
+#define HDA_CORBRP 0x4A /* CORB Read Pointer */
+#define HDA_CORBCTL 0x4C /* CORB Control */
+#define HDA_CORBSTS 0x4D /* CORB Status */
+#define HDA_CORBSIZE 0x4E /* CORB Size */
+#define HDA_RIRBLBASE 0x50 /* RIRB Lower Base Address */
+#define HDA_RIRBUBASE 0x54 /* RIRB Upper Base Address */
+#define HDA_RIRBWP 0x58 /* RIRB Write Pointer */
+#define HDA_RINTCNT 0x5A /* Response Interrupt Count */
+#define HDA_RIRBCTL 0x5C /* RIRB Control */
+#define HDA_RIRBSTS 0x5D /* RIRB Status */
+#define HDA_RIRBSIZE 0x5E /* RIRB Size */
+#define HDA_IC 0x60 /* Immediate Command Output Interface */
+#define HDA_IR 0x64 /* Immediate Command Input Interface */
+#define HDA_IRS 0x68 /* Immediate Command Status */
+#define HDA_DPLBASE 0x70 /* DMA Position Lower Base Address */
+#define HDA_DPUBASE 0x74 /* DMA Position Upper Base Address */
+
+#define HDA_SD_CTL 0x0
+#define HDA_SD_STS 0x3
+#define HDA_SD_LPIB 0x4
+#define HDA_SD_CBL 0x8
+#define HDA_SD_LVI 0xC
+#define HDA_SD_FIFOSIZE 0x10
+#define HDA_SD_FORMAT 0x12
+#define HDA_SD_BDLPL 0x18
+#define HDA_SD_BDLPU 0x1C
+#define HDA_SD_LPIBA 0x2004
+
+#define HDA_SDI0CTL 0x80 /* Stream Descriptor Control */
+#define HDA_SDI0STS 0x83 /* Stream Descriptor Status */
+#define HDA_SDI0LPIB 0x84 /* Link Position in Current Buffer */
+#define HDA_SDI0CBL 0x88 /* Cyclic Buffer Length */
+#define HDA_SDI0LVI 0x8C /* Last Valid Index */
+#define HDA_SDI0FIFOSIZE 0x90 /* FIFO Size */
+#define HDA_SDI0FORMAT 0x92 /* Format */
+#define HDA_SDI0BDLPL 0x98 /* List Pointer - Lower */
+#define HDA_SDI0BDLPU 0x9C /* List Pointer - Upper */
+#define HDA_SDI0LPIBA 0x2084 /* Link Position in Buffer n Alias */
+
+#define HDA_SDI1CTL 0xA0 /* Stream Descriptor Control */
+#define HDA_SDI1STS 0xA3 /* Stream Descriptor Status */
+#define HDA_SDI1LPIB 0xA4 /* Link Position in Current Buffer */
+#define HDA_SDI1CBL 0xA8 /* Cyclic Buffer Length */
+#define HDA_SDI1LVI 0xAC /* Last Valid Index */
+#define HDA_SDI1FIFOSIZE 0xB0 /* FIFO Size */
+#define HDA_SDI1FORMAT 0xB2 /* Format */
+#define HDA_SDI1BDLPL 0xB8 /* List Pointer - Lower */
+#define HDA_SDI1BDLPU 0xBC /* List Pointer - Upper */
+#define HDA_SDI1LPIBA 0x20A4 /* Link Position in Buffer n Alias */
+
+#define HDA_SDI2CTL 0xC0 /* Stream Descriptor Control */
+#define HDA_SDI2STS 0xC3 /* Stream Descriptor Status */
+#define HDA_SDI2LPIB 0xC4 /* Link Position in Current Buffer */
+#define HDA_SDI2CBL 0xC8 /* Cyclic Buffer Length */
+#define HDA_SDI2LVI 0xCC /* Last Valid Index */
+#define HDA_SDI2FIFOSIZ 0xD0 /* FIFO Size */
+#define HDA_SDI2FORMAT 0xD2 /* Format */
+#define HDA_SDI2BDLPL 0xD8 /* List Pointer - Lower */
+#define HDA_SDI2BDLPU 0xDC /* List Pointer - Upper */
+#define HDA_SDI2LPIBA 0x20D4 /* Link Position in Buffer n Alias */
+
+#define HDA_SDI3CTL 0xE0 /* Stream Descriptor Control */
+#define HDA_SDI3STS 0xE3 /* Stream Descriptor Status */
+#define HDA_SDI3LPIB 0xE4 /* Link Position in Current Buffer */
+#define HDA_SDI3CBL 0xE8 /* Cyclic Buffer Length */
+#define HDA_SDI3LVI 0xEC /* Last Valid Index */
+#define HDA_SDI3FIFOSIZE 0xF0 /* FIFO Size */
+#define HDA_SDI3FORMAT 0xF2 /* Format */
+#define HDA_SDI3BDLPL 0xF8 /* List Pointer - Lower */
+#define HDA_SDI3BDLPU 0xFC /* List Pointer - Upper */
+#define HDA_SDI3LPIBA 0x20E4 /* Link Position in Buffer n Alias */
+
+#define HDA_SDO0CTL 0x100 /* Stream Descriptor Control */
+#define HDA_SDO0STS 0x103 /* Stream Descriptor Status */
+#define HDA_SDO0LPIB 0x104 /* Link Position in Current Buffer */
+#define HDA_SDO0CBL 0x108 /* Cyclic Buffer Length */
+#define HDA_SDO0LVI 0x10C /* Last Valid Index */
+#define HDA_SDO0FIFOSIZE 0x110 /* FIFO Size */
+#define HDA_SDO0FORMAT 0x112 /* Format */
+#define HDA_SDO0BDLPL 0x118 /* List Pointer - Lower */
+#define HDA_SDO0BDLPU 0x11C /* List Pointer - Upper */
+#define HDA_SDO0LPIBA 0x2104 /* Link Position in Buffer n Alias */
+
+#define HDA_SDO1CTL 0x120 /* Stream Descriptor Control */
+#define HDA_SDO1STS 0x123 /* Stream Descriptor Status */
+#define HDA_SDO1LPIB 0x124 /* Link Position in Current Buffer */
+#define HDA_SDO1CBL 0x128 /* Cyclic Buffer Length */
+#define HDA_SDO1LVI 0x12C /* Last Valid Index */
+#define HDA_SDO1FIFOSIZE 0x130 /* FIFO Size */
+#define HDA_SDO1FORMAT 0x132 /* Format */
+#define HDA_SDO1BDLPL 0x138 /* List Pointer - Lower */
+#define HDA_SDO1BDLPU 0x13C /* List Pointer - Upper */
+#define HDA_SDO1LPIBA 0x2124 /* Link Position in Buffer n Alias */
+
+#define HDA_SDO2CTL 0x140 /* Stream Descriptor Control */
+#define HDA_SDO2STS 0x143 /* Stream Descriptor Status */
+#define HDA_SDO2LPIB 0x144 /* Link Position in Current Buffer */
+#define HDA_SDO2CBL 0x148 /* Cyclic Buffer Length */
+#define HDA_SDO2LVI 0x14C /* Last Valid Index */
+#define HDA_SDO2FIFOSIZE 0x150 /* FIFO Size */
+#define HDA_SDO2FORMAT 0x152 /* Format */
+#define HDA_SDO2BDLPL 0x158 /* List Pointer - Lower */
+#define HDA_SDO2BDLPU 0x15C /* List Pointer - Upper */
+#define HDA_SDO2LPIBA 0x2144 /* Link Position in Buffer n Alias */
+
+#define HDA_SDO3CTL 0x160 /* Stream Descriptor Control */
+#define HDA_SDO3STS 0x163 /* Stream Descriptor Status */
+#define HDA_SDO3LPIB 0x164 /* Link Position in Current Buffer */
+#define HDA_SDO3CBL 0x168 /* Cyclic Buffer Length */
+#define HDA_SDO3LVI 0x16C /* Last Valid Index */
+#define HDA_SDO3FIFOSIZE 0x170 /* FIFO Size */
+#define HDA_SDO3FORMAT 0x172 /* Format */
+#define HDA_SDO3BDLPL 0x178 /* List Pointer - Lower */
+#define HDA_SDO3BDLPU 0x17C /* List Pointer - Upper */
+
+#define HWINFO_SIZE 256
+
+/*
+ * Debugging ioctl calls
+ */
+
+typedef struct
+{
+ int cad, wid;
+ char name[32];
+} hda_name_t;
+
+typedef struct
+{
+ int cad, wid;
+ char info[4000];
+} hda_widget_info_t;
+
+#define HDA_IOCTL_WRITE __SIOWR('H', 0, int)
+#define HDA_IOCTL_READ __SIOWR('H', 1, int)
+#define HDA_IOCTL_NAME __SIOWR('H', 2, hda_name_t)
+#define HDA_IOCTL_WIDGET __SIOWR('H', 3, hda_widget_info_t)
diff --git a/kernel/drv/oss_hdaudio/hdaudio_abit_AA8.c b/kernel/drv/oss_hdaudio/hdaudio_abit_AA8.c
new file mode 100644
index 0000000..6d7366d
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/hdaudio_abit_AA8.c
@@ -0,0 +1,347 @@
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+/* Codec index is 0 */
+/* Codec vendor 10ec:0880 */
+/* HD codec revision 0.9 (5.0) (0x00090500) */
+/* Subsystem ID 08800000 */
+/* Default amplifier caps: in=00000000, out=00000000 */
+#include "oss_hdaudio_cfg.h"
+#include "hdaudio.h"
+#include "hdaudio_codec.h"
+#include "hdaudio_dedicated.h"
+
+int
+hdaudio_abit_AA8_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad,
+ int top_group)
+{
+ int ctl = 0;
+
+ DDB (cmn_err (CE_CONT, "hdaudio_abit_AA8_mixer_init got called.\n"));
+
+ /* Handle PIN widgets */
+ {
+ int n, group, pin_group;
+
+ n = 0;
+
+ HDA_GROUP (pin_group, top_group, "jack");
+
+ if (HDA_PIN_GROUP (0x14, group, pin_group, "green", n, "jack", 4)) /* Pin widget 0x14 */
+ {
+ /* Src 0xc=front */
+ if (HDA_PINSELECT (0x14, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "front input");
+ HDA_OUTMUTE (0x14, group, "mute", UNMUTE);
+ }
+
+ if (HDA_PIN_GROUP (0x15, group, pin_group, "black", n, "jack", 4)) /* Pin widget 0x15 */
+ {
+ /* Src 0xd=rear */
+ if (HDA_PINSELECT (0x15, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "rear input");
+ HDA_OUTMUTE (0x15, group, "mute", UNMUTE);
+ }
+
+ if (HDA_PIN_GROUP (0x16, group, pin_group, "C-L", n, "jack", 4)) /* Pin widget 0x16 */
+ {
+ /* Src 0xe=center/LFE */
+ if (HDA_PINSELECT (0x16, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "center/LFE input");
+ HDA_OUTMUTE (0x16, group, "mute", UNMUTE);
+ }
+
+ if (HDA_PIN_GROUP (0x17, group, pin_group, "surr", n, "jack", 4)) /* Pin widget 0x17 */
+ {
+ /* Src 0xf=side */
+ if (HDA_PINSELECT (0x17, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "side input");
+ HDA_OUTMUTE (0x17, group, "mute", UNMUTE);
+ }
+
+ if (HDA_PIN_GROUP (0x18, group, pin_group, "pink1", n, "jack", 4)) /* Pin widget 0x18 */
+ {
+ /* Src 0x10=out-source */
+ if (HDA_PINSELECT (0x18, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "out-source input");
+ HDA_OUTMUTE (0x18, group, "mute", UNMUTE);
+
+ /* Widget 0x10 (out-source) */
+ /* Src 0xc=front */
+ /* Src 0xd=rear */
+ /* Src 0xe=center/LFE */
+ /* Src 0xf=side */
+ if (HDA_SELECT (0x10, "out-source", ctl, group, -1))
+ {
+ HDA_CHOICES (ctl, "front rear center/LFE side");
+ }
+ }
+
+ if (HDA_PIN_GROUP (0x19, group, pin_group, "pink2", n, "jack", 4)) /* Pin widget 0x19 */
+ {
+ /* Src 0x11=out-source */
+ if (HDA_PINSELECT (0x19, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "out-source input");
+ HDA_OUTMUTE (0x19, group, "mute", UNMUTE);
+
+ /* Widget 0x11 (out-source) */
+ /* Src 0xc=front */
+ /* Src 0xd=rear */
+ /* Src 0xe=center/LFE */
+ /* Src 0xf=side */
+ if (HDA_SELECT (0x11, "out-source", ctl, group, -1))
+ {
+ HDA_CHOICES (ctl, "front rear center/LFE side");
+ }
+ }
+
+ if (HDA_PIN_GROUP (0x1a, group, pin_group, "blue1", n, "jack", 4)) /* Pin widget 0x1a */
+ {
+ /* Src 0x12=out-source */
+ if (HDA_PINSELECT (0x1a, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "out-source input");
+ HDA_OUTMUTE (0x1a, group, "mute", UNMUTE);
+
+ /* Widget 0x12 (out-source) */
+ /* Src 0xc=front */
+ /* Src 0xd=rear */
+ /* Src 0xe=center/LFE */
+ /* Src 0xf=side */
+ if (HDA_SELECT (0x12, "out-source", ctl, group, -1))
+ {
+ HDA_CHOICES (ctl, "front rear center/LFE side");
+ }
+ }
+
+ if (HDA_PIN_GROUP (0x1b, group, pin_group, "blue2", n, "jack", 4)) /* Pin widget 0x1b */
+ {
+ /* Src 0x13=out-source */
+ if (HDA_PINSELECT (0x1b, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "out-source input");
+ HDA_OUTMUTE (0x1b, group, "mute", UNMUTE);
+
+ /* Widget 0x13 (out-source) */
+ /* Src 0xc=front */
+ /* Src 0xd=rear */
+ /* Src 0xe=center/LFE */
+ /* Src 0xf=side */
+ if (HDA_SELECT (0x13, "out-source", ctl, group, -1))
+ {
+ HDA_CHOICES (ctl, "front rear center/LFE side");
+ }
+ }
+
+ if (HDA_PIN_GROUP (0x1c, group, pin_group, "cd", n, "jack", 4)) /* Pin widget 0x1c */
+ {
+ if (HDA_PINSELECT (0x1c, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "input");
+ }
+
+ if (HDA_PIN_GROUP (0x1d, group, pin_group, "beep", n, "jack", 4)) /* Pin widget 0x1d */
+ {
+ if (HDA_PINSELECT (0x1d, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "input");
+ }
+ }
+ /* Handle ADC widgets */
+ {
+ int n, group, rec_group;
+
+ n = 0;
+
+ HDA_GROUP (rec_group, top_group, "record");
+
+ if (HDA_ADC_GROUP (0x07, group, rec_group, "rec1", n, "record", 2)) /* ADC widget 0x07 */
+ {
+ /* Src 0x18=pink1 */
+ /* Src 0x19=pink2 */
+ /* Src 0x1a=blue1 */
+ /* Src 0x1b=blue2 */
+ /* Src 0x1c=cd */
+ /* Src 0x14=green */
+ /* Src 0x15=black */
+ if (HDA_SELECT (0x07, "src", ctl, group, -1))
+ {
+ HDA_CHOICES (ctl, "pink1 pink2 blue1 blue2 cd green black");
+ }
+ {
+ int amp_group;
+
+ HDA_GROUP (amp_group, group, "vol");
+ HDA_INAMP (0x07, 0, amp_group, "pink1", 90); /* From widget 0x18 */
+ HDA_INAMP (0x07, 1, amp_group, "pink2", 90); /* From widget 0x19 */
+ HDA_INAMP (0x07, 2, amp_group, "blue1", 90); /* From widget 0x1a */
+ HDA_INAMP (0x07, 3, amp_group, "blue2", 90); /* From widget 0x1b */
+ HDA_INAMP (0x07, 4, amp_group, "cd", 90); /* From widget 0x1c */
+ HDA_INAMP (0x07, 5, amp_group, "green", 90); /* From widget 0x14 */
+ HDA_INAMP (0x07, 6, amp_group, "black", 90); /* From widget 0x15 */
+ }
+ }
+
+ if (HDA_ADC_GROUP (0x08, group, rec_group, "rec2", n, "record", 2)) /* ADC widget 0x08 */
+ {
+ /* Src 0x18=pink1 */
+ /* Src 0x19=pink2 */
+ /* Src 0x1a=blue1 */
+ /* Src 0x1b=blue2 */
+ /* Src 0x1c=cd */
+ /* Src 0x14=green */
+ /* Src 0x15=black */
+ if (HDA_SELECT (0x08, "src", ctl, group, -1))
+ {
+ HDA_CHOICES (ctl, "pink1 pink2 blue1 blue2 cd green black");
+ }
+ {
+ int amp_group;
+
+ HDA_GROUP (amp_group, group, "vol");
+ HDA_INAMP (0x08, 0, amp_group, "pink1", 90); /* From widget 0x18 */
+ HDA_INAMP (0x08, 1, amp_group, "pink2", 90); /* From widget 0x19 */
+ HDA_INAMP (0x08, 2, amp_group, "blue1", 90); /* From widget 0x1a */
+ HDA_INAMP (0x08, 3, amp_group, "blue2", 90); /* From widget 0x1b */
+ HDA_INAMP (0x08, 4, amp_group, "cd", 90); /* From widget 0x1c */
+ HDA_INAMP (0x08, 5, amp_group, "green", 90); /* From widget 0x14 */
+ HDA_INAMP (0x08, 6, amp_group, "black", 90); /* From widget 0x15 */
+ }
+ }
+
+ if (HDA_ADC_GROUP (0x09, group, rec_group, "rec3", n, "record", 2)) /* ADC widget 0x09 */
+ {
+ /* Src 0x18=pink1 */
+ /* Src 0x19=pink2 */
+ /* Src 0x1a=blue1 */
+ /* Src 0x1b=blue2 */
+ /* Src 0x1c=cd */
+ /* Src 0xb=inputmix */
+ /* Src 0x14=green */
+ /* Src 0x15=black */
+ /* Src 0x16=C-L */
+ /* Src 0x17=surr */
+ if (HDA_SELECT (0x09, "src", ctl, group, -1))
+ {
+ HDA_CHOICES (ctl,
+ "pink1 pink2 blue1 blue2 cd inputmix green black C-L surr");
+ }
+ {
+ int amp_group;
+
+ HDA_GROUP (amp_group, group, "vol");
+ HDA_INAMP (0x09, 0, amp_group, "pink1", 90); /* From widget 0x18 */
+ HDA_INAMP (0x09, 1, amp_group, "pink2", 90); /* From widget 0x19 */
+ HDA_INAMP (0x09, 2, amp_group, "blue1", 90); /* From widget 0x1a */
+ HDA_INAMP (0x09, 3, amp_group, "blue2", 90); /* From widget 0x1b */
+ HDA_INAMP (0x09, 4, amp_group, "cd", 90); /* From widget 0x1c */
+ HDA_INAMP (0x09, 5, amp_group, "inputmix", 90); /* From widget 0x0b */
+ HDA_INAMP (0x09, 6, amp_group, "green", 90); /* From widget 0x14 */
+ HDA_INAMP (0x09, 7, amp_group, "black", 90); /* From widget 0x15 */
+ HDA_INAMP (0x09, 8, amp_group, "C-L", 90); /* From widget 0x16 */
+ HDA_INAMP (0x09, 9, amp_group, "surr", 90); /* From widget 0x17 */
+ }
+ }
+
+#if 0
+ if (HDA_ADC_GROUP (0x0a, group, rec_group, "spdif-in", n, "record", 2)) /* ADC widget 0x0a */
+ {
+ /* Src 0x1f=spdin */
+ }
+#endif
+ }
+ /* Handle misc widgets */
+ {
+ int n, group, misc_group;
+
+ n = 0;
+
+ HDA_GROUP (misc_group, top_group, "misc");
+
+ if (HDA_MISC_GROUP (0x0c, group, misc_group, "front", n, "misc", 8)) /* Misc widget 0x0c */
+ {
+ /* Src 0x2=front */
+ /* Src 0xb=inputmix */
+ HDA_OUTAMP (0x0c, group, "-", 90);
+ {
+ int amp_group;
+
+ HDA_GROUP (amp_group, group, "mute");
+ HDA_INMUTE (0x0c, 0, amp_group, "front", UNMUTE); /* From widget 0x02 */
+ HDA_INMUTE (0x0c, 1, amp_group, "inputmix", UNMUTE); /* From widget 0x0b */
+ }
+ }
+
+ if (HDA_MISC_GROUP (0x0d, group, misc_group, "rear", n, "misc", 8)) /* Misc widget 0x0d */
+ {
+ /* Src 0x3=rear */
+ /* Src 0xb=inputmix */
+ HDA_OUTAMP (0x0d, group, "-", 90);
+ {
+ int amp_group;
+
+ HDA_GROUP (amp_group, group, "mute");
+ HDA_INMUTE (0x0d, 0, amp_group, "rear", UNMUTE); /* From widget 0x03 */
+ HDA_INMUTE (0x0d, 1, amp_group, "inputmix", UNMUTE); /* From widget 0x0b */
+ }
+ }
+
+ if (HDA_MISC_GROUP (0x0e, group, misc_group, "center/LFE", n, "misc", 8)) /* Misc widget 0x0e */
+ {
+ /* Src 0x4=center/LFE */
+ /* Src 0xb=inputmix */
+ HDA_OUTAMP (0x0e, group, "-", 90);
+ {
+ int amp_group;
+
+ HDA_GROUP (amp_group, group, "mute");
+ HDA_INMUTE (0x0e, 0, amp_group, "center/LFE", UNMUTE); /* From widget 0x04 */
+ HDA_INMUTE (0x0e, 1, amp_group, "inputmix", UNMUTE); /* From widget 0x0b */
+ }
+ }
+
+ if (HDA_MISC_GROUP (0x0f, group, misc_group, "side", n, "misc", 8)) /* Misc widget 0x0f */
+ {
+ /* Src 0x5=side */
+ /* Src 0xb=inputmix */
+ HDA_OUTAMP (0x0f, group, "-", 90);
+ {
+ int amp_group;
+
+ HDA_GROUP (amp_group, group, "mute");
+ HDA_INMUTE (0x0f, 0, amp_group, "side", UNMUTE); /* From widget 0x05 */
+ HDA_INMUTE (0x0f, 1, amp_group, "inputmix", UNMUTE); /* From widget 0x0b */
+ }
+ }
+
+ if (HDA_MISC_GROUP (0x0b, group, misc_group, "inputmix", n, "misc", 0)) /* Misc widget 0x0b */
+ {
+ /* Src 0x18=pink1 */
+ /* Src 0x19=pink2 */
+ /* Src 0x1a=blue1 */
+ /* Src 0x1b=blue2 */
+ /* Src 0x1c=cd */
+ /* Src 0x1d=beep */
+ /* Src 0x14=green */
+ /* Src 0x15=black */
+ {
+ int amp_group;
+
+ HDA_GROUP (amp_group, group, "vol");
+ HDA_INAMP (0x0b, 0, amp_group, "pink1", 90); /* From widget 0x18 */
+ HDA_INAMP (0x0b, 1, amp_group, "pink2", 90); /* From widget 0x19 */
+ HDA_INAMP (0x0b, 2, amp_group, "blue1", 90); /* From widget 0x1a */
+ HDA_INAMP (0x0b, 3, amp_group, "blue2", 90); /* From widget 0x1b */
+ HDA_INAMP (0x0b, 4, amp_group, "cd", 90); /* From widget 0x1c */
+ HDA_INAMP (0x0b, 5, amp_group, "beep", 90); /* From widget 0x1d */
+ HDA_INAMP (0x0b, 6, amp_group, "green", 90); /* From widget 0x14 */
+ HDA_INAMP (0x0b, 7, amp_group, "black", 90); /* From widget 0x15 */
+ }
+ }
+ }
+ return 0;
+}
diff --git a/kernel/drv/oss_hdaudio/hdaudio_asus_P4B_E.c b/kernel/drv/oss_hdaudio/hdaudio_asus_P4B_E.c
new file mode 100644
index 0000000..c7f416c
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/hdaudio_asus_P4B_E.c
@@ -0,0 +1,395 @@
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+/* Codec index is 0 */
+/* Codec vendor 11d4:1988 */
+/* HD codec revision 1.0 (4.0) (0x00100400) */
+/* Subsystem ID 104381e1 */
+/* Default amplifier caps: in=80000000, out=00052727 */
+#include "oss_hdaudio_cfg.h"
+#include "hdaudio.h"
+#include "hdaudio_codec.h"
+#include "hdaudio_dedicated.h"
+
+int
+hdaudio_Asus_P4B_E_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad,
+ int top_group)
+{
+ int ctl = 0;
+
+ DDB (cmn_err (CE_CONT, "hdaudio_Asus_P4B_E_mixer_init got called.\n"));
+
+ HDA_OUTAMP_F (0x04, top_group, "front", 100, MIXF_PCMVOL);
+ HDA_COLOR (ctl, OSS_RGB_GREEN);
+ HDA_OUTAMP_F (0x0a, top_group, "side", 100, MIXF_PCMVOL);
+ HDA_COLOR (ctl, OSS_RGB_GRAY);
+ HDA_OUTAMP_F (0x05, top_group, "center/LFE", 100, MIXF_PCMVOL);
+ HDA_COLOR (ctl, OSS_RGB_ORANGE);
+ HDA_OUTAMP_F (0x06, top_group, "rear", 100, MIXF_PCMVOL);
+ HDA_COLOR (ctl, OSS_RGB_BLACK);
+ HDA_OUTAMP_F (0x03, top_group, "headphone", 100, MIXF_PCMVOL);
+ HDA_OUTAMP_F (0x21, top_group, "input-mix", 100, MIXF_PCMVOL);
+ HDA_OUTAMP_F (0x10, top_group, "pcbeep", 100, MIXF_PCMVOL);
+
+ /* Handle misc widgets */
+ {
+ int n, group;
+
+ n = 0;
+
+ HDA_GROUP (group, top_group, "input-mix");
+
+ //if (HDA_MISC_GROUP(0x20, group, misc_group, "input-mix", n, "misc", 4)) /* Misc widget 0x20 */
+ {
+ /* Src 0x39=fppink-micboost */
+ /* Src 0x33=blue-insel */
+ /* Src 0x38=fpgreen-micboost */
+ /* Src 0x3d=green-micboost */
+ /* Src 0x34=pink-insel */
+ /* Src 0x3b=black-micboost */
+ /* Src 0x18=cd */
+ /* Src 0x1a=beep */
+ HDA_INAMP (0x20, 0, group, "fp-pink", 100); /* From widget 0x39 */
+ HDA_COLOR (ctl, OSS_RGB_PINK);
+
+ HDA_INAMP (0x20, 1, group, "blue", 100); /* From widget 0x33 */
+ HDA_COLOR (ctl, OSS_RGB_BLUE);
+
+ HDA_INAMP (0x20, 2, group, "fp-green", 100); /* From widget 0x38 */
+ HDA_COLOR (ctl, OSS_RGB_GREEN);
+
+ HDA_INAMP (0x20, 3, group, "green", 100); /* From widget 0x3d */
+ HDA_COLOR (ctl, OSS_RGB_GREEN);
+
+ HDA_INAMP (0x20, 4, group, "pink", 100); /* From widget 0x34 */
+ HDA_COLOR (ctl, OSS_RGB_PINK);
+
+ HDA_INAMP (0x20, 5, group, "black", 100); /* From widget 0x3b */
+ HDA_COLOR (ctl, OSS_RGB_BLACK);
+
+ HDA_INAMP (0x20, 6, group, "cd", 100); /* From widget 0x18 */
+ HDA_COLOR (ctl, 0);
+
+ HDA_INAMP (0x20, 7, group, "pcbeep", 100); /* From widget 0x1a */
+ HDA_COLOR (ctl, 0);
+
+#if 0
+ // This seems to be unnecessary selector
+ /* Widget 0x33 (blue-insel) */
+ /* Src 0x3a=blue-micboost */
+ /* Src 0x25=grey */
+ /* Src 0x24=orange */
+ if (HDA_SELECT (0x33, "src", ctl, group, -1))
+ {
+ HDA_CHOICES (ctl, "blue grey orange");
+ }
+#endif
+
+#if 0
+ // This seems to be unnecessary selector
+ /* Widget 0x34 (pink-insel) */
+ /* Src 0x3c=pink-micboost */
+ /* Src 0x25=grey */
+ /* Src 0x24=orange */
+ if (HDA_SELECT (0x34, "src", ctl, group, -1))
+ {
+ HDA_CHOICES (ctl, "pink grey orange");
+ }
+#endif
+ }
+ }
+ /* Handle ADC widgets */
+ {
+ int n, group, rec_group;
+
+ n = 0;
+
+ HDA_GROUP (rec_group, top_group, "record");
+
+#if 0
+ if (HDA_ADC_GROUP (0x07, group, rec_group, "spdin", n, "record", 4)) /* ADC widget 0x07 */
+ {
+ /* Src 0x1c=spdif-in */
+ }
+#endif
+
+ if (HDA_ADC_GROUP (0x08, group, rec_group, "rec1", n, "record", 4)) /* ADC widget 0x08 */
+ {
+ /* Src 0xc=rec1-src */
+
+ /* Widget 0x0c (rec1-src) */
+ /* Src 0x38=fpgreen-micboost */
+ /* Src 0xbc= (0x3c=porte-boost?) */
+ /* Src 0x18=int-black */
+ /* Src 0x24=orange */
+ /* Src 0x25=grey */
+ /* Src 0x3d=green-micboost */
+ /* Src 0x20=input-mix */
+ if (HDA_SELECT (0x0c, "src", ctl, group, -1))
+ {
+ HDA_CHOICES (ctl, "fp-green pink cd orange grey green input-mix");
+ }
+ HDA_OUTAMP_F (0x0c, group, "-", 100, MIXF_RECVOL);
+ }
+
+ if (HDA_ADC_GROUP (0x09, group, rec_group, "rec2", n, "record", 4)) /* ADC widget 0x09 */
+ {
+ /* Src 0xd=rec2-src */
+
+ /* Widget 0x0d (rec2-src) */
+ /* Src 0x38=fpgreen-micboost */
+ /* Src 0xbc= (0x3c=porte-boost?) */
+ /* Src 0x18=int-black */
+ /* Src 0x24=orange */
+ /* Src 0x25=grey */
+ /* Src 0x3d=green-micboost */
+ /* Src 0x20=input-mix */
+ if (HDA_SELECT (0x0d, "src", ctl, group, -1))
+ {
+ HDA_CHOICES (ctl, "fp-green pink cd orange grey green input-mix");
+ }
+ HDA_OUTAMP_F (0x0d, group, "-", 100, MIXF_RECVOL);
+ }
+
+ if (HDA_ADC_GROUP (0x0f, group, rec_group, "rec3", n, "record", 4)) /* ADC widget 0x0f */
+ {
+ /* Src 0xe=rec3-src */
+
+ /* Widget 0x0e (rec3-src) */
+ /* Src 0x38=fpgreen-micboost */
+ /* Src 0xbc= (0x3c=porte-boost?) */
+ /* Src 0x18=int-black */
+ /* Src 0x24=orange */
+ /* Src 0x25=grey */
+ /* Src 0x3d=green-micboost */
+ /* Src 0x20=input-mix */
+ if (HDA_SELECT (0x0e, "src", ctl, group, -1))
+ {
+ HDA_CHOICES (ctl, "fp-green pink cd orange grey green input-mix");
+ }
+ HDA_OUTAMP_F (0x0e, group, "-", 100, MIXF_RECVOL);
+ }
+ }
+
+ /* Handle PIN widgets */
+ {
+ int n, group, pin_group;
+
+ n = 0;
+
+ HDA_GROUP (pin_group, top_group, "jack");
+
+ if (HDA_PIN_GROUP (0x11, group, pin_group, "fp-green", n, "jack", 4)) /* Pin widget 0x11 */
+ {
+ /* Src 0x22=headphon-mix */
+ if (HDA_PINSELECT (0x11, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "headphone-out input");
+ HDA_OUTMUTE (0x11, group, "inmute", UNMUTE);
+
+ /* Widget 0x22 (headphon-mix) */
+ /* Src 0x37=fpgreen-outsel */
+ /* Src 0x21=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP (amp_group, group, "mute");
+ HDA_INMUTE_F (0x22, 0, amp_group, "headphone", UNMUTE, MIXF_MAINVOL); /* From widget 0x37 */
+ HDA_INMUTE_F (0x22, 1, amp_group, "input-mix", UNMUTE, MIXF_MAINVOL); /* From widget 0x21 */
+ }
+ /* Widget 0x38 (fpgreen-micboost) */
+ /* Src 0x11=fp-green */
+ HDA_OUTAMP (0x38, group, "micboost", 100);
+ }
+
+ if (HDA_PIN_GROUP (0x14, group, pin_group, "fp-pink", n, "jack", 4)) /* Pin widget 0x14 */
+ {
+ /* Src 0x2b=fp-mic-mix */
+ if (HDA_PINSELECT (0x14, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "headphone-out input");
+ HDA_OUTMUTE (0x14, group, "inmute", UNMUTE);
+
+ /* Widget 0x2b (fp-mic-mix) */
+ /* Src 0x30=fppink-outsel */
+ /* Src 0x21=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP (amp_group, group, "mute");
+ HDA_INMUTE_F (0x2b, 0, amp_group, "headphone", UNMUTE, MIXF_MAINVOL); /* From widget 0x30 */
+ HDA_INMUTE_F (0x2b, 1, amp_group, "input-mix", UNMUTE, MIXF_MAINVOL); /* From widget 0x21 */
+ }
+ /* Widget 0x39 (fppink-micboost) */
+ /* Src 0x14=fp-pink */
+ HDA_OUTAMP (0x39, group, "micboost", 100);
+ }
+
+ if (HDA_PIN_GROUP (0x12, group, pin_group, "green", n, "jack", 0)) /* Pin widget 0x12 */
+ {
+ /* Src 0x29=front-mix */
+ if (HDA_PINSELECT (0x12, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "front-out input");
+ HDA_OUTMUTE (0x12, group, "inmute", UNMUTE);
+
+ /* Widget 0x29 (front-mix) */
+ /* Src 0x4=front */
+ /* Src 0x21=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP (amp_group, group, "mute");
+ HDA_INMUTE_F (0x29, 0, amp_group, "front", UNMUTE, MIXF_MAINVOL); /* From widget 0x04 */
+ HDA_INMUTE_F (0x29, 1, amp_group, "input-mix", UNMUTE, MIXF_MAINVOL); /* From widget 0x21 */
+ }
+ /* Widget 0x3d (green-micboost) */
+ /* Src 0x12=green */
+ HDA_OUTAMP (0x3d, group, "micboost", 100);
+ }
+
+ if (HDA_PIN_GROUP (0x13, group, pin_group, "int-black", n, "jack", 4)) /* Pin widget 0x13 */
+ {
+ /* Src 0x2d=mono-mixdown */
+ if (HDA_PINSELECT (0x13, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "mono-out input");
+ HDA_OUTAMP (0x13, group, "invol", 100);
+
+ /* Widget 0x2d (mono-mixdown) */
+ /* Src 0x1e=mono-mix */
+
+ /* Widget 0x1e (mono-mix) */
+ /* Src 0x36=mono-sel */
+ /* Src 0x21=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP (amp_group, group, "mute");
+ HDA_INMUTE_F (0x1e, 0, amp_group, "mono", UNMUTE, MIXF_MAINVOL); /* From widget 0x36 */
+ HDA_INMUTE_F (0x1e, 1, amp_group, "input-mix", UNMUTE, MIXF_MAINVOL); /* From widget 0x21 */
+ }
+ }
+
+ if (HDA_PIN_GROUP (0x15, group, pin_group, "blue", n, "jack", 4)) /* Pin widget 0x15 */
+ {
+ /* Src 0x2c=linein-mix */
+ if (HDA_PINSELECT (0x15, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "front-out input");
+ HDA_OUTMUTE (0x15, group, "inmute", UNMUTE);
+
+ /* Widget 0x2c (linein-mix) */
+ /* Src 0x31=blue-outsel */
+ /* Src 0x21=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP (amp_group, group, "mute");
+ HDA_INMUTE_F (0x2c, 0, amp_group, "front", UNMUTE, MIXF_MAINVOL); /* From widget 0x31 */
+ HDA_INMUTE_F (0x2c, 1, amp_group, "input-mix", UNMUTE, MIXF_MAINVOL); /* From widget 0x21 */
+ }
+ /* Src 0x15=linein */
+ HDA_OUTAMP (0x3a, group, "micboost", 100);
+ }
+
+ if (HDA_PIN_GROUP (0x16, group, pin_group, "black", n, "jack", 4)) /* Pin widget 0x16 */
+ {
+ /* Src 0x2a=rear-mix */
+ if (HDA_PINSELECT (0x16, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "rear-out input");
+ HDA_OUTMUTE (0x16, group, "inmute", UNMUTE);
+
+ /* Widget 0x2a (rear-mix) */
+ /* Src 0x6=rear */
+ /* Src 0x21=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP (amp_group, group, "mute");
+ HDA_INMUTE_F (0x2a, 0, amp_group, "rear", UNMUTE, MIXF_MAINVOL); /* From widget 0x06 */
+ HDA_INMUTE_F (0x2a, 1, amp_group, "input-mix", UNMUTE, MIXF_MAINVOL); /* From widget 0x21 */
+ }
+ /* Widget 0x3b (black-micboost) */
+ /* Src 0x16=black */
+ HDA_OUTAMP (0x3b, group, "micboost", 100);
+ }
+
+ if (HDA_PIN_GROUP (0x17, group, pin_group, "pink", n, "jack", 0)) /* Pin widget 0x17 */
+ {
+ /* Src 0x26=mic-mix */
+ if (HDA_PINSELECT (0x17, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "center/LFE-out input");
+ HDA_OUTMUTE (0x17, group, "inmute", UNMUTE);
+
+ /* Widget 0x26 (mic-mix) */
+ /* Src 0x32=pink-outsel */
+ /* Src 0x21=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP (amp_group, group, "mute");
+ HDA_INMUTE_F (0x26, 0, amp_group, "center/LFE", UNMUTE, MIXF_MAINVOL); /* From widget 0x32 */
+ HDA_INMUTE_F (0x26, 1, amp_group, "input-mix", UNMUTE, MIXF_MAINVOL); /* From widget 0x21 */
+ }
+ /* Src 0x17=mic */
+ HDA_OUTAMP (0x3c, group, "micboost", 100);
+ }
+
+#if 0
+ if (HDA_PIN_GROUP (0x18, group, pin_group, "int-black", n, "jack", 4)) /* Pin widget 0x18 */
+ {
+ if (HDA_PINSELECT (0x18, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "input");
+ }
+
+ if (HDA_PIN_GROUP (0x1a, group, pin_group, "int-black", n, "jack", 4)) /* Pin widget 0x1a */
+ {
+ if (HDA_PINSELECT (0x1a, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "input");
+ }
+#endif
+
+ if (HDA_PIN_GROUP (0x24, group, pin_group, "orange", n, "jack", 4)) /* Pin widget 0x24 */
+ {
+ /* Src 0x27=center/LFE-mix */
+ if (HDA_PINSELECT (0x24, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "center/LFE-out input");
+ HDA_OUTMUTE (0x24, group, "inmute", UNMUTE);
+
+ /* Widget 0x27 (center/LFE-mix) */
+ /* Src 0x5=center/LFE */
+ /* Src 0x21=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP (amp_group, group, "mute");
+ HDA_INMUTE_F (0x27, 0, amp_group, "center/LFE", UNMUTE, MIXF_MAINVOL); /* From widget 0x05 */
+ HDA_INMUTE_F (0x27, 1, amp_group, "input-mix", UNMUTE, MIXF_MAINVOL); /* From widget 0x21 */
+ }
+ }
+
+ if (HDA_PIN_GROUP (0x25, group, pin_group, "grey", n, "jack", 4)) /* Pin widget 0x25 */
+ {
+ /* Src 0x28=side-mix */
+ if (HDA_PINSELECT (0x25, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "side-out input");
+ HDA_OUTMUTE (0x25, group, "inmute", UNMUTE);
+
+ /* Widget 0x28 (side-mix) */
+ /* Src 0xa=side */
+ /* Src 0x21=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP (amp_group, group, "mute");
+ HDA_INMUTE_F (0x28, 0, amp_group, "side", UNMUTE, MIXF_MAINVOL); /* From widget 0x0a */
+ HDA_INMUTE_F (0x28, 1, amp_group, "input-mix", UNMUTE, MIXF_MAINVOL); /* From widget 0x21 */
+ }
+ }
+ }
+ return 0;
+}
diff --git a/kernel/drv/oss_hdaudio/hdaudio_asus_m9.c b/kernel/drv/oss_hdaudio/hdaudio_asus_m9.c
new file mode 100644
index 0000000..054b4eb
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/hdaudio_asus_m9.c
@@ -0,0 +1,36 @@
+/*
+ * Purpose: init handler for Asus m9.
+ */
+
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+/* Codec index is 0 */
+/* Codec vendor 10ec:0260 */
+/* HD codec revision 1.0 (4.0) (0x00100400) */
+/* Subsystem ID 1025160d */
+#include "oss_hdaudio_cfg.h"
+#include "hdaudio.h"
+#include "hdaudio_codec.h"
+#include "hdaudio_dedicated.h"
+#include "hdaudio_mixers.h"
+
+int
+hdaudio_asus_m9_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group)
+{
+ DDB(cmn_err(CE_CONT, "hdaudio_asus_m9_mixer_init got called.\n"));
+
+ corb_write (mixer, cad, 0x1b, 0, SET_EAPD, 0);
+
+ return hdaudio_generic_mixer_init(dev, mixer, cad, top_group);
+}
+
diff --git a/kernel/drv/oss_hdaudio/hdaudio_codec.c b/kernel/drv/oss_hdaudio/hdaudio_codec.c
new file mode 100644
index 0000000..fb29840
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/hdaudio_codec.c
@@ -0,0 +1,3580 @@
+/*
+ * Purpose: Codec handling for Intel High Definition Audio (HDA/Azalia).
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#include "oss_hdaudio_cfg.h"
+#include "hdaudio.h"
+#include "hdaudio_codec.h"
+#include "hdaudio_codecids.h"
+
+extern int hdaudio_snoopy;
+extern int hdaudio_jacksense;
+extern int hdaudio_noskip;
+
+static codec_t NULL_codec = { 0 }; /* TODO: Temporary workaround - to be removed */
+
+
+/* Si3055 functions (implemented in hdaudio_si3055.c) */
+extern void hdaudio_si3055_endpoint_init(hdaudio_mixer_t *mixer, int cad);
+extern void hdaudio_si3055_set_rate(hdaudio_mixer_t *mixer, int cad, int rate);
+extern int hdaudio_si3055_set_offhook(hdaudio_mixer_t *mixer, int cad, int offhook);
+
+
+static int attach_codec (hdaudio_mixer_t * mixer, int cad, char *hw_info,
+ unsigned int pci_subdevice, int group_type);
+int
+hdaudio_mixer_get_outendpoints (hdaudio_mixer_t * mixer,
+ hdaudio_endpointinfo_t ** endpoints, int size)
+{
+ int i;
+
+ if (size != sizeof (hdaudio_endpointinfo_t))
+ {
+ cmn_err (CE_WARN, "Bad endpoint size\n");
+ return OSS_EIO;
+ }
+
+ *endpoints = (hdaudio_endpointinfo_t *) & mixer->outendpoints;
+
+ for (i = 0; i < mixer->num_outendpoints; i++)
+ {
+ hdaudio_endpointinfo_t *ep = *endpoints + i;
+
+ ep->skip = mixer->codecs[ep->cad]->widgets[ep->base_wid].skip;
+
+ if (hdaudio_snoopy)
+ {
+ char *s = ep->name + strlen(ep->name);
+ sprintf(s, ":%d:%d", ep->cad, ep->base_wid);
+ }
+ }
+ return mixer->num_outendpoints;
+}
+
+int
+hdaudio_mixer_get_inendpoints (hdaudio_mixer_t * mixer,
+ hdaudio_endpointinfo_t ** endpoints, int size)
+{
+ int i;
+
+ if (size != sizeof (hdaudio_endpointinfo_t))
+ {
+ cmn_err (CE_WARN, "Bad endpoint size\n");
+ return OSS_EIO;
+ }
+
+ *endpoints = (hdaudio_endpointinfo_t *) & mixer->inendpoints;
+
+ for (i = 0; i < mixer->num_inendpoints; i++)
+ {
+ hdaudio_endpointinfo_t *ep = *endpoints + i;
+
+ ep->skip = mixer->codecs[ep->cad]->widgets[ep->base_wid].skip;
+
+ if (hdaudio_snoopy)
+ {
+ char *s = ep->name + strlen(ep->name);
+ sprintf(s, ":%d:%d", ep->cad, ep->base_wid);
+ }
+ }
+ return mixer->num_inendpoints;
+}
+
+/*ARGSUSED*/
+static int
+hda_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg)
+{
+ if (cmd == SOUND_MIXER_READ_DEVMASK ||
+ cmd == SOUND_MIXER_READ_RECMASK || cmd == SOUND_MIXER_READ_RECSRC ||
+ cmd == SOUND_MIXER_READ_STEREODEVS)
+ return *arg = 0;
+
+ return OSS_EINVAL;
+}
+
+static mixer_driver_t hda_mixer_driver = {
+ hda_mixer_ioctl
+};
+
+
+static void
+propagate_names (hdaudio_mixer_t * mixer)
+{
+ int c, w;
+ int i;
+/*
+ * Check if the same name can be used for all the widgets on an unique path.
+ */
+
+ for (i = 0; i < 20; i++)
+ for (c = 0; c < mixer->ncodecs; c++)
+ if (mixer->codecs[c] != &NULL_codec)
+ {
+ for (w = 1; w < mixer->codecs[c]->nwidgets; w++)
+ {
+ widget_t *widget = &mixer->codecs[c]->widgets[w];
+ widget_t *src_widget =
+ &mixer->codecs[c]->widgets[widget->connections[0]];
+
+ if (widget->nconn != 1)
+ continue;
+
+#if 0
+ if (src_widget->wid_type == NT_PIN
+ || src_widget->wid_type == NT_DAC)
+ continue;
+
+ if (src_widget->wid_type != NT_MIXER && src_widget->wid_type != NT_VENDOR) /* Mixer */
+ continue;
+#endif
+
+ strcpy (widget->name, src_widget->name);
+
+ /*
+ * Copy widget's RGB color to all widgets in the path
+ */
+ if (widget->rgbcolor == 0)
+ widget->rgbcolor = src_widget->rgbcolor;
+ }
+ }
+#if 0
+ // Debugging code
+ for (c = 0; c < mixer->ncodecs; c++)
+ if (mixer->codecs[c] != &NULL_codec)
+ for (w = 1; w < mixer->codecs[c]->nwidgets; w++)
+ {
+ widget_t *widget = &mixer->codecs[c]->widgets[w];
+
+ cmn_err(CE_CONT, "w= %02x rgb=%06x: %s (%s)\n", w, widget->rgbcolor, widget->name, widget->color);
+ }
+#endif
+}
+
+static void
+check_names (hdaudio_mixer_t * mixer)
+{
+ int c, c2, w, w2;
+ int n, start;
+
+/*
+ * Make sure all widgets have unique names.
+ */
+ for (c = 0; c < mixer->ncodecs; c++)
+ if (mixer->codecs[c] != &NULL_codec)
+ {
+ for (w = 1; w < mixer->codecs[c]->nwidgets; w++)
+ {
+ char tmp[16];
+ n = 0;
+ if (mixer->codecs[c]->widgets[w].skip) /* Not available */
+ continue;
+
+ strcpy (tmp, mixer->codecs[c]->widgets[w].name);
+
+ start = w + 1;
+
+ for (c2 = c; c2 < mixer->ncodecs; c2++)
+ {
+ for (w2 = start; w2 < mixer->codecs[c2]->nwidgets; w2++)
+ {
+ if (mixer->codecs[c2]->widgets[w2].skip) /* Not available */
+ continue;
+
+ if (strcmp (tmp, mixer->codecs[c2]->widgets[w2].name) ==
+ 0)
+ n++;
+ }
+
+ start = 1;
+ }
+
+ if (n > 0) /* Duplicates found */
+ {
+ n = 0;
+ start = w;
+ for (c2 = c; c2 < mixer->ncodecs; c2++)
+ {
+ for (w2 = start; w2 < mixer->codecs[c2]->nwidgets; w2++)
+ {
+ if (mixer->codecs[c2]->widgets[w2].skip) /* Not available */
+ continue;
+
+ if (strcmp (tmp, mixer->codecs[c2]->widgets[w2].name)
+ == 0)
+ {
+ n++;
+ sprintf (mixer->codecs[c2]->widgets[w2].name,
+ "%s%d", tmp, n);
+ }
+ }
+
+ start = 1;
+ }
+ }
+ }
+ }
+}
+
+int
+hdaudio_amp_maxval (unsigned int ampcaps)
+{
+ int step, range, maxval;
+
+ range = ((ampcaps >> AMPCAP_NUMSTEPS_SHIFT) & AMPCAP_NUMSTEPS_MASK) + 1;
+ step = ((ampcaps >> AMPCAP_STEPSIZE_SHIFT) & AMPCAP_STEPSIZE_MASK) + 1;
+
+ maxval = range * step * 25; /* Now in 0.01 dB steps */
+ maxval = (maxval / 10) - 1; /* Truncate to centibel (0.1 dB) scaling */
+
+ return maxval;
+}
+
+static int
+scaleout_vol (int v, unsigned int ampcaps)
+{
+ int step, range, maxval;
+
+ range = ((ampcaps >> AMPCAP_NUMSTEPS_SHIFT) & AMPCAP_NUMSTEPS_MASK) + 1;
+ step = ((ampcaps >> AMPCAP_STEPSIZE_SHIFT) & AMPCAP_STEPSIZE_MASK) + 1;
+
+ maxval = range * step * 25; /* Now in 0.01 dB steps */
+ maxval = (maxval / 10) - 1; /* Truncate to centibel (0.1 dB) scaling */
+ if (v > maxval)
+ v = maxval;
+
+ v *= 10; /* centibels -> millibels */
+
+ v = (v + (25 * step) / 2) / (25 * step);
+
+ if (v > 0x7f || v >= range)
+ {
+ v = range - 1;
+ }
+
+ if (v < 0)
+ v = 0;
+ return v;
+}
+
+static int
+scalein_vol (int v, unsigned int ampcaps)
+{
+ int step, range, maxval;
+
+ range = ((ampcaps >> AMPCAP_NUMSTEPS_SHIFT) & AMPCAP_NUMSTEPS_MASK) + 1;
+ step = ((ampcaps >> AMPCAP_STEPSIZE_SHIFT) & AMPCAP_STEPSIZE_MASK) + 1;
+
+ maxval = range * step * 25; /* Now in 0.01 dB steps */
+ maxval = (maxval / 10) - 1; /* Truncate to centibel (0.1 dB) scaling */
+
+ v *= step * 25; /* Convert to millibels */
+
+ v = v / 10 - 1; /* millibels -> centibels */
+
+ if (v > maxval)
+ {
+ v = maxval;
+ }
+ if (v < 0)
+ v = 0;
+
+ return v;
+}
+
+static int
+handle_insrcselect (hdaudio_mixer_t * mixer, widget_t * widget, int value)
+{
+/*
+ * Emulated (recording) source selection based on input amp mute controls.
+ *
+ * Mute all inputs other than the selected one.
+ */
+ int i;
+
+ widget->current_selector = value;
+
+ for (i = 0; i < widget->nconn; i++)
+ {
+ int v = (i == value) ? 0 : 0x80;
+
+ corb_write (mixer, widget->cad, widget->wid, 0,
+ SET_GAIN (0, 1, 1, 1, i), v);
+ }
+
+ return value;
+}
+
+int
+hdaudio_set_control (int dev, int ctrl, unsigned int cmd, int value)
+{
+ hdaudio_mixer_t *mixer = mixer_devs[dev]->devc;
+
+ unsigned int cad, wid, linked_wid, typ, ix, left, right, a, b, v;
+ widget_t *widget;
+
+ ix = ctrl & 0xff;
+ typ = (ctrl >> 8) & 0xff;
+ wid = (ctrl >> 16) & 0xff;
+ cad = (ctrl >> 24) & 0xff;
+
+ if (cad >= mixer->ncodecs)
+ return OSS_EIO;
+
+ if (wid >= mixer->codecs[cad]->nwidgets)
+ return OSS_EIO;
+
+ widget = &mixer->codecs[cad]->widgets[wid];
+
+ if (mixer->codecs[cad]->vendor_flags & VF_VAIO_HACK)
+ linked_wid = (wid == 0x02) ? 0x05 : ((wid == 0x05) ? 0x02 : 0);
+ else
+ linked_wid = 0;
+
+ if (cmd == SNDCTL_MIX_READ)
+ switch (typ)
+ {
+ case CT_INGAINSEL:
+ if (!corb_read (mixer, cad, wid, 0, GET_GAIN (0, 0), ix, &a, &b))
+ return OSS_EIO;
+ return a & 0x7f; // TODO: Handle mute
+ break;
+
+ case CT_INMONO:
+ if (!corb_read (mixer, cad, wid, 0, GET_GAIN (0, 0), ix, &a, &b))
+ return OSS_EIO;
+ if (a & 0x80)
+ left = 0;
+ else
+ left = scalein_vol (a, widget->inamp_caps);
+ return left | (left << 16);
+ break;
+
+ case CT_INSTEREO:
+ if (!corb_read (mixer, cad, wid, 0, GET_GAIN (0, 1), ix, &a, &b))
+ return OSS_EIO;
+ if (a & 0x80)
+ left = 0;
+ else
+ left = scalein_vol (a, widget->inamp_caps);
+ if (!corb_read (mixer, cad, wid, 0, GET_GAIN (0, 0), ix, &a, &b))
+ return OSS_EIO;
+ if (a & 0x80)
+ right = 0;
+ else
+ right = scalein_vol (a, widget->inamp_caps);
+ return left | (right << 16);
+ break;
+
+ case CT_INMUTE:
+ if (!corb_read (mixer, cad, wid, 0, GET_GAIN (0, 0), ix, &a, &b))
+ return OSS_EIO;
+ return (a >> 7) & 0x01;
+ break;
+
+ case CT_INSRC: /* Inverse mute */
+ if (!corb_read (mixer, cad, wid, 0, GET_GAIN (0, 0), ix, &a, &b))
+ return OSS_EIO;
+ return !((a >> 7) & 0x01);
+ break;
+
+ case CT_SELECT:
+ return widget->current_selector;
+ break;
+
+ case CT_INSRCSELECT: /* Emulated selector based on input mute controls */
+ return widget->current_selector;
+
+ case CT_OUTGAINSEL:
+ if (!corb_read (mixer, cad, wid, 0, GET_GAIN (1, 1), 0, &a, &b))
+ return OSS_EIO;
+ return a; // TODO: Handle mute
+ break;
+
+ case CT_OUTMONO:
+ if (!corb_read (mixer, cad, wid, 0, GET_GAIN (1, 1), 0, &a, &b))
+ return OSS_EIO;
+ left = a & 0x7f;
+ if (a & 0x80)
+ left = 0;
+ else
+ left = scalein_vol (a, widget->outamp_caps);
+ return left | (left << 16);
+ break;
+
+ case CT_OUTSTEREO:
+ if (!corb_read (mixer, cad, wid, 0, GET_GAIN (1, 1), 0, &a, &b))
+ return OSS_EIO;
+ left = a & 0x7f;
+ if (a & 0x80)
+ left = 0;
+ else
+ left = scalein_vol (a, widget->outamp_caps);
+ if (!corb_read (mixer, cad, wid, 0, GET_GAIN (1, 0), 0, &a, &b))
+ return OSS_EIO;
+ right = a & 0x7f;
+ if (a & 0x80)
+ right = 0;
+ else
+ right = scalein_vol (a, widget->outamp_caps);
+ return left | (right << 16);
+ break;
+
+ case CT_OUTMUTE:
+ if (!corb_read (mixer, cad, wid, 0, GET_GAIN (1, 0), 0, &a, &b))
+ return OSS_EIO;
+ return (a >> 7) & 0x01;
+ break;
+
+ default:
+ return OSS_EINVAL;
+ }
+
+ if (cmd == SNDCTL_MIX_WRITE)
+ {
+ switch (typ)
+ {
+ case CT_INMONO:
+ v = (value & 0xffff);
+ if (v == 0)
+ v = 0x80; /* Muted */
+ else
+ v = scaleout_vol (v, widget->inamp_caps);
+
+ corb_write (mixer, cad, wid, 0, SET_GAIN (0, 1, 1, 1, ix), v);
+ return value;
+ break;
+
+ case CT_INSTEREO:
+ v = (value & 0xffff);
+ if (v == 0)
+ v = 0x80; /* Muted */
+ else
+ v = scaleout_vol (v, widget->inamp_caps);
+
+ corb_write (mixer, cad, wid, 0, SET_GAIN (0, 1, 1, 0, ix), v);
+ v = ((value >> 16) & 0xffff);
+ if (v == 0)
+ v = 0x80; /* Muted */
+ else
+ v = scaleout_vol (v, widget->inamp_caps);
+ corb_write (mixer, cad, wid, 0, SET_GAIN (0, 1, 0, 1, ix), v);
+ return value;
+ break;
+
+ case CT_INGAINSEL:
+ v = (value & 0x7f);
+ // TODO: Handle mute
+ corb_write (mixer, cad, wid, 0, SET_GAIN (0, 1, 1, 0, ix), v);
+ corb_write (mixer, cad, wid, 0, SET_GAIN (0, 1, 0, 1, ix), v);
+ return value;
+ break;
+
+ case CT_INMUTE:
+ v = 0;
+ if (value)
+ v = 0x80;
+ corb_write (mixer, cad, wid, 0, SET_GAIN (0, 1, 1, 1, ix), v);
+ return value;
+ break;
+
+ case CT_INSRC: /* Inverse mute */
+ v = 0;
+ if (!value)
+ v = 0x80;
+ corb_write (mixer, cad, wid, 0, SET_GAIN (0, 1, 1, 1, ix), v);
+ return value;
+ break;
+
+ case CT_SELECT:
+ if (value < 0)
+ value = 0;
+ widget->current_selector = value;
+
+ if (value < widget->nconn)
+ {
+ /* Output source select */
+ corb_write (mixer, cad, wid, 0, SET_SELECTOR, value);
+ /* Enable output and HP amp. Set vref=Ground */
+ corb_write (mixer, cad, wid, 0, SET_PINCTL, 0xc0);
+ }
+ else
+ {
+ /* Input select
+ * Program the correct VRef Values
+ */
+
+ if (widget->pin_type == PIN_IN) /* Line-in */
+ {
+ corb_write (mixer, cad, wid, 0, SET_PINCTL, 0x20); /*Ground*/
+ }
+ else /* Mic-in */
+ {
+ corb_write (mixer, cad, wid, 0, SET_PINCTL, 0x24); /*Vref=8
+ 0% */
+ }
+ }
+ return value;
+ break;
+
+ case CT_INSRCSELECT: /* Emulated selector based on input mute mask */
+ if (value < 0)
+ value = 0;
+ if (value >= widget->nconn)
+ value = widget->nconn;
+ return handle_insrcselect (mixer, widget, value);
+ break;
+
+ case CT_OUTGAINSEL:
+ // TODO: Handle mute
+ corb_write (mixer, cad, wid, 0, SET_GAIN (1, 0, 1, 0, ix), value);
+ corb_write (mixer, cad, wid, 0, SET_GAIN (1, 0, 0, 1, ix), value);
+ return value;
+ break;
+
+ case CT_OUTMONO:
+ v = (value & 0xffff);
+ if (v == 0)
+ v = 0x80; /* Muted */
+ else
+ v = scaleout_vol (v, widget->outamp_caps);
+ corb_write (mixer, cad, wid, 0, SET_GAIN (1, 0, 1, 0, ix), v);
+ if (linked_wid)
+ corb_write (mixer, cad, wid, 0, SET_GAIN (1, 0, 1, 0, ix), v);
+ return value;
+ break;
+
+ case CT_OUTSTEREO:
+ v = (value & 0xffff);
+ if (v == 0)
+ v = 0x80; /* Muted */
+ else
+ v = scaleout_vol (v, widget->outamp_caps);
+ corb_write (mixer, cad, wid, 0, SET_GAIN (1, 0, 1, 0, ix), v);
+ if (linked_wid)
+ corb_write (mixer, cad, linked_wid, 0, SET_GAIN (1, 0, 1, 0, ix), v);
+ v = ((value >> 16) & 0xffff);
+ if (v == 0)
+ v = 0x80; /* Muted */
+ else
+ v = scaleout_vol (v, widget->outamp_caps);
+ corb_write (mixer, cad, wid, 0, SET_GAIN (1, 0, 0, 1, ix), v);
+ if (linked_wid)
+ corb_write (mixer, cad, linked_wid, 0, SET_GAIN (1, 0, 0, 1, ix), v);
+ return value;
+ break;
+
+ case CT_OUTMUTE:
+ v = 0;
+ if (value)
+ v = 0x80;
+ corb_write (mixer, cad, wid, 0, SET_GAIN (1, 0, 1, 1, ix), v);
+ if (linked_wid)
+ corb_write (mixer, cad, linked_wid, 0, SET_GAIN (1, 0, 1, 1, ix), v);
+ return value;
+ break;
+
+ default:
+ return OSS_EINVAL;
+ }
+ }
+
+ return OSS_EINVAL;
+}
+
+static int
+perform_pin_sense (hdaudio_mixer_t * mixer)
+{
+ int cad, wid;
+ unsigned int a, b;
+ int plugged_in;
+ codec_t *codec;
+
+ for (cad = 0; cad < mixer->ncodecs; cad++)
+ if (mixer->codecs[cad] != &NULL_codec)
+ {
+ codec = mixer->codecs[cad];
+
+ for (wid = 1; wid < mixer->codecs[cad]->nwidgets; wid++)
+ if (mixer->codecs[cad]->widgets[wid].wid_type == NT_PIN) /* Pin complex */
+ {
+ widget_t *widget = &mixer->codecs[cad]->widgets[wid];
+
+ widget->impsense = -1;
+ widget->sensed_pin = widget->pin_type;
+
+ plugged_in = 1;
+
+ /* Load the sense information */
+ if ((widget->pincaps & PINCAP_JACKSENSE_CAPABLE)
+ || (widget->pincaps & PINCAP_IMPSENSE_CAPABLE))
+ {
+ int tmout = 0;
+ if (!corb_read
+ (mixer, cad, wid, 0, GET_PIN_SENSE, 0, &a, &b))
+ a = 0x7fffffff; /* No jack present */
+
+ if (a & (1UL << 31)) /* Jack present */
+ if (widget->pincaps & PINCAP_TRIGGERR_RQRD) /* Trigger required */
+ {
+ corb_write (mixer, cad, wid, 0, TRIGGER_PIN_SENSE, 0);
+
+ do
+ {
+ oss_udelay (10);
+ if (!corb_read
+ (mixer, cad, wid, 0, GET_PIN_SENSE, 0, &a,
+ &b))
+ break;
+ }
+ while (tmout++ < 10000
+ && ((a & 0x7fffffff) == 0x7fffffff));
+ }
+
+ if (!corb_read
+ (mixer, cad, wid, 0, GET_PIN_SENSE, 0, &a, &b))
+ continue;
+ }
+ else
+ continue;
+
+ /* Precence detect */
+ if (widget->pincaps & PINCAP_JACKSENSE_CAPABLE)
+ {
+#if 0
+ if (a & (1UL << 31))
+ cmn_err (CE_WARN, "%s jack is plugged in\n",
+ widget->name);
+ else
+ cmn_err (CE_WARN, "%s NOT plugged in\n", widget->name);
+#endif
+ if (!(a & (1UL << 31)))
+ plugged_in = 0;
+ }
+
+ widget->plugged_in = plugged_in;
+ if (plugged_in)
+ codec->num_jacks_plugged++;
+
+ /* Impedance sensing */
+ widget->impsense = a & 0x7fffffff;
+ if (plugged_in && (widget->pincaps & PINCAP_IMPSENSE_CAPABLE))
+ if (hdaudio_jacksense > 0)
+ if (widget->impsense != 0x7fffffff) /* Sense operation finished */
+ {
+ /* cmn_err (CE_WARN, "%s sense %08x (%d)\n", widget->name, a, a & 0x7fffffff); */
+
+ if (widget->impsense >= 1000000) /* High impedance (line-in) pin */
+ {
+ /* cmn_err(CE_CONT, " --> Line level input\n"); */
+ widget->pin_type = widget->sensed_pin = PIN_IN;
+ }
+ else if (widget->impsense <= 10000) /* Low impedance (speaker/hp out) */
+ {
+ /* cmn_err(CE_CONT, " --> Output pin\n"); */
+ widget->pin_type = widget->sensed_pin = PIN_OUT;
+ }
+ else /* Something between low and high (mic?) */
+ {
+ /* cmn_err(CE_CONT, " --> Mic level input\n"); */
+ widget->pin_type = widget->sensed_pin = PIN_MIC;
+ }
+ }
+ }
+ }
+
+/*
+ * Set all pins to correct mode
+ */
+
+ for (cad = 0; cad < mixer->ncodecs; cad++)
+ if (mixer->codecs[cad] != &NULL_codec)
+ for (wid = 1; wid < mixer->codecs[cad]->nwidgets; wid++)
+ {
+ if (mixer->codecs[cad]->widgets[wid].wid_type == NT_PIN) /* Pin complex */
+ {
+ widget_t *widget = &mixer->codecs[cad]->widgets[wid];
+
+ if (widget->pin_type == PIN_IN
+ || widget->pin_type == PIN_UNKNOWN)
+ { /* Input PIN */
+ corb_write (mixer, cad, wid, 0, SET_PINCTL, 0x20);
+ }
+ if (widget->pin_type == PIN_MIC)
+ { /* Input PIN (mic) */
+ /* TODO: Handle mic amp */
+ corb_write (mixer, cad, wid, 0, SET_PINCTL, 0x24);
+ }
+ else
+ { /* Output PIN */
+ corb_write (mixer, cad, wid, 0, SET_PINCTL, 0xc0);
+ }
+
+ if (widget->pincaps & PINCAP_EAPD)
+ {
+ unsigned int eapd, dummy;
+ DDB (cmn_err
+ (CE_CONT, "Pin widget %d is EAPD capable.\n",
+ widget->wid));
+ if (corb_read
+ (mixer, cad, wid, 0, GET_EAPD, 0, &eapd, &dummy))
+ {
+ eapd |= 0x02; /* EAPD enable */
+ corb_write (mixer, cad, wid, 0, SET_EAPD, eapd);
+ }
+ }
+ }
+ }
+
+ return 1;
+}
+
+static int
+hdaudio_mix_init (int dev)
+{
+ hdaudio_mixer_t *mixer = mixer_devs[dev]->devc;
+ char tmp[32];
+ int err;
+ int working_codecs=0;
+ int cad;
+
+/*
+ * First pass. Count the number of active codecs.
+ */
+
+ for (cad = 0; cad < mixer->ncodecs; cad++)
+ if (mixer->codecs[cad] != &NULL_codec)
+ {
+ codec_t *codec = mixer->codecs[cad];
+
+ if (codec == &NULL_codec || codec->nwidgets == 0) /* Codec not active */
+ continue;
+
+ /*
+ * Enable the new generic mixer driver
+ */
+ if (codec->mixer_init == NULL)
+ {
+ codec->mixer_init = hdaudio_generic_mixer_init;
+ }
+
+ if (codec->active && codec->mixer_init != NULL_mixer_init)
+ working_codecs++;
+ }
+
+ /*
+ * Second pass. Initialize the mixer interfaces for all active codecs.
+ */
+
+ for (cad = 0; cad < mixer->ncodecs; cad++)
+ if (mixer->codecs[cad] != &NULL_codec)
+ {
+ codec_t *codec = mixer->codecs[cad];
+ int group = 0;
+
+ if (codec == &NULL_codec || codec->nwidgets == 0) /* Codec not active */
+ continue;
+
+ if (working_codecs > 1)
+ {
+ sprintf (tmp, "codec%d", cad + 1);
+ if ((group = mixer_ext_create_group (dev, 0, tmp)) < 0)
+ {
+ return group;
+ }
+ }
+
+ if (codec->active && codec->mixer_init != NULL_mixer_init)
+ {
+ if ((err = codec->mixer_init (dev, mixer, cad, group)) < 0)
+ {
+ if (err != OSS_EAGAIN)
+ return err;
+ /*
+ * We got EAGAIN whic means that we should fall
+ * to the old generic mixer.
+ */
+ if ((err =
+ hdaudio_generic_mixer_init (dev, mixer, cad, group)) < 0)
+ {
+ return err;
+ }
+ }
+ else
+ continue;
+ }
+ }
+
+ if (mixer->client_mixer_init != 0)
+ mixer->client_mixer_init (dev);
+
+ return 0;
+}
+
+static void
+copy_endpoints(hdaudio_mixer_t * mixer, codec_t *codec, int pass)
+{
+ int i;
+
+/*
+ * Install output endpoints from the codec to the global endpoint table.
+ */
+
+ for (i = 0; i < codec->num_outendpoints; i++)
+ {
+ hdaudio_endpointinfo_t *ep = &codec->outendpoints[i];
+ ep->skip = mixer->codecs[ep->cad]->widgets[ep->base_wid].skip;
+ }
+
+ for (i=0;i<codec->num_outendpoints;i++)
+ {
+ int ix = (codec->multich_map >> (i * 4)) & 0x0f;
+ hdaudio_endpointinfo_t *ep = &codec->outendpoints[ix];
+
+ ep->skip = mixer->codecs[ep->cad]->widgets[ep->base_wid].skip;
+
+ if (ep->skip || ep->already_used)
+ continue;
+
+ switch (pass)
+ {
+ case 0: /* Pick analog endpoints */
+ if (ep->is_digital)
+ continue;
+ break;
+
+ case 1: /* Pick digital endpoints */
+ if (!ep->is_digital)
+ continue;
+ break;
+ }
+
+ if (mixer->copied_outendpoints >= HDA_MAX_OUTSTREAMS)
+ {
+ cmn_err (CE_WARN,
+ "Too many output endpoints (%d)\n",
+ mixer->copied_outendpoints);
+ continue;
+ }
+
+ memcpy(&mixer->outendpoints[mixer->copied_outendpoints++], ep, sizeof(*ep));
+ ep->already_used=1;
+ }
+ mixer->num_outendpoints = mixer->copied_outendpoints;
+
+/*
+ * Install input endpoints from the codec to the global endpoint table.
+ */
+
+ for (i = 0; i < codec->num_inendpoints; i++)
+ {
+ hdaudio_endpointinfo_t *ep = &codec->inendpoints[i];
+ ep->skip = mixer->codecs[ep->cad]->widgets[ep->base_wid].skip;
+ }
+
+ for (i=0;i<codec->num_inendpoints;i++)
+ {
+ hdaudio_endpointinfo_t *ep = &codec->inendpoints[i];
+
+ ep->skip = mixer->codecs[ep->cad]->widgets[ep->base_wid].skip;
+
+ if (ep->skip || ep->already_used)
+ continue;
+
+ switch (pass)
+ {
+ case 0: /* Pick analog endpoints */
+ if (ep->is_digital)
+ continue;
+ break;
+
+ case 1: /* Pick digital endpoints */
+ if (!ep->is_digital)
+ continue;
+ break;
+ }
+
+ if (mixer->copied_inendpoints >= HDA_MAX_INSTREAMS)
+ {
+ cmn_err (CE_WARN,
+ "Too many output endpoints (%d)\n",
+ mixer->copied_inendpoints);
+ continue;
+ }
+
+ memcpy(&mixer->inendpoints[mixer->copied_inendpoints++], ep, sizeof(*ep));
+ ep->already_used=1;
+ }
+ mixer->num_inendpoints = mixer->copied_inendpoints;
+}
+
+ /*ARGSUSED*/
+ hdaudio_mixer_t *
+hdaudio_mixer_create (char *name, void *devc,
+ oss_device_t * osdev,
+ hdmixer_write_t writefunc,
+ hdmixer_read_t readfunc, unsigned int codecmask,
+ unsigned int vendor_id, unsigned int subvendor_id)
+{
+ hdaudio_mixer_t *mixer;
+ int i, func;
+ int ncodecs = 0;
+ char tmp[128];
+ int mixer_dev;
+
+ char *hw_info = osdev->hw_info; /* For printing hardware information */
+
+ if ((mixer = PMALLOC (osdev, sizeof (*mixer))) == NULL)
+ {
+ cmn_err (CE_WARN, "hdaudio_mixer_create: Out of memory\n");
+ return NULL;
+ }
+
+ memset (mixer, 0, sizeof (*mixer));
+
+ mixer->devc = devc;
+ mixer->osdev = osdev;
+ mixer->mixer_dev = 0;
+ strncpy (mixer->name, name, sizeof (mixer->name));
+ mixer->name[sizeof (mixer->name) - 1] = 0;
+
+ for (i = 0; i < MAX_CODECS; i++)
+ mixer->codecs[i] = &NULL_codec;
+
+ mixer->read = readfunc;
+ mixer->write = writefunc;
+ mixer->codecmask = codecmask;
+
+ sprintf (hw_info, "HD Audio controller %s\n"
+ "Vendor ID 0x%08x\n"
+ "Subvendor ID 0x%08x\n", name, vendor_id, subvendor_id);
+ hw_info += strlen (hw_info);
+
+ if (hdaudio_snoopy)
+ {
+ sprintf(hw_info, "**** Warning: Diagnostic mode enabled (hdaudio_snoopy) ****\n");
+ hw_info += strlen (hw_info);
+ }
+
+/*
+ * Search first all audio function groups for all codecs and then
+ * handle modem function groups.
+ */
+ for (func=1;func<=2;func++)
+ for (i = 0; i < 16; i++)
+ if (mixer->codecmask & (1 << i))
+ {
+ if (attach_codec (mixer, i, hw_info, subvendor_id, func) >= 0)
+ ncodecs++;
+ hw_info += strlen (hw_info);
+ }
+
+ if (ncodecs == 0)
+ {
+ cmn_err (CE_WARN, "No hdaudio codecs were detected\n");
+ return NULL;
+ }
+
+/*
+ * The attach_codec routine copied all analog endpoints to the global endpoint
+ * table. Now pick possible digital endpoints from the active codecs.
+ */
+
+ for (i = 0; i < 16; i++)
+ if (mixer->codecmask & (1 << i))
+ if (mixer->codecs[i]->active)
+ {
+ copy_endpoints(mixer, mixer->codecs[i], 1); /* Copy digital endpoints from codec to mixer */
+ }
+
+ if (!mixer->remap_avail)
+ check_names (mixer);
+
+ propagate_names (mixer);
+ perform_pin_sense (mixer);
+
+ if (mixer->chip_name == NULL)
+ mixer->chip_name = "None";
+ DDB (cmn_err (CE_CONT, "Mixer: %s %s\n", mixer->name, mixer->chip_name));
+ //sprintf (tmp, "%s %s", mixer->name, mixer->chip_name);
+ sprintf (tmp, "High Definition Audio %s", mixer->chip_name);
+
+ if ((mixer_dev = oss_install_mixer (OSS_MIXER_DRIVER_VERSION,
+ osdev,
+ osdev,
+ tmp,
+ &hda_mixer_driver,
+ sizeof (mixer_driver_t), mixer)) < 0)
+ {
+ return NULL;
+ }
+
+ mixer_devs[mixer_dev]->hw_devc = devc;
+ mixer_devs[mixer_dev]->priority = 10; /* Known motherboard device */
+ mixer->mixer_dev = mixer_dev;
+ mixer_ext_set_init_fn (mixer->mixer_dev, hdaudio_mix_init,
+ mixer->ncontrols * 4);
+ touch_mixer (mixer->mixer_dev);
+
+ return mixer;
+}
+
+/*ARGSUSED*/
+static int
+find_playvol_widget (hdaudio_mixer_t * mixer, int cad, int wid)
+{
+ int this_wid = wid;
+
+ return this_wid;
+}
+
+/*ARGSUSED*/
+static int
+find_recvol_widget (hdaudio_mixer_t * mixer, int cad, int wid)
+{
+ int this_wid = wid;
+
+ return this_wid;
+}
+
+/*ARGSUSED*/
+static int
+find_recsrc_widget (hdaudio_mixer_t * mixer, int cad, int wid)
+{
+ int this_wid = wid;
+
+ return this_wid;
+}
+
+static int
+attach_node (hdaudio_mixer_t * mixer, int cad, int wid, int parent)
+{
+ static const char *widget_types[16] = {
+ "Audio output",
+ "Audio input",
+ "Audio mixer",
+ "Audio selector",
+ "Pin complex",
+ "Power widget",
+ "Volume knob",
+ "Beep generator",
+ "Reserved8",
+ "Reserved9",
+ "ReservedA",
+ "ReservedB",
+ "ReservedC",
+ "ReservedD",
+ "ReservedE",
+ "Vendor defined audio"
+ };
+
+ static const char *widget_id[16] = {
+ "pcm",
+ "rec",
+ "mix",
+ "select",
+ "jack",
+ "power",
+ "vol",
+ "beep",
+ "unkn",
+ "unkn",
+ "unkn",
+ "unkn",
+ "unkn",
+ "unkn",
+ "unkn",
+ "vendor"
+ };
+
+ static const int bit_sizes[] = {
+ 8,
+ 16,
+ 20,
+ 24,
+ 32
+ };
+
+ static const unsigned int bit_fmts[] = {
+ AFMT_U8,
+ AFMT_S16_LE,
+ AFMT_S32_LE,
+ AFMT_S32_LE,
+ AFMT_S32_LE
+ };
+
+ static const int srates[] = {
+ 8000,
+ 11025,
+ 16000,
+ 22050,
+ 32000,
+ 44100,
+ 48000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+ 384000
+ };
+
+ unsigned int widget_caps, b, pstate, group_type_in;
+ unsigned int inamp_caps, outamp_caps;
+ int group_type, wid_type;
+ int i;
+
+ codec_t *codec = mixer->codecs[cad];
+ widget_t *widget;
+
+ if (codec == &NULL_codec)
+ return 0;
+
+ if (!corb_read
+ (mixer, cad, wid, 0, GET_PARAMETER, HDA_GROUP_TYPE, &group_type_in, &b))
+ group_type_in = 0;
+
+ if (wid >= MAX_WIDGETS)
+ {
+ cmn_err (CE_WARN, "Too many widgets for codec %d (%d/%d)\n", cad, wid, MAX_WIDGETS);
+ return 0;
+ }
+
+ mixer->ncontrols++;
+
+ codec->nwidgets = wid + 1;
+ widget = &codec->widgets[wid];
+
+ widget->cad = cad;
+ widget->wid = wid;
+
+ widget->rgbcolor = 0;
+
+ group_type = group_type_in & 0xff;
+ widget->group_type = group_type_in;
+
+ DDB (cmn_err (CE_CONT, "Node %d, parent %d type %d, unsol capa %d\n",
+ wid, parent, group_type, !!(group_type_in & 0x100)));
+
+ if (!corb_read
+ (mixer, cad, wid, 0, GET_PARAMETER, HDA_WIDGET_CAPS, &widget_caps, &b))
+ {
+ cmn_err (CE_WARN, "GET_PARAMETER HDA_WIDGET_CAPS failed\n");
+ return 0;
+ }
+
+ if (widget_caps & WCAP_AMP_CAP_OVERRIDE) /* Amp param override? */
+ {
+ if (!corb_read (mixer, widget->cad, widget->wid, 0,
+ GET_PARAMETER, HDA_OUTPUTAMP_CAPS, &outamp_caps, &b))
+ {
+ cmn_err (CE_WARN, "GET_PARAMETER HDA_OUTPUTAMP_CAPS failed\n");
+ return OSS_EIO;
+ }
+ widget->outamp_caps = outamp_caps;
+
+ if (!corb_read (mixer, widget->cad, widget->wid, 0,
+ GET_PARAMETER, HDA_INPUTAMP_CAPS, &inamp_caps, &b))
+ {
+ cmn_err (CE_WARN, "GET_PARAMETER HDA_INPUTAMP_CAPS failed\n");
+ return OSS_EIO;
+ }
+ widget->inamp_caps = inamp_caps;
+ }
+ else
+ {
+ widget->outamp_caps = outamp_caps = codec->default_outamp_caps;
+ widget->inamp_caps = inamp_caps = codec->default_inamp_caps;
+ }
+
+ if (!corb_read (mixer, cad, wid, 0, GET_POWER_STATE, 0, &pstate, &b))
+ return 0;
+
+ /* power up each of the widgets if there is a Power Capability (1<<10) */
+ if (widget_caps & WCAP_POWER_CTL)
+ corb_write (mixer, cad, wid, 0, SET_POWER_STATE, 0);
+
+ widget->widget_caps = widget_caps;
+
+ wid_type = (widget_caps >> 20) & 0x0f;
+ DDB (cmn_err
+ (CE_CONT, "\tWidget type %d (%s)(%s)\n", wid_type,
+ widget_types[wid_type], widget_id[wid_type]));
+ DDB (cmn_err (CE_CONT, "\tPower State %d\n", pstate));
+
+ if (widget_caps & WCAP_CONN_LIST)
+ {
+ unsigned int clen;
+ /* Handle connection list */
+
+ if (!corb_read
+ (mixer, cad, wid, 0, GET_PARAMETER, HDA_CONNLIST_LEN, &clen, &b))
+ {
+ cmn_err (CE_WARN, "GET_PARAMETER HDA_CONNLIST_LEN failed\n");
+ return 0;
+ }
+
+ if (clen & 0x80)
+ {
+ cmn_err (CE_WARN, "Long form connection list not supported\n");
+ return 0;
+ }
+
+ if (clen > 0)
+ {
+ if (clen > MAX_CONN)
+ {
+ cmn_err (CE_WARN, "Too many connections\n");
+ return 0;
+ }
+
+ DDB (cmn_err (CE_CONT, "\tConn list (%d): ", clen));
+
+ for (i = 0; i < clen; i += 4)
+ {
+ int j;
+ unsigned int a;
+
+ if (!corb_read
+ (mixer, cad, wid, 0, GET_CONNECTION_LIST_ENTRY, i, &a, &b))
+ {
+ cmn_err (CE_WARN, "GET_CONNECTION_LIST_ENTRY failed\n");
+ return 0;
+ }
+
+ for (j = 0; j < 4 && (i + j) < clen; j++)
+ {
+ int v, is_range = 0;
+
+ if (widget->nconn >= MAX_CONN)
+ {
+ cmn_err (CE_WARN,
+ "Too many connections for widget %d (%d)\n",
+ widget->wid, widget->nconn);
+ break;
+ }
+
+ v = (a >> (j * 8)) & 0xff;
+ DDB (cmn_err (CE_CONT, "%d ", v));
+
+ if (v & 0x80)
+ {
+ is_range = 1;
+ v &= ~0x80;
+ }
+
+ if (v < 0 || v >= MAX_WIDGETS)
+ {
+ cmn_err (CE_NOTE,
+ "Connection %d for widget %d is out of range (%d) - Skipped\n",
+ j, widget->wid, v);
+ continue;
+ }
+
+ if (is_range) /* Range: prev...v */
+ {
+ int x;
+ codec->widgets[v].references[codec->widgets[v].
+ refcount++] = wid;
+
+ if (widget->nconn < 1)
+ {
+ cmn_err (CE_CONT,
+ "Bad range connection for widget %d\n",
+ widget->wid);
+ continue;
+ }
+
+ for (x = widget->connections[widget->nconn - 1] + 1;
+ x <= v; x++)
+ {
+ if (widget->nconn >= MAX_CONN)
+ {
+ cmn_err (CE_WARN,
+ "Too many connections(B) for widget %d (%d)\n",
+ widget->wid, widget->nconn);
+ break;
+ }
+
+ widget->connections[widget->nconn++] = x;
+ codec->widgets[x].references[codec->widgets[x].
+ refcount++] = wid;
+ }
+ }
+ else
+ {
+ widget->connections[widget->nconn++] = v;
+ codec->widgets[v].references[codec->widgets[v].
+ refcount++] = wid;
+ }
+ }
+ }
+
+ DDB (cmn_err (CE_CONT, "\n"));
+ }
+ }
+
+ widget->wid_type = wid_type;
+
+ strcpy (widget->name, widget_id[wid_type]);
+
+ if (group_type == 0) /* Not group but a widget */
+ switch (wid_type)
+ {
+ case NT_DAC: /* Audio output */
+ case NT_ADC: /* Audio input */
+ {
+ unsigned int sizes;
+ int j;
+ hdaudio_endpointinfo_t *endpoint;
+
+ if (wid_type == 0)
+ { /* Output endpoint */
+ if (mixer->num_outendpoints >= HDA_MAX_OUTSTREAMS)
+ {
+ cmn_err (CE_WARN, "Too many output endpoints\n");
+ return 0;
+ }
+
+ endpoint = &codec->outendpoints[codec->num_outendpoints++];
+
+ endpoint->stream_number = endpoint->default_stream_number =
+ ++mixer->num_outendpoints;
+ endpoint->ix = codec->num_outendpoints - 1;
+ endpoint->iddle_stream = 0;
+ }
+ else
+ { /* Input endpoint */
+ if (mixer->num_inendpoints >= HDA_MAX_INSTREAMS)
+ {
+ cmn_err (CE_WARN, "Too many input endpoints\n");
+ return 0;
+ }
+
+ endpoint = &codec->inendpoints[codec->num_inendpoints++];
+
+ endpoint->stream_number = endpoint->default_stream_number =
+ ++mixer->num_inendpoints;
+ endpoint->ix = codec->num_inendpoints - 1;
+ endpoint->iddle_stream = 0;
+ }
+
+ endpoint->cad = cad;
+ endpoint->base_wid = wid;
+ endpoint->recsrc_wid = wid;
+ endpoint->volume_wid = wid;
+ endpoint->nrates=0;
+ endpoint->name = widget->name;
+
+ widget->endpoint = endpoint;
+
+ /*
+ * Find the widgets that manage rec/play volumes and recording
+ * source selection.
+ */
+ switch (wid_type)
+ {
+ case NT_DAC:
+ endpoint->volume_wid = find_playvol_widget (mixer, cad, wid);
+ break;
+
+ case NT_ADC:
+ endpoint->volume_wid = find_recvol_widget (mixer, cad, wid);
+ endpoint->recsrc_wid = find_recsrc_widget (mixer, cad, wid);
+ break;
+ }
+
+ if (widget->widget_caps & WCAP_STEREO)
+ endpoint->channels = 2;
+ else
+ endpoint->channels = 1;
+
+ sizes = codec->sizes;
+
+ if (widget->widget_caps & WCAP_DIGITAL) /* Digital */
+ {
+ endpoint->is_digital = 1;
+ if (wid_type == NT_ADC)
+ strcpy (widget->name, "spdifin");
+ else
+ {
+ strcpy (widget->name, "spdifout");
+ corb_write (mixer, cad, wid, 0, SET_SPDIF_CONTROL1, 1); /* Digital output enable */
+ endpoint->iddle_stream = FRONT_STREAM;
+ }
+
+ endpoint->fmt_mask |= AFMT_AC3;
+ if (sizes & (1 << 20)) /* 32 bits */
+ {
+ endpoint->fmt_mask |= AFMT_SPDIF_RAW;
+ }
+ }
+ else
+ {
+ endpoint->is_digital = 0;
+ }
+
+ if (widget->widget_caps & WCAP_FORMAT_OVERRIDE) /* Override */
+ {
+ if (!corb_read (mixer, cad, wid, 0,
+ GET_PARAMETER, HDA_PCM_SIZES, &sizes, &b))
+ {
+ cmn_err (CE_WARN, "GET_PARAMETER HDA_PCM_SIZES failed\n");
+ return 0;
+ }
+ }
+
+ widget->sizes = sizes;
+ endpoint->sizemask = sizes;
+ if (sizes == 0)
+ {
+ corb_read (mixer, cad, codec->afg, 0, GET_PARAMETER,
+ HDA_PCM_SIZES, &sizes, &b);
+ widget->sizes = sizes;
+ endpoint->sizemask = sizes;
+ }
+
+ DDB (cmn_err (CE_CONT, "\tPCM Size/Rate %08x\n", sizes));
+
+ for (j = 0; j < 5; j++)
+ if (sizes & (1 << (j + 16)))
+ {
+ DDB (cmn_err
+ (CE_CONT, "\t\tSupports %d bits\n", bit_sizes[j]));
+ endpoint->fmt_mask |= bit_fmts[j];
+ }
+
+ for (j = 0; j < 12; j++)
+ if (sizes & (1 << j))
+ {
+ DDB (cmn_err (CE_CONT, "\t\tSupports %d Hz\n", srates[j]));
+ if (endpoint->nrates < 20)
+ {
+ endpoint->rates[endpoint->nrates++] = srates[j];
+ }
+ }
+
+ if ((widget->widget_caps & WCAP_DIGITAL) && wid_type == NT_DAC) /* Digital output */
+ {
+ /*
+ * Select front output as the default stream number. In
+ * this way copy of the analog front signal will automatically
+ * be delivered to the S/PDIF outpput when the S/PDIF device
+ * is not being used for some other purpose.
+ */
+ corb_write (mixer, cad, wid, 0, SET_CONVERTER,
+ FRONT_STREAM << 4);
+ }
+ else
+ {
+ /* Select the iddle stream (0) for analog outputs */
+ corb_write (mixer, cad, wid, 0, SET_CONVERTER,
+ IDDLE_STREAM << 4);
+ }
+ /* Select 48 kHz/16 bits/stereo */
+ corb_write (mixer, cad, wid, 0, SET_CONVERTER_FORMAT, 0x0009);
+
+ }
+ break;
+
+ case NT_KNOB: /* Volume knob */
+ /* Clear the direct control bit */
+ corb_write (mixer, cad, wid, 0, SET_VOLUME_KNOB_CONTROL, 0x00);
+ break;
+
+ case NT_PIN: /* Pin complex */
+ {
+ unsigned int conf;
+ unsigned int pincaps;
+
+ if (!corb_read
+ (mixer, cad, wid, 0, GET_CONFIG_DEFAULT, 0, &conf, &b))
+ return 0;
+ if (!corb_read (mixer, cad, wid, 0,
+ GET_PARAMETER, HDA_PIN_CAPS, &pincaps, &b))
+ {
+ cmn_err (CE_WARN, "GET_PARAMETER HDA_PIN_CAPS failed\n");
+ return 0;
+ }
+
+ widget->pincaps = pincaps;
+#if 0
+ if (widget->widget_caps & WCAP_UNSOL_CAPABLE)
+ corb_write (mixer, cad, wid, 0, SET_UNSOLICITED, 0x80 | wid);
+#endif
+
+ if (conf != 0)
+ {
+ int default_device = (conf >> 20) & 0x0f;
+ int default_loc = (conf >> 24) & 0x3f;
+ int conn = (conf >> 30) & 0x03;
+
+ int color;
+ int no_color=0;
+ char *name = NULL, *loc = "", *color_name = NULL;
+
+ color = (conf >> 12) & 0x0f;
+
+ if (pincaps & (1 << 6))
+ cmn_err (CE_WARN, "Balanced I/O not supported\n");
+ if (!(pincaps & (1 << 5))) /* No input */
+ widget->pin_type = PIN_OUT;
+ if (!(pincaps & (1 << 4)))
+ widget->pin_type = PIN_IN;
+
+ DDB (cmn_err (CE_CONT, "\tConfig default %08x\n", conf));
+
+ if ((default_loc & 0x0f) == 0x1) /* Rear panel - default */
+ loc = "";
+ if ((default_loc & 0x0f) == 0x2) /* Front panel */
+ loc = "fp-";
+ if ((default_loc & 0xf0) == 0x10) /* Internal func - eg cd/tad/spk */
+ loc = "int-";
+
+ if (conn == 1 && !(hdaudio_noskip & 1)) /* Pin not connected to anything */
+ {
+ widget->skip = 1;
+ widget->skip_output = 1;
+ }
+
+ switch (default_device)
+ {
+ case 0x0:
+ name = "lineout";
+ widget->pin_type = PIN_OUT;
+ break;
+ case 0x1:
+ name = "speaker";
+ no_color=1;
+ widget->pin_type = PIN_OUT;
+ break;
+ case 0x2:
+ name = "headphone";
+ widget->pin_type = PIN_OUT;
+ break;
+ case 0x3:
+ name = "cd";
+ no_color=1;
+ widget->pin_type = PIN_IN;
+ break;
+ case 0x4:
+ name = "spdifout";
+ no_color=1;
+ widget->pin_type = PIN_OUT;
+ break;
+ case 0x5:
+ name = "digout";
+ no_color=1;
+ widget->pin_type = PIN_OUT;
+ break;
+ case 0x6:
+ name = "modem";
+ no_color=1;
+ break;
+ case 0x7:
+ name = "phone";
+ no_color=1;
+ break;
+ case 0x8:
+ name = "linein";
+ widget->pin_type = PIN_IN;
+ break;
+ case 0x9:
+ name = "aux";
+ break;
+ case 0xa:
+ name = "mic";
+ widget->pin_type = PIN_MIC;
+ break;
+ case 0xb:
+ name = "telephony";
+ no_color=1;
+ break;
+ case 0xc:
+ name = "spdifin";
+ no_color=1;
+ break;
+ case 0xd:
+ name = "digin";
+ no_color=1;
+ break;
+ case 0xe:
+ name = "reserved";
+ no_color=1;
+ break;
+ case 0xf: /* Unused pin widget */
+ if (hdaudio_noskip & 2) break;
+ widget->skip = 1;
+ widget->skip_output = 1;
+ break;
+ }
+
+/* process only colored jacks and skip fixed function jacks */
+ switch (color)
+ {
+ case 0x1:
+ color_name = "black";
+ widget->rgbcolor = OSS_RGB_BLACK;
+ break;
+ case 0x2:
+ color_name = "gray";
+ widget->rgbcolor = OSS_RGB_GRAY;
+ break;
+ case 0x3:
+ color_name = "blue";
+ widget->rgbcolor = OSS_RGB_BLUE;
+ break;
+ case 0x4:
+ color_name = "green";
+ widget->rgbcolor = OSS_RGB_GREEN;
+ break;
+ case 0x5:
+ color_name = "red";
+ widget->rgbcolor = OSS_RGB_RED;
+ break;
+ case 0x6:
+ color_name = "orange";
+ widget->rgbcolor = OSS_RGB_ORANGE;
+ break;
+ case 0x7:
+ color_name = "yellow";
+ widget->rgbcolor = OSS_RGB_YELLOW;
+ break;
+ case 0x8:
+ color_name = "purple";
+ widget->rgbcolor = OSS_RGB_PURPLE;
+ break;
+ case 0x9:
+ color_name = "pink";
+ widget->rgbcolor = OSS_RGB_PINK;
+ break;
+ case 0xe:
+ color_name = "white";
+ widget->rgbcolor = OSS_RGB_WHITE;
+ break;
+
+ default:
+ if (name != NULL)
+ color_name = name;
+ else
+ color_name = "internal";
+ }
+
+ if (no_color)
+ widget->rgbcolor = 0;
+
+ if (default_device == 0xf) /* Not present */
+ {
+ widget->rgbcolor=0;
+ color_name = "internal";
+ }
+
+ sprintf (widget->color, "%s%s", loc, color_name);
+/*
+ * By Hannu 20080111
+ * Use jack color as the widget name if no name was defined or if the default
+ * function is lineout. This fixes the problem of several jacks being named
+ * as lineout.
+ */
+ if (name == NULL || default_device == 0x00)
+ name = color_name;
+ sprintf (widget->name, "%s%s", loc, name);
+ }
+ }
+ break;
+ }
+
+#if 0
+ if (!corb_read
+ (mixer, cad, wid, 0, GET_PARAMETER, HDA_NODE_COUNT, &node_count, &b))
+ {
+ cmn_err (CE_WARN, "GET_PARAMETER HDA_NODE_COUNT1 failed\n");
+ return 0;
+ }
+
+ first_node = (node_count >> 16) & 0xff;
+ num_nodes = node_count & 0xff;
+
+ DDB (cmn_err
+ (CE_CONT, "\tFirst node %d, num nodes %d\n", first_node, num_nodes));
+
+ if (first_node > 0)
+ for (i = first_node; i < first_node + num_nodes; i++)
+ if (!attach_node (mixer, cad, i, wid))
+ return 0;
+#endif
+/*
+ * Handle hardcodded widget names
+ */
+
+ if (codec->remap != NULL)
+ {
+ int w;
+
+ mixer->remap_avail=1;
+
+ for (w = 0; codec->remap[w] != NULL; w++)
+ if (w == wid)
+ {
+ char *s = codec->remap[w];
+
+ if (*s != 0)
+ {
+ strcpy (widget->name, s);
+ if (*widget->color == 0)
+ {
+ strcpy (widget->color, widget->name);
+ }
+ }
+ break;
+ }
+ }
+
+ return 1;
+}
+
+/*ARGSUSED*/
+static int
+attach_group (hdaudio_mixer_t * mixer, int cad, int wid, int parent, int group_type)
+{
+ unsigned int a, b, gt;
+ int i, first_node, num_nodes;
+ codec_t *codec = mixer->codecs[cad];
+
+ if (codec == &NULL_codec)
+ return 0;
+
+ if (!corb_read (mixer, cad, wid, 0,
+ GET_PARAMETER, HDA_OUTPUTAMP_CAPS,
+ &codec->default_outamp_caps, &b))
+ {
+ cmn_err (CE_WARN, "GET_PARAMETER HDA_OUTPUTAMP_CAPS failed\n");
+ return 0;
+ }
+
+ if (!corb_read (mixer, cad, wid, 0,
+ GET_PARAMETER, HDA_INPUTAMP_CAPS,
+ &codec->default_inamp_caps, &b))
+ {
+ cmn_err (CE_WARN, "GET_PARAMETER HDA_INPUTTAMP_CAPS failed\n");
+ return 0;
+ }
+
+ if (!corb_read (mixer, cad, wid, 0, GET_PARAMETER, HDA_NODE_COUNT, &a, &b))
+ {
+ cmn_err (CE_WARN, "GET_PARAMETER HDA_NODE_COUNT2 failed\n");
+ return 0;
+ }
+
+ first_node = (a >> 16) & 0xff;
+ num_nodes = a & 0xff;
+
+ corb_read (mixer, cad, wid, 0, GET_PARAMETER, HDA_GROUP_TYPE, &gt, &b);
+ DDB (cmn_err (CE_CONT, "\tGroup %d First node %d, num nodes %d\n", wid,
+ first_node, num_nodes));
+/*
+ * Ignore other than audio function groups. Codecs probably allocate
+ * higher widget number for the modem group than the audio group. So in this
+ * way we can have smaller MAX_WIDGETS which in turn conserves memory.
+ */
+ if ((gt & 0xff) != group_type)
+ return 0;
+
+ if (corb_read (mixer, cad, wid, 0, GET_PARAMETER, HDA_PCM_SIZES, &a, &b))
+ {
+ codec->sizes = a;
+ }
+
+ if (first_node > 0)
+ for (i = first_node; i < first_node + num_nodes; i++)
+ if (!attach_node (mixer, cad, i, wid))
+ return 0;
+
+ if (num_nodes >= 1)
+ codec->active=1;
+ return 1;
+}
+
+static void
+polish_widget_list (hdaudio_mixer_t * mixer, int cad)
+{
+ widget_t *widget;
+ codec_t *codec;
+ int wid, conn, loop;
+ int skip = 0;
+ int do_jacksense = 0;
+
+ if (hdaudio_noskip & 4) return;
+
+ if (mixer->codecs[cad] == &NULL_codec)
+ {
+ cmn_err (CE_WARN, "Bad codec %d\n", cad);
+ }
+ codec = mixer->codecs[cad];
+
+ for (wid = 0; wid < codec->nwidgets; wid++)
+ {
+ widget = &codec->widgets[wid];
+ }
+
+/*
+ * Use jack sensing information to remove unconnected I/O pints from the mixer
+ * interface.
+ */
+
+ do_jacksense = 1;
+ if (codec->num_jacks_plugged < 1 || hdaudio_jacksense < 1)
+ do_jacksense = 0;
+
+ if (do_jacksense)
+ for (wid = 0; wid < codec->nwidgets; wid++)
+ {
+ widget = &codec->widgets[wid];
+
+ if (widget->wid_type != NT_PIN) /* Not a pin widget */
+ continue;
+
+ if (!widget->plugged_in)
+ widget->skip = 1;
+ }
+
+ /*
+ * Check all widgets and mark them unusable (skip=1) if all of their input
+ * connections are marked to be skipped.
+ *
+ * This needs to be done number of times so that the skip status propagates
+ * to the end of the longest path.
+ */
+
+ for (loop = 0; loop < codec->nwidgets / 4; loop++) /* nwidgets/4 iterations */
+ for (wid = 0; wid < codec->nwidgets; wid++)
+ {
+ widget = &codec->widgets[wid];
+
+ if (widget->skip || widget->used)
+ continue;
+
+ skip = 1; /* For now */
+ for (conn = 0; conn < widget->nconn; conn++)
+ {
+ if (!codec->widgets[widget->connections[conn]].skip)
+ skip = 0; /* Cannot be skipped */
+ }
+
+ if (skip && widget->nconn > 0)
+ {
+ widget->skip = 1;
+ widget->used = 1;
+ }
+ }
+
+/*
+ * Do the same backwards. Remove widgets that don't have connectivity to any
+ * of the pin widgets.
+ */
+ for (loop = 0; loop < codec->nwidgets / 4; loop++) /* nwidgets/4 iterations */
+ for (wid = 0; wid < codec->nwidgets; wid++)
+ {
+ widget = &codec->widgets[wid];
+
+ if (widget->skip_output || widget->used)
+ continue;
+
+ skip = 1; /* For now */
+ for (conn = 0; conn < widget->refcount; conn++)
+ {
+ if (!codec->widgets[widget->references[conn]].skip_output)
+ skip = 0; /* Cannot be skipped */
+ }
+
+ if (skip && widget->refcount > 0)
+ {
+ widget->skip_output = 1;
+ widget->used = 1;
+ }
+ }
+
+/*
+ * Final pass.
+ */
+
+ for (wid = 0; wid < codec->nwidgets; wid++)
+ {
+ widget = &codec->widgets[wid];
+
+ if (widget->skip_output)
+ {
+ widget->skip = 1;
+ }
+ }
+}
+
+
+/* ARGSUSED */
+static int
+attach_codec (hdaudio_mixer_t * mixer, int cad, char *hw_info,
+ unsigned int pci_subdevice, int group_type)
+{
+ unsigned int a, b, x;
+ int subix, ix, i;
+ int first_node, num_nodes;
+ int has_audio_group = 0;
+ codec_t *codec;
+
+ if (cad >= MAX_CODECS)
+ {
+ cmn_err (CE_WARN, "attach_codec: Too many codecs %d\n", cad);
+ return OSS_EIO;
+ }
+
+ mixer->ncodecs = cad + 1;
+
+ if (mixer->codecs[cad] == &NULL_codec)
+ {
+ if ((codec = PMALLOC (mixer->osdev, sizeof (*codec))) == NULL)
+ {
+ cmn_err (CE_CONT, "Cannot allocate codec descriptor\n");
+ return OSS_ENOMEM;
+ }
+
+ memset (codec, 0, sizeof (*codec));
+
+ mixer->codecs[cad] = codec;
+ }
+ else
+ {
+ codec = mixer->codecs[cad];
+ }
+
+ corb_write (mixer, cad, 0, 0, SET_POWER_STATE, 0); /* Power up everything */
+
+ if (!corb_read (mixer, cad, 0, 0, GET_PARAMETER, HDA_VENDOR, &a, &b))
+ {
+ if (group_type == 1)
+ {
+ sprintf (hw_info, " Codec %2d: Not present\n", cad);
+ cmn_err (CE_NOTE,
+ "attach_codec: Codec #%d is not physically present\n",
+ cad);
+ }
+ return OSS_EIO;
+ }
+ codec->vendor_id = a;
+
+/*
+ * Find out the primary group list
+ */
+
+ if (!corb_read (mixer, cad, 0, 0, GET_PARAMETER, HDA_NODE_COUNT, &x, &b))
+ {
+ cmn_err (CE_WARN, "GET_PARAMETER HDA_NODE_COUNT3 failed\n");
+ return OSS_EIO;
+ }
+
+ codec->first_node = first_node = (x >> 16) & 0xff;
+ num_nodes = x & 0xff;
+
+/*
+ * Check if this one is an audio codec (has an audio function group)
+ */
+ for (i = first_node; i < first_node + num_nodes; i++)
+ {
+ unsigned int gt;
+
+ corb_read (mixer, cad, i, 0, GET_PARAMETER, HDA_GROUP_TYPE, &gt, &b);
+/*
+ * Handle only the function group type requested by the upper layer code.
+ */
+ if ((gt & 0xff) != 1)
+ continue;
+
+ has_audio_group = 1;
+ }
+
+/*
+ * Find codec specific settings
+ */
+ for (ix = 0; codecs[ix].id != 0; ix++)
+ if (codecs[ix].id == a)
+ break;
+
+ DDB (cmn_err
+ (CE_CONT, "HD audio Codec ID: %08x (%s)\n", a, codecs[ix].name));
+
+ if (codecs[ix].id == 0) /* Unknown codec */
+ {
+ if (group_type == 1)
+ sprintf (hw_info, " Codec %2d: Unknown (0x%08x", cad, a);
+ cmn_err (CE_NOTE, "HDA codec 0x%08x not known yet\n", a);
+ /*
+ * Create hexadecimal codec ID
+ */
+ if (has_audio_group && mixer->chip_name == NULL)
+ if ((mixer->chip_name = PMALLOC (mixer->osdev, 32)) != NULL)
+ {
+ sprintf (mixer->chip_name, "0x%08x", a);
+ }
+ }
+ else
+ {
+ if (group_type == 1)
+ sprintf (hw_info, " Codec %2d: %s (0x%08x", cad, codecs[ix].name, a);
+ }
+
+ if (has_audio_group && mixer->chip_name == NULL)
+ {
+ mixer->chip_name = codecs[ix].name;
+
+ }
+
+ if (codecs[ix].remap != NULL)
+ codec->remap = codecs[ix].remap;
+
+ if (codecs[ix].flags != 0)
+ codec->vendor_flags = codecs[ix].flags;
+
+ if (codecs[ix].mixer_init != NULL)
+ codec->mixer_init = codecs[ix].mixer_init;
+
+ codec->multich_map = codecs[ix].multich_map;
+
+ if (corb_read (mixer, cad, 0, 0, GET_PARAMETER, HDA_REVISION, &a, &b))
+ {
+ DDB (cmn_err (CE_CONT, "HDA codec revision %d.%d (%d.%d) (0x%08x)\n",
+ (a >> 20) & 0xf,
+ (a >> 16) & 0xf, (a >> 8) & 0xff, a & 0xff, a));
+ }
+ else
+ DDB (cmn_err (CE_CONT, "Can't get codec revision\n"));
+
+ codec->codec_desc = &codecs[ix];
+
+ DDB (cmn_err (CE_CONT, "**** Codec %d ****\n", cad));
+ DDB (cmn_err
+ (CE_CONT, "First node %d, num nodes %d\n", first_node, num_nodes));
+
+ for (i = first_node; i < first_node + num_nodes; i++)
+ {
+ corb_read (mixer, cad, i, 0, GET_PARAMETER, HDA_GROUP_TYPE, &a, &b);
+ if ((a & 0xff) == group_type) /* Proper function group type */
+ {
+ codec->afg = i;
+
+ if (corb_read (mixer, cad, i, 0, GET_SUBSYSTEM_ID, 0, &a, &b))
+ {
+ DDB (cmn_err (CE_CONT, "Subsystem ID = 0x%08x\n", a));
+
+ if (group_type == 1)
+ {
+ /* Append subvendor ID to hw_info */
+ hw_info += strlen (hw_info);
+ sprintf (hw_info, "/0x%08x", a);
+ }
+
+ codec->subvendor_id = a;
+
+ for (subix = 0; subdevices[subix].id != 0; subix++)
+ if (subdevices[subix].id == a)
+ {
+ if (subdevices[subix].main_id != 0)
+ if (subdevices[subix].main_id != codec->vendor_id)
+ continue;
+
+ if (subdevices[subix].pci_subdevice != 0)
+ if (subdevices[subix].pci_subdevice != pci_subdevice)
+ continue;
+
+
+ DDB (cmn_err
+ (CE_CONT, "Subdevice known as %s\n",
+ subdevices[subix].name));
+ if (group_type == 1)
+ {
+ hw_info += strlen (hw_info);
+ sprintf (hw_info, " %s", subdevices[subix].name);
+ }
+ if (subdevices[subix].remap != NULL)
+ {
+ codec->remap = subdevices[subix].remap;
+ }
+
+ if (subdevices[subix].multich_map != 0)
+ codec->multich_map = subdevices[subix].multich_map;
+ if (subdevices[subix].flags != 0)
+ codec->vendor_flags = subdevices[subix].flags;
+ if (subdevices[subix].mixer_init != NULL)
+ {
+ codec->mixer_init = subdevices[subix].mixer_init;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ hw_info += strlen (hw_info);
+ if (group_type == 1)
+ strcpy (hw_info, ")\n");
+
+ if (codec->multich_map == 0)
+ {
+ codec->multich_map = 0x76543210; /* Sequential order */
+ }
+
+ for (i = first_node; i < first_node + num_nodes; i++)
+ {
+ if (!attach_group (mixer, cad, i, 0, group_type))
+ continue;
+
+ /* power up the AFG! */
+ corb_write (mixer, cad, i, 0, SET_POWER_STATE, 0);
+ }
+
+ /* Initialize and setup manually endpoints for Si3055. */
+ if ((mixer->codecs[cad]->vendor_flags & VF_SI3055_HACK) && (group_type == 2))
+ {
+ hdaudio_si3055_endpoint_init(mixer, cad);
+ }
+
+ if (has_audio_group)
+ {
+ polish_widget_list (mixer, cad);
+ }
+
+ copy_endpoints(mixer, codec, 0); /* Copy analog endpoints from codec to mixer */
+
+ return (has_audio_group) ? 0 : OSS_EIO;
+}
+
+int
+hdaudio_mixer_get_mixdev (hdaudio_mixer_t * mixer)
+{
+ return mixer->mixer_dev;
+}
+
+void
+hdaudio_mixer_set_initfunc (hdaudio_mixer_t * mixer,
+ mixer_create_controls_t func)
+{
+ mixer->client_mixer_init = func;
+}
+
+#define BASE44k (1<<14)
+
+static const struct hdaudio_rate_def _hdaudio_rates[] = {
+ /* 48 kHz based rates */
+ {192000, (3 << 11)}, /* 4x */
+ {96000, (1 << 11)}, /* 2x */
+ {48000, 0}, /* 1x */
+ {24000, (1 << 8)}, /* 1/2x */
+ {16000, (2 << 8)}, /* 1/3x */
+ {12000, (3 << 8)}, /* 1/4x */
+ {9600, (4 << 8)}, /* 1/5x */
+ {8000, (5 << 8)}, /* 1/6x */
+ /* TODO: These rates didn't work for some reason. */
+ /* 44.1 kHz based rates */
+ {176400, BASE44k | (3 << 11)}, /* 4x */
+ {88200, BASE44k | (1 << 11)}, /* 2x */
+ {44100, BASE44k}, /* 1x */
+ {22050, BASE44k | (1 << 8)}, /* 1/2x */
+ {14700, BASE44k | (2 << 8)}, /* 1/3x */
+ {11025, BASE44k | (3 << 8)}, /* 1/4x */
+ {8820, BASE44k | (4 << 8)}, /* 1/5x */
+ {7350, BASE44k | (5 << 8)}, /* 1/6x */
+ {0}
+};
+
+const struct hdaudio_rate_def *hdaudio_rates = _hdaudio_rates;
+
+int
+hdaudio_codec_setup_endpoint (hdaudio_mixer_t * mixer,
+ hdaudio_endpointinfo_t * endpoint,
+ int rate, int channels, int fmt,
+ int stream_number, unsigned int *setupbits)
+{
+
+ unsigned int tmp, spdif, dummy;
+ int i;
+
+ endpoint->auto_muted = 0;
+
+ if (!corb_read
+ (mixer, endpoint->cad, endpoint->base_wid, 0, GET_SPDIF_CONTROL, 0,
+ &spdif, &dummy))
+ spdif = 0;
+
+ spdif &= ~(1 << 5); /* Audio */
+
+ tmp = 0;
+
+ if (fmt == AFMT_AC3)
+ channels = 2;
+
+ tmp |= channels - 1;
+
+ switch (fmt)
+ {
+ case AFMT_U8:
+ break;
+
+ case AFMT_S16_LE:
+ tmp |= 0x00000010;
+ break;
+
+ case AFMT_S32_LE:
+ if (endpoint->sizemask & (1 << 20)) /* 32 bits */
+ tmp |= 0x00000040;
+ else if (endpoint->sizemask & (1 << 19)) /* 24 bits */
+ tmp |= 0x00000030;
+ else if (endpoint->sizemask & (1 << 18)) /* 20 bits */
+ tmp |= 0x00000020;
+ else
+ {
+ cmn_err (CE_WARN, "Bad bit size\n");
+ return OSS_EIO;
+ }
+ break;
+
+ case AFMT_AC3:
+ tmp &= 0xff;
+ tmp |= 0x11; /* 16 bits stereo */
+ spdif |= (1 << 5); /* Data */
+ break;
+
+ case AFMT_SPDIF_RAW:
+ tmp &= 0xff;
+ tmp |= 0x81; /* 32 bits stereo */
+ break;
+
+ default:
+ cmn_err (CE_WARN, "Bad format %x\n", fmt);
+ return OSS_EIO;
+ }
+
+ corb_write (mixer, endpoint->cad, endpoint->base_wid, 0, SET_SPDIF_CONTROL1,
+ spdif & 0xff);
+/*
+ * Finally the sample rate setup
+ */
+
+ for (i = 0; hdaudio_rates[i].rate != 0; i++)
+ if (hdaudio_rates[i].rate == rate)
+ {
+ tmp |= hdaudio_rates[i].mask;
+ break;
+ }
+
+ *setupbits = tmp;
+
+ if (mixer->codecs[endpoint->cad]->vendor_flags & VF_SI3055_HACK)
+ {
+ hdaudio_si3055_set_rate(mixer, endpoint->cad, rate);
+ }
+
+ corb_write (mixer, endpoint->cad, endpoint->base_wid, 0,
+ SET_CONVERTER_FORMAT, tmp);
+ corb_write (mixer, endpoint->cad, endpoint->base_wid, 0, SET_CONVERTER,
+ stream_number << 4);
+
+ if (channels > 2)
+ {
+ /*
+ * Set up the converters for the other stereo pairs
+ */
+#if 1
+ // TODO: Test this
+
+ int n = (channels + 1) / 2;
+
+ for (i = 1; i < n; i++)
+ {
+ hdaudio_endpointinfo_t *ep;
+ ep = &endpoint[i];
+
+ corb_write (mixer, ep->cad, ep->base_wid, 0, SET_CONVERTER_FORMAT,
+ tmp);
+ corb_write (mixer, ep->cad, ep->base_wid, 0, SET_CONVERTER,
+ (stream_number << 4) | (i * 2));
+ }
+#endif
+ }
+
+ if (mixer->codecs[endpoint->cad]->vendor_flags & VF_VAIO_HACK)
+ {
+ /*
+ * STAC9872 specific hack. In Sony VAIO configurations, the DAC widget
+ * used for the headphone jack needs to duplicate the stream playing on
+ * the DAC widget for the speaker when not in multichannel mode.
+ */
+ if (channels <= 2 && endpoint->base_wid == 0x05)
+ {
+ corb_write (mixer, endpoint->cad, 0x02, 0, SET_CONVERTER_FORMAT,
+ tmp);
+ corb_write (mixer, endpoint->cad, 0x02, 0, SET_CONVERTER,
+ stream_number << 4);
+ }
+ }
+
+#if 1
+ if (mixer->codecs[endpoint->cad]->vendor_flags & VF_ALC88X_HACK)
+ {
+ /*
+ * ALC88x specfic hack. These codecs cannot play S/PDIF unless the front
+ * DAC widget is playing the same stream.
+ *
+ * Analog front output (widget 0x14) will be automatically muted.
+ */
+ if (endpoint->is_digital)
+ {
+ unsigned int v, b;
+
+ hdaudio_codec_setup_endpoint (mixer, &mixer->outendpoints[0], rate,
+ channels, fmt, stream_number, &tmp);
+
+ if (fmt == AFMT_AC3)
+ if (corb_read
+ (mixer, endpoint->cad, 0x14, 0, GET_GAIN (1, 0), 0, &v, &b))
+ {
+ endpoint->auto_muted = !(v & 0x80);
+
+ v |= 0x80; /* Mute */
+ corb_write (mixer, endpoint->cad, 0x14, 0,
+ SET_GAIN (1, 0, 1, 1, 0), v);
+ }
+ }
+ }
+#endif
+
+ return 0;
+}
+
+int
+hdaudio_codec_reset_endpoint (hdaudio_mixer_t * mixer,
+ hdaudio_endpointinfo_t * endpoint, int channels)
+{
+
+ int i;
+ unsigned int v, b;
+ int n = (channels + 1) / 2;
+
+ /*
+ * Set all converters to play stream iddle stream (usually 0=silence).
+ */
+
+ corb_write (mixer, endpoint->cad, endpoint->base_wid, 0, SET_CONVERTER,
+ endpoint->iddle_stream << 4);
+
+#if 1
+ // TODO: Test this
+ if (channels > 2)
+ for (i = 1; i < n; i++)
+ {
+ hdaudio_endpointinfo_t *ep;
+
+ ep = &endpoint[i];
+
+ corb_write (mixer, ep->cad, ep->base_wid, 0, SET_CONVERTER,
+ ep->iddle_stream << 4);
+ }
+#endif
+
+ if (mixer->codecs[endpoint->cad]->vendor_flags & VF_VAIO_HACK)
+ /* Also set headphone DAC to play the iddle stream */
+ if (channels <= 2 && endpoint->base_wid == 0x05)
+ {
+ corb_write (mixer, endpoint->cad, 0x02, 0, SET_CONVERTER,
+ endpoint->iddle_stream << 4);
+ }
+
+ if (mixer->codecs[endpoint->cad]->vendor_flags & VF_ALC88X_HACK)
+ if (endpoint->is_digital && endpoint->auto_muted) /* Restore automatic analog mute back to normal */
+ {
+ if (corb_read
+ (mixer, endpoint->cad, 0x14, 0, GET_GAIN (1, 0), 0, &v, &b))
+ {
+ v &= ~0x80; /* Unmute */
+ corb_write (mixer, endpoint->cad, 0x14, 0,
+ SET_GAIN (1, 0, 1, 1, 0), v);
+ endpoint->auto_muted = 0;
+ }
+ }
+
+ return 0;
+}
+
+/*ARGSUSED*/
+void
+hda_codec_unsol (hdaudio_mixer_t * mixer, unsigned int upper,
+ unsigned int lower)
+{
+ DDB (cmn_err (CE_CONT, "Unsol event %08x %08x\n", upper, lower));
+}
+
+int
+hda_codec_getname (hdaudio_mixer_t * mixer, hda_name_t * name)
+{
+ widget_t *widget;
+
+ if (name->cad >= mixer->ncodecs)
+ return OSS_EIO;
+ if (mixer->codecs[name->cad] == &NULL_codec)
+ return OSS_EIO;
+#if 1
+ if (name->wid >= mixer->codecs[name->cad]->nwidgets)
+ return OSS_EIO;
+#endif
+
+ widget = &mixer->codecs[name->cad]->widgets[name->wid];
+ strcpy (name->name, widget->name);
+
+ return 0;
+}
+
+int
+hda_codec_getwidget (hdaudio_mixer_t * mixer, hda_widget_info_t * info)
+{
+ widget_t *widget;
+
+ if (info->cad >= mixer->ncodecs)
+ return OSS_EIO;
+ if (mixer->codecs[info->cad] == &NULL_codec)
+ return OSS_EIO;
+
+ widget = &mixer->codecs[info->cad]->widgets[info->wid];
+ if (info->wid >= mixer->codecs[info->cad]->nwidgets)
+ return OSS_EIO;
+ if (widget == NULL)
+ return OSS_EIO;
+ memcpy (info->info, widget, sizeof (*widget));
+
+ return 0;
+}
+
+int
+hdaudio_codec_audio_ioctl (hdaudio_mixer_t * mixer,
+ hdaudio_endpointinfo_t * endpoint,
+ unsigned int cmd, ioctl_arg arg)
+{
+ //widget_t *base_widget = &mixer->codecs[endpoint->cad]->widgets[endpoint->base_wid];
+ widget_t *recsrc_widget =
+ &mixer->codecs[endpoint->cad]->widgets[endpoint->recsrc_wid];
+ widget_t *volume_widget =
+ &mixer->codecs[endpoint->cad]->widgets[endpoint->volume_wid];
+ char tmp[128], *t;
+ unsigned int linked_wid, a, b;
+ int i, v, left, right;
+ int nsteps;
+
+ if (mixer->codecs[endpoint->cad]->vendor_flags & VF_VAIO_HACK)
+ linked_wid = (endpoint->volume_wid == 0x02) ? 0x05 :
+ ((endpoint->volume_wid == 0x05) ? 0x02 : 0);
+ else
+ linked_wid = 0;
+
+ switch (cmd)
+ {
+ case SNDCTL_DSP_GET_RECSRC_NAMES:
+ *tmp = 0;
+ t = tmp;
+
+ for (i = 0; i < recsrc_widget->nconn; i++)
+ {
+ if (*tmp) /* Not empty */
+ *t++ = ' ';
+ strcpy (t,
+ mixer->codecs[recsrc_widget->cad]->widgets[recsrc_widget->
+ connections[i]].
+ name);
+ t += strlen (t);
+ }
+ return oss_encode_enum ((oss_mixer_enuminfo *) arg, tmp, 0);
+ break;
+
+ case SNDCTL_DSP_GET_RECSRC:
+ if (!corb_read
+ (mixer, recsrc_widget->cad, recsrc_widget->wid, 0, GET_SELECTOR, 0,
+ &a, &b))
+ return OSS_EIO;
+ return *arg = a;
+ break;
+
+ case SNDCTL_DSP_SET_RECSRC:
+ a = *arg;
+ if (a > recsrc_widget->nconn)
+ return OSS_EIO;
+
+ corb_write (mixer, recsrc_widget->cad, recsrc_widget->wid, 0,
+ SET_SELECTOR, a);
+ recsrc_widget->current_selector = a;
+ mixer_devs[mixer->mixer_dev]->modify_counter++;
+ return *arg = a;
+ break;
+
+ case SNDCTL_DSP_GETRECVOL:
+ nsteps = (volume_widget->inamp_caps >> 8) & 0x3f;
+ if (nsteps < 1)
+ nsteps = 1;
+ if (!corb_read
+ (mixer, volume_widget->cad, volume_widget->wid, 0, GET_GAIN (0, 1),
+ 0, &a, &b))
+ return OSS_EIO;
+ if (a & 0x80) /* Muted */
+ left = 0;
+ else
+ left = ((a & 0x7f) * 100) / nsteps;
+ if (!corb_read
+ (mixer, volume_widget->cad, volume_widget->wid, 0, GET_GAIN (0, 0),
+ 0, &a, &b))
+ return OSS_EIO;
+ if (a & 0x80) /* Muted */
+ right = 0;
+ else
+ right = ((a & 0x7f) * 100) / nsteps;
+ v = left | (right << 8);
+ return *arg = v;
+ break;
+
+ case SNDCTL_DSP_SETRECVOL:
+ v = *arg;
+
+ left = v & 0xff;
+ right = (v >> 8) & 0xff;
+
+ if (left < 0)
+ left = 0;
+ if (left > 100)
+ left = 100;
+ if (right < 0)
+ right = 0;
+ if (right > 100)
+ right = 100;
+ v = left | (right << 8);
+
+ nsteps = (volume_widget->inamp_caps >> 8) & 0x3f;
+ if (nsteps < 1)
+ nsteps = 1;
+
+ a = (left * nsteps) / 100;
+ if (left == 0)
+ a |= 0x80; /* Mute */
+ corb_write (mixer, volume_widget->cad, volume_widget->wid, 0,
+ SET_GAIN (0, 1, 1, 0, 0), a);
+ a = (right * nsteps) / 100;
+ if (right == 0)
+ a |= 0x80; /* Mute */
+ corb_write (mixer, volume_widget->cad, volume_widget->wid, 0,
+ SET_GAIN (0, 1, 0, 1, 0), a);
+
+ mixer_devs[mixer->mixer_dev]->modify_counter++;
+ return *arg = v;
+ break;
+
+ case SNDCTL_DSP_GETPLAYVOL:
+ nsteps = (volume_widget->outamp_caps >> 8) & 0x3f;
+ if (nsteps < 1)
+ nsteps = 1;
+ if (!corb_read
+ (mixer, volume_widget->cad, volume_widget->wid, 0, GET_GAIN (1, 1),
+ 0, &a, &b))
+ return OSS_EIO;
+ if (a & 0x80) /* Muted */
+ left = 0;
+ else
+ left = ((a & 0x7f) * 100) / nsteps;
+ if (!corb_read
+ (mixer, volume_widget->cad, volume_widget->wid, 0, GET_GAIN (1, 0),
+ 0, &a, &b))
+ return OSS_EIO;
+ if (a & 0x80) /* Muted */
+ right = 0;
+ else
+ right = ((a & 0x7f) * 100) / nsteps;
+ v = left | (right << 8);
+ return *arg = v;
+ break;
+
+ case SNDCTL_DSP_SETPLAYVOL:
+ v = *arg;
+
+ left = v & 0xff;
+ right = (v >> 8) & 0xff;
+
+ if (left < 0)
+ left = 0;
+ if (left > 100)
+ left = 100;
+ if (right < 0)
+ right = 0;
+ if (right > 100)
+ right = 100;
+ v = left | (right << 8);
+
+ nsteps = (volume_widget->outamp_caps >> 8) & 0x3f;
+ if (nsteps < 1)
+ nsteps = 1;
+
+ a = (left * nsteps) / 100;
+ if (left == 0)
+ a |= 0x80; /* Mute */
+ corb_write (mixer, volume_widget->cad, volume_widget->wid, 0,
+ SET_GAIN (1, 0, 1, 0, 0), a);
+ if (linked_wid)
+ corb_write (mixer, volume_widget->cad, linked_wid, 0,
+ SET_GAIN (1, 0, 1, 0, 0), a);
+ a = (right * nsteps) / 100;
+ if (right == 0)
+ a |= 0x80; /* Mute */
+ corb_write (mixer, volume_widget->cad, volume_widget->wid, 0,
+ SET_GAIN (1, 0, 0, 1, 1), a);
+ if (linked_wid)
+ corb_write (mixer, volume_widget->cad, linked_wid, 0,
+ SET_GAIN (1, 0, 0, 1, 1), a);
+
+ mixer_devs[mixer->mixer_dev]->modify_counter++;
+ return *arg = v;
+ break;
+
+ case SNDCTL_DSP_MODEM_OFFHOOK:
+ if (!endpoint->is_modem)
+ {
+ return OSS_EINVAL;
+ }
+ v = *arg;
+ if (mixer->codecs[endpoint->cad]->vendor_flags & VF_SI3055_HACK)
+ {
+ v = hdaudio_si3055_set_offhook(mixer, endpoint->cad, v);
+ }
+ else
+ {
+ return OSS_ENOTSUP;
+ }
+ return *arg = v;
+ break;
+ }
+
+ return OSS_EINVAL;
+}
+
+/*
+ * Support routines for dedicated mixer drivers
+ */
+
+void
+hda_codec_add_group (int dev, hdaudio_mixer_t * mixer, int cad, int *group,
+ int parent_group, const char *name)
+{
+ int grp;
+
+ if ((grp = mixer_ext_create_group (dev, parent_group, name)) > 0)
+ *group = grp;
+}
+
+int
+hda_codec_add_pingroup (int dev, hdaudio_mixer_t * mixer, int cad, int wid,
+ int *group, int top_group, int *parent_group,
+ const char *name, int *n, const char *parent_name,
+ int group_size)
+{
+ int grp;
+ widget_t *widget;
+ codec_t *codec;
+
+ if (cad < 0 || cad >= mixer->ncodecs)
+ return OSS_ENXIO;
+ codec = mixer->codecs[cad];
+
+ if (wid < 0 || wid >= codec->nwidgets)
+ return OSS_ENXIO;
+
+ widget = &codec->widgets[wid];
+ if (widget->used || widget->skip)
+ return 0;
+
+ if (group_size <= 1 || ((*n % group_size) == 0 && *n > 0))
+ {
+ if ((grp = mixer_ext_create_group (dev, top_group, parent_name)) > 0)
+ *parent_group = grp;
+ *n = 0;
+ }
+
+ if ((grp = mixer_ext_create_group (dev, *parent_group, name)) > 0)
+ *group = grp;
+ (*n)++;
+
+ return 1;
+}
+
+int
+hda_codec_add_adcgroup (int dev, hdaudio_mixer_t * mixer, int cad, int wid,
+ int *group, int top_group, int *parent_group,
+ const char *name, int *n, const char *parent_name,
+ int group_size)
+{
+ int grp;
+ widget_t *widget;
+ codec_t *codec;
+
+ if (cad < 0 || cad >= mixer->ncodecs)
+ return OSS_ENXIO;
+ codec = mixer->codecs[cad];
+
+ if (wid < 0 || wid >= codec->nwidgets)
+ return OSS_ENXIO;
+
+ widget = &codec->widgets[wid];
+ if (widget->used || widget->skip)
+ return 0;
+
+ if (group_size <= 1 || ((*n % group_size) == 0 && *n > 0))
+ {
+ if ((grp = mixer_ext_create_group (dev, top_group, parent_name)) > 0)
+ *parent_group = grp;
+ *n = 0;
+ }
+
+ if ((grp = mixer_ext_create_group (dev, *parent_group, name)) > 0)
+ *group = grp;
+ (*n)++;
+
+ return 1;
+}
+
+int
+hda_codec_add_miscgroup (int dev, hdaudio_mixer_t * mixer, int cad, int wid,
+ int *group, int top_group, int *parent_group,
+ const char *name, int *n, const char *parent_name,
+ int group_size)
+{
+ int grp;
+ widget_t *widget;
+ codec_t *codec;
+
+ if (cad < 0 || cad >= mixer->ncodecs)
+ return OSS_ENXIO;
+ codec = mixer->codecs[cad];
+
+ if (wid < 0 || wid >= codec->nwidgets)
+ return OSS_ENXIO;
+
+ widget = &codec->widgets[wid];
+ if (widget->used || widget->skip)
+ return 0;
+
+ if (group_size <= 1 || ((*n % group_size) == 0 && *n > 0))
+ {
+ if ((grp = mixer_ext_create_group (dev, top_group, parent_name)) > 0)
+ *parent_group = grp;
+ *n = 0;
+ }
+
+ if ((grp = mixer_ext_create_group (dev, *parent_group, name)) > 0)
+ *group = grp;
+ (*n)++;
+
+ return 1;
+}
+
+int
+create_outgain_selector (hdaudio_mixer_t * mixer, widget_t * widget,
+ int group, const char *name)
+{
+ int num = MIXNUM (widget, CT_OUTGAINSEL, 0);
+ int ctl, i;
+ int maxval;
+ int offs, step, range;
+ oss_mixext *ent;
+
+ char tmp[128], *t;
+
+ range =
+ ((widget->outamp_caps >> AMPCAP_NUMSTEPS_SHIFT) & AMPCAP_NUMSTEPS_MASK) +
+ 1;
+ step =
+ ((widget->outamp_caps >> AMPCAP_STEPSIZE_SHIFT) & AMPCAP_STEPSIZE_MASK) +
+ 1;
+ offs = (widget->outamp_caps >> AMPCAP_OFFSET_SHIFT) & AMPCAP_OFFSET_MASK;
+
+ maxval = range;
+
+ if (widget->outamp_caps & AMPCAP_MUTE)
+ maxval += 1;
+
+ if ((ctl = mixer_ext_create_control (mixer->mixer_dev,
+ group,
+ num, hdaudio_set_control,
+ MIXT_ENUM,
+ name, maxval,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return ctl;
+
+ t = tmp;
+ *t = 0;
+
+ for (i = 0; i < range; i++)
+ {
+ int v;
+
+ v = (i - offs) * step * 4;
+
+ if (*tmp != 0)
+ *t++ = ' ';
+
+ sprintf (t, "%d.%ddB", v / 10, v % 10);
+ t += strlen (t);
+ }
+
+ if (widget->outamp_caps & AMPCAP_MUTE)
+ {
+ if (*tmp != 0)
+ *t++ = ' ';
+ strcpy (t, "mute");
+ t += strlen (t);
+ }
+
+ mixer_ext_set_strings (mixer->mixer_dev, ctl, tmp, 0);
+ /* Copy RGB color */
+ if (widget->rgbcolor != 0)
+ if ((ent = mixer_find_ext (mixer->mixer_dev, ctl)) != NULL)
+ ent->rgbcolor = widget->rgbcolor;
+
+ return 0;
+}
+
+int
+create_ingain_selector (hdaudio_mixer_t * mixer, codec_t * codec,
+ widget_t * widget, int group, int ix,
+ const char *name)
+{
+ int num = MIXNUM (widget, CT_INGAINSEL, ix);
+
+ int ctl, i;
+ int maxval;
+ int offs, step, range;
+ oss_mixext *ent;
+
+ char tmp[128], *t;
+
+ range =
+ ((widget->inamp_caps >> AMPCAP_NUMSTEPS_SHIFT) & AMPCAP_NUMSTEPS_MASK) +
+ 1;
+ step =
+ ((widget->inamp_caps >> AMPCAP_STEPSIZE_SHIFT) & AMPCAP_STEPSIZE_MASK) +
+ 1;
+ offs = (widget->inamp_caps >> AMPCAP_OFFSET_SHIFT) & AMPCAP_OFFSET_MASK;
+
+ maxval = range;
+
+ if (widget->inamp_caps & AMPCAP_MUTE)
+ maxval += 1;
+
+ if ((ctl = mixer_ext_create_control (mixer->mixer_dev,
+ group,
+ num, hdaudio_set_control,
+ MIXT_ENUM,
+ name, maxval,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return ctl;
+
+ t = tmp;
+ *t = 0;
+
+ for (i = 0; i < range; i++)
+ {
+ int v;
+
+ v = (i - offs) * step * 4;
+
+ if (*tmp != 0)
+ *t++ = ' ';
+
+ sprintf (t, "%d.%ddB", v / 10, v % 10);
+ t += strlen (t);
+ }
+
+ if (widget->inamp_caps & AMPCAP_MUTE)
+ {
+ if (*tmp != 0)
+ *t++ = ' ';
+ strcpy (t, "mute");
+ t += strlen (t);
+ }
+
+ mixer_ext_set_strings (mixer->mixer_dev, ctl, tmp, 0);
+ /* Copy RGB color */
+ if (widget->color != 0)
+ if ((ent = mixer_find_ext (mixer->mixer_dev, ctl)) != NULL)
+ ent->rgbcolor = widget->rgbcolor;
+ return 0;
+}
+
+int
+hda_codec_add_outamp (int dev, hdaudio_mixer_t * mixer, int cad, int wid,
+ int group, const char *name, int percent,
+ unsigned int flags)
+{
+ widget_t *widget;
+ codec_t *codec;
+ int typ, num, maxval, val, ctl = 0;
+ int range, step;
+ oss_mixext *ent;
+ extern int mixer_muted;
+
+ if (cad < 0 || cad >= mixer->ncodecs)
+ return OSS_ENXIO;
+ codec = mixer->codecs[cad];
+
+ if (wid < 0 || wid >= codec->nwidgets)
+ return OSS_ENXIO;
+
+ widget = &codec->widgets[wid];
+
+ range =
+ ((widget->
+ outamp_caps >> AMPCAP_NUMSTEPS_SHIFT) & AMPCAP_NUMSTEPS_MASK) + 1;
+ step =
+ ((widget->
+ outamp_caps >> AMPCAP_STEPSIZE_SHIFT) & AMPCAP_STEPSIZE_MASK) + 1;
+
+ if (step > 20 /* 5dB */ && range < 5)
+ {
+ create_outgain_selector (mixer, widget, group, name);
+ }
+ else
+ {
+
+ maxval = hdaudio_amp_maxval (widget->outamp_caps);
+
+ if (widget->widget_caps & WCAP_STEREO)
+ {
+ typ = MIXT_STEREOSLIDER16;
+ num = MIXNUM (widget, CT_OUTSTEREO, 0);
+ }
+ else
+ {
+ typ = MIXT_MONOSLIDER16;
+ num = MIXNUM (widget, CT_OUTMONO, 0);
+ }
+
+ if ((ctl = mixer_ext_create_control (mixer->mixer_dev,
+ group,
+ num, hdaudio_set_control,
+ typ,
+ name, maxval,
+ flags | MIXF_READABLE |
+ MIXF_WRITEABLE |
+ MIXF_CENTIBEL)) < 0)
+ return ctl;
+
+ /* setup volume */
+ val = (maxval * percent) / 100;
+ val = val | (val << 16);
+ if (mixer_muted)
+ val = 0;
+ /* Copy RGB color */
+ if (widget->rgbcolor != 0)
+ if ((ent = mixer_find_ext (dev, ctl)) != NULL)
+ ent->rgbcolor = widget->rgbcolor;
+ hdaudio_set_control (mixer->mixer_dev, num, SNDCTL_MIX_WRITE, val);
+ }
+
+ return ctl;
+}
+
+int
+hda_codec_add_outmute (int dev, hdaudio_mixer_t * mixer, int cad, int wid,
+ int group, const char *name, int muted)
+{
+ widget_t *widget;
+ codec_t *codec;
+ int ctl = 0;
+ oss_mixext *ent;
+
+ if (cad < 0 || cad >= mixer->ncodecs)
+ return OSS_ENXIO;
+ codec = mixer->codecs[cad];
+
+ if (wid < 0 || wid >= codec->nwidgets)
+ return OSS_ENXIO;
+
+ widget = &codec->widgets[wid];
+
+ if (widget->outamp_caps & AMPCAP_MUTE) /* Only mute control */
+ {
+ // name = "mute";
+
+ if ((ctl = mixer_ext_create_control (mixer->mixer_dev,
+ group,
+ MIXNUM (widget,
+ CT_OUTMUTE, 0),
+ hdaudio_set_control,
+ MIXT_MUTE, name, 2,
+ MIXF_READABLE |
+ MIXF_WRITEABLE)) < 0)
+ return ctl;
+ /* Copy RGB color */
+ if (widget->rgbcolor != 0)
+ if ((ent = mixer_find_ext (dev, ctl)) != NULL)
+ ent->rgbcolor = widget->rgbcolor;
+
+ hdaudio_set_control (mixer->mixer_dev,
+ MIXNUM (widget, CT_OUTMUTE, 0),
+ SNDCTL_MIX_WRITE, muted);
+ return ctl;
+ }
+ return 0;
+}
+
+int
+hda_codec_add_inamp (int dev, hdaudio_mixer_t * mixer, int cad, int wid,
+ int ix, int group, const char *name, int percent, int flags)
+{
+ widget_t *widget;
+ widget_t *src_widget;
+ codec_t *codec;
+ int typ, num, maxval, val, ctl = 0, range, step;
+ oss_mixext *ent;
+ extern int mixer_muted;
+
+ if (cad < 0 || cad >= mixer->ncodecs)
+ return OSS_ENXIO;
+ codec = mixer->codecs[cad];
+
+ if (wid < 0 || wid >= codec->nwidgets)
+ return OSS_ENXIO;
+
+ widget = &codec->widgets[wid];
+
+ range =
+ ((widget->
+ outamp_caps >> AMPCAP_NUMSTEPS_SHIFT) & AMPCAP_NUMSTEPS_MASK) + 1;
+ step =
+ ((widget->
+ outamp_caps >> AMPCAP_STEPSIZE_SHIFT) & AMPCAP_STEPSIZE_MASK) + 1;
+
+ if (step > 20 /* 5dB */ && range < 5)
+ {
+ return create_ingain_selector (mixer, codec, widget, group, ix, name);
+ }
+ maxval = hdaudio_amp_maxval (widget->inamp_caps);
+
+ if (widget->widget_caps & WCAP_STEREO)
+ {
+ typ = MIXT_STEREOSLIDER16;
+ num = MIXNUM (widget, CT_INSTEREO, ix);
+ }
+ else
+ {
+ typ = MIXT_MONOSLIDER16;
+ num = MIXNUM (widget, CT_INMONO, ix);
+ }
+
+ if (codec->widgets[widget->connections[ix]].skip)
+ {
+ hdaudio_set_control (mixer->mixer_dev, num, SNDCTL_MIX_WRITE, 0);
+ return 0;
+ }
+
+ if ((ctl = mixer_ext_create_control (mixer->mixer_dev,
+ group,
+ num,
+ hdaudio_set_control,
+ typ,
+ name, maxval,
+ MIXF_READABLE |
+ MIXF_WRITEABLE | MIXF_CENTIBEL | flags)) < 0)
+ return ctl;
+
+ /* Setup initial volume */
+ val = (maxval * percent) / 100;
+ val = val | (val << 16);
+ if (mixer_muted)
+ val = 0;
+
+ hdaudio_set_control (mixer->mixer_dev, num, SNDCTL_MIX_WRITE, val);
+
+ /* Copy RGB color */
+ src_widget = &codec->widgets[widget->connections[ix]];
+ if (src_widget->rgbcolor != 0)
+ if ((ent = mixer_find_ext (dev, ctl)) != NULL)
+ ent->rgbcolor = src_widget->rgbcolor;
+
+ return ctl;
+}
+
+int
+hda_codec_add_inmute (int dev, hdaudio_mixer_t * mixer, int cad, int wid,
+ int ix, int group, const char *name, int muted, unsigned int flags)
+{
+ widget_t *widget;
+ codec_t *codec;
+ oss_mixext *ent;
+ int ctl = 0;
+
+ if (cad < 0 || cad >= mixer->ncodecs)
+ return OSS_ENXIO;
+ codec = mixer->codecs[cad];
+
+ if (wid < 0 || wid >= codec->nwidgets)
+ return OSS_ENXIO;
+
+ widget = &codec->widgets[wid];
+
+ if (codec->widgets[widget->connections[ix]].skip)
+ {
+ int num = MIXNUM (widget, CT_INMUTE, ix);
+ hdaudio_set_control (mixer->mixer_dev, num, SNDCTL_MIX_WRITE, 1);
+ return 0;
+ }
+
+ if ((ctl = mixer_ext_create_control (mixer->mixer_dev,
+ group,
+ MIXNUM (widget,
+ CT_INMUTE, ix),
+ hdaudio_set_control,
+ MIXT_MUTE, name, 2,
+ flags | MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return ctl;
+ /* Copy RGB color */
+ if (widget->rgbcolor != 0)
+ if ((ent = mixer_find_ext (dev, ctl)) != NULL)
+ ent->rgbcolor = widget->rgbcolor;
+
+ hdaudio_set_control (mixer->mixer_dev,
+ MIXNUM (widget, CT_INMUTE, ix),
+ SNDCTL_MIX_WRITE, muted);
+ return ctl;
+}
+
+int
+hda_codec_set_inmute (int dev, hdaudio_mixer_t * mixer, int cad, int wid,
+ int ix, int group, const char *name, int muted)
+{
+ widget_t *widget;
+ codec_t *codec;
+
+ if (cad < 0 || cad >= mixer->ncodecs)
+ return OSS_ENXIO;
+ codec = mixer->codecs[cad];
+
+ if (wid < 0 || wid >= codec->nwidgets)
+ return OSS_ENXIO;
+
+ widget = &codec->widgets[wid];
+
+// cmn_err(CE_CONT, "Set inmute 0x%02x:%d=%d\n", wid, ix, muted);
+ return hdaudio_set_control (mixer->mixer_dev,
+ MIXNUM (widget, CT_INMUTE, ix),
+ SNDCTL_MIX_WRITE, muted);
+}
+
+int
+hda_codec_add_insrc (int dev, hdaudio_mixer_t * mixer, int cad, int wid,
+ int ix, int group, const char *name, int unselected)
+{
+ widget_t *widget;
+ codec_t *codec;
+ oss_mixext *ent;
+ int ctl = 0;
+
+ if (cad < 0 || cad >= mixer->ncodecs)
+ return OSS_ENXIO;
+ codec = mixer->codecs[cad];
+
+ if (wid < 0 || wid >= codec->nwidgets)
+ return OSS_ENXIO;
+
+ widget = &codec->widgets[wid];
+
+ if (codec->widgets[widget->connections[ix]].skip)
+ {
+ int num = MIXNUM (widget, CT_INMUTE, ix);
+
+ hdaudio_set_control (mixer->mixer_dev, num, SNDCTL_MIX_WRITE, 1);
+ return 0;
+ }
+
+ if ((ctl = mixer_ext_create_control (mixer->mixer_dev,
+ group,
+ MIXNUM (widget,
+ CT_INSRC, ix),
+ hdaudio_set_control,
+ MIXT_ONOFF, name, 2,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return ctl;
+ /* Copy RGB color */
+ if (widget->rgbcolor != 0)
+ if ((ent = mixer_find_ext (dev, ctl)) != NULL)
+ ent->rgbcolor = widget->rgbcolor;
+
+ hdaudio_set_control (mixer->mixer_dev,
+ MIXNUM (widget, CT_INSRC, ix),
+ SNDCTL_MIX_WRITE, unselected);
+ return ctl;
+}
+
+int
+hda_codec_add_insrcselect (int dev, hdaudio_mixer_t * mixer, int cad, int wid,
+ int group, int *ctl, const char *name,
+ int initial_selection)
+{
+ widget_t *widget;
+ codec_t *codec;
+ int i;
+ oss_mixext *ext;
+
+ *ctl = 0;
+
+ if (cad < 0 || cad >= mixer->ncodecs)
+ return OSS_ENXIO;
+ codec = mixer->codecs[cad];
+
+ if (wid < 0 || wid >= codec->nwidgets)
+ return OSS_ENXIO;
+
+ widget = &codec->widgets[wid];
+
+ if ((*ctl = mixer_ext_create_control (mixer->mixer_dev,
+ group,
+ MIXNUM (widget,
+ CT_INSRCSELECT, 0),
+ hdaudio_set_control,
+ MIXT_ENUM, name, widget->nconn,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return 0;
+
+ ext = mixer_find_ext (mixer->mixer_dev, *ctl);
+ if (ext == NULL)
+ {
+ cmn_err (CE_WARN, "Cannot locate the mixer extension (x)\n");
+ return OSS_EIO;
+ }
+
+ /* Copy RGB color */
+ if (widget->color != 0)
+ ext->rgbcolor = widget->rgbcolor;
+
+ memset (ext->enum_present, 0, sizeof (ext->enum_present));
+
+ for (i = 0; i < widget->nconn; i++)
+ {
+
+ /*
+ * ensure that the connection list has a valid widget id - some
+ * devices have bogus connection lists
+ */
+ if (codec->widgets[widget->connections[i]].wid < codec->first_node)
+ continue;
+
+ /*
+ * Show only widgets that are not marked to be ignored.
+ * Also hide I/O pins that are known to be outputs.
+ */
+ if (!codec->widgets[widget->connections[i]].skip
+ && codec->widgets[widget->connections[i]].sensed_pin != PIN_OUT)
+ ext->enum_present[i / 8] |= (1 << (i % 8));
+ }
+
+ hdaudio_set_control (mixer->mixer_dev,
+ MIXNUM (widget, CT_INSRCSELECT, 0),
+ SNDCTL_MIX_WRITE, initial_selection);
+ return 1;
+}
+
+int
+hda_codec_add_select (int dev, hdaudio_mixer_t * mixer, int cad, int wid,
+ int group, const char *name, int *ctl,
+ int initial_select)
+{
+ widget_t *widget;
+ codec_t *codec;
+ oss_mixext *ext;
+ int i;
+
+ *ctl = 0;
+
+ if (cad < 0 || cad >= mixer->ncodecs)
+ return OSS_ENXIO;
+ codec = mixer->codecs[cad];
+
+ if (wid < 0 || wid >= codec->nwidgets)
+ return OSS_ENXIO;
+
+ widget = &codec->widgets[wid];
+
+ if ((*ctl = mixer_ext_create_control (mixer->mixer_dev,
+ group,
+ MIXNUM (widget, CT_SELECT, 0),
+ hdaudio_set_control,
+ MIXT_ENUM,
+ name,
+ widget->nconn,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return *ctl;
+
+ ext = mixer_find_ext (mixer->mixer_dev, *ctl);
+
+ if (ext == NULL)
+ {
+ cmn_err (CE_WARN, "Cannot locate the mixer extension (x)\n");
+ return OSS_EIO;
+ }
+ /* Copy RGB color */
+ if (widget->color != 0)
+ ext->rgbcolor = widget->rgbcolor;
+
+ memset (ext->enum_present, 0, sizeof (ext->enum_present));
+
+ for (i = 0; i < widget->nconn; i++)
+ {
+
+ /*
+ * ensure that the connection list has a valid widget id - some
+ * devices have bogus connection lists
+ */
+ if (codec->widgets[widget->connections[i]].wid < codec->first_node)
+ continue;
+
+ /*
+ * Show only widgets that are not marked to be ignored.
+ * Also hide I/O pins that are known to be outputs.
+ */
+ if (!codec->widgets[widget->connections[i]].skip
+ && codec->widgets[widget->connections[i]].sensed_pin != PIN_OUT)
+ ext->enum_present[i / 8] |= (1 << (i % 8));
+ }
+
+ if (initial_select > -1)
+ widget->current_selector = initial_select;
+
+ if (widget->current_selector >= widget->nconn)
+ widget->current_selector = 0;
+ corb_write (mixer, widget->cad, widget->wid, 0, SET_SELECTOR,
+ widget->current_selector);
+
+ return *ctl;
+}
+
+int
+hda_codec_add_pinselect (int dev, hdaudio_mixer_t * mixer, int cad, int wid,
+ int group, const char *name, int *ctl,
+ int initial_select)
+{
+ widget_t *widget;
+ codec_t *codec;
+ unsigned int conf, b;
+ oss_mixext *ext;
+ int i;
+
+ *ctl = 0;
+
+ if (cad < 0 || cad >= mixer->ncodecs)
+ return OSS_ENXIO;
+ codec = mixer->codecs[cad];
+
+ if (wid < 0 || wid >= codec->nwidgets)
+ return OSS_ENXIO;
+
+ widget = &codec->widgets[wid];
+
+ if ((*ctl = mixer_ext_create_control (mixer->mixer_dev,
+ group,
+ MIXNUM (widget, CT_SELECT, 0),
+ hdaudio_set_control,
+ MIXT_ENUM,
+ name,
+ widget->nconn + 1,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return *ctl;
+
+ ext = mixer_find_ext (mixer->mixer_dev, *ctl);
+
+ if (ext == NULL)
+ {
+ cmn_err (CE_WARN, "Cannot locate the mixer extension (x)\n");
+ return OSS_EIO;
+ }
+
+ /* Copy RGB color */
+ if (widget->color != 0)
+ ext->rgbcolor = widget->rgbcolor;
+ memset (ext->enum_present, 0, sizeof (ext->enum_present));
+
+ for (i = 0; i < widget->nconn; i++)
+ {
+
+ /*
+ * ensure that the connection list has a valid widget id - some
+ * devices have bogus connection lists
+ */
+ if (codec->widgets[widget->connections[i]].wid < codec->first_node)
+ continue;
+
+ /*
+ * Show only widgets that are not marked to be ignored.
+ * Also hide I/O pins that are known to be outputs.
+ */
+ if (!codec->widgets[widget->connections[i]].skip
+ && codec->widgets[widget->connections[i]].sensed_pin != PIN_OUT)
+ ext->enum_present[i / 8] |= (1 << (i % 8));
+ }
+
+ /*
+ * Enable the input selection (if available)
+ */
+ i = widget->nconn;
+ if (widget->pincaps & PINCAP_INPUT_CAPABLE)
+ ext->enum_present[i / 8] |= (1 << (i % 8));
+
+/*
+ * Set the initial value.
+ *
+ * Use the default sequence as an index to the output source selectors.
+ */
+ if (widget->sensed_pin == PIN_OUT)
+ {
+ if (corb_read
+ (mixer, widget->cad, widget->wid, 0, GET_CONFIG_DEFAULT, 0, &conf,
+ &b))
+ {
+ int association, sequence;
+
+ association = (conf >> 4) & 0x0f;
+ sequence = conf & 0x0f;
+
+ if (association != 0)
+ {
+ widget->current_selector = sequence;
+ }
+
+ }
+ }
+ else if (widget->sensed_pin == PIN_IN || widget->sensed_pin == PIN_MIC)
+ widget->current_selector = widget->nconn; /* Turn on input mode */
+
+ if (initial_select > -1)
+ widget->current_selector = initial_select;
+
+ if (widget->current_selector < 0
+ || widget->current_selector >= widget->nconn + 1)
+ widget->current_selector = 0;
+
+ if (widget->current_selector < widget->nconn)
+ {
+ /* Output source select */
+ corb_write (mixer, cad, wid, 0, SET_SELECTOR, widget->current_selector);
+ /* Enable output and HP amp. Set vref=Ground */
+ corb_write (mixer, cad, wid, 0, SET_PINCTL, 0xc0);
+ }
+ else
+ {
+ /* Input select
+ * Program the correct VRef Values
+ */
+
+ if (widget->pin_type == PIN_IN) /* Line-in */
+ {
+ corb_write (mixer, cad, wid, 0, SET_PINCTL, 0x20); /*Ground*/
+ }
+ else /* Mic-in */
+ {
+ corb_write (mixer, cad, wid, 0, SET_PINCTL, 0x24); /*Vref=8
+ 0% */
+ }
+ }
+
+ return *ctl;
+}
+
+void
+hda_codec_set_select (int dev, hdaudio_mixer_t * mixer, int cad, int wid,
+ int value)
+{
+ codec_t *codec;
+ widget_t *widget;
+
+ if (cad < 0 || cad >= mixer->ncodecs)
+ return;
+ codec = mixer->codecs[cad];
+
+ if (wid < 0 || wid >= codec->nwidgets)
+ return;
+
+ widget = &codec->widgets[wid];
+
+ widget->current_selector = value;
+
+ corb_write (mixer, cad, wid, 0, SET_SELECTOR, value);
+}
+
+void
+hda_codec_set_pinselect (int dev, hdaudio_mixer_t * mixer, int cad, int wid,
+ int value)
+{
+ codec_t *codec;
+ widget_t *widget;
+
+ if (cad < 0 || cad >= mixer->ncodecs)
+ return;
+ codec = mixer->codecs[cad];
+
+ if (wid < 0 || wid >= codec->nwidgets)
+ return;
+
+ widget = &codec->widgets[wid];
+
+ widget->current_selector = value;
+
+ if (widget->current_selector < widget->nconn)
+ {
+ /* Output source select */
+ corb_write (mixer, cad, wid, 0, SET_SELECTOR, widget->current_selector);
+ /* Enable output and HP amp. Set vref=Ground */
+ corb_write (mixer, cad, wid, 0, SET_PINCTL, 0xc0);
+ }
+ else
+ {
+ /* Input select
+ * Program the correct VRef Values
+ */
+
+ if (widget->pin_type == PIN_IN) /* Line-in */
+ {
+ corb_write (mixer, cad, wid, 0, SET_PINCTL, 0x20); /*Ground*/
+ }
+ else /* Mic-in */
+ {
+ corb_write (mixer, cad, wid, 0, SET_PINCTL, 0x24); /*Vref=8
+ 0% */
+ }
+ }
+}
+
+int
+hda_codec_add_choices (int dev, hdaudio_mixer_t * mixer, int ctl,
+ const char *choiselist)
+{
+ mixer_ext_set_strings (dev, ctl, choiselist, 0);
+
+ return 0;
+}
+
+void
+hda_codec_set_color(int dev, hdaudio_mixer_t *mixer, int ctl, int color)
+{
+ oss_mixext *ext;
+
+ if ((ext = mixer_find_ext (mixer->mixer_dev, ctl)) != NULL)
+ {
+ ext->rgbcolor = color;
+//cmn_err(CE_CONT, "Mixer %s rgb->%06x\n", ext->extname, ext->rgbcolor);
+ }
+}
diff --git a/kernel/drv/oss_hdaudio/hdaudio_codec.h b/kernel/drv/oss_hdaudio/hdaudio_codec.h
new file mode 100644
index 0000000..c990e31
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/hdaudio_codec.h
@@ -0,0 +1,379 @@
+/*
+ * Purpose: Definitions of HD audio codec functions, structures and macros
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+#ifndef HDAUDIO_CODEC_H
+#define HDAUDIO_CODEC_H
+
+#define HDA_MAX_OUTSTREAMS 12
+#define HDA_MAX_INSTREAMS 8
+
+#define MAX_CODECS 16
+#define MAX_WIDGETS 150
+#define MAX_CONN 24
+
+#define CT_INMONO 0x0001
+#define CT_INSTEREO 0x0002
+#define CT_INMUTE 0x0003
+#define CT_INSRC 0x0004 /* Negation of a mute control */
+#define CT_SELECT 0x0005
+
+#define CT_OUTMONO 0x0006
+#define CT_OUTSTEREO 0x0007
+#define CT_OUTMUTE 0x0008
+
+#define CT_OUTGAINSEL 0x0009
+#define CT_INGAINSEL 0x000a
+#define CT_INSRCSELECT 0x000b
+
+#define corb_read(m, cad, wid, d, verb, parm, p1, p2) \
+ m->read(m->devc, cad, wid, d, verb, parm, p1, p2)
+#define corb_write(m, cad, wid, d, verb, parm) \
+ m->write(m->devc, cad, wid, d, verb, parm)
+
+#define MIXNUM(widget, ty, ix) \
+ ((ix) | (ty<<8) | (widget->wid<<16) | (widget->cad << 24))
+extern int hdaudio_set_control (int dev, int ctrl, unsigned int cmd,
+ int value);
+
+typedef struct _hdaudio_mixer_struct hdaudio_mixer_t;
+
+/*
+ * Reserved (predefined) stream numbers
+ */
+#define IDDLE_STREAM 0 /* Reserved for silence */
+#define FRONT_STREAM 1 /* Reserved for front channel stereo pair */
+
+typedef struct
+{
+ char *name;
+ int is_output;
+ unsigned char ix, cad, base_wid;
+ unsigned char recsrc_wid;
+ unsigned char volume_wid;
+ int borrow_count; /* Number of other endpoints grouped with this one (for multich) */
+ int binding;
+ int afg;
+ int min_rate, max_rate;
+ int channels;
+ int fmt_mask;
+ volatile int busy;
+ int nrates, rates[20];
+ unsigned int sizemask;
+ int stream_number, default_stream_number, iddle_stream;
+ int is_digital;
+ int is_modem;
+ int auto_muted;
+ int skip; /* Not connected to anywhere */
+ int already_used;
+} hdaudio_endpointinfo_t;
+
+/*
+ * Codec requests
+ */
+
+#define GET_GAIN(inout, leftright) (0xb00|(inout<<7)|(leftright<<5))
+#define SET_GAIN(out,inp,lft, rght, ix) (0x300|(out<<7)|(inp<<6)|(lft<<5)|(rght<<4)|ix)
+#define GET_CONVERTER_FORMAT 0xa00
+#define SET_CONVERTER_FORMAT 0x200
+#define GET_PARAMETER 0xf00
+#define GET_SELECTOR 0xf01
+#define SET_SELECTOR 0x701
+#define GET_CONNECTION_LIST_ENTRY 0xf02
+#define GET_PROCESSING_STATE 0xf03
+#define SET_PROCESSING_STATE 0x703
+#define GET_SDI_SELECT 0xf04
+#define SET_SDI_SELECT 0x704
+#define GET_CONVERTER 0xf06
+#define SET_CONVERTER 0x706
+#define GET_PINCTL 0xf07
+#define SET_PINCTL 0x707
+#define GET_UNSOLICITED 0xf08
+#define SET_UNSOLICITED 0x708
+#define GET_CONFIG_DEFAULT 0xf1c
+#define SET_POWER_STATE 0x705
+#define GET_POWER_STATE 0xf05
+#define GET_PIN_SENSE 0xf09
+#define TRIGGER_PIN_SENSE 0x709
+#define GET_BEEP 0xf0a
+#define SET_BEEP 0x70a
+#define GET_EAPD 0xf0c
+#define SET_EAPD 0x70c
+#define GET_SPDIF_CONTROL 0xf0d
+#define SET_SPDIF_CONTROL1 0x70d
+#define SET_SPDIF_CONTROL2 0x70e
+#define GET_VOLUME_KNOB_CONTROL 0xf0f
+#define SET_VOLUME_KNOB_CONTROL 0x70f
+#define GET_GPI_DATA 0xf10
+#define SET_GPI_DATA 0x710
+#define GET_GPI_WAKE 0xf11
+#define SET_GPI_WAKE 0x711
+#define GET_GPI_UNSOL 0xf12
+#define SET_GPI_UNSOL 0x712
+#define GET_GPI_STICKY 0xf13
+#define SET_GPI_STICKY 0x713
+#define GET_GPO_DATA 0xf14
+#define SET_GPO_DATA 0x714
+#define GET_GPIO_DATA 0xf15
+#define SET_GPIO_DATA 0x715
+#define GET_GPIO_ENABLE 0xf16
+#define SET_GPIO_ENABLE 0x716
+#define GET_GPIO_DIR 0xf17
+#define SET_GPIO_DIR 0x717
+#define GET_GPIO_WKEN 0xf18
+#define SET_GPIO_WKEN 0x718
+#define GET_GPIO_UNSOL 0xf19
+#define SET_GPIO_UNSOL 0x719
+#define GET_GPIO_STICKY 0xf1a
+#define SET_GPIO_STICKY 0x71a
+#define GET_SUBSYSTEM_ID 0xf20
+#define GET_STRIPE_CONTROL 0xf24
+#define SET_CODEC_RESET 0x7ff
+
+/*
+ * Parameters
+ */
+
+#define HDA_VENDOR 0x00
+#define HDA_REVISION 0x02
+#define HDA_NODE_COUNT 0x04
+#define HDA_GROUP_TYPE 0x05
+#define HDA_AUDIO_GROUP_CAPS 0x08
+#define HDA_WIDGET_CAPS 0x09
+#define HDA_PCM_SIZES 0x0a
+#define HDA_STREAM_FMTS 0x0b
+#define HDA_PIN_CAPS 0x0c
+#define HDA_INPUTAMP_CAPS 0x0d
+#define HDA_CONNLIST_LEN 0x0e
+#define HDA_SUPPORTED_POWER_STATES 0x0f
+#define HDA_PROCESSING_CAPS 0x10
+#define HDA_GPIO_COUNT 0x11
+#define HDA_OUTPUTAMP_CAPS 0x12
+#define HDA_VOLUMEKNOB_CAPS 0x13
+
+typedef int (*hdmixer_write_t) (void *devc, unsigned int cad,
+ unsigned int nid, unsigned int d,
+ unsigned int verb, unsigned int parm);
+typedef int (*hdmixer_read_t) (void *devc, unsigned int cad, unsigned int nid,
+ unsigned int d, unsigned int verb,
+ unsigned int parm, unsigned int *upper,
+ unsigned int *lower);
+
+#ifdef _KERNEL
+extern hdaudio_mixer_t *hdaudio_mixer_create (char *name, void *devc,
+ oss_device_t * osdev,
+ hdmixer_write_t writefunc,
+ hdmixer_read_t readfunc,
+ unsigned int codecmask,
+ unsigned int vendor_id,
+ unsigned int subvendor_id);
+extern void hdaudio_mixer_set_initfunc (hdaudio_mixer_t * mixer,
+ mixer_create_controls_t func);
+#endif
+
+extern int hdaudio_mixer_get_mixdev (hdaudio_mixer_t * mixer);
+
+extern int hdaudio_mixer_get_outendpoints (hdaudio_mixer_t * mixer,
+ hdaudio_endpointinfo_t **
+ endpoints, int size);
+extern int hdaudio_mixer_get_inendpoints (hdaudio_mixer_t * mixer,
+ hdaudio_endpointinfo_t ** endpoints,
+ int size);
+
+extern int hdaudio_codec_setup_endpoint (hdaudio_mixer_t * mixer,
+ hdaudio_endpointinfo_t * endpoint,
+ int rate, int channels, int fmt,
+ int stream_number,
+ unsigned int *setupbits);
+extern int hdaudio_codec_reset_endpoint (hdaudio_mixer_t * mixer,
+ hdaudio_endpointinfo_t * endpoint,
+ int channels);
+extern int hdaudio_codec_audio_ioctl (hdaudio_mixer_t * mixer,
+ hdaudio_endpointinfo_t * endpoint,
+ unsigned int cmd, ioctl_arg arg);
+
+extern int hda_codec_getname (hdaudio_mixer_t * mixer, hda_name_t *name);
+extern int hda_codec_getwidget (hdaudio_mixer_t * mixer, hda_widget_info_t *info);
+extern void hda_codec_unsol (hdaudio_mixer_t * mixer, unsigned int upper,
+ unsigned int lower);
+extern int hdaudio_amp_maxval (unsigned int ampcaps);
+
+/*
+ * Audio widget types
+ */
+
+#define NT_DAC 0
+#define NT_ADC 1
+#define NT_MIXER 2
+#define NT_SELECT 3
+#define NT_PIN 4
+#define NT_POWER 5
+#define NT_KNOB 6
+#define NT_BEEP 7
+#define NT_VENDOR 15
+
+#endif
+
+/*
+ * Struct for sample rate table
+ */
+struct hdaudio_rate_def
+{
+ unsigned int rate, mask;
+};
+
+extern const struct hdaudio_rate_def *hdaudio_rates;
+
+typedef struct
+{
+ unsigned char cad, wid;
+ char name[32];
+ char color[32];
+ unsigned char wid_type, group_type;
+ char used; /* Already bound to mixer */
+ char skip; /* Ignore this widget as input source */
+ char skip_output; /* Ignore this widget as output target */
+
+ unsigned int widget_caps;
+#define WCAP_STEREO (1 << 0)
+#define WCAP_INPUT_AMP_PRESENT (1 << 1)
+#define WCAP_OUTPUT_AMP_PRESENT (1 << 2)
+#define WCAP_AMP_CAP_OVERRIDE (1 << 3)
+#define WCAP_FORMAT_OVERRIDE (1 << 4)
+#define WCAP_STRIPE (1 << 5)
+#define WCAP_PROC_WIDGET (1 << 6)
+#define WCAP_UNSOL_CAPABLE (1 << 7)
+#define WCAP_CONN_LIST (1 << 8)
+#define WCAP_DIGITAL (1 << 9)
+#define WCAP_POWER_CTL (1 << 10)
+#define WCAP_LR_SWAP (1 << 11)
+
+ unsigned int inamp_caps;
+ unsigned int outamp_caps;
+#define AMPCAP_MUTE (1UL<<31)
+#define AMPCAP_STEPSIZE_SHIFT 16
+#define AMPCAP_STEPSIZE_MASK 0x7f
+#define AMPCAP_NUMSTEPS_SHIFT 8
+#define AMPCAP_NUMSTEPS_MASK 0x7f
+#define AMPCAP_OFFSET_SHIFT 0
+#define AMPCAP_OFFSET_MASK 0x7f
+
+ unsigned int pincaps;
+#define PINCAP_EAPD (1<<16)
+#define PINCAP_VREF_MASK 0x7f
+#define PINCAP_VREF_SHIFT 8
+#define PINCAP_RESERVED (1<<7)
+#define PINCAP_BALANCED_PINS (1<<6)
+#define PINCAP_INPUT_CAPABLE (1<<5)
+#define PINCAP_OUTPUT_CAPABLE (1<<4)
+#define PINCAP_HP_DRIVE_CAPABLE (1<<3)
+#define PINCAP_JACKSENSE_CAPABLE (1<<2)
+#define PINCAP_TRIGGERR_RQRD (1<<1)
+#define PINCAP_IMPSENSE_CAPABLE (1<<0)
+
+ unsigned int sizes;
+
+ int nconn;
+ short connections[MAX_CONN];
+ short references[MAX_CONN];
+ short refcount;
+ char plugged_in;
+ int impsense; /* Impedanse sensing result. -1=no info */
+ char pin_type, sensed_pin;
+#define PIN_UNKNOWN 0
+#define PIN_IN 1
+#define PIN_MIC 2
+#define PIN_OUT 3
+#define PIN_HEADPHONE 4
+ hdaudio_endpointinfo_t *endpoint;
+ int current_selector;
+ int rgbcolor; /* RGB value of the jack color */
+ int association, sequence;
+} widget_t;
+
+typedef int (*hda_mixer_init_func) (int dev, hdaudio_mixer_t * mixer, int cad,
+ int group);
+
+typedef struct codec_desc codec_desc_t;
+
+typedef struct
+{
+ const codec_desc_t *codec_desc;
+ int active; /* Codec has audio or modem functionality */
+ unsigned long vendor_flags;
+
+ int nwidgets;
+ widget_t widgets[MAX_WIDGETS];
+ int afg;
+ int jack_number;
+ int first_node;
+ int num_jacks_plugged;
+ unsigned int sizes;
+ hda_mixer_init_func mixer_init;
+ unsigned int default_inamp_caps;
+ unsigned int default_outamp_caps;
+ unsigned int vendor_id, subvendor_id;
+
+ int speaker_mode;
+#define SPKMODE_DEFAULT 0
+#define SPKMODE_SPEAKER 1
+#define SPKMODE_STEREO 2
+#define SPKMODE_51 3
+#define SPKMODE_71 4
+
+ int num_inendpoints;
+ hdaudio_endpointinfo_t inendpoints[HDA_MAX_INSTREAMS];
+ int num_outendpoints;
+ hdaudio_endpointinfo_t outendpoints[HDA_MAX_OUTSTREAMS];
+
+ unsigned int multich_map; /* See codec_desc_t.multich_map */
+
+ int mixer_mode; /* For use by dedicated drivers. Initially set to 0 */
+ char **remap;
+} codec_t;
+
+struct _hdaudio_mixer_struct
+{
+ void *devc;
+ oss_device_t *osdev;
+
+ char name[64];
+ char *chip_name;
+ int mixer_dev;
+
+ hdmixer_write_t write;
+ hdmixer_read_t read;
+
+ unsigned int codecmask;
+
+ int ncodecs;
+ codec_t *codecs[MAX_CODECS];
+
+ int ncontrols; /* Total number of widgets (all codecs) */
+
+ int num_inendpoints, copied_inendpoints;
+ hdaudio_endpointinfo_t inendpoints[HDA_MAX_INSTREAMS];
+ int num_outendpoints, copied_outendpoints;
+ hdaudio_endpointinfo_t outendpoints[HDA_MAX_OUTSTREAMS];
+
+ int npins; /* Used for managing the width of the connectors group */
+ int split_connectors; /* Fearless flag used for splitting the connectors geroup */
+ mixer_create_controls_t client_mixer_init;
+
+ int remap_avail;
+};
+
+extern int create_outgain_selector (hdaudio_mixer_t * mixer, widget_t * widget, int group, const char *name);
+extern int create_ingain_selector (hdaudio_mixer_t * mixer, codec_t * codec, widget_t * widget, int group, int ix, const char *name);
+
+#include "hdaudio_mixers.h"
diff --git a/kernel/drv/oss_hdaudio/hdaudio_codecids.h b/kernel/drv/oss_hdaudio/hdaudio_codecids.h
new file mode 100644
index 0000000..22e6c70
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/hdaudio_codecids.h
@@ -0,0 +1,1154 @@
+/*
+ * Purpose: Definitions for HDaudio codec chips known by OSS.
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+/*
+ * NULL mixer init function. Used to disable mixer creation for given codec.
+ */
+static int NULL_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group) { return 0;}
+
+struct codec_desc
+{
+ unsigned int id; /* Codec id (however subdevice ID in the subdevices[] table) */
+ char *name;
+ unsigned long flags;
+#define VF_NONE 0x00000000
+#define VF_ALC88X_HACK 0x00000001 /* ALC88x requires special handling for
+S/PDIF */
+#define VF_VAIO_HACK 0x00000002 /* VAIO STAC9872 requires special
+handling for headphone DAC */
+#define VF_SI3055_HACK 0x00000004 /* Si3055 modem requires manual endpoint
+setuping and rate and ioctl hacks. */
+ char **remap;
+
+ /*
+ * Order of the output converter (DAC) widgets in multi channel mode.
+ * If left to 0 then 0x76543210 (sequential order) is assumed. Some
+ * motherboards are known to use their own channel ordering which can be
+ * fixed using this approach.
+ */
+ unsigned int multich_map;
+ hda_mixer_init_func mixer_init;
+ unsigned int main_id; /* In the subdevices[] table this is used for the codec ID */
+ unsigned int pci_subdevice; /* PCI subdevice ID of the controller (subdevices[]) */
+};
+
+
+/*
+ * Meaningful widget naming schemes for some known codecs.
+ * The list is terminated by a NULL string. An empty string
+ * (EMPTY_STR) means that the automatically selected name will be used.
+ *
+ * Define EMPTY_STR as a pointer to a common "" variable to conserve space.
+ */
+
+static const char hda_empty_string[] = "";
+#define EMPTY_STR hda_empty_string
+
+static const char *alc880remap[] = {
+ EMPTY_STR, /* 0 */
+ EMPTY_STR, /* 1 */
+ "front", /* 2 */
+ "rear", /* 3 */
+ "center/LFE", /* 4 */
+ "side", /* 5 */
+ "spdif-out", /* 6 */
+ "rec1", /* 7 */
+ "rec2", /* 8 */
+ "rec3", /* 9 */
+ "spdif-in", /* 10 */
+ "inputmix", /* 11 */
+ "front", /* 12 */
+ "rear", /* 13 */
+ "center/LFE", /* 14 */
+ "side", /* 15 */
+ "source-a", /* 16 */
+ "source-b", /* 17 */
+ "source-c", /* 18 */
+ "source-d", /* 19 */
+ EMPTY_STR, /* 20 */
+ EMPTY_STR, /* 21 */
+ EMPTY_STR, /* 22 */
+ EMPTY_STR, /* 23 */
+ EMPTY_STR, /* 24 */
+ EMPTY_STR, /* 25 */
+ EMPTY_STR, /* 26 */
+ EMPTY_STR, /* 27 */
+ EMPTY_STR, /* 28 */
+ EMPTY_STR, /* 29 */
+ EMPTY_STR, /* 30 */
+ EMPTY_STR, /* 31 */
+ EMPTY_STR, /* 32 */
+ EMPTY_STR, /* 33 */
+ EMPTY_STR, /* 34 */
+ EMPTY_STR, /* 35 */
+ EMPTY_STR, /* 36 */
+ "fp-front", /* 37 */
+ NULL
+};
+
+static const char *alc883remap[] = {
+ EMPTY_STR, /* 0 */
+ EMPTY_STR, /* 1 */
+ "front", /* 2 */
+ "rear", /* 3 */
+ "center/LFE", /* 4 */
+ "side", /* 5 */
+ "spdif-out", /* 6 */
+ EMPTY_STR, /* 7 */
+ "rec1", /* 8 */
+ "rec2", /* 9 */
+ "spdif-in", /* a */
+ "input-mix", /* b */
+ "front", /* c */
+ "rear", /* d */
+ "center/LFE", /* e */
+ "side", /* f */
+ EMPTY_STR, /* 10 */
+ EMPTY_STR, /* 11 */
+ EMPTY_STR, /* 12 */
+ EMPTY_STR, /* 13 */
+ EMPTY_STR, /* 14 */
+ EMPTY_STR, /* 15 */
+ EMPTY_STR, /* 16 */
+ EMPTY_STR, /* 17 */
+ EMPTY_STR, /* 18 */
+ EMPTY_STR, /* 19 */
+ EMPTY_STR, /* 1a */
+ EMPTY_STR, /* 1b */
+ EMPTY_STR, /* 1c */
+ EMPTY_STR, /* 1d */
+ EMPTY_STR, /* 1e */
+ EMPTY_STR, /* 1f */
+ EMPTY_STR, /* 20 */
+ EMPTY_STR, /* 21 */
+ EMPTY_STR, /* 22 */
+ EMPTY_STR, /* 23 */
+ EMPTY_STR, /* 24 */
+ "pcm4", /* 25 */
+ "pcm4", /* 26 */
+ NULL
+};
+
+static const char *alc260remap[] = {
+ EMPTY_STR, /* 0x00 */
+ EMPTY_STR, /* 0x01 */
+ "front", /* 0x02 */
+ "spdif-out", /* 0x03 */
+ "rec1", /* 0x04 */
+ "rec2", /* 0x05 */
+ "spdif-in", /* 0x06 */
+ "inputmix", /* 0x07 */
+ "speaker-mix", /* 0x08 */
+ "headphone-mix", /* 0x09 */
+ "mono-mix", /* 0x0a */
+ EMPTY_STR, /* 0x0b */
+ EMPTY_STR, /* 0x0c */
+ EMPTY_STR, /* 0x0d */
+ EMPTY_STR, /* 0x0e */
+ "speaker", /* 0x0f */
+ "headphone", /* 0x10 */
+ "mono", /* 0x11 */
+ EMPTY_STR, /* 0x12 */
+ EMPTY_STR, /* 0x13 */
+ EMPTY_STR, /* 0x14 */
+ EMPTY_STR, /* 0x15 */
+ EMPTY_STR, /* 0x16 */
+ "beep", /* 0x17 */
+ "spdif-out", /* 0x18 */
+ "spdif-in", /* 0x19 */
+ NULL
+};
+
+static const char *alc262remap[] = {
+ EMPTY_STR, /* 0x00 */
+ EMPTY_STR, /* 0x01 */
+ "speaker", /* 0x02 */
+ "headphone", /* 0x03 */
+ EMPTY_STR, /* 0x04 */
+ EMPTY_STR, /* 0x05 */
+ "spdif-out", /* 0x06 */
+ "rec1", /* 0x07 */
+ "rec2", /* 0x08 */
+ "rec3", /* 0x09 */
+ "spdif-in", /* 0x0a */
+ "mix", /* 0x0b */
+ EMPTY_STR, /* 0x0c */
+ EMPTY_STR, /* 0x0d */
+ EMPTY_STR, /* 0x0e */
+ EMPTY_STR, /* 0x0f */
+ EMPTY_STR, /* 0x10 */
+ "mono", /* 0x11 */
+ "dmic", /* 0x12 */
+ EMPTY_STR, /* 0x13 */
+ "line-out", /* 0x14 */
+ "headphone", /* 0x15 */
+ "mono", /* 0x16 */
+ "beep", /* 0x17 */
+ EMPTY_STR, /* 0x18 */
+ EMPTY_STR, /* 0x19 */
+ EMPTY_STR, /* 0x1a */
+ EMPTY_STR, /* 0x1b */
+ EMPTY_STR, /* 0x1c */
+ "beep", /* 0x1d */
+ "spdif-out", /* 0x1e */
+ "spdif-in", /* 0x1f */
+ NULL
+};
+
+static const char *alc662remap[] = {
+ EMPTY_STR, /* 0x00 */
+ EMPTY_STR, /* 0x01 */
+ "front", /* 0x02 */
+ "rear", /* 0x03 */
+ "center/LFE", /* 0x04 */
+ EMPTY_STR, /* 0x05 */
+ "spdif-out", /* 0x06 */
+ "spdout", /* 0x07 */
+ "rec1", /* 0x08 */
+ "rec2", /* 0x09 */
+ EMPTY_STR, /* 0x0a */
+ "mix", /* 0x0b */
+ "front", /* 0x0c */
+ "rear", /* 0x0d */
+ "c/lfe", /* 0x0e */
+ EMPTY_STR, /* 0x0f */
+ EMPTY_STR, /* 0x10 */
+ EMPTY_STR, /* 0x11 */
+ EMPTY_STR, /* 0x12 */
+ EMPTY_STR, /* 0x13 */
+ EMPTY_STR, /* 0x14 */
+ EMPTY_STR, /* 0x15 */
+ EMPTY_STR, /* 0x16 */
+ EMPTY_STR, /* 0x17 */
+ EMPTY_STR, /* 0x18 */
+ EMPTY_STR, /* 0x19 */
+ EMPTY_STR, /* 0x1a */
+ EMPTY_STR, /* 0x1b */
+ EMPTY_STR, /* 0x1c */
+ EMPTY_STR, /* 0x1d */
+ EMPTY_STR, /* 0x1e */
+ EMPTY_STR, /* 0x1f */
+ EMPTY_STR, /* 0x20 */
+ EMPTY_STR, /* 0x21 */
+ EMPTY_STR, /* 0x22 */
+ EMPTY_STR, /* 0x23 */
+ NULL
+};
+
+static const char *alc861remap[] = {
+ EMPTY_STR, /* 0x00 */
+ EMPTY_STR, /* 0x01 */
+ EMPTY_STR, /* 0x02 */
+ "front", /* 0x03 */
+ "side", /* 0x04 */
+ "center/LFE", /* 0x05 */
+ "rear", /* 0x06 */
+ "spdout", /* 0x07 */
+ "rec", /* 0x08 */
+ EMPTY_STR, /* 0x09 */
+ EMPTY_STR, /* 0x0a */
+ EMPTY_STR, /* 0x0b */
+ EMPTY_STR, /* 0x0c */
+ EMPTY_STR, /* 0x0d */
+ EMPTY_STR, /* 0x0e */
+ EMPTY_STR, /* 0x0f */
+ EMPTY_STR, /* 0x10 */
+ EMPTY_STR, /* 0x11 */
+ EMPTY_STR, /* 0x12 */
+ EMPTY_STR, /* 0x13 */
+ "recmix", /* 0x14 */
+ "outmix", /* 0x15 */
+ "frontmix", /* 0x16 */
+ "sidemix", /* 0x17 */
+ "c/l-mix", /* 0x18 */
+ "rearmix", /* 0x19 */
+ "line2-mix", /* 0x1a */
+ "mic2mix", /* 0x1b */
+ "line1mix", /* 0x1c */
+ EMPTY_STR, /* 0x1d */
+ "vendor", /* 0x1e */
+ "center/LFE", /* 0x1f */
+ "side", /* 0x20 */
+ EMPTY_STR, /* 0x21 */
+ EMPTY_STR, /* 0x22 */
+ "beep", /* 0x23 */
+ NULL
+};
+
+static const char *cmi9880remap[] = {
+ EMPTY_STR, /* 0 */
+ EMPTY_STR, /* 1 */
+ EMPTY_STR, /* 2 */
+ "front", /* 3 */
+ "rear", /* 4 */
+ "side", /* 5 */
+ "center/LFE", /* 6 */
+ "spdif-out", /* 7 */
+ EMPTY_STR, /* 8 */
+ EMPTY_STR, /* 9 */
+ EMPTY_STR, /* 10 */
+ EMPTY_STR, /* 11 */
+ EMPTY_STR, /* 12 */
+ EMPTY_STR, /* 13 */
+ EMPTY_STR, /* 14 */
+ EMPTY_STR, /* 15 */
+ EMPTY_STR, /* 16 */
+ EMPTY_STR, /* 17 */
+ EMPTY_STR, /* 18 */
+ EMPTY_STR, /* 19 */
+ EMPTY_STR, /* 20 */
+ EMPTY_STR, /* 21 */
+ EMPTY_STR, /* 22 */
+ "pcbeep", /* 23 */
+ EMPTY_STR, /* 24 */
+ EMPTY_STR, /* 25 */
+ EMPTY_STR, /* 26 */
+ EMPTY_STR, /* 27 */
+ EMPTY_STR, /* 28 */
+ EMPTY_STR, /* 29 */
+ EMPTY_STR, /* 30 */
+ EMPTY_STR, /* 31 */
+ NULL
+};
+
+static const char *ad1981remap[] = {
+ EMPTY_STR, /* 0 */
+ EMPTY_STR, /* 1 */
+ "spdif", /* 2 */
+ "play", /* 3 */
+ "rec", /* 4 */
+ EMPTY_STR, /* 5 */
+ EMPTY_STR, /* 6 */
+ EMPTY_STR, /* 7 */
+ EMPTY_STR, /* 8 */
+ EMPTY_STR, /* 9 */
+ "spdif-out", /* a */
+ "mono-sel", /* b */
+ "mic-mix", /* c */
+ "pcbeep-sel", /* d */
+ "rec-mix", /* e */
+ "mono-mix", /* f */
+ "digital-beep", /* 10 */
+ "frontmix-amp", /* 11 */
+ "mic-mixamp", /* 12 */
+ "linein-mixamp", /* 13 */
+ "powerdown", /* 14 */
+ "rec-sel", /* 15 */
+ EMPTY_STR, /* 16 */
+ EMPTY_STR, /* 17 */
+ EMPTY_STR, /* 18 */
+ EMPTY_STR, /* 19 */
+ "lineout-mixamp", /* 1a */
+ "aux-mixamp", /* 1b */
+ "mic-mixamp", /* 1c */
+ "CD-mixamp", /* 1d */
+ "fp-mic-mute", /* 1e */
+ "mic-mute", /* 1f */
+ NULL
+};
+
+static const char *ad1983remap[] = {
+ EMPTY_STR, /* 0 */
+ EMPTY_STR, /* 1 */
+ "spdif", /* 2 */
+ "play", /* 3 */
+ "rec", /* 4 */
+ EMPTY_STR, /* 5 */
+ EMPTY_STR, /* 6 */
+ EMPTY_STR, /* 7 */
+ EMPTY_STR, /* 8 */
+ EMPTY_STR, /* 9 */
+ "spdif-out", /* a */
+ "mono-sel", /* b */
+ "mic-boost", /* c */
+ "linein-sel", /* d */
+ "rec-mix", /* e */
+ "mono-mix", /* f */
+ "digital-beep", /* 10 */
+ "frontmix-amp", /* 11 */
+ "mic-mixamp", /* 12 */
+ "linein-mixamp", /* 13 */
+ "rec-sel", /* 14 */
+ "powerdown", /* 15 */
+ NULL
+};
+
+static const char *ad1984remap[] = {
+ EMPTY_STR, /* 0 */
+ EMPTY_STR, /* 1 */
+ "spdif", /* 2 */
+ "headphone", /* 3 */
+ "front", /* 4 */
+ "dig-mic1", /* 5 */
+ "dig-mic2", /* 6 */
+ "headphone-mix", /* 7 */
+ "rec1", /* 8 */
+ "rec2", /* 9 */
+ "lineout-mix", /* a */
+ "aux-mix", /* b */
+ "rec1-sel", /* c */
+ "rec2-sel", /* d */
+ "mono-sel", /* e */
+ "aux-sel", /* f */
+ "beep", /* 10 */
+ EMPTY_STR, /* 11 */
+ EMPTY_STR, /* 12 */
+ EMPTY_STR, /* 13 */
+ EMPTY_STR, /* 14 */
+ EMPTY_STR, /* 15 */
+ EMPTY_STR, /* 16 */
+ EMPTY_STR, /* 17 */
+ EMPTY_STR, /* 18 */
+ "mixer-powerdown", /* 19 */
+ "beep", /* 1a */
+ "spdif-out", /* 1b */
+ EMPTY_STR, /* 1c */
+ EMPTY_STR, /* 1d */
+ "mono-mix", /* 1e */
+ "mono-downmix", /* 1f */
+ "input-mix", /* 20 */
+ "input-mixamp", /* 21 */
+ "headphone-sel", /* 22 */
+ "dock-sel", /* 23 */
+ "dock-mix", /* 24 */
+ "dock-micboost", /* 25 */
+ EMPTY_STR, /* 26 */
+ NULL
+};
+
+static const char *ad1986remap[] = {
+ EMPTY_STR, /* 0 */
+ EMPTY_STR, /* 1 */
+ "spdif-out", /* 2 */
+ "front", /* 3 */
+ "rear", /* 4 */
+ "center/LFE", /* 5 */
+ "rec", /* 6 */
+ "recmon", /* 7 */
+ "mono-mix", /* 8 */
+ "stereo-downmix", /* 9 */
+ "headphone-sel", /* a */
+ "lineout-sel", /* b */
+ "rear-sel", /* c */
+ "center/LFE-sel", /* d */
+ "mono-sel", /* e */
+ "mic-sel", /* f */
+ "linein-sel", /* 10 */
+ "mic-src", /* 11 */
+ "rec-src" /* 12 */
+ "mic-mix", /* 13 */
+ "phone-mix", /* 14 */
+ "cd-mix", /* 15 */
+ "aux-mix", /* 16 */
+ "linein-mix", /* 17 */
+ "beep", /* 18 */
+ "digital-beep", /* 19 */
+ EMPTY_STR, /* 1a */
+ EMPTY_STR, /* 1b */
+ EMPTY_STR, /* 1c */
+ EMPTY_STR, /* 1d */
+ EMPTY_STR, /* 1e */
+ EMPTY_STR, /* 1f */
+ EMPTY_STR, /* 20 */
+ EMPTY_STR, /* 21 */
+ EMPTY_STR, /* 22 */
+ EMPTY_STR, /* 23 */
+ EMPTY_STR, /* 24 */
+ "spdif-out", /* 25 */
+ "analog-powerdown", /* 26 */
+ "mic-c/LFE-mix", /* 27 */
+ "mic-linein-mix", /* 28 */
+ "c/LFE-linein-mix", /* 29 */
+ "mic-linein-c/LFE-mix", /* 2a */
+ "mic-sel", /* 2b */
+ NULL
+};
+
+static const char *ad1988remap[] = {
+ EMPTY_STR, /* 0 */
+ EMPTY_STR, /* 1 */ /* This is both audio-fgr and DAC???? */
+ "spdout", /* 2 */ /* Not used? */
+ "headphone", /* 3 */
+ "front", /* 4 */
+ "center/LFE", /* 5 */
+ "rear", /* 6 */
+ "spdin", /* 7 */
+ "rec1", /* 8 */
+ "rec2", /* 9 */
+ "side", /* a */
+ "spdin-src", /* b */
+ "rec1-src", /* c */
+ "rec2-src", /* d */
+ "rec3-src", /* e */
+ "rec3", /* f */
+ "pcbeep", /* 10 */
+ EMPTY_STR, /* 11 */
+ EMPTY_STR, /* 12 */
+ EMPTY_STR, /* 13 */
+ EMPTY_STR, /* 14 */
+ EMPTY_STR, /* 15 */
+ EMPTY_STR, /* 16 */
+ EMPTY_STR, /* 17 */
+ EMPTY_STR, /* 18 */
+ "power-down", /* 19 */
+ "beep", /* 1a */
+ "spdif-out", /* 1b */
+ "spdif-in", /* 1c */
+ "spdifout-mix", /* 1d */
+ "mono-mix", /* 1e */
+ "main-volume", /* 1f */
+ "analog-mix", /* 20 */
+ "outamp", /* 21 */
+ "headphon-mix", /* 22 */
+ "hp-powerdown", /* 23 */
+ EMPTY_STR, /* 24 */
+ EMPTY_STR, /* 25 */
+ "mic-mix", /* 26 */
+ "center/LFE-mix", /* 27 */
+ "side-mix", /* 28 */
+ "front-mix", /* 29 */
+ "rear-mix", /* 2a */
+ "fp-mic-mix", /* 2b */
+ "linein-mix", /* 2c */
+ "mono-mixdown", /* 2d */
+ EMPTY_STR, /* 2e */
+ "bias-pdown", /* 2f */
+ "fppink-outsel", /* 30 */
+ "blue-outsel", /* 31 */
+ "pink-outsel", /* 32 */
+ "blue-insel", /* 33 */
+ "pink-insel", /* 34 */
+ EMPTY_STR, /* 35 */
+ "mono-sel", /* 36 */
+ "fpgreen-outsel", /* 37 */
+ "fpgreen-micboost", /* 38 */
+ "fppink-micboost", /* 39 */
+ "blue-micboost", /* 3a */
+ "black-micboost", /* 3b */
+ "pink-micboost", /* 3c */
+ "green-micboost", /* 3d */
+ NULL
+};
+
+#if 0
+static const char *stac9200remap[] = {
+ EMPTY_STR, /* 0 */
+ EMPTY_STR, /* 0x01 */
+ "play", /* 0x02 */
+ "rec", /* 0x03 */
+ "spdif-in", /* 0x04 */
+ "spdif-out", /* 0x05 */
+ EMPTY_STR, /* 0x06 */
+ "playmux", /* 0x07 */
+ EMPTY_STR, /* 0x08 */
+ EMPTY_STR, /* 0x09 */
+ "recmux", /* 0x0a */
+ "mainvol", /* 0x0b */
+ "inputmux", /* 0x0c */
+ EMPTY_STR, /* 0x0d */
+ EMPTY_STR, /* 0x0e */
+ EMPTY_STR, /* 0x0f */
+ EMPTY_STR, /* 0x10 */
+ EMPTY_STR, /* 0x11 */
+ EMPTY_STR, /* 0x12 */
+ "mono-mix", /* 0x13 */
+ "beep", /* 0x14 */
+ NULL
+};
+#endif
+
+static const char *stac920xremap[] = {
+ EMPTY_STR, /* 0 */
+ EMPTY_STR, /* 0x01 */
+ EMPTY_STR, /* 0x02 */
+ EMPTY_STR, /* 0x03 */
+ EMPTY_STR, /* 0x04 */
+ EMPTY_STR, /* 0x05 */
+ EMPTY_STR, /* 0x06 */
+ EMPTY_STR, /* 0x07 */
+ EMPTY_STR, /* 0x08 */
+ EMPTY_STR, /* 0x09 */
+ EMPTY_STR, /* 0x0a */
+ EMPTY_STR, /* 0x0b */
+ EMPTY_STR, /* 0x0c */
+ EMPTY_STR, /* 0x0d */
+ EMPTY_STR, /* 0x0e */
+ EMPTY_STR, /* 0x0f */
+ "front", /* 0x10 */
+ "rear", /* 0x11 */
+ "rec1", /* 0x12 */
+ "rec2", /* 0x13 */
+ "mono-out", /* 0x14 */
+ "mono-mix", /* 0x15 */
+ "cd", /* 0x16 */
+ "dig-mic1", /* 0x17 */
+ "dig-mic2", /* 0x18 */
+ "input1-mux", /* 0x19 */
+ "input2-mux", /* 0x1a */
+ "rec1-vol", /* 0x1b */
+ "rec2-vol", /* 0x1c */
+ "rec1-mux", /* 0x1d */
+ "rec2-mux", /* 0x1e */
+ "spdif-out", /* 0x1f */
+ "spdif-in", /* 0x20 */
+ "digital-out", /* 0x21 */
+ "digital-in", /* 0x22 */
+ "beep", /* 0x23 */
+ "mastervol", /* 0x24 */
+ EMPTY_STR, /* 0x25 */
+ NULL
+};
+
+static const char *stac925xremap[] = {
+ EMPTY_STR, /* 0 */
+ EMPTY_STR, /* 0x01 */
+ "play", /* 0x02 */
+ "rec", /* 0x03 */
+ "spdif-in", /* 0x04 */
+ "spdif-out", /* 0x05 */
+ "output-mux", /* 0x06 */
+ EMPTY_STR, /* 0x07 */
+ EMPTY_STR, /* 0x08 */
+ "rec-vol", /* 0x09 */
+ EMPTY_STR, /* 0x0a */
+ EMPTY_STR, /* 0x0b */
+ EMPTY_STR, /* 0x0c */
+ EMPTY_STR, /* 0x0d */
+ "vol", /* 0x0e */
+ "input-mux", /* 0x0f */
+ EMPTY_STR, /* 0x10 */
+ EMPTY_STR, /* 0x11 */
+ "mono-mix", /* 0x12 */
+ "beep", /* 0x13 */
+ "rec-mux", /* 0x14 */
+ EMPTY_STR, /* 0x15 */
+ NULL
+};
+
+static const char *stac922xremap[] = {
+ EMPTY_STR, /* 0 */
+ EMPTY_STR, /* 0x01 */
+ "front", /* 0x02 */
+ "center/LFE", /* 0x03 */
+ "rear", /* 0x04 */
+ "side", /* 0x05 */
+ "rec1", /* 0x06 */
+ "rec2", /* 0x07 */
+ "spdif-out", /* 0x08 */
+ "spdif-in", /* 0x09 */
+ EMPTY_STR, /* 0x0a */
+ EMPTY_STR, /* 0x0b */
+ EMPTY_STR, /* 0x0c */
+ EMPTY_STR, /* 0x0d */
+ EMPTY_STR, /* 0x0e */
+ EMPTY_STR, /* 0x0f */
+ EMPTY_STR, /* 0x10 */
+ EMPTY_STR, /* 0x11 */
+ "rec1mux", /* 0x12 */
+ "rec2mux", /* 0x13 */
+ "pcbeep", /* 0x14 */
+ "cd", /* 0x15 */
+ "mainvol", /* 0x16 */
+ "rec1vol", /* 0x17 */
+ "rec2vol", /* 0x18 */
+ "adat", /* 0x19 */
+ "i2s-out", /* 0x1a */
+ "i2s-in", /* 0x1b */
+ NULL
+};
+
+static const char *stac923xremap[] = {
+ EMPTY_STR, /* 0 */
+ EMPTY_STR, /* 0x01 */
+ "front", /* 0x02 */
+ "center/LFE", /* 0x03 */
+ "rear", /* 0x04 */
+ "side", /* 0x05 */
+ "headphone", /* 0x06 */
+ "rec1", /* 0x07 */
+ "rec2", /* 0x08 */
+ "rec3", /* 0x09 */
+ EMPTY_STR, /* 0x0a */
+ EMPTY_STR, /* 0x0b */
+ EMPTY_STR, /* 0x0c */
+ EMPTY_STR, /* 0x0d */
+ EMPTY_STR, /* 0x0e */
+ EMPTY_STR, /* 0x0f */
+ EMPTY_STR, /* 0x10 */
+ EMPTY_STR, /* 0x11 */
+ "cd", /* 0x12 */
+ "dig-mic1", /* 0x13 */
+ "dig-mic2", /* 0x14 */
+ "input1-mux", /* 0x15 */
+ "input2-mux", /* 0x16 */
+ "input3-mux", /* 0x17 */
+ "rec1vol", /* 0x18 */
+ "rec2vol", /* 0x19 */
+ "rec3vol", /* 0x1a */
+ "rec1-mux", /* 0x1b */
+ "rec2-mux", /* 0x1c */
+ "rec3-mux", /* 0x1d */
+ "spdif-out", /* 0x1e */
+ "adat", /* 0x1f */
+ NULL
+};
+
+
+static const char *conexant_modem_remap[] =
+{
+ EMPTY_STR, /* 0x00 */
+ EMPTY_STR, /* 0x01 */
+ EMPTY_STR, /* 0x02 */
+ EMPTY_STR, /* 0x03 */
+ EMPTY_STR, /* 0x04 */
+ EMPTY_STR, /* 0x05 */
+ EMPTY_STR, /* 0x06 */
+ EMPTY_STR, /* 0x07 */
+ EMPTY_STR, /* 0x08 */
+ EMPTY_STR, /* 0x09 */
+ EMPTY_STR, /* 0x0a */
+ EMPTY_STR, /* 0x0b */
+ EMPTY_STR, /* 0x0c */
+ EMPTY_STR, /* 0x0d */
+ EMPTY_STR, /* 0x0e */
+ EMPTY_STR, /* 0x0f */
+ EMPTY_STR, /* 0x10 */
+ EMPTY_STR, /* 0x11 */
+ EMPTY_STR, /* 0x12 */
+ EMPTY_STR, /* 0x13 */
+ EMPTY_STR, /* 0x14 */
+ EMPTY_STR, /* 0x15 */
+ EMPTY_STR, /* 0x16 */
+ EMPTY_STR, /* 0x17 */
+ EMPTY_STR, /* 0x18 */
+ EMPTY_STR, /* 0x19 */
+ EMPTY_STR, /* 0x1a */
+ EMPTY_STR, /* 0x1b */
+ EMPTY_STR, /* 0x1c */
+ EMPTY_STR, /* 0x1d */
+ EMPTY_STR, /* 0x1e */
+ EMPTY_STR, /* 0x1f */
+ EMPTY_STR, /* 0x20 */
+ EMPTY_STR, /* 0x21 */
+ EMPTY_STR, /* 0x22 */
+ EMPTY_STR, /* 0x23 */
+ EMPTY_STR, /* 0x24 */
+ EMPTY_STR, /* 0x25 */
+ EMPTY_STR, /* 0x26 */
+ EMPTY_STR, /* 0x27 */
+ EMPTY_STR, /* 0x28 */
+ EMPTY_STR, /* 0x29 */
+ EMPTY_STR, /* 0x2a */
+ EMPTY_STR, /* 0x2b */
+ EMPTY_STR, /* 0x2c */
+ EMPTY_STR, /* 0x2d */
+ EMPTY_STR, /* 0x2e */
+ EMPTY_STR, /* 0x2f */
+ EMPTY_STR, /* 0x30 */
+ EMPTY_STR, /* 0x31 */
+ EMPTY_STR, /* 0x32 */
+ EMPTY_STR, /* 0x33 */
+ EMPTY_STR, /* 0x34 */
+ EMPTY_STR, /* 0x35 */
+ EMPTY_STR, /* 0x36 */
+ EMPTY_STR, /* 0x37 */
+ EMPTY_STR, /* 0x38 */
+ EMPTY_STR, /* 0x39 */
+ EMPTY_STR, /* 0x3a */
+ EMPTY_STR, /* 0x3b */
+ EMPTY_STR, /* 0x3c */
+ EMPTY_STR, /* 0x3d */
+ EMPTY_STR, /* 0x3e */
+ EMPTY_STR, /* 0x3f */
+ EMPTY_STR, /* 0x40 */
+ EMPTY_STR, /* 0x41 */
+ EMPTY_STR, /* 0x42 */
+ EMPTY_STR, /* 0x43 */
+ EMPTY_STR, /* 0x44 */
+ EMPTY_STR, /* 0x45 */
+ EMPTY_STR, /* 0x46 */
+ EMPTY_STR, /* 0x47 */
+ EMPTY_STR, /* 0x48 */
+ EMPTY_STR, /* 0x49 */
+ EMPTY_STR, /* 0x4a */
+ EMPTY_STR, /* 0x4b */
+ EMPTY_STR, /* 0x4c */
+ EMPTY_STR, /* 0x4d */
+ EMPTY_STR, /* 0x4e */
+ EMPTY_STR, /* 0x4f */
+ EMPTY_STR, /* 0x50 */
+ EMPTY_STR, /* 0x51 */
+ EMPTY_STR, /* 0x52 */
+ EMPTY_STR, /* 0x53 */
+ EMPTY_STR, /* 0x54 */
+ EMPTY_STR, /* 0x55 */
+ EMPTY_STR, /* 0x56 */
+ EMPTY_STR, /* 0x57 */
+ EMPTY_STR, /* 0x58 */
+ EMPTY_STR, /* 0x59 */
+ EMPTY_STR, /* 0x5a */
+ EMPTY_STR, /* 0x5b */
+ EMPTY_STR, /* 0x5c */
+ EMPTY_STR, /* 0x5d */
+ EMPTY_STR, /* 0x5e */
+ EMPTY_STR, /* 0x5f */
+ EMPTY_STR, /* 0x60 */
+ EMPTY_STR, /* 0x61 */
+ EMPTY_STR, /* 0x62 */
+ EMPTY_STR, /* 0x63 */
+ EMPTY_STR, /* 0x64 */
+ EMPTY_STR, /* 0x65 */
+ EMPTY_STR, /* 0x66 */
+ EMPTY_STR, /* 0x67 */
+ EMPTY_STR, /* 0x68 */
+ EMPTY_STR, /* 0x69 */
+ EMPTY_STR, /* 0x6a */
+ EMPTY_STR, /* 0x6b */
+ EMPTY_STR, /* 0x6c */
+ EMPTY_STR, /* 0x6d */
+ EMPTY_STR, /* 0x6e */
+ EMPTY_STR, /* 0x6f */
+ "modem-control",/* 0x70 */ // Vendor defined widget
+ "modem-in", /* 0x71 */
+ "modem-out", /* 0x72 */
+ "modem-jack", /* 0x73 */
+ NULL
+};
+
+extern int hdaudio_GPIO_init_1 (int dev, hdaudio_mixer_t * mixer, int cad, int top_group);
+
+static const codec_desc_t codecs[] = {
+ /* Realtek HDA codecs */
+ {0x10ec0260, "ALC260", VF_NONE, (char **) &alc260remap},
+ {0x10ec0262, "ALC262", VF_NONE, (char **) &alc262remap},
+ {0x10ec0268, "ALC268", VF_NONE, (char **) &alc262remap},
+ {0x10ec0662, "ALC662", VF_NONE, (char **) &alc662remap},
+ {0x10ec0663, "ALC663", VF_NONE, (char **) &alc662remap},
+ {0x10ec0861, "ALC861", VF_NONE, (char **) &alc861remap},
+ {0x10ec0862, "ALC862", VF_NONE, (char **) &alc861remap},
+ {0x10ec0880, "ALC880", VF_ALC88X_HACK, (char **) &alc880remap},
+ {0x10ec0882, "ALC882", VF_ALC88X_HACK, (char **) &alc880remap},
+ {0x10ec0883, "ALC883", VF_ALC88X_HACK, (char **) &alc883remap},
+ {0x10ec0885, "ALC885", VF_ALC88X_HACK, (char **) &alc883remap},
+ {0x10ec0887, "ALC887", VF_ALC88X_HACK, (char **) &alc883remap},
+ {0x10ec0888, "ALC888", VF_ALC88X_HACK, (char **) &alc883remap},
+ {0x10ec0889, "ALC889", VF_ALC88X_HACK, (char **) &alc883remap},
+ {0x10ec0892, "ALC892", VF_ALC88X_HACK, (char **) &alc883remap},
+
+ /* CMedia HDA codecs */
+ {0x13f69880, "CMI9880", VF_NONE, (char **) &cmi9880remap},
+ {0x434d4980, "CMI9880", VF_NONE, (char **) &cmi9880remap},
+
+ {0x111d7603, "92HD75B3X5", VF_NONE, NULL, 0, hdaudio_GPIO_init_1},
+ {0x111d7608, "92HD75B2X5", VF_NONE, NULL, 0, hdaudio_GPIO_init_1},
+ {0x111d76b0, "92HD71B8X", VF_NONE, NULL, 0, hdaudio_GPIO_init_1},
+ {0x111d76b1, "92HD71B8X", VF_NONE, NULL, 0, hdaudio_GPIO_init_1},
+ {0x111d76b2, "92HD71B7X", VF_NONE, NULL, 0, hdaudio_GPIO_init_1},
+ {0x111d76b3, "92HD71B7X", VF_NONE, NULL, 0, hdaudio_GPIO_init_1},
+ {0x111d76b4, "92HD71B6X", VF_NONE, NULL, 0, hdaudio_GPIO_init_1},
+ {0x111d76b5, "92HD71B6X", VF_NONE, NULL, 0, hdaudio_GPIO_init_1},
+ {0x111d76b6, "92HD71B5X", VF_NONE, NULL, 0, hdaudio_GPIO_init_1},
+ {0x111d76b7, "92HD71B5X", VF_NONE, NULL, 0, hdaudio_GPIO_init_1},
+
+ /* Analog Devices HDA codecs */
+ {0x11d41981, "AD1981", VF_NONE, (char **) &ad1981remap, 0x76543021},
+ {0x11d41983, "AD1983", VF_NONE, (char **) &ad1983remap, 0x76543021},
+ {0x11d41984, "AD1984", VF_NONE, (char **) &ad1984remap, 0x76543012},
+ {0x11d41986, "AD1986A", VF_NONE, (char **) &ad1986remap, 0x76540321},
+ {0x11d41988, "AD1988A", VF_NONE, (char **) &ad1988remap, 0x76015432},
+ {0x11d4198b, "AD1988B", VF_NONE, (char **) &ad1988remap, 0x76015432},
+
+ /* Sigmatel HDA codecs (some of them) */
+ {0x83847690, "STAC9200", VF_NONE, (char **) &stac920xremap },
+ {0x838476a0, "STAC9205", VF_NONE, (char **) &stac920xremap },
+ {0x838476a1, "STAC9205D", VF_NONE, (char **) &stac920xremap },
+ {0x838476a2, "STAC9204", VF_NONE, (char **) &stac920xremap },
+ {0x838476a3, "STAC9204D", VF_NONE, (char **) &stac920xremap },
+
+ /* Apple Macbook ids */
+ {0x83847880, "STAC9220 A1", VF_NONE, (char **) &stac922xremap },
+ {0x83847882, "STAC9220 A2", VF_NONE, (char **) &stac922xremap },
+ {0x83847680, "STAC9221 A1", VF_NONE, (char **) &stac922xremap },
+
+ {0x83847681, "STAC9220D", VF_NONE, (char **) &stac922xremap },
+ {0x83847682, "STAC9221", VF_NONE, (char **) &stac922xremap },
+ {0x83847683, "STAC9221D", VF_NONE, (char **) &stac922xremap },
+
+ {0x83847610, "STAC9230XN", VF_NONE, (char **) &stac923xremap },
+ {0x83847611, "STAC9230DN", VF_NONE, (char **) &stac923xremap },
+ {0x83847612, "STAC9230XT", VF_NONE, (char **) &stac923xremap },
+ {0x83847613, "STAC9230DT", VF_NONE, (char **) &stac923xremap },
+ {0x83847614, "STAC9229X", VF_NONE, (char **) &stac923xremap },
+ {0x83847615, "STAC9229D", VF_NONE, (char **) &stac923xremap },
+ {0x83847616, "STAC9228X", VF_NONE, (char **) &stac923xremap },
+ {0x83847617, "STAC9228D", VF_NONE, (char **) &stac923xremap },
+ {0x83847618, "STAC9227X", VF_NONE, (char **) &stac923xremap }, /* Intel D975XBX2 (at least) */
+ {0x83847619, "STAC9227D", VF_NONE, (char **) &stac923xremap },
+
+ {0x838476a4, "STAC9255", VF_NONE, (char **) &stac925xremap },
+ {0x838476a5, "STAC9255D", VF_NONE, (char **) &stac925xremap },
+ {0x838476a6, "STAC9254", VF_NONE, (char **) &stac925xremap },
+ {0x838476a7, "STAC9254D", VF_NONE, (char **) &stac925xremap },
+
+ {0x83847620, "STAC9274", VF_NONE, (char **) &stac923xremap },
+ {0x83847621, "STAC9274D", VF_NONE, (char **) &stac923xremap },
+ {0x83847622, "STAC9273X", VF_NONE, (char **) &stac923xremap },
+ {0x83847623, "STAC9273D", VF_NONE, (char **) &stac923xremap },
+ {0x83847624, "STAC9272X", VF_NONE, (char **) &stac923xremap },
+ {0x83847625, "STAC9272D", VF_NONE, (char **) &stac923xremap },
+ {0x83847626, "STAC9271X", VF_NONE, (char **) &stac923xremap },
+ {0x83847627, "STAC9271D", VF_NONE, (char **) &stac923xremap },
+
+ {0x83847628, "STAC9274X5NH", VF_NONE, (char **) &stac923xremap },
+ {0x83847629, "STAC9274D5NH", VF_NONE, (char **) &stac923xremap },
+ {0x83847661, "CXD9872RD", VF_NONE, NULL, 0x76543012},
+ {0x83847662, "STAC9872AK", VF_NONE, NULL, 0x76543012},
+ {0x83847664, "STAC9872K", VF_NONE, NULL, 0x76543210}, /* Vaio VGN-AR51J */
+
+ /* Conexant */
+ {0x14f15045, "CX20548", VF_NONE, NULL, 0x76543201},
+ {0x14f15047, "CX20551", VF_NONE, NULL, 0x76543201},
+ {0x14f15051, "CX20561", VF_NONE, NULL, 0x76543210},
+ {0x14f12c06, "Conexant2c06", VF_NONE, (char **) &conexant_modem_remap, 0, NULL_mixer_init}, /* Modem codec (Vaio) */
+ {0x14f12bfa, "Conexant2bfa", VF_NONE, (char **) &conexant_modem_remap, 0, NULL_mixer_init}, /* Modem codec (Acer Ferrari 5k) */
+
+ /* Si3055 and compatible modems */
+ {0x163c3055, "Si3055", VF_SI3055_HACK, NULL, 0, NULL_mixer_init },
+ {0x163c3155, "Si3155", VF_SI3055_HACK, NULL, 0, NULL_mixer_init },
+ {0x11c13026, "Agere3026", VF_SI3055_HACK, NULL, 0, NULL_mixer_init },
+ {0x11c13055, "Agere3055", VF_SI3055_HACK, NULL, 0, NULL_mixer_init },
+ {0x11c13155, "Agere3155", VF_SI3055_HACK, NULL, 0, NULL_mixer_init },
+ {0x10573055, "Motorola3055", VF_SI3055_HACK, NULL, 0, NULL_mixer_init },
+ {0x10573057, "Motorola3057", VF_SI3055_HACK, NULL, 0, NULL_mixer_init },
+ {0x10573155, "Motorola3155", VF_SI3055_HACK, NULL, 0, NULL_mixer_init },
+
+ /* Creative Labs */
+ {0x1102000a, "Createive XFi XTreme", VF_NONE, NULL, 0x76543210},
+
+ {0x11c11040, "Agere HDA Modem", VF_NONE, NULL, 0, NULL_mixer_init},
+
+ /* Unknown */
+ {0, "Unknown"}
+};
+
+static const char *abit_AA8_remap[] = {
+ EMPTY_STR, /* 0 */
+ EMPTY_STR, /* 1 */
+ "front", /* 2 */
+ "rear", /* 3 */
+ "center/LFE", /* 4 */
+ "side", /* 5 */
+ "spdif-out", /* 6 */
+ "rec1", /* 7 */
+ "rec2", /* 8 */
+ "rec3", /* 9 */
+ "spdif-in", /* 10 */
+ "inputmix", /* 11 */
+ "front", /* 12 */
+ "rear", /* 13 */
+ "center/LFE", /* 14 */
+ "side", /* 15 */
+ "out-source", /* 16 */
+ "out-source", /* 17 */
+ "out-source", /* 18 */
+ "out-source", /* 19 */
+ "green1", /* 20 */
+ "black1", /* 21 */
+ "C-L", /* 22 */
+ "surr", /* 23 */
+ "pink1", /* 24 */
+ "pink2", /* 25 */
+ "blue1", /* 26 */
+ "blue2", /* 27 */
+ "cd", /* 28 */
+ "beep", /* 29 */
+ "spdout", /* 30 */
+ "spdin", /* 31 */
+ "vendor", /* 32 */
+ "vol", /* 33 */
+ NULL
+};
+
+static const char *vaio_remap[] = {
+ EMPTY_STR, /* 0x00 */
+ EMPTY_STR, /* 0x01 */
+ "headphone", /* 0x02 */
+ "pcm", /* 0x03 */ // Unused
+ "pcm", /* 0x04 */ // Unused
+ "speaker", /* 0x05 */
+ "rec1", /* 0x06 */
+ "rec1-vol", /* 0x07 */
+ "rec", /* 0x08 */
+ "rec-vol", /* 0x09 */
+ "headphone", /* 0x0a */
+ EMPTY_STR, /* 0x0b */ // Unused
+ EMPTY_STR, /* 0x0c */ // Unused
+ "mic", /* 0x0d */
+ EMPTY_STR, /* 0x0e */
+ "int-speaker", /* 0x0f */
+ "spdifout1", /* 0x10 */
+ "int-digout1", /* 0x11 */
+ "spdifin", /* 0x12 */
+ "int-digout", /* 0x13 */
+ "int-mic", /* 0x14 */
+ "rec", /* 0x15 */
+ "beep", /* 0x16 */
+ "vol", /* 0x17 */
+ "spdifout", /* 0x18 */
+ NULL
+};
+
+static const char *alc262_vaio_remap[] =
+{
+ EMPTY_STR, /* 0x00 */
+ EMPTY_STR, /* 0x01 */
+ "speaker", /* 0x02 */
+ "headphone", /* 0x03 */
+ "vendor", /* 0x04 */
+ "vendor", /* 0x05 */
+ "hdmi-out", /* 0x06 */
+ "rec1", /* 0x07 */
+ "rec2", /* 0x08 */
+ "rec3", /* 0x09 */
+ "spdif-in", /* 0x0a */
+ "mix", /* 0x0b */
+ "mix", /* 0x0c */
+ "mix", /* 0x0d */
+ "mono", /* 0x0e */
+ "vendor", /* 0x0f */
+ "vendor", /* 0x10 */
+ "mono", /* 0x11 */
+ "dmic", /* 0x12 */
+ "vendor", /* 0x13 */
+ "line-out", /* 0x14 */
+ "headphone", /* 0x15 */
+ "mono", /* 0x16 */
+ "beep", /* 0x17 */
+ "speaker", /* 0x18 */
+ "speaker", /* 0x19 */
+ "speaker", /* 0x1a */
+ "speaker", /* 0x1b */
+ "speaker", /* 0x1c */
+ "beep", /* 0x1d */
+ "spdif-out", /* 0x1e */
+ "spdif-in", /* 0x1f */
+ "vendor", /* 0x20 */
+ "vol", /* 0x21 */
+ "rec3", /* 0x22 */
+ "rec2", /* 0x23 */
+ "rec1", /* 0x24 */
+ NULL
+};
+
+/*
+ * Table for subsystem ID's that require special handling
+ */
+
+extern int hdaudio_Asus_P4B_E_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group);
+extern int hdaudio_scaleoP_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group);
+extern int hdaudio_abit_AA8_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group);
+extern int hdaudio_ferrari5k_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group);
+extern int hdaudio_vaio_vgn_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group);
+extern int hdaudio_thinkpad_r61_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group);
+extern int hdaudio_mac_sigmatel_GPIO_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group);
+extern int hdaudio_mac_realtek_GPIO_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group);
+extern int hdaudio_eeepc_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group);
+extern int hdaudio_asus_a7k_GPIO_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group);
+extern int hdaudio_asus_m9_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group);
+
+static const codec_desc_t subdevices[] = {
+ {0x98801019, "ECS 915P-A", VF_NONE, NULL, 0x76541320},
+ {0x104381e1, "Asus P4B-E/AD1988A", VF_NONE, (char **) &ad1988remap, 0x76015432, hdaudio_Asus_P4B_E_mixer_init},
+ // {0x1043e601, "ScaleoP/ALC888", VF_ALC88X_HACK, (char **) &alc883remap, 0, hdaudio_scaleoP_mixer_init},
+
+ /* Abit AA8 (at least some revisions) have bogus codec config information,
+ * including the subvendor ID. 0x08800000 is used by many other motherboards
+ * too. Have to use the pci_subvendor field for alc880 based devices:
+ * 0x147b1039 = Abit AA8 motherboard.
+ * 0x15849077 = Rock Pegasus 665 laptop.
+ */
+ {0x08800000, "Abit AA8/ALC880", VF_ALC88X_HACK, (char **) &abit_AA8_remap, 0, hdaudio_abit_AA8_mixer_init, 0, 0x147b1039},
+
+ {0x10250000, "Ferrari5k/ALC883", VF_ALC88X_HACK, (char **) &alc883remap, 0, hdaudio_ferrari5k_mixer_init, 0x10ec0883, 0x1025010a},
+ {0x10250000, "Acer_aspire5052/ALC883", VF_ALC88X_HACK, (char **) &alc883remap, 0, hdaudio_ferrari5k_mixer_init, 0x10ec0883, 0x1025010f},
+ {0x1025160d, "Acer_travelmate4060/ALC260", VF_NONE, (char **) &alc260remap, 0, hdaudio_GPIO_init_1, 0x10ec0260, 0x1025008f},
+ {0x10431153, "Asus M9", VF_NONE, (char **) &ad1986remap, 0x76540321, hdaudio_asus_m9_mixer_init},
+
+ /*
+ **** Sony Vaio VGN-AR51 ***
+ * Has three codecs. Primary (Stac9872K), Modem (Conexant) and
+ * HDMI digital output (ALC262). Disable the mixer entries for codecs 2 and 3.
+ */
+ {0x104d2200, "Vaio/STAC9872K", VF_VAIO_HACK, (char**) &vaio_remap, 0x76540213, hdaudio_vaio_vgn_mixer_init, 0x83847664, 0x104d9016},
+ /* 2nd codec (#1) is "Conexant2c06" modem */
+ {0x104d2200, "Vaio/HDMI", VF_NONE, (char **) &alc262_vaio_remap, 0, NULL_mixer_init, 0x10ec0262, 0x104d9016},
+ /****/
+
+ /*
+ * Sony VAIO SZ2, SZ3, FE and FE31B
+ */
+ {0x104d0700, "Vaio/CXD9872RD", VF_VAIO_HACK, (char**) &vaio_remap, 0x76540213, hdaudio_vaio_vgn_mixer_init, 0x83847661, 0x104d81e6},
+ {0x104d1000, "Vaio/CXD9872RD", VF_VAIO_HACK, (char**) &vaio_remap, 0x76540213, hdaudio_vaio_vgn_mixer_init, 0x83847661, 0x104d81ef},
+ {0x104d0c00, "Vaio/CXD9872RD", VF_VAIO_HACK, (char**) &vaio_remap, 0x76540213, hdaudio_vaio_vgn_mixer_init, 0x83847661, 0x104d81ef},
+
+ /*
+ * Sony VAIO AR
+ */
+ {0x104d1300, "Vaio/CXD9872AKD", VF_VAIO_HACK, (char**) &vaio_remap, 0x76540213, hdaudio_vaio_vgn_mixer_init, 0x83847664, 0x104d81fd},
+
+ /*
+ * Sony Vaio SZ (SZ650) has two codecs, STAC9872AK and Conexant modem.
+ * Assume the audio codec is identical with Vaio AGN (above).
+ */
+ {0x104d1e00, "Vaio/STAC9872AK", VF_VAIO_HACK, (char**) &vaio_remap, 0x76540213, hdaudio_vaio_vgn_mixer_init, 0x83847662, 0x104d9008},
+ /* Vaio VGC-LA1 */
+ {0x104d1200, "Vaio/STAC9872AK", VF_VAIO_HACK, (char**) &vaio_remap, 0x76540213, hdaudio_vaio_vgn_mixer_init, 0x83847662, 0x104d8205},
+
+/*
+ * Thinkpad R61
+ */
+ {0x17aa20bb, "Thinkpad R61", VF_NONE, (char**) &ad1984remap, 0, hdaudio_thinkpad_r61_mixer_init},
+
+/*
+ * Asus Eee PC (model 900 at least)
+ */
+ {0x10438337, "Asus Eee PC", VF_NONE, NULL, 0, hdaudio_eeepc_mixer_init},
+
+ /*
+ * Asus A7K
+ */
+ {0x10431339, "Asus A7K", VF_ALC88X_HACK, (char **) &alc880remap, 0, hdaudio_asus_a7k_GPIO_init}, // ALC660
+
+/*
+ * Known Macintosh systems
+ */
+ {0x106b0800, "Intel Mac V1", VF_NONE, (char **) &stac922xremap, 0, hdaudio_mac_sigmatel_GPIO_init},
+ {0x106b0700, "Intel Mac V2", VF_NONE, (char **) &stac922xremap, 0, hdaudio_mac_sigmatel_GPIO_init},
+ {0x106b0600, "Intel Mac V2", VF_NONE, (char **) &stac922xremap, 0, hdaudio_mac_sigmatel_GPIO_init},
+ {0x106b0200, "Intel Mac V3", VF_NONE, (char **) &stac922xremap, 0, hdaudio_mac_sigmatel_GPIO_init},
+ {0x106b0e00, "Intel Mac V3", VF_NONE, (char **) &stac922xremap, 0, hdaudio_mac_sigmatel_GPIO_init},
+ {0x106b0f00, "Intel Mac V3", VF_NONE, (char **) &stac922xremap, 0, hdaudio_mac_sigmatel_GPIO_init},
+ {0x106b1600, "Intel Mac V3", VF_NONE, (char **) &stac922xremap, 0, hdaudio_mac_sigmatel_GPIO_init},
+ {0x106b1700, "Intel Mac V3", VF_NONE, (char **) &stac922xremap, 0, hdaudio_mac_sigmatel_GPIO_init},
+ {0x106b1e00, "Intel Mac V3", VF_NONE, (char **) &stac922xremap, 0, hdaudio_mac_sigmatel_GPIO_init},
+ {0x106b1a00, "Intel Mac V4", VF_NONE, (char **) &stac922xremap, 0, hdaudio_mac_sigmatel_GPIO_init},
+ // {0x00000100, "Intel Mac V4", VF_NONE, (char **) &stac922xremap, ?, hdaudio_mac_sigmatel_GPIO_init},
+ {0x106b0a00, "Intel Mac V5", VF_NONE, (char **) &stac922xremap, 0, hdaudio_mac_sigmatel_GPIO_init},
+ {0x106b2200, "Intel Mac V5", VF_NONE, (char **) &stac922xremap, 0, hdaudio_mac_sigmatel_GPIO_init},
+ {0x106b3200, "Intel iMac", VF_ALC88X_HACK, (char **) &alc883remap, 0, hdaudio_mac_realtek_GPIO_init}, // ALC885
+
+ {0, "Unknown"}
+};
+
diff --git a/kernel/drv/oss_hdaudio/hdaudio_dedicated.h b/kernel/drv/oss_hdaudio/hdaudio_dedicated.h
new file mode 100644
index 0000000..a4ddcfb
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/hdaudio_dedicated.h
@@ -0,0 +1,74 @@
+/*
+ * Purpose: Definitions for dedicated HD audio codec drivers
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+#ifndef HDAUDIO_DEDICATED_H
+#define HDAUDIO_DEDICATED_H
+
+extern void hda_codec_add_group(int dev, hdaudio_mixer_t * mixer, int cad, int *group, int parent_group, const char *name);
+extern int hda_codec_add_pingroup(int dev, hdaudio_mixer_t * mixer, int cad, int wid, int *group, int top_group, int *parent_group, const char *name, int *n, const char *parent_name, int group_size);
+extern int hda_codec_add_adcgroup(int dev, hdaudio_mixer_t * mixer, int cad, int wid, int *group, int top_group, int *parent_group, const char *name, int *n, const char *parent_name, int group_size);
+extern int hda_codec_add_miscgroup(int dev, hdaudio_mixer_t * mixer, int cad, int wid, int *group, int top_group, int *parent_group, const char *name, int *n, const char *parent_name, int group_size);
+
+#define HDA_GROUP(group, top_group, name) hda_codec_add_group(dev, mixer, cad, &group, top_group, name)
+#define HDA_PIN_GROUP(wid, group, pin_group, name, n, parent_name, group_size) \
+ hda_codec_add_pingroup(dev, mixer, cad, wid, &group, top_group, &pin_group, name, &n, parent_name, group_size)
+#define HDA_ADC_GROUP(wid, group, rec_group, name, n, parent_name, group_size) \
+ hda_codec_add_adcgroup(dev, mixer, cad, wid, &group, top_group, &rec_group, name, &n, parent_name, group_size)
+#define HDA_MISC_GROUP(wid, group, misc_group, name, n, parent_name, group_size) \
+ hda_codec_add_miscgroup(dev, mixer, cad, wid, &group, top_group, &misc_group, name, &n, parent_name, group_size)
+
+#define UNMUTE 0
+#define MUTE 1
+#define RECORD 1
+#define NOREC 0
+
+extern int hda_codec_add_outamp(int dev, hdaudio_mixer_t * mixer, int cad, int wid, int group, const char *name, int percent, unsigned int flags);
+extern int hda_codec_add_outmute(int dev, hdaudio_mixer_t * mixer, int cad, int wid, int group, const char *name, int state);
+#define HDA_OUTAMP(wid, group, name, percent) ctl=hda_codec_add_outamp(dev, mixer, cad, wid, group, name, percent, 0)
+#define HDA_OUTAMP_F(wid, group, name, percent, flags) hda_codec_add_outamp(dev, mixer, cad, wid, group, name, percent, flags)
+#define HDA_OUTMUTE(wid, group, name, muted) ctl=hda_codec_add_outmute(dev, mixer, cad, wid, group, name, muted)
+
+extern int hda_codec_add_inamp(int dev, hdaudio_mixer_t * mixer, int cad, int wid, int ix, int group, const char *name, int percent, unsigned int flags);
+extern int hda_codec_add_inmute(int dev, hdaudio_mixer_t * mixer, int cad, int wid, int ix, int group, const char *name, int muted, int flags);
+extern int hda_codec_set_inmute(int dev, hdaudio_mixer_t * mixer, int cad, int wid, int ix, int muted);
+extern int hda_codec_add_insrc(int dev, hdaudio_mixer_t * mixer, int cad, int wid, int ix, int group, const char *name, int muted);
+extern int hda_codec_add_insrcselect(int dev, hdaudio_mixer_t * mixer, int cad, int wid, int group, int *ctl, const char *name, int initial_selection);
+
+#define HDA_INAMP(wid, ix, group, name, percent) ctl=hda_codec_add_inamp(dev, mixer, cad, wid, ix, group, name, percent, 0)
+#define HDA_INAMP_F(wid, ix, group, name, percent, flags) ctl=hda_codec_add_inamp(dev, mixer, cad, wid, ix, group, name, percent, flags)
+
+#define HDA_INMUTE(wid, ix, group, name, muted) ctl=hda_codec_add_inmute(dev, mixer, cad, wid, ix, group, name, muted, 0)
+#define HDA_INMUTE_F(wid, ix, group, name, muted, flags) ctl=hda_codec_add_inmute(dev, mixer, cad, wid, ix, group, name, muted, flags)
+#define HDA_SET_INMUTE(wid, ix, muted) hda_codec_set_inmute(dev, mixer, cad, wid, ix, muted)
+#define HDA_INSRC(wid, ix, group, name, muted) hda_codec_add_insrc(dev, mixer, cad, wid, ix, group, name, muted)
+#define HDA_INSRC_SELECT(wid, group, ctl, name, initial_selection) \
+ hda_codec_add_insrcselect(dev, mixer, cad, wid, group, &ctl, name, initial_selection)
+
+extern int hda_codec_add_select(int dev, hdaudio_mixer_t *mixer, int cad, int wid, int group, const char *name, int *ctl, int initial_select);
+extern void hda_codec_set_select(int dev, hdaudio_mixer_t *mixer, int cad, int wid, int value);
+extern void hda_codec_set_pinselect(int dev, hdaudio_mixer_t *mixer, int cad, int wid, int value);
+extern int hda_codec_add_pinselect(int dev, hdaudio_mixer_t *mixer, int cad, int wid, int group, const char *name, int *ctl, int initial_select);
+#define HDA_SELECT(wid, name, ctl, group, sel) hda_codec_add_select(dev, mixer, cad, wid, group, name, &ctl, sel)
+#define HDA_PINSELECT(wid, ctl, group, name, sel) hda_codec_add_pinselect(dev, mixer, cad, wid, group, name, &ctl, sel)
+#define HDA_SETSELECT(wid, value) hda_codec_set_select(dev, mixer, cad, wid, value)
+#define HDA_SET_PINSELECT(wid, value) hda_codec_set_pinselect(dev, mixer, cad, wid, value)
+
+extern int hda_codec_add_choices(int dev, hdaudio_mixer_t *mixer, int ctl, const char *choiselist);
+#define HDA_CHOICES(ctl, choicelist) hda_codec_add_choices(dev, mixer, ctl, choicelist)
+
+extern void hda_codec_set_color(int dev, hdaudio_mixer_t *mixer, int ctl, int color);
+#define HDA_COLOR(ctl, color) hda_codec_set_color(dev, mixer, ctl, color)
+
+#endif
+
diff --git a/kernel/drv/oss_hdaudio/hdaudio_eeepc.c b/kernel/drv/oss_hdaudio/hdaudio_eeepc.c
new file mode 100644
index 0000000..8fef9f7
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/hdaudio_eeepc.c
@@ -0,0 +1,293 @@
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+/*
+ * Purpose: Dedicated HDaudio mixer driver for Asus Eee PC
+ */
+
+/* Codec index is 0 */
+/* Codec vendor 10ec:0662 */
+/* HD codec revision 1.0 (1.1) (0x00100101) */
+/* Subsystem ID 10438337 */
+/* Default amplifier caps: in=00000000, out=00000000 */
+#include "oss_hdaudio_cfg.h"
+#include "hdaudio.h"
+#include "hdaudio_codec.h"
+#include "hdaudio_dedicated.h"
+
+int
+hdaudio_eeepc_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group)
+{
+ int ctl=0;
+
+ DDB(cmn_err(CE_CONT, "hdaudio_eeepc_mixer_init got called.\n"));
+
+ HDA_OUTAMP(0x02, top_group, "pcm-front", 100);
+ HDA_OUTAMP(0x04, top_group, "pcm-rear", 100);
+
+ /* Handle PIN widgets */
+ {
+ int n, group, pin_group;
+
+ n=0;
+
+ HDA_GROUP(pin_group, top_group, "jack");
+
+ if (HDA_PIN_GROUP(0x14, group, pin_group, "speaker", n, "jack", 4)) /* Pin widget 0x14 */
+ {
+ /* Src 0xc=front */
+#if 1
+ HDA_SET_PINSELECT(0x14, 0); // Hardwire as output
+#else
+ if (HDA_PINSELECT(0x14, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "front-out input");
+#endif
+ HDA_OUTMUTE(0x14, group, "mute", UNMUTE);
+ }
+
+#if 0
+ if (HDA_PIN_GROUP(0x15, group, pin_group, "black", n, "jack", 4)) /* Pin widget 0x15 */
+ {
+ /* Src 0xd=rear */
+ if (HDA_PINSELECT(0x15, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "rear-out input");
+ HDA_OUTMUTE(0x15, group, "mute", UNMUTE);
+ }
+
+ if (HDA_PIN_GROUP(0x16, group, pin_group, "black", n, "jack", 4)) /* Pin widget 0x16 */
+ {
+ /* Src 0xe=rear */
+ if (HDA_PINSELECT(0x16, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "rear-out input");
+ HDA_OUTMUTE(0x16, group, "mute", UNMUTE);
+ }
+#endif
+
+ if (HDA_PIN_GROUP(0x19, group, pin_group, "mic", n, "jack", 4)) /* Pin widget 0x19 */
+ {
+ /* Src 0xc=front */
+ /* Src 0xe=rear */
+#if 0
+ if (HDA_PINSELECT(0x19, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "front-out rear-out input");
+ // HDA_OUTMUTE(0x19, group, "inmute", UNMUTE);
+#else
+ HDA_SET_PINSELECT(0x19, 2); // Hardwire to input
+#endif
+ HDA_INAMP(0x19, 0, group, "in", 90); /* From widget 0x0c */
+ }
+
+#if 0
+ if (HDA_PIN_GROUP(0x1a, group, pin_group, "black", n, "jack", 4)) /* Pin widget 0x1a */
+ {
+ /* Src 0xd=rear */
+ if (HDA_PINSELECT(0x1a, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "rear-out input");
+ HDA_OUTMUTE(0x1a, group, "inmute", UNMUTE);
+ }
+#endif
+
+ if (HDA_PIN_GROUP(0x1b, group, pin_group, "green", n, "jack", 4)) /* Pin widget 0x1b */
+ {
+ /* Src 0xc=front */
+ /* Src 0xe=rear */
+ if (HDA_PINSELECT(0x1b, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "front-out rear-out input");
+ HDA_OUTMUTE(0x1b, group, "inmute", UNMUTE);
+ HDA_INAMP(0x1b, 0, group, "out", 90); /* From widget 0x0c */
+ }
+
+ if (HDA_PIN_GROUP(0x18, group, pin_group, "pink", n, "jack", 4)) /* Pin widget 0x18 */
+ {
+ /* Src 0xe=rear */
+ if (HDA_PINSELECT(0x18, ctl, group, "mode", 1))
+ HDA_CHOICES(ctl, "rear-out input");
+ HDA_OUTMUTE(0x18, group, "mute", UNMUTE);
+ HDA_INAMP(0x18, 0, group, "in", 90); /* From widget 0x0e */
+ }
+
+#if 0
+ if (HDA_PIN_GROUP(0x1c, group, pin_group, "black", n, "jack", 4)) /* Pin widget 0x1c */
+ {
+ if (HDA_PINSELECT(0x1c, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "input");
+ }
+
+ if (HDA_PIN_GROUP(0x1d, group, pin_group, "purple", n, "jack", 4)) /* Pin widget 0x1d */
+ {
+ if (HDA_PINSELECT(0x1d, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "input");
+ }
+#endif
+ }
+ /* Handle ADC widgets */
+ {
+ int n, group, rec_group;
+
+ n=0;
+
+ HDA_GROUP(rec_group, top_group, "record");
+
+ if (HDA_ADC_GROUP(0x08, group, rec_group, "pcmin0", n, "record", 4)) /* ADC widget 0x08 */
+ {
+ /* Src 0x23=mix */
+ HDA_INAMP(0x08, 0, group, "-", 90); /* From widget 0x23 */
+
+ /* Widget 0x23 (mix) */
+ /* Src 0x18=pink */
+ /* Src 0x19=mic */
+ /* Src 0x1a=black */
+ /* Src 0x1b=green */
+ /* Src 0x1c=black */
+ /* Src 0x1d=purple */
+ /* Src 0x14=speaker */
+ /* Src 0x15=black */
+ /* Src 0x16=black */
+ /* Src 0xb=mix */
+ {
+ int amp_group;
+
+ HDA_GROUP(amp_group, group, "rec-mute");
+ HDA_INMUTE(0x23, 0, amp_group, "pink", MUTE); /* From widget 0x18 */
+ HDA_INMUTE(0x23, 1, amp_group, "mic", UNMUTE); /* From widget 0x19 */
+ HDA_INMUTE(0x23, 2, amp_group, "black", MUTE); /* From widget 0x1a */
+ HDA_INMUTE(0x23, 3, amp_group, "green", MUTE); /* From widget 0x1b */
+ HDA_INMUTE(0x23, 4, amp_group, "black", MUTE); /* From widget 0x1c */
+ HDA_INMUTE(0x23, 5, amp_group, "purple", MUTE); /* From widget 0x1d */
+ HDA_INMUTE(0x23, 6, amp_group, "speaker", MUTE); /* From widget 0x14 */
+ HDA_INMUTE(0x23, 7, amp_group, "black", MUTE); /* From widget 0x15 */
+ HDA_INMUTE(0x23, 8, amp_group, "black", MUTE); /* From widget 0x16 */
+ HDA_INMUTE(0x23, 9, amp_group, "input-mixer", MUTE); /* From widget 0x0b */
+ }
+ }
+
+ if (HDA_ADC_GROUP(0x09, group, rec_group, "pcmin1", n, "record", 4)) /* ADC widget 0x09 */
+ {
+ /* Src 0x22=mix */
+ HDA_INAMP(0x09, 0, group, "-", 90); /* From widget 0x22 */
+
+ /* Widget 0x22 (mix) */
+ /* Src 0x18=pink */
+ /* Src 0x19=mic */
+ /* Src 0x1a=black */
+ /* Src 0x1b=green */
+ /* Src 0x1c=black */
+ /* Src 0x1d=purple */
+ /* Src 0x14=speaker */
+ /* Src 0x15=black */
+ /* Src 0x16=black */
+ /* Src 0xb=mix */
+ {
+ int amp_group;
+
+ HDA_GROUP(amp_group, group, "rec-mute");
+ HDA_INMUTE(0x22, 0, amp_group, "pink", MUTE); /* From widget 0x18 */
+ HDA_INMUTE(0x22, 1, amp_group, "mic", MUTE); /* From widget 0x19 */
+ HDA_INMUTE(0x22, 2, amp_group, "black", MUTE); /* From widget 0x1a */
+ HDA_INMUTE(0x22, 3, amp_group, "green", MUTE); /* From widget 0x1b */
+ HDA_INMUTE(0x22, 4, amp_group, "black", MUTE); /* From widget 0x1c */
+ HDA_INMUTE(0x22, 5, amp_group, "purple", MUTE); /* From widget 0x1d */
+ HDA_INMUTE(0x22, 6, amp_group, "speaker", MUTE); /* From widget 0x14 */
+ HDA_INMUTE(0x22, 7, amp_group, "black", MUTE); /* From widget 0x15 */
+ HDA_INMUTE(0x22, 8, amp_group, "black", MUTE); /* From widget 0x16 */
+ HDA_INMUTE(0x22, 9, amp_group, "input-mixer", UNMUTE); /* From widget 0x0b */
+ }
+ }
+ }
+ /* Handle misc widgets */
+ {
+ int n, group, misc_group;
+
+ n=0;
+
+ HDA_GROUP(misc_group, top_group, "misc");
+
+ if (HDA_MISC_GROUP(0x0c, group, misc_group, "front", n, "misc", 8)) /* Misc widget 0x0c */
+ {
+ /* Src 0x2=front */
+ /* Src 0xb=mix */
+ {
+ int amp_group;
+
+ HDA_GROUP(amp_group, group, "mute");
+ HDA_INMUTE(0x0c, 0, amp_group, "pcm-front", UNMUTE); /* From widget 0x02 */
+ HDA_INMUTE(0x0c, 1, amp_group, "input-mixer", UNMUTE); /* From widget 0x0b */
+ }
+ }
+
+ if (HDA_MISC_GROUP(0x0d, group, misc_group, "rear", n, "misc", 8)) /* Misc widget 0x0d */
+ {
+ /* Src 0x3=rear */
+ /* Src 0xb=mix */
+ {
+ int amp_group;
+
+ HDA_GROUP(amp_group, group, "mute");
+ HDA_INMUTE(0x0d, 0, amp_group, "pcm-rear", UNMUTE); /* From widget 0x03 */
+ HDA_INMUTE(0x0d, 1, amp_group, "input-mixer", UNMUTE); /* From widget 0x0b */
+ }
+ }
+
+ if (HDA_MISC_GROUP(0x0e, group, misc_group, "rear", n, "misc", 8)) /* Misc widget 0x0e */
+ {
+ /* Src 0x4=rear */
+ /* Src 0xb=mix */
+ {
+ int amp_group;
+
+ HDA_GROUP(amp_group, group, "mute");
+ HDA_INMUTE(0x0e, 0, amp_group, "rear", UNMUTE); /* From widget 0x04 */
+ HDA_INMUTE(0x0e, 1, amp_group, "input-mixer", UNMUTE); /* From widget 0x0b */
+ }
+ }
+
+#if 0
+ if (HDA_MISC_GROUP(0x02, group, misc_group, "pcm-front", n, "misc", 8)) /* Misc widget 0x02 */
+ {
+ HDA_OUTAMP(0x02, group, "-", 90);
+ }
+
+ if (HDA_MISC_GROUP(0x03, group, misc_group, "pcm", n, "misc", 8)) /* Misc widget 0x03 */
+ {
+ HDA_OUTAMP(0x03, group, "-", 0);
+ }
+
+ if (HDA_MISC_GROUP(0x04, group, misc_group, "pcm-rear", n, "misc", 8)) /* Misc widget 0x04 */
+ {
+ HDA_OUTAMP(0x04, group, "-", 90);
+ }
+#endif
+
+ if (HDA_MISC_GROUP(0x0b, group, misc_group, "input-mixer", n, "misc", 8)) /* Misc widget 0x0b */
+ {
+ /* Src 0x18=rear */
+ /* Src 0x19=mic */
+ /* Src 0x1a=rear */
+ /* Src 0x1b=headphone */
+ /* Src 0x1c=speaker */
+ /* Src 0x1d=speaker */
+ /* Src 0x14=front */
+ /* Src 0x15=rear */
+ /* Src 0x16=rear */
+ HDA_INAMP(0x0b, 0, group, "pink", 20); /* From widget 0x18 */
+ HDA_INAMP(0x0b, 1, group, "mic", 20); /* From widget 0x19 */
+ HDA_INAMP(0x0b, 2, group, "black", 90); /* From widget 0x1a */
+ HDA_INAMP(0x0b, 3, group, "green", 90); /* From widget 0x1b */
+ HDA_INAMP(0x0b, 4, group, "black", 90); /* From widget 0x1c */
+ HDA_INAMP(0x0b, 5, group, "purple", 90); /* From widget 0x1d */
+ // HDA_INAMP(0x0b, 6, group, "speaker", 90); /* From widget 0x14 */
+ HDA_INAMP(0x0b, 7, group, "black", 90); /* From widget 0x15 */
+ HDA_INAMP(0x0b, 8, group, "black", 90); /* From widget 0x16 */
+ }
+ }
+ return 0;
+}
diff --git a/kernel/drv/oss_hdaudio/hdaudio_ferrari5k.c b/kernel/drv/oss_hdaudio/hdaudio_ferrari5k.c
new file mode 100644
index 0000000..1ee3eca
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/hdaudio_ferrari5k.c
@@ -0,0 +1,303 @@
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+/* Codec index is 1 */
+/* Codec vendor 10ec:0883 */
+/* HD codec revision 1.0 (0.2) (0x00100002) */
+/* Subsystem ID 10250000 */
+/* Default amplifier caps: in=00000000, out=00000000 */
+#include "oss_hdaudio_cfg.h"
+#include "hdaudio.h"
+#include "hdaudio_codec.h"
+#include "hdaudio_dedicated.h"
+
+int
+hdaudio_ferrari5k_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group)
+{
+ int ctl=0;
+
+ DDB(cmn_err(CE_CONT, "hdaudio_ferrari5k_mixer_init got called.\n"));
+
+ // Main volume controls for PCM channels. Moved from the misc group
+ HDA_OUTAMP_F(0x0c, top_group, "front", 90, MIXF_MAINVOL);
+ HDA_OUTAMP_F(0x0d, top_group, "rear", 90, MIXF_MAINVOL);
+ HDA_OUTAMP_F(0x0e, top_group, "center/lfe", 90, MIXF_MAINVOL);
+ HDA_OUTAMP_F(0x0f, top_group, "side", 90, MIXF_MAINVOL);
+ HDA_OUTAMP_F(0x26, top_group, "pcm4", 90, MIXF_MAINVOL);
+
+ // Mute controls for the output pins
+ HDA_OUTMUTE(0x15, top_group, "speaker-mute", UNMUTE);
+ HDA_OUTMUTE(0x14, top_group, "headph-mute", UNMUTE);
+ HDA_OUTMUTE(0x1a, top_group, "lineout-mute", UNMUTE);
+ HDA_OUTMUTE(0x18, top_group, "mic-jack-mute", UNMUTE);
+
+ /* Handle PIN widgets */
+ {
+ int n, group, pin_group;
+
+ n=0;
+
+ HDA_GROUP(pin_group, top_group, "jack");
+
+ if (HDA_PIN_GROUP(0x15, group, pin_group, "int-speaker", n, "jack", 8)) /* Pin widget 0x15 */
+ {
+ /* Src 0xc=front */
+ /* Src 0xd=rear */
+ /* Src 0xe=center/LFE */
+ /* Src 0xf=side */
+ /* Src 0x26=pcm4 */
+ if (HDA_PINSELECT(0x15, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "front-out rear-out center/LFE-out side-out pcm4-out unused");
+ //HDA_INAMP(0x15, 0, group, "inlevel", 90); /* From widget 0x0c */
+ }
+
+ if (HDA_PIN_GROUP(0x14, group, pin_group, "headphone", n, "jack", 8)) /* Pin widget 0x14 */
+ {
+ /* Src 0xc=front */
+ /* Src 0xd=rear */
+ /* Src 0xe=center/LFE */
+ /* Src 0xf=side */
+ /* Src 0x26=pcm4 */
+ if (HDA_PINSELECT(0x14, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "front-out rear-out center/LFE-out side-out pcm4-out input");
+ HDA_INAMP(0x14, 0, group, "inlevel", 90); /* From widget 0x0c */
+ }
+
+ if (HDA_PIN_GROUP(0x18, group, pin_group, "ext-mic", n, "jack", 8)) /* Pin widget 0x18 */
+ {
+ /* Src 0xc=front */
+ /* Src 0xd=rear */
+ /* Src 0xe=center/LFE */
+ /* Src 0xf=side */
+ /* Src 0x26=pcm4 */
+ if (HDA_PINSELECT(0x18, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "front-out rear-out center/LFE-out side-out pcm4-out input");
+ HDA_INAMP(0x18, 0, group, "inlevel", 90); /* From widget 0x0c */
+ }
+
+ if (HDA_PIN_GROUP(0x19, group, pin_group, "int-mic", n, "jack", 8)) /* Pin widget 0x19 */
+ {
+ /* Src 0xc=front */
+ /* Src 0xd=rear */
+ /* Src 0xe=center/LFE */
+ /* Src 0xf=side */
+ /* Src 0x26=pcm4 */
+#if 1
+ HDA_SETSELECT(0x19, 5); // Hardwired to mic-input
+#else
+ if (HDA_PINSELECT(0x19, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "front-out rear-out center/LFE-out side-out pcm4-out input");
+#endif
+ //HDA_OUTMUTE(0x19, group, "outmute", UNMUTE);
+ HDA_INAMP(0x19, 0, group, "inlevel", 90); /* From widget 0x0c */
+ }
+
+ if (HDA_PIN_GROUP(0x1a, group, pin_group, "line-out", n, "jack", 8)) /* Pin widget 0x1a */
+ {
+ /* Src 0xc=front */
+ /* Src 0xd=rear */
+ /* Src 0xe=center/LFE */
+ /* Src 0xf=side */
+ /* Src 0x26=pcm4 */
+ if (HDA_PINSELECT(0x1a, ctl, group, "mode", 0))
+ HDA_CHOICES(ctl, "front-out rear-out center/LFE-out side-out pcm4-out input");
+ HDA_INAMP(0x1a, 0, group, "inlevel", 90); /* From widget 0x0c */
+ }
+ }
+
+ /* Handle ADC widgets */
+ {
+ int n, group, rec_group;
+
+ n=0;
+
+ HDA_GROUP(rec_group, top_group, "record");
+
+ if (HDA_ADC_GROUP(0x08, group, rec_group, "rec1", n, "record", 4)) /* ADC widget 0x08 */
+ {
+ /* Src 0x23=rec1 */
+ HDA_INAMP_F(0x08, 0, group, "rec1", 80, MIXF_RECVOL); /* From widget 0x23 */
+
+ /* Widget 0x23 (rec1) */
+ /* Src 0x18=ext-mic */
+ /* Src 0x19=int-mic */
+ /* Src 0x1a=line-out */
+ /* Src 0x1b=black */
+ /* Src 0x1c=black */
+ /* Src 0x1d=black */
+ /* Src 0x14=black */
+ /* Src 0x15=int-speaker */
+ /* Src 0x16=black */
+ /* Src 0x17=black */
+ /* Src 0xb=input-mix */
+ {
+#if 1
+ if (HDA_INSRC_SELECT(0x23, group, ctl, "recsrc", 1))
+ HDA_CHOICES(ctl, "ext-mic int-mic line-out-jack unused unused unused unused unused unused unused input-mix");
+#else
+ HDA_GROUP(amp_group, group, "mute");
+ HDA_INMUTE(0x23, 0, amp_group, "ext-mic", UNMUTE); /* From widget 0x18 */
+ HDA_INMUTE(0x23, 1, amp_group, "int-mic", UNMUTE); /* From widget 0x19 */
+ HDA_INMUTE(0x23, 2, amp_group, "line-out", UNMUTE); /* From widget 0x1a */
+ HDA_INMUTE(0x23, 3, amp_group, "black", UNMUTE); /* From widget 0x1b */
+ HDA_INMUTE(0x23, 4, amp_group, "black", UNMUTE); /* From widget 0x1c */
+ HDA_INMUTE(0x23, 5, amp_group, "black", UNMUTE); /* From widget 0x1d */
+ HDA_INMUTE(0x23, 6, amp_group, "headph-jack", UNMUTE); /* From widget 0x14 */
+ HDA_INMUTE(0x23, 7, amp_group, "int-speaker", UNMUTE); /* From widget 0x15 */
+ HDA_INMUTE(0x23, 8, amp_group, "black", UNMUTE); /* From widget 0x16 */
+ HDA_INMUTE(0x23, 9, amp_group, "black", UNMUTE); /* From widget 0x17 */
+ HDA_INMUTE(0x23, 10, amp_group, "input-mix", MUTE); /* From widget 0x0b */
+#endif
+ }
+ }
+
+ if (HDA_ADC_GROUP(0x09, group, rec_group, "rec2", n, "record", 4)) /* ADC widget 0x09 */
+ {
+ /* Src 0x22=rec2 */
+ HDA_INAMP_F(0x09, 0, group, "rec2", 80, MIXF_RECVOL); /* From widget 0x22 */
+
+ /* Widget 0x22 (rec2) */
+ /* Src 0x18=ext-mic */
+ /* Src 0x19=int-mic */
+ /* Src 0x1a=line-out */
+ /* Src 0x1b=black */
+ /* Src 0x1c=black */
+ /* Src 0x1d=black */
+ /* Src 0x14=black */
+ /* Src 0x15=int-speaker */
+ /* Src 0x16=black */
+ /* Src 0x17=black */
+ /* Src 0xb=input-mix */
+ {
+#if 1
+ if (HDA_INSRC_SELECT(0x22, group, ctl, "recsrc", 1))
+ HDA_CHOICES(ctl, "ext-mic int-mic line-out-jack unused unused unused unused unused unused unused input-mix");
+#else
+ HDA_GROUP(amp_group, group, "mute");
+ HDA_INMUTE(0x22, 0, amp_group, "ext-mic", UNMUTE); /* From widget 0x18 */
+ HDA_INMUTE(0x22, 1, amp_group, "int-mic", UNMUTE); /* From widget 0x19 */
+ HDA_INMUTE(0x22, 2, amp_group, "line-out", UNMUTE); /* From widget 0x1a */
+ HDA_INMUTE(0x22, 3, amp_group, "black", UNMUTE); /* From widget 0x1b */
+ HDA_INMUTE(0x22, 4, amp_group, "black", UNMUTE); /* From widget 0x1c */
+ HDA_INMUTE(0x22, 5, amp_group, "black", UNMUTE); /* From widget 0x1d */
+ HDA_INMUTE(0x22, 6, amp_group, "headph-jack", UNMUTE); /* From widget 0x14 */
+ HDA_INMUTE(0x22, 7, amp_group, "int-speaker", UNMUTE); /* From widget 0x15 */
+ HDA_INMUTE(0x22, 8, amp_group, "black", UNMUTE); /* From widget 0x16 */
+ HDA_INMUTE(0x22, 9, amp_group, "black", UNMUTE); /* From widget 0x17 */
+ HDA_INMUTE(0x22, 10, amp_group, "input-mix", MUTE); /* From widget 0x0b */
+#endif
+ }
+ }
+
+ if (HDA_ADC_GROUP(0x0a, group, rec_group, "spdif-in", n, "record", 4)) /* ADC widget 0x0a */
+ {
+ /* Src 0x1f=speaker */
+ }
+ }
+ /* Handle misc widgets */
+ {
+ int n, group, misc_group;
+
+ n=0;
+
+ HDA_GROUP(misc_group, top_group, "misc");
+
+ if (HDA_MISC_GROUP(0x0c, group, misc_group, "front", n, "misc", 8)) /* Misc widget 0x0c */
+ {
+ /* Src 0x2=front */
+ /* Src 0xb=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP(amp_group, group, "mute");
+ HDA_INMUTE(0x0c, 0, amp_group, "front", UNMUTE); /* From widget 0x02 */
+ HDA_INMUTE(0x0c, 1, amp_group, "input-mix", MUTE); /* From widget 0x0b */
+ }
+ }
+
+ if (HDA_MISC_GROUP(0x0d, group, misc_group, "rear", n, "misc", 8)) /* Misc widget 0x0d */
+ {
+ /* Src 0x3=rear */
+ /* Src 0xb=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP(amp_group, group, "mute");
+ HDA_INMUTE(0x0d, 0, amp_group, "rear", UNMUTE); /* From widget 0x03 */
+ HDA_INMUTE(0x0d, 1, amp_group, "input-mix", MUTE); /* From widget 0x0b */
+ }
+ }
+
+ if (HDA_MISC_GROUP(0x0e, group, misc_group, "center/LFE", n, "misc", 8)) /* Misc widget 0x0e */
+ {
+ /* Src 0x4=center/LFE */
+ /* Src 0xb=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP(amp_group, group, "mute");
+ HDA_INMUTE(0x0e, 0, amp_group, "center/LFE", UNMUTE); /* From widget 0x04 */
+ HDA_INMUTE(0x0e, 1, amp_group, "input-mix", MUTE); /* From widget 0x0b */
+ }
+ }
+
+ if (HDA_MISC_GROUP(0x0f, group, misc_group, "side", n, "misc", 8)) /* Misc widget 0x0f */
+ {
+ /* Src 0x5=side */
+ /* Src 0xb=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP(amp_group, group, "mute");
+ HDA_INMUTE(0x0f, 0, amp_group, "side", UNMUTE); /* From widget 0x05 */
+ HDA_INMUTE(0x0f, 1, amp_group, "input-mix", MUTE); /* From widget 0x0b */
+ }
+ }
+
+ if (HDA_MISC_GROUP(0x26, group, misc_group, "pcm4", n, "misc", 8)) /* Misc widget 0x26 */
+ {
+ /* Src 0x25=pcm4 */
+ /* Src 0xb=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP(amp_group, group, "mute");
+ HDA_INMUTE(0x26, 0, amp_group, "pcm4", UNMUTE); /* From widget 0x25 */
+ HDA_INMUTE(0x26, 1, amp_group, "input-mix", MUTE); /* From widget 0x0b */
+ }
+ }
+
+ if (HDA_MISC_GROUP(0x0b, group, misc_group, "input-mix", n, "misc", 8)) /* Misc widget 0x0b */
+ {
+ /* Src 0x18=mic */
+ /* Src 0x19=int-mic */
+ /* Src 0x1a=linein */
+ /* Src 0x1b=speaker */
+ /* Src 0x1c=speaker */
+ /* Src 0x1d=speaker */
+ /* Src 0x14=headphone */
+ /* Src 0x15=int-speaker */
+ /* Src 0x16=speaker */
+ /* Src 0x17=speaker */
+ HDA_INAMP(0x0b, 0, group, "ext-mic", 10); /* From widget 0x18 */
+ HDA_INAMP(0x0b, 1, group, "int-mic", 10); /* From widget 0x19 */
+ HDA_INAMP(0x0b, 2, group, "line-out", 0); /* From widget 0x1a */
+ HDA_INAMP(0x0b, 3, group, "black", 0); /* From widget 0x1b */
+ HDA_INAMP(0x0b, 4, group, "black", 0); /* From widget 0x1c */
+ HDA_INAMP(0x0b, 5, group, "black", 0); /* From widget 0x1d */
+ //HDA_INAMP(0x0b, 6, group, "headph-jack", 90); /* From widget 0x14 */
+ //HDA_INAMP(0x0b, 7, group, "int-speaker", 0); /* From widget 0x15 */
+ HDA_INAMP(0x0b, 8, group, "black", 0); /* From widget 0x16 */
+ HDA_INAMP(0x0b, 9, group, "black", 0); /* From widget 0x17 */
+ }
+ }
+ return 0;
+}
diff --git a/kernel/drv/oss_hdaudio/hdaudio_generic.c b/kernel/drv/oss_hdaudio/hdaudio_generic.c
new file mode 100644
index 0000000..2ea9811
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/hdaudio_generic.c
@@ -0,0 +1,868 @@
+/*
+ * Purpose: Default mixer/control panel driver for HDA codecs
+ *
+ * This generic driver is used to create mixer/control panels for HDaudio
+ * codec chips that don't have any dedicated driver available.
+ *
+ * This driver will obtain the widget definitions from the codec and then
+ * try to guess a mixer layout that makes some sense. However this approach
+ * works properly only with a small set of codecs.
+ *
+ * Most codecs are unbearably complex and provide loads of redundant
+ * functionality. The generic driver approach will not properly work with them
+ * because the mixer (GUI) layout will become too large to fit on any screen.
+ * In addition such automatically generated mixer controls will not make any
+ * sense to the users. So in the future the only possible approach will be
+ * creating dedicated mixer drivers for all possible codecs in the market.
+ * Unfortunately in some cases the driver may even need to be motherboard
+ * specific. Apparently this is going to be enormous task.
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#include "oss_hdaudio_cfg.h"
+#include "hdaudio.h"
+#include "hdaudio_codec.h"
+
+extern int hdaudio_snoopy;
+extern int hdaudio_jacksense;
+extern int hdaudio_noskip;
+
+static int
+count_linked_controls (hdaudio_mixer_t * mixer, codec_t * codec,
+ widget_t * widget, int recursive)
+{
+/*
+ * This function counts the number of mixer control elements this
+ * widget has.
+ * If recursive==1 then control counts of the previous widgets in the
+ * processing chain will be counted if number of inputs is exactly 1.
+ * Input sources are not checked if number of connections is larger than 1
+ * because separate mixer group is required for such widgets.
+ *
+ * Note! The policies used by this function must match exactly the policies
+ * used by follow_widget_chain()
+ */
+ int count = 0;
+
+ if (widget->skip)
+ return 0;
+
+ /*
+ * Output amp?
+ */
+ if (widget->widget_caps & WCAP_OUTPUT_AMP_PRESENT)
+ count += 1;
+
+ /*
+ * Input amp(s)?
+ */
+ if (widget->widget_caps & WCAP_INPUT_AMP_PRESENT)
+ {
+ if (widget->wid_type == NT_MIXER)
+ count += widget->nconn;
+ else
+ count++;
+ }
+
+ /*
+ * Input selector?
+ */
+ if (widget->wid_type == NT_SELECT && widget->nconn > 1)
+ count += 1;
+
+ if (recursive)
+ if (widget->nconn == 1) /* Exactly one input wource */
+ count +=
+ count_linked_controls (mixer, codec,
+ &codec->widgets[widget->connections[0]],
+ recursive);
+
+ return count;
+}
+
+/*ARGSUSED*/
+static int
+attach_amplifiers (int dev, hdaudio_mixer_t * mixer, codec_t * codec,
+ widget_t * widget, int group, int group_mode)
+{
+ int i, cnum, ninputs;
+ int g = group;
+ int use_mutegroup = 0;
+ oss_mixext *ent;
+
+/*
+ * Control for input amplifier(s)
+ */
+ if (widget->widget_caps & WCAP_INPUT_AMP_PRESENT)
+ {
+ if (widget->wid_type == NT_MIXER)
+ ninputs = widget->nconn;
+ else
+ ninputs = 1;
+
+ /*
+ * Check if it's possible to save horizontal space by creating a separate
+ * mute group. In this way the names of mute selectors become shorter.
+ */
+ if (!(widget->inamp_caps & ~AMPCAP_MUTE)
+ && (widget->inamp_caps & AMPCAP_MUTE) && ninputs > 2)
+ {
+ use_mutegroup = 1;
+ if ((g =
+ mixer_ext_create_group (mixer->mixer_dev, group, "mute")) < 0)
+ return g;
+ }
+
+ for (i = 0; i < ninputs; i++) /* All inputs */
+ {
+ char tmpname[32], tmp[40];
+ char *name = codec->widgets[widget->connections[i]].name;
+
+ if (ninputs == 1) /* Hide name */
+ name = "-";
+
+ if (codec->widgets[widget->connections[i]].skip)
+ continue;
+
+ if (widget->inamp_caps & ~AMPCAP_MUTE) /* Supports gain control */
+ {
+ int typ, num, maxval, val, range, step;
+ range =
+ ((widget->
+ outamp_caps >> AMPCAP_NUMSTEPS_SHIFT) &
+ AMPCAP_NUMSTEPS_MASK) + 1;
+ step =
+ ((widget->
+ outamp_caps >> AMPCAP_STEPSIZE_SHIFT) &
+ AMPCAP_STEPSIZE_MASK) + 1;
+
+ if (step > 20 /* 5dB */ && range < 5)
+ {
+ create_ingain_selector (mixer, codec, widget, group, i,
+ name);
+ continue;
+ }
+ maxval = hdaudio_amp_maxval (widget->inamp_caps);
+
+ if (widget->widget_caps & WCAP_STEREO)
+ {
+ typ = MIXT_STEREOSLIDER16;
+ num = MIXNUM (widget, CT_INSTEREO, i);
+ }
+ else
+ {
+ typ = MIXT_MONOSLIDER16;
+ num = MIXNUM (widget, CT_INMONO, i);
+ }
+
+ if (hdaudio_snoopy > 0)
+ {
+ sprintf (tmp, "%s:R%x", name, widget->wid);
+ name = tmp;
+ }
+
+ if ((cnum = mixer_ext_create_control (mixer->mixer_dev,
+ group,
+ num,
+ hdaudio_set_control,
+ typ,
+ name, maxval,
+ MIXF_READABLE |
+ MIXF_WRITEABLE |
+ MIXF_CENTIBEL)) < 0)
+ return cnum;
+
+ /* Copy RGB color */
+ if (widget->rgbcolor != 0)
+ if ((ent = mixer_find_ext (dev, cnum)) != NULL)
+ ent->rgbcolor = widget->rgbcolor;
+
+ /* Setup initial volume */
+ val = (maxval * 8) / 10; /* 80% of the maximum */
+ val = val | (val << 16);
+
+ hdaudio_set_control (mixer->mixer_dev, num, SNDCTL_MIX_WRITE,
+ val);
+ continue; /* Skip to the next input */
+ }
+
+ if (widget->inamp_caps & AMPCAP_MUTE) /* Supports only mute */
+ {
+ if (use_mutegroup)
+ strcpy (tmpname, name);
+ else
+ sprintf (tmpname, "%s-mute", name);
+ name = tmpname;
+
+ if (hdaudio_snoopy > 0)
+ {
+ sprintf (tmp, "%s:Q%x", name, widget->wid);
+ name = tmp;
+ }
+
+ if ((cnum = mixer_ext_create_control (mixer->mixer_dev,
+ g,
+ MIXNUM (widget,
+ CT_INMUTE, i),
+ hdaudio_set_control,
+ MIXT_MUTE, name, 2,
+ MIXF_READABLE |
+ MIXF_WRITEABLE)) < 0)
+ return cnum;
+ /* Copy RGB color */
+ if (widget->rgbcolor != 0)
+ if ((ent = mixer_find_ext (dev, cnum)) != NULL)
+ ent->rgbcolor = widget->rgbcolor;
+
+ hdaudio_set_control (mixer->mixer_dev,
+ MIXNUM (widget, CT_INMUTE, i),
+ SNDCTL_MIX_WRITE, 0);
+ }
+
+ }
+ }
+
+/*
+ * Output amplifier control
+ */
+
+ if (widget->widget_caps & WCAP_OUTPUT_AMP_PRESENT)
+ {
+ char tmp[32];
+ char *name = "-";
+
+ if (hdaudio_snoopy)
+ name = "outamp";
+
+ if (widget->outamp_caps & ~AMPCAP_MUTE) /* Has gain control */
+ {
+ int range, step, typ, num, maxval, val;
+ range =
+ ((widget->
+ outamp_caps >> AMPCAP_NUMSTEPS_SHIFT) & AMPCAP_NUMSTEPS_MASK) +
+ 1;
+ step =
+ ((widget->
+ outamp_caps >> AMPCAP_STEPSIZE_SHIFT) & AMPCAP_STEPSIZE_MASK) +
+ 1;
+
+ if (step > 20 /* 5dB */ && range < 5)
+ {
+ create_outgain_selector (mixer, widget, group, name);
+ }
+ else
+ {
+
+ maxval = hdaudio_amp_maxval (widget->outamp_caps);
+
+ if (widget->widget_caps & WCAP_STEREO)
+ {
+ typ = MIXT_STEREOSLIDER16;
+ num = MIXNUM (widget, CT_OUTSTEREO, 0);
+ }
+ else
+ {
+ typ = MIXT_MONOSLIDER16;
+ num = MIXNUM (widget, CT_OUTMONO, 0);
+ }
+
+ if (hdaudio_snoopy > 0)
+ {
+ sprintf (tmp, "%s:V%x", name, widget->wid);
+ name = tmp;
+ }
+ else
+ {
+ sprintf (tmp, "%s", widget->name);
+ name = tmp;
+ }
+
+ if ((cnum = mixer_ext_create_control (mixer->mixer_dev,
+ group,
+ num, hdaudio_set_control,
+ typ,
+ name, maxval,
+ MIXF_READABLE |
+ MIXF_WRITEABLE |
+ MIXF_CENTIBEL)) < 0)
+ return cnum;
+
+ /* Copy RGB color */
+ if (widget->rgbcolor != 0)
+ if ((ent = mixer_find_ext (dev, cnum)) != NULL)
+ ent->rgbcolor = widget->rgbcolor;
+
+ /* setup volume */
+ val = (maxval * 8) / 10; /* 80% of the maximum */
+ val = val | (val << 16);
+ hdaudio_set_control (mixer->mixer_dev, num, SNDCTL_MIX_WRITE,
+ val);
+ }
+ }
+ else if (widget->outamp_caps & AMPCAP_MUTE) /* Only mute control */
+ {
+ char tmpname[32];
+ name = "mute";
+ if (hdaudio_snoopy > 0)
+ {
+ sprintf (tmpname, "%s:U%x", name, widget->wid);
+ name = tmpname;
+ }
+
+ if ((cnum = mixer_ext_create_control (mixer->mixer_dev,
+ group,
+ MIXNUM (widget,
+ CT_OUTMUTE, 0),
+ hdaudio_set_control,
+ MIXT_MUTE, name, 2,
+ MIXF_READABLE |
+ MIXF_WRITEABLE)) < 0)
+ return cnum;
+
+ /* Copy RGB color */
+ if (widget->rgbcolor != 0)
+ if ((ent = mixer_find_ext (dev, cnum)) != NULL)
+ ent->rgbcolor = widget->rgbcolor;
+
+ hdaudio_set_control (mixer->mixer_dev,
+ MIXNUM (widget, CT_OUTMUTE, 0),
+ SNDCTL_MIX_WRITE, 0);
+ }
+ }
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+attach_selector (int dev, hdaudio_mixer_t * mixer, codec_t * codec,
+ widget_t * widget, int group, int group_mode)
+{
+ unsigned int c, b;
+ char *name = "src";
+
+ int i, ctl;
+ char tmp[256], *t = tmp;
+ oss_mixext *ext;
+ int count = 0;
+
+
+ /*
+ * first check to see if there are more that 2 valid options to create a
+ * selector for.
+ */
+ for (i = 0; i < widget->nconn; i++)
+ if (!codec->widgets[widget->connections[i]].skip
+ && codec->widgets[widget->connections[i]].sensed_pin != PIN_OUT)
+ count++;
+
+ if (count < 2)
+ return 0;
+
+
+ name = widget->name;
+
+ if (corb_read (mixer, widget->cad, widget->wid, 0, GET_SELECTOR, 0, &c, &b))
+ widget->current_selector = c;
+
+ if (hdaudio_snoopy > 0)
+ {
+ sprintf (tmp, "%s:%x", name, widget->wid);
+ name = tmp;
+ }
+
+
+ if ((ctl = mixer_ext_create_control (mixer->mixer_dev,
+ group,
+ MIXNUM (widget, CT_SELECT, 0),
+ hdaudio_set_control,
+ MIXT_ENUM,
+ name,
+ widget->nconn,
+ MIXF_READABLE | MIXF_WRITEABLE)) < 0)
+ return ctl;
+
+ *tmp = 0;
+ ext = mixer_find_ext (mixer->mixer_dev, ctl);
+
+ if (ext == NULL)
+ {
+ cmn_err (CE_WARN, "Cannot locate the mixer extension (a)\n");
+ return OSS_EIO;
+ }
+
+ /* Copy RGB color */
+ ext->rgbcolor = widget->rgbcolor;
+
+ memset (ext->enum_present, 0, sizeof (ext->enum_present));
+
+ for (i = 0; i < widget->nconn; i++)
+ {
+ char *s;
+
+ /*
+ * ensure that the connection list has a valid widget id - some
+ * devices have bogus connection lists
+ */
+ if (codec->widgets[widget->connections[i]].wid < codec->first_node)
+ continue;
+
+ s = codec->widgets[widget->connections[i]].name;
+ if (strlen (tmp) + strlen (s) + 1 < sizeof (tmp) - 1)
+ {
+ if (*tmp != 0)
+ *t++ = ' ';
+ strcpy (t, s);
+ if (hdaudio_snoopy > 0)
+ sprintf (t, "A%s:%x", s,
+ mixer->codecs[widget->cad]->widgets[widget->
+ connections[i]].wid);
+ t += strlen (t);
+
+ /*
+ * Show only widgets that are not marked to be ignored.
+ * Also hide I/O pins that are known to be outputs.
+ */
+ if (!codec->widgets[widget->connections[i]].skip
+ && codec->widgets[widget->connections[i]].sensed_pin != PIN_OUT)
+ ext->enum_present[i / 8] |= (1 << (i % 8));
+ else
+ {
+ if (widget->current_selector == i)
+ widget->current_selector++;
+ }
+ }
+ }
+ mixer_ext_set_strings (mixer->mixer_dev, ctl, tmp, 0);
+
+ if (widget->current_selector >= widget->nconn)
+ widget->current_selector = 0;
+ corb_write (mixer, widget->cad, widget->wid, 0, SET_SELECTOR,
+ widget->current_selector);
+
+ return 0;
+}
+
+static int
+follow_widget_chain (int dev, hdaudio_mixer_t * mixer, codec_t * codec,
+ widget_t * widget, int group)
+{
+ int err;
+
+ if (widget->used) /* Already handled */
+ return 0;
+
+ widget->used = 1;
+
+ if (widget->nconn >= 1)
+ if ((err =
+ follow_widget_chain (dev, mixer, codec,
+ &codec->widgets[widget->connections[0]],
+ group)) < 0)
+ return err;
+
+ if ((err = attach_amplifiers (dev, mixer, codec, widget, group, 1)) < 0)
+ return err;
+
+ if (widget->wid_type == NT_SELECT)
+ if ((err = attach_selector (dev, mixer, codec, widget, group, 1)) < 0)
+ return err;
+
+ return 0;
+}
+
+static int
+attach_pin_widget (int dev, hdaudio_mixer_t * mixer, codec_t * codec,
+ widget_t * widget, int parent_group)
+{
+ int group = parent_group, g;
+ unsigned int b, c, conf;
+ int i, ctl, err;
+ int inselects = 0, outselects = 0, linked_controls = 0;
+ int num_amps = 0;
+ char tmp[256], *t = tmp;
+ oss_mixext *ext;
+
+ if (widget->pincaps & PINCAP_OUTPUT_CAPABLE)
+ {
+ outselects = widget->nconn;
+
+ if (widget->nconn == 1) /* Exactly one connection */
+ {
+ linked_controls =
+ count_linked_controls (mixer, codec,
+ &codec->widgets[widget->connections[0]],
+ 1);
+ }
+ }
+
+ if (widget->pincaps & PINCAP_INPUT_CAPABLE)
+ {
+ if (!(widget->widget_caps & WCAP_DIGITAL)) /* Analog pin */
+ {
+ inselects = 1;
+ }
+ }
+
+ if (widget->widget_caps & WCAP_INPUT_AMP_PRESENT)
+ {
+ num_amps++;
+ }
+
+ if (widget->widget_caps & WCAP_OUTPUT_AMP_PRESENT)
+ {
+ num_amps++;
+ }
+
+ if ((inselects + outselects > 1) || num_amps > 0 || linked_controls > 0) /* Have something to control */
+ {
+ if (widget->color[0] == 0) /* Empty name */
+ sprintf (widget->color, "jack%02x", widget->wid);
+ if ((g =
+ mixer_ext_create_group (mixer->mixer_dev, group,
+ widget->color)) < 0)
+ return g;
+
+ if (corb_read
+ (mixer, widget->cad, widget->wid, 0, GET_SELECTOR, 0, &c, &b))
+ widget->current_selector = c;
+
+ if (inselects + outselects > 1)
+ {
+ if ((ctl = mixer_ext_create_control (mixer->mixer_dev,
+ g,
+ MIXNUM (widget, CT_SELECT, 0),
+ hdaudio_set_control,
+ MIXT_ENUM,
+ "mode",
+ inselects + outselects,
+ MIXF_READABLE |
+ MIXF_WRITEABLE)) < 0)
+ return ctl;
+
+ *tmp = 0;
+ ext = mixer_find_ext (mixer->mixer_dev, ctl);
+
+ if (ext == NULL)
+ {
+ cmn_err (CE_WARN, "Cannot locate the mixer extension (b)\n");
+ return OSS_EIO;
+ }
+ /* Copy RGB color */
+ ext->rgbcolor = widget->rgbcolor;
+
+ memset (ext->enum_present, 0, sizeof (ext->enum_present));
+
+ for (i = 0; i < widget->nconn; i++)
+ {
+ char *s;
+
+ s = codec->widgets[widget->connections[i]].name;
+ if (strlen (tmp) + strlen (s) + 1 < sizeof (tmp) - 1)
+ {
+ if (*tmp != 0)
+ *t++ = ' ';
+ strcpy (t, s);
+ if (hdaudio_snoopy > 0)
+ sprintf (t, "A%s:%x", s,
+ mixer->codecs[widget->cad]->widgets[widget->
+ connections
+ [i]].wid);
+ t += strlen (t);
+
+ /*
+ * Show only widgets that are not marked to be ignored.
+ */
+ if (!codec->widgets[widget->connections[i]].skip)
+ ext->enum_present[i / 8] |= (1 << (i % 8));
+ else
+ {
+ if (widget->current_selector == i)
+ widget->current_selector++;
+ }
+ }
+ }
+
+/*
+ * Use the default sequence as an index to the output source selectors.
+ */
+ if (widget->sensed_pin == PIN_OUT)
+ if (corb_read
+ (mixer, widget->cad, widget->wid, 0, GET_CONFIG_DEFAULT, 0,
+ &conf, &b))
+ {
+ int association, sequence;
+
+ association = (conf >> 4) & 0x0f;
+ sequence = conf & 0x0f;
+
+ if (association != 0)
+ {
+ widget->current_selector = sequence;
+ }
+
+ }
+
+ if (widget->current_selector >= widget->nconn)
+ widget->current_selector = 0;
+
+ if (inselects > 0) /* Input capable */
+ {
+ char *s;
+
+ i = widget->nconn;
+ s = widget->name;
+
+ if (*tmp != 0)
+ *t++ = ' ';
+
+ strcpy (t, "input");
+
+ t += strlen (t);
+ ext->enum_present[i / 8] |= (1 << (i % 8));
+ i++;
+
+ if (widget->pin_type == PIN_IN)
+ widget->current_selector = widget->nconn;
+ }
+
+ mixer_ext_set_strings (mixer->mixer_dev, ctl, tmp, 0);
+ }
+
+ hdaudio_set_control (mixer->mixer_dev,
+ MIXNUM (widget, CT_SELECT, 0),
+ SNDCTL_MIX_WRITE, widget->current_selector);
+
+ if ((err = attach_amplifiers (dev, mixer, codec, widget, g, 0)) < 0)
+ return err;
+
+ if (widget->nconn == 1)
+ if ((err =
+ follow_widget_chain (dev, mixer, codec,
+ &codec->widgets[widget->connections[0]],
+ g)) < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int
+attach_record_widget (int dev, hdaudio_mixer_t * mixer, codec_t * codec,
+ widget_t * widget, int parent_group)
+{
+ int group = parent_group, g;
+ int err;
+ int linked_controls = 0;
+ int num_amps = 0;
+
+ if (widget->widget_caps & WCAP_INPUT_AMP_PRESENT)
+ {
+ num_amps++;
+ }
+
+ if (widget->widget_caps & WCAP_OUTPUT_AMP_PRESENT)
+ {
+ num_amps++;
+ }
+
+ if (widget->nconn == 1) /* Exactly one connection */
+ {
+ linked_controls =
+ count_linked_controls (mixer, codec,
+ &codec->widgets[widget->connections[0]], 1);
+ }
+
+ if (num_amps > 0 || linked_controls > 1) /* Have something to control */
+ {
+ if ((g =
+ mixer_ext_create_group (mixer->mixer_dev, group,
+ widget->name)) < 0)
+ return g;
+
+ if (widget->nconn == 1)
+ if ((err =
+ follow_widget_chain (dev, mixer, codec,
+ &codec->widgets[widget->connections[0]],
+ g)) < 0)
+ return err;
+
+ if ((err = attach_amplifiers (dev, mixer, codec, widget, g, 0)) < 0)
+ return err;
+ if ((err = attach_selector (dev, mixer, codec, widget, g, 0)) < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int
+attach_misc_widget (int dev, hdaudio_mixer_t * mixer, codec_t * codec,
+ widget_t * widget, int parent_group)
+{
+ int err;
+ int nselect = 0;
+ int num_amps = 0;
+
+ if (widget->widget_caps & WCAP_INPUT_AMP_PRESENT)
+ {
+ num_amps++;
+ }
+
+ if (widget->widget_caps & WCAP_OUTPUT_AMP_PRESENT)
+ {
+ num_amps++;
+ }
+
+ if ((widget->wid_type == NT_SELECT || widget->wid_type == NT_MIXER)
+ && widget->nconn > 0)
+ nselect = widget->nconn;
+
+ if (num_amps > 0 || nselect > 1) /* Have something to control */
+ {
+#if 0
+ if ((g =
+ mixer_ext_create_group (mixer->mixer_dev, group,
+ widget->name)) < 0)
+ return g;
+#endif
+ if ((err =
+ attach_amplifiers (dev, mixer, codec, widget, parent_group,
+ 0)) < 0)
+ return err;
+
+ if (nselect > 1)
+ if ((err =
+ attach_selector (dev, mixer, codec, widget, parent_group,
+ 0)) < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+int
+hdaudio_generic_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad,
+ int parent_group)
+{
+ unsigned int vendorid, b;
+ int err;
+ int wid, n;
+ codec_t *codec;
+ widget_t *widget;
+ int group = parent_group;
+
+ if (mixer->codecs[cad] == NULL)
+ {
+ cmn_err (CE_WARN, "Bad codec %d\n", cad);
+ return OSS_EIO;
+ }
+ codec = mixer->codecs[cad];
+
+ if (!corb_read (mixer, cad, 0, 0, GET_PARAMETER, HDA_VENDOR, &vendorid, &b))
+ {
+ cmn_err (CE_WARN, "Cannot get codec ID\n");
+ return OSS_EIO;
+ }
+
+/*
+ * First handle all the PIN widgets
+ */
+ n = 0;
+
+ for (wid = 0; wid < codec->nwidgets; wid++)
+ {
+ widget = &codec->widgets[wid];
+
+ if (widget->wid_type != NT_PIN) /* Not a pin widget */
+ continue;
+
+ widget->used = 1;
+
+ if (widget->skip) /* Unused/unconnected PIN widget */
+ {
+ continue;
+ }
+
+ if ((n++ % 3) == 0)
+ {
+ if ((group =
+ mixer_ext_create_group (mixer->mixer_dev, parent_group,
+ "jack")) < 0)
+ return group;
+ }
+
+
+ if ((err = attach_pin_widget (dev, mixer, codec, widget, group)) < 0)
+ return err;
+ }
+
+/*
+ * Next handle all the ADC widgets
+ */
+ n = 0;
+ for (wid = 0; wid < codec->nwidgets; wid++)
+ {
+ widget = &codec->widgets[wid];
+
+ if (widget->wid_type != NT_ADC) /* Not a pin widget */
+ continue;
+
+ if (widget->skip)
+ continue;
+
+ widget->used = 1;
+
+ if ((n++ % 3) == 0)
+ {
+ if ((group =
+ mixer_ext_create_group (mixer->mixer_dev, parent_group,
+ "record")) < 0)
+ return group;
+ }
+
+ if ((err = attach_record_widget (dev, mixer, codec, widget, group)) < 0)
+ return err;
+ }
+
+/*
+ * Finally handle all the widgets that have not been attached yet
+ */
+
+ n = 0;
+ for (wid = 0; wid < codec->nwidgets; wid++)
+ {
+ widget = &codec->widgets[wid];
+
+ if (widget->skip)
+ continue;
+
+ if (widget->used) /* Already handled */
+ continue;
+
+ widget->used = 1;
+
+ if (count_linked_controls (mixer, codec, widget, 0) > 0)
+ if ((n++ % 4) == 0)
+ {
+ if ((group =
+ mixer_ext_create_group (mixer->mixer_dev, parent_group,
+ "misc")) < 0)
+ return group;
+ }
+
+ if ((err = attach_misc_widget (dev, mixer, codec, widget, group)) < 0)
+ return err;
+ }
+ return 0;
+}
diff --git a/kernel/drv/oss_hdaudio/hdaudio_gpio_handlers.c b/kernel/drv/oss_hdaudio/hdaudio_gpio_handlers.c
new file mode 100644
index 0000000..357b689
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/hdaudio_gpio_handlers.c
@@ -0,0 +1,128 @@
+/*
+ * Purpose: GPIO initialization handlers for some High Definition Audio systems
+ *
+ * This file contains codec initialization functions for some HDaudio
+ * systems that require initialization of GPIO bits. All functions should
+ * return OSS_EAGAIN so that hdaudio_codec.c knows to call the generic
+ * codec/mixer initialization routine for the codec. Alternatively the
+ * GPIO init function may call the codec/mixer init function for the
+ * given system directly (return my_mixer_init_func()).
+ *
+ * Note that if the system has a dedicated mixer initialization function
+ * then also GPIO initialization needs to be performed in the mixer init
+ * function (since the same mixer_init function pointers in hdaudio_codecds.h
+ * are shared for both purposes).
+ *
+ * For example:
+ *
+ * int
+ * hdaudio_GPIO_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group)
+ * {
+ * codec_t *codec = mixer->codecs[cad];
+ * int afg = codec->afg; // Audio function group root widget
+ *
+ * // Now use the corb_read() and corb_write() functions to set the
+ * // GPIO related verbs (registers) to the required values.
+ *
+ * return OSS_EAGAIN; // Fallback
+ * }
+ *
+ * To write the GPIO registers you can use:
+ *
+ * corb_write (mixer, cad, afg, 0, SET_GPIO_DIR, 0xNNNNNNNN);
+ * corb_write (mixer, cad, afg, 0, SET_GPIO_ENABLE, 0xNNNNNNNN);
+ * corb_write (mixer, cad, afg, 0, SET_GPIO_DATA, 0xNNNNNNNN);
+ *
+ * Also (if necessary) you can use the following calls. However they will probably
+ * need changes to hdaudio_codec.c so that the unsolicited responses are handled peoperly:
+ *
+ * corb_write (mixer, cad, afg, 0, SET_GPIO_WKEN, 0xNNNNNNNN);
+ * corb_write (mixer, cad, afg, 0, SET_GPIO_UNSOL, 0xNNNNNNNN);
+ * corb_write (mixer, cad, afg, 0, SET_GPIO_STICKY, 0xNNNNNNNN);
+ *
+ * Next the function prototype should be added to hdaudio_codecids.h. Finally
+ * edit the subdevices[] array in hdaudio_codecids.h so that the function
+ * gets called when given codec (subsystem vendor+device) is detected in the
+ * system. It is not recommended to use the codecs[] table to
+ * detect systems that need GPIO handling. The same codec may be used
+ * in many different systems and most of them don't require GPIO init.
+ * However this is possible if the handler uses the subvendor+subdevice ID to detect the system.
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#include "oss_hdaudio_cfg.h"
+#include "hdaudio.h"
+#include "hdaudio_codec.h"
+#include "hdaudio_codecids.h"
+
+int
+hdaudio_mac_GPIO_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group)
+{
+ codec_t *codec = mixer->codecs[cad];
+ int afg = codec->afg; // Audio function group root widget
+ unsigned int subdevice = codec->subvendor_id;
+ unsigned int codec_id = codec->vendor_id;
+
+ // TODO: Populate this function with the real stuff
+
+cmn_err(CE_CONT, "hdaudio_mac_GPIO_init() entered, afg=%d, subdevice=0x%08x, codec=0x%08x\n", afg, subdevice, codec_id);
+
+ return OSS_EAGAIN; /* Continue with the default mixer init */
+}
+
+int
+hdaudio_mac_sigmatel_GPIO_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group)
+{
+cmn_err(CE_CONT, "iMac Sigmatel hdaudio initialization\n");
+ return hdaudio_mac_GPIO_init(dev, mixer, cad, top_group);
+}
+
+int
+hdaudio_mac_realtek_GPIO_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group)
+{
+ codec_t *codec = mixer->codecs[cad];
+ int afg = codec->afg; // Audio function group root widget
+
+cmn_err(CE_CONT, "iMac Realtek hdaudio initialization\n");
+
+ corb_write (mixer, cad, afg, 0, SET_GPIO_DIR, 0xffffffff);
+ corb_write (mixer, cad, afg, 0, SET_GPIO_ENABLE, 0xffffffff);
+ corb_write (mixer, cad, afg, 0, SET_GPIO_DATA, 0xffffffff);
+ return hdaudio_mac_GPIO_init(dev, mixer, cad, top_group);
+}
+
+int
+hdaudio_asus_a7k_GPIO_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group)
+{
+ DDB(cmn_err(CE_CONT, "hdaudio_asus_a7k_GPIO_init got called.\n"));
+
+ corb_write (mixer, cad, 0x01, 0, SET_GPIO_ENABLE, 3);
+ corb_write (mixer, cad, 0x01, 0, SET_GPIO_DIR, 1);
+ corb_write (mixer, cad, 0x01, 0, SET_GPIO_DATA, 1);
+
+ return hdaudio_generic_mixer_init(dev, mixer, cad, top_group);
+}
+
+int
+hdaudio_GPIO_init_1 (int dev, hdaudio_mixer_t * mixer, int cad, int top_group)
+{
+ DDB(cmn_err(CE_CONT, "hdaudio_GPIO_init_1 got called.\n"));
+
+ /* Acer TravelMate 4060 and similar Aspire series, with ALC260 codec, need
+ * that we init GPIO to get internal speaker and headphone jack working. */
+ corb_write(mixer, cad, 0x01, 0, SET_GPIO_ENABLE, 1);
+ corb_write(mixer, cad, 0x01, 0, SET_GPIO_DIR, 1);
+ corb_write(mixer, cad, 0x01, 0, SET_GPIO_DATA, 1);
+
+ return hdaudio_generic_mixer_init(dev, mixer, cad, top_group);
+}
diff --git a/kernel/drv/oss_hdaudio/hdaudio_mixers.h b/kernel/drv/oss_hdaudio/hdaudio_mixers.h
new file mode 100644
index 0000000..eb92910
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/hdaudio_mixers.h
@@ -0,0 +1,20 @@
+/*
+ * Purpose: Declarations of some functions and structures for HD audio mixers
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+/*
+ * Prototype definitions for dedicated HDAudio codec/mixer drivers.
+ */
+
+extern int hdaudio_generic_mixer_init (int dev, hdaudio_mixer_t * mixer,
+ int cad, int group);
diff --git a/kernel/drv/oss_hdaudio/hdaudio_scaleoP.c b/kernel/drv/oss_hdaudio/hdaudio_scaleoP.c
new file mode 100644
index 0000000..b4bab5f
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/hdaudio_scaleoP.c
@@ -0,0 +1,277 @@
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+/* Codec index is 0 */
+/* Codec vendor 0804:7d10 */
+/* HD codec revision 1.0 (0.1) (0x00100001) */
+/* Subsystem ID 1043e601 */
+/* Default amplifier caps: in=00000000, out=00000000 */
+#include "oss_hdaudio_cfg.h"
+#include "hdaudio.h"
+#include "hdaudio_codec.h"
+#include "hdaudio_dedicated.h"
+
+int
+hdaudio_scaleoP_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad,
+ int top_group)
+{
+ int ctl = 0;
+
+ DDB (cmn_err (CE_CONT, "hdaudio_scaleoP_mixer_init got called.\n"));
+
+ HDA_OUTAMP_F (0x0c, top_group, "front", 90, MIXF_PCMVOL);
+ HDA_OUTAMP_F (0x0d, top_group, "rear", 90, MIXF_PCMVOL);
+ HDA_OUTAMP_F (0x0e, top_group, "center/LFE", 90, MIXF_PCMVOL);
+ HDA_OUTAMP_F (0x0f, top_group, "side", 90, MIXF_PCMVOL);
+ HDA_OUTAMP_F (0x26, top_group, "pcm4", 90, MIXF_PCMVOL);
+
+ /*
+ * Unmute all inputs for the above sliders.
+ */
+ HDA_SET_INMUTE (0x0c, 0, UNMUTE); /* From widget 0x02 */
+ HDA_SET_INMUTE (0x0c, 1, UNMUTE); /* From widget 0x0b */
+ HDA_SET_INMUTE (0x0d, 0, UNMUTE); /* From widget 0x03 */
+ HDA_SET_INMUTE (0x0d, 1, UNMUTE); /* From widget 0x0b */
+ HDA_SET_INMUTE (0x0e, 0, UNMUTE); /* From widget 0x04 */
+ HDA_SET_INMUTE (0x0e, 1, UNMUTE); /* From widget 0x0b */
+ HDA_SET_INMUTE (0x0f, 0, UNMUTE); /* From widget 0x05 */
+ HDA_SET_INMUTE (0x0f, 1, UNMUTE); /* From widget 0x0b */
+ HDA_SET_INMUTE (0x26, 0, UNMUTE); /* From widget 0x25 */
+ HDA_SET_INMUTE (0x26, 1, UNMUTE); /* From widget 0x0b */
+
+ /* Handle ADC widgets */
+ {
+ int n, group, rec_group;
+
+ n = 0;
+
+ HDA_GROUP (rec_group, top_group, "record");
+
+ if (HDA_ADC_GROUP (0x08, group, rec_group, "rec1", n, "record", 2)) /* ADC widget 0x08 */
+ {
+ /* Src 0x23=mix */
+ HDA_INAMP (0x08, 0, group, "-", 90); /* From widget 0x23 */
+
+ /* Widget 0x23 (mix) */
+ /* Src 0x18=pink */
+ /* Src 0x19=fp-pink */
+ /* Src 0x1a=blue */
+ /* Src 0x1b=fp-green */
+ /* Src 0x1c=int-aux */
+ /* Src 0x1d=black */
+ /* Src 0x14=green */
+ /* Src 0x15=black */
+ /* Src 0x16=orange */
+ /* Src 0x17=grey */
+ /* Src 0x0b=input-mix */
+ if (HDA_INSRC_SELECT (0x23, group, ctl, "recsrc", 10))
+ HDA_CHOICES (ctl,
+ "pink fp-pink blue fp-green int-aux black green black orange grey input-mix");
+ }
+
+ if (HDA_ADC_GROUP (0x09, group, rec_group, "rec2", n, "record", 2)) /* ADC widget 0x09 */
+ {
+ /* Src 0x22=mix */
+ HDA_INAMP (0x09, 0, group, "-", 90); /* From widget 0x22 */
+
+ /* Widget 0x22 (mix) */
+ /* Src 0x18=pink */
+ /* Src 0x19=fp-pink */
+ /* Src 0x1a=blue */
+ /* Src 0x1b=fp-green */
+ /* Src 0x1c=int-aux */
+ /* Src 0x1d=black */
+ /* Src 0x14=green */
+ /* Src 0x15=black */
+ /* Src 0x16=orange */
+ /* Src 0x17=grey */
+ /* Src 0xb=input-mix */
+ if (HDA_INSRC_SELECT (0x23, group, ctl, "recsrc", 10))
+ HDA_CHOICES (ctl,
+ "pink fp-pink blue fp-green int-aux black green black orange grey input-mix");
+ }
+
+#if 0
+ if (HDA_ADC_GROUP (0x0a, group, rec_group, "spdif-in", n, "record", 4)) /* ADC widget 0x0a */
+ {
+ /* Src 0x1f=speaker */
+ }
+#endif
+ }
+ /* Handle misc widgets */
+ {
+ int n, group;
+
+ n = 0;
+
+ HDA_GROUP (group, top_group, "input-mix");
+
+ /* Src 0x18=mic */
+ /* Src 0x19=fp-mic */
+ /* Src 0x1a=linein */
+ /* Src 0x1b=fp-headphone */
+ /* Src 0x1c=int-aux */
+ /* Src 0x1d=speaker */
+ /* Src 0x14=lineout */
+ /* Src 0x15=lineout */
+ /* Src 0x16=lineout */
+ /* Src 0x17=lineout */
+ HDA_INAMP (0x0b, 0, group, "pink", 30); /* From widget 0x18 */
+ HDA_INAMP (0x0b, 2, group, "blue", 90); /* From widget 0x1a */
+ HDA_INAMP (0x0b, 4, group, "int-aux", 90); /* From widget 0x1c */
+ HDA_INAMP (0x0b, 5, group, "black", 90); /* From widget 0x1d */
+ HDA_INAMP (0x0b, 6, group, "green", 90); /* From widget 0x14 */
+ HDA_INAMP (0x0b, 7, group, "black", 90); /* From widget 0x15 */
+ HDA_INAMP (0x0b, 8, group, "orange", 90); /* From widget 0x16 */
+ HDA_INAMP (0x0b, 9, group, "grey", 90); /* From widget 0x17 */
+ HDA_INAMP (0x0b, 3, group, "fp-green", 90); /* From widget 0x1b */
+ HDA_INAMP (0x0b, 1, group, "fp-pink", 30); /* From widget 0x19 */
+ }
+ /* Handle PIN widgets */
+ {
+ int n, group, pin_group;
+
+ n = 0;
+
+ HDA_GROUP (pin_group, top_group, "jack");
+
+ if (HDA_PIN_GROUP (0x14, group, pin_group, "green", n, "jack", 4)) /* Pin widget 0x14 */
+ {
+ /* Src 0xc=front */
+ /* Src 0xd=rear */
+ /* Src 0xe=center/LFE */
+ /* Src 0xf=side */
+ /* Src 0x26=pcm4 */
+ if (HDA_PINSELECT (0x14, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl,
+ "front-out rear-out center/LFE-out side-out pcm4-out input");
+ HDA_OUTMUTE (0x14, group, "inmute", UNMUTE);
+ HDA_INAMP (0x14, 0, group, "in", 90); /* From widget 0x0c */
+ }
+
+ if (HDA_PIN_GROUP (0x15, group, pin_group, "black", n, "jack", 4)) /* Pin widget 0x15 */
+ {
+ /* Src 0xc=front */
+ /* Src 0xd=rear */
+ /* Src 0xe=center/LFE */
+ /* Src 0xf=side */
+ /* Src 0x26=pcm4 */
+ if (HDA_PINSELECT (0x15, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl,
+ "front-out rear-out center/LFE-out side-out pcm4-out input");
+ HDA_OUTMUTE (0x15, group, "inmute", UNMUTE);
+ HDA_INAMP (0x15, 0, group, "in", 90); /* From widget 0x0c */
+ }
+
+ if (HDA_PIN_GROUP (0x16, group, pin_group, "orange", n, "jack", 4)) /* Pin widget 0x16 */
+ {
+ /* Src 0xc=front */
+ /* Src 0xd=rear */
+ /* Src 0xe=center/LFE */
+ /* Src 0xf=side */
+ /* Src 0x26=pcm4 */
+ if (HDA_PINSELECT (0x16, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl,
+ "front-out rear-out center/LFE-out side-out pcm4-out input");
+ HDA_OUTMUTE (0x16, group, "inmute", UNMUTE);
+ HDA_INAMP (0x16, 0, group, "in", 90); /* From widget 0x0c */
+ }
+
+ if (HDA_PIN_GROUP (0x17, group, pin_group, "grey", n, "jack", 4)) /* Pin widget 0x17 */
+ {
+ /* Src 0xc=front */
+ /* Src 0xd=rear */
+ /* Src 0xe=center/LFE */
+ /* Src 0xf=side */
+ /* Src 0x26=pcm4 */
+ if (HDA_PINSELECT (0x17, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl,
+ "front-out rear-out center/LFE-out side-out pcm4-out input");
+ HDA_OUTMUTE (0x17, group, "inmute", UNMUTE);
+ HDA_INAMP (0x17, 0, group, "in", 90); /* From widget 0x0c */
+ }
+
+ if (HDA_PIN_GROUP (0x18, group, pin_group, "pink", n, "jack", 4)) /* Pin widget 0x18 */
+ {
+ /* Src 0xc=front */
+ /* Src 0xd=rear */
+ /* Src 0xe=center/LFE */
+ /* Src 0xf=side */
+ /* Src 0x26=pcm4 */
+ if (HDA_PINSELECT (0x18, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl,
+ "front-out rear-out center/LFE-out side-out pcm4-out input");
+ HDA_OUTMUTE (0x18, group, "inmute", UNMUTE);
+ HDA_INAMP (0x18, 0, group, "in", 90); /* From widget 0x0c */
+ }
+
+ if (HDA_PIN_GROUP (0x1a, group, pin_group, "blue", n, "jack", 4)) /* Pin widget 0x1a */
+ {
+ /* Src 0xc=front */
+ /* Src 0xd=rear */
+ /* Src 0xe=center/LFE */
+ /* Src 0xf=side */
+ /* Src 0x26=pcm4 */
+ if (HDA_PINSELECT (0x1a, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl,
+ "front-out rear-out center/LFE-out side-out pcm4-out input");
+ HDA_OUTMUTE (0x1a, group, "inmute", UNMUTE);
+ HDA_INAMP (0x1a, 0, group, "in", 90); /* From widget 0x0c */
+ }
+
+ if (HDA_PIN_GROUP (0x1b, group, pin_group, "fp-green", n, "jack", 4)) /* Pin widget 0x1b */
+ {
+ /* Src 0xc=front */
+ /* Src 0xd=rear */
+ /* Src 0xe=center/LFE */
+ /* Src 0xf=side */
+ /* Src 0x26=pcm4 */
+ if (HDA_PINSELECT (0x1b, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl,
+ "front-out rear-out center/LFE-out side-out pcm4-out input");
+ HDA_OUTMUTE (0x1b, group, "inmute", UNMUTE);
+ HDA_INAMP (0x1b, 0, group, "in", 90); /* From widget 0x0c */
+ }
+
+ if (HDA_PIN_GROUP (0x19, group, pin_group, "fp-pink", n, "jack", 4)) /* Pin widget 0x19 */
+ {
+ /* Src 0xc=front */
+ /* Src 0xd=rear */
+ /* Src 0xe=center/LFE */
+ /* Src 0xf=side */
+ /* Src 0x26=pcm4 */
+ if (HDA_PINSELECT (0x19, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl,
+ "front-out rear-out center/LFE-out side-out pcm4-out input");
+ HDA_OUTMUTE (0x19, group, "inmute", UNMUTE);
+ HDA_INAMP (0x19, 0, group, "in", 90); /* From widget 0x0c */
+ }
+
+#if 0
+ /*
+ * Non-used pins
+ */
+ if (HDA_PIN_GROUP (0x1c, group, pin_group, "int-aux", n, "jack", 4)) /* Pin widget 0x1c */
+ {
+ if (HDA_PINSELECT (0x1c, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "input");
+ }
+
+ if (HDA_PIN_GROUP (0x1d, group, pin_group, "black", n, "jack", 4)) /* Pin widget 0x1d */
+ {
+ if (HDA_PINSELECT (0x1d, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "input");
+ }
+#endif
+ }
+
+ return 0;
+}
diff --git a/kernel/drv/oss_hdaudio/hdaudio_si3055.c b/kernel/drv/oss_hdaudio/hdaudio_si3055.c
new file mode 100644
index 0000000..d47fbbd
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/hdaudio_si3055.c
@@ -0,0 +1,206 @@
+/*
+ * Purpose: Driver for Si3055 and compatible modems.
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+
+/*
+ * Documentation Note
+ *
+ * There is no publicly available documentation for Si3055. However,
+ * there is a very similar modem (Si3038) for which a datasheet is
+ * publicly available:
+ *
+ * https://www.silabs.com/Support%20Documents/TechnicalDocs/si3038.pdf
+ *
+ * This driver was written by reading the ALSA code, looking for a
+ * similar modem (Si3038), and figuring out the corresponding Si3055
+ * register IDs for Si3038 registers, again by reading the ALSA code,
+ * and by checking the default after-reset values.
+ *
+ */
+
+#include "oss_hdaudio_cfg.h"
+#include "hdaudio.h"
+#include "hdaudio_codec.h"
+#include "hdaudio_dedicated.h"
+#include "hdaudio_mixers.h"
+
+/*
+ * Si3055 register IDs.
+ */
+#define SI3055_EXT_MODEM_STATUS 2
+#define SI3055_LINE_RATE 3
+#define SI3055_HDA_STREAMS 4
+#define SI3055_GPIO_CONFIG 5
+#define SI3055_GPIO_PIN_STATUS 10
+#define SI3055_LINE_CONFIG 13
+
+
+/* Corresponding registers in Si3038 (for reference):
+ *
+ * SI3055_EXT_MODEM_STATUS 3Eh (Extended Modem Status & Control)
+ * SI3055_LINE_RATE 40h (Line 1 DAC/ADC Rate)
+ * SI3055_GPIO_PIN_STATUS 54h (GPIO Pin Status)
+ * SI3055_LINE_CONFIG 56h (Line Side Configuration 1)
+ */
+
+/*
+ * The SI3055_HDA_STREAMS register has no corresponding in Si3038.
+ * It contains the playback and recording stream descriptors in the
+ * following format:
+ *
+ * ((playback_stream_num << 4) << 8) | (recording_stream_num << 4)
+ */
+
+/* Si3055 verbs. */
+#define SI3055_REG_GET_VERB 0x900
+#define SI3055_REG_SET_VERB 0x100
+
+/* Convenience macros for reading from and writing to Si3055 registers. */
+#define SI3055_REG_GET(mixer, cad, reg, a, b) corb_read(mixer, cad, reg, 0, SI3055_REG_GET_VERB, 0, a, b)
+#define SI3055_REG_SET(mixer, cad, reg, val) corb_write(mixer, cad, reg, 0, SI3055_REG_SET_VERB, val)
+
+
+void hdaudio_si3055_set_rate(hdaudio_mixer_t *mixer, int cad, int rate)
+{
+ SI3055_REG_SET(mixer, cad, SI3055_LINE_RATE, rate);
+}
+
+int hdaudio_si3055_set_offhook(hdaudio_mixer_t *mixer, int cad, int offhook)
+{
+ unsigned int a, b;
+ SI3055_REG_GET(mixer, cad, SI3055_GPIO_PIN_STATUS, &a, &b);
+
+ if (offhook)
+ {
+ a |= 0x1; /* Set Off-Hook bit */
+ }
+ else
+ {
+ a &= ~0x1; /* Unset Off-Hook bit */
+ }
+
+ SI3055_REG_SET(mixer, cad, SI3055_GPIO_PIN_STATUS, a);
+ SI3055_REG_GET(mixer, cad, SI3055_GPIO_PIN_STATUS, &a, &b);
+ return ((a & 0x1) == 0x1);
+}
+
+void hdaudio_si3055_endpoint_init(hdaudio_mixer_t *mixer, int cad)
+{
+ codec_t *codec = mixer->codecs[cad]; /* Modem codec */
+ widget_t *widget; /* MFG widget */
+ hdaudio_endpointinfo_t *endpoint;
+ unsigned int a, b; /* Used for reading data. */
+ int tmout; /* Timeout counter. */
+
+ /* Output and input stream numbers. */
+ int playback_stream_num, recording_stream_num;
+
+
+ DDB(cmn_err(CE_CONT, "hdaudio_si3055_endpoint_init got called.\n"));
+
+ /* Reset the modem codec. */
+ corb_write(mixer, cad, 0x00, 0, SET_CODEC_RESET, 0);
+ corb_write(mixer, cad, codec->afg, 0, SET_CONVERTER, IDDLE_STREAM << 4);
+ corb_write(mixer, cad, codec->afg, 0, SET_CONVERTER_FORMAT, 0);
+
+ /* Set 9600Hz as the initial line sampling rate.
+ * It can be changed later when desired.
+ */
+ SI3055_REG_SET(mixer, cad, SI3055_LINE_RATE, 9600);
+
+ /* Assign the "unused" value to the playback and recording
+ * stream descriptors (ref. HDAudio_03.pdf, page 40).
+ */
+ SI3055_REG_SET(mixer, cad, SI3055_HDA_STREAMS, 0x0000);
+
+ /* Write 0x0000 to the Extended Modem Status & Control register
+ * to power up the modem (ref. si3038.pdf, page 22).
+ */
+ SI3055_REG_SET(mixer, cad, SI3055_EXT_MODEM_STATUS, 0x0000);
+
+ /* Wait for the modem to complete power up. The lower 8 bits
+ * indicate that it is ready (ref. si3038.pdf, page 22).
+ */
+ tmout = 10;
+ do
+ {
+ SI3055_REG_GET(mixer, cad, SI3055_EXT_MODEM_STATUS, &a, &b);
+ DDB(cmn_err(CE_CONT, "si3055: ext modem status: %04x.\n", a));
+ oss_udelay(1000);
+ }
+ while(((a & 0xf) == 0) && --tmout);
+
+ if ((a & 0xf) == 0)
+ {
+ cmn_err(CE_WARN, "si3055: power up timeout (status: %04x).\n", a);
+ }
+
+ /* This register contains 0x1fff after reset. We need to set it
+ * to zero to get the modem working. No corresponding register
+ * could be found in the Si3038 datasheet.
+ */
+ SI3055_REG_SET(mixer, cad, SI3055_GPIO_CONFIG, 0x0000);
+
+ /* Program line interface parameters. The register value after
+ * a reset is 0xF010. Set it to 0x0010 to unmute the analog
+ * receive and transmit paths.
+ */
+ SI3055_REG_SET(mixer, cad, SI3055_LINE_CONFIG, 0x0010);
+
+ /* Setup the widget info. */
+ widget = &codec->widgets[codec->afg];
+ widget->endpoint = &codec->inendpoints[0];
+ widget->sizes = 0x20000; /* 16 bits */
+ strcpy(widget->name, "modem");
+
+ /* Setup the output endpoint. */
+ codec->num_outendpoints = 1;
+ endpoint = &codec->outendpoints[0];
+ endpoint->ix = 0;
+ endpoint->iddle_stream = 0;
+ endpoint->cad = cad;
+ endpoint->base_wid = codec->afg;
+ endpoint->recsrc_wid = endpoint->volume_wid = -1;
+ endpoint->nrates = 3;
+ endpoint->rates[0] = 8000;
+ endpoint->rates[1] = 9600;
+ endpoint->rates[2] = 16000;
+ endpoint->name = widget->name;
+ endpoint->channels = 1;
+ endpoint->is_digital = 0;
+ endpoint->is_modem = 1;
+ endpoint->sizemask = widget->sizes;
+ endpoint->fmt_mask = AFMT_S16_LE;
+ endpoint->afg = codec->afg;
+ endpoint->min_rate = 8000;
+ endpoint->max_rate = 16000;
+
+ /* Setup the input endpoint. */
+ codec->num_inendpoints = 1;
+ memcpy(&codec->inendpoints[0], endpoint, sizeof(*endpoint));
+
+ /* Choose stream numbers for output and input streams. */
+ playback_stream_num = ++mixer->num_outendpoints;
+ endpoint->stream_number = endpoint->default_stream_number = playback_stream_num;
+
+ endpoint = &codec->inendpoints[0];
+ recording_stream_num = ++mixer->num_inendpoints;
+ endpoint->stream_number = endpoint->default_stream_number = recording_stream_num;
+
+ /* Setup the stream numbers. */
+ SI3055_REG_SET(mixer, cad, SI3055_HDA_STREAMS, ((playback_stream_num << 4) << 8) |
+ (recording_stream_num << 4));
+}
+
diff --git a/kernel/drv/oss_hdaudio/hdaudio_thinkpad_r61.c b/kernel/drv/oss_hdaudio/hdaudio_thinkpad_r61.c
new file mode 100755
index 0000000..047c493
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/hdaudio_thinkpad_r61.c
@@ -0,0 +1,301 @@
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+/* Codec index is 0 */
+/* Codec vendor 11d4:1984 */
+/* HD codec revision 1.0 (4.0) (0x00100400) */
+/* Subsystem ID 17aa20bb */
+/* Default amplifier caps: in=80000000, out=00052727 */
+#include "oss_hdaudio_cfg.h"
+#include "hdaudio.h"
+#include "hdaudio_codec.h"
+#include "hdaudio_dedicated.h"
+
+int
+hdaudio_thinkpad_r61_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad, int top_group)
+{
+ int ctl=0;
+
+ DDB(cmn_err(CE_CONT, "hdaudio_thinkpad_r61_mixer_init got called.\n"));
+
+ /* Handle PIN widgets */
+ {
+ int n, group, pin_group;
+
+ n=0;
+
+ HDA_GROUP(pin_group, top_group, "jack");
+
+ if (HDA_PIN_GROUP(0x11, group, pin_group, "green", n, "jack", 4)) /* Pin widget 0x11 */
+ {
+ /* Src 0x7=headphone-mix */
+ if (HDA_PINSELECT(0x11, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "headphone-mix-out input");
+ HDA_OUTMUTE(0x11, group, "inmute", UNMUTE);
+ }
+
+ if (HDA_PIN_GROUP(0x12, group, pin_group, "int-speaker", n, "jack", 4)) /* Pin widget 0x12 */
+ {
+ /* Src 0xa=lineout-mix */
+ if (HDA_PINSELECT(0x12, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "lineout-mix-out input");
+ HDA_OUTMUTE(0x12, group, "inmute", UNMUTE);
+ }
+
+ if (HDA_PIN_GROUP(0x13, group, pin_group, "int-speaker", n, "jack", 4)) /* Pin widget 0x13 */
+ {
+ /* Src 0x1f=mono-mix */
+ if (HDA_PINSELECT(0x13, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "mono-mix-out input");
+ HDA_OUTAMP(0x13, group, "invol", 90);
+ }
+
+ if (HDA_PIN_GROUP(0x14, group, pin_group, "red", n, "jack", 4)) /* Pin widget 0x14 */
+ {
+ if (HDA_PINSELECT(0x14, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "input");
+ HDA_INAMP(0x14, 0, group, "out", 0); /* From widget 0x00 */
+ }
+
+ if (HDA_PIN_GROUP(0x15, group, pin_group, "int-mic", n, "jack", 4)) /* Pin widget 0x15 */
+ {
+ if (HDA_PINSELECT(0x15, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "input");
+ HDA_INAMP(0x15, 0, group, "out", 0); /* From widget 0x00 */
+ }
+
+ if (HDA_PIN_GROUP(0x16, group, pin_group, "int-cd", n, "jack", 4)) /* Pin widget 0x16 */
+ {
+ /* Src 0xb=aux-mix */
+ if (HDA_PINSELECT(0x16, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "aux-mix-out input");
+ HDA_OUTMUTE(0x16, group, "inmute", UNMUTE);
+ }
+
+ if (HDA_PIN_GROUP(0x17, group, pin_group, "int-mic", n, "jack", 4)) /* Pin widget 0x17 */
+ {
+ if (HDA_PINSELECT(0x17, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "input");
+ }
+
+ if (HDA_PIN_GROUP(0x18, group, pin_group, "int-mic", n, "jack", 4)) /* Pin widget 0x18 */
+ {
+ if (HDA_PINSELECT(0x18, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "input");
+ }
+
+ if (HDA_PIN_GROUP(0x1a, group, pin_group, "int-internal", n, "jack", 4)) /* Pin widget 0x1a */
+ {
+ if (HDA_PINSELECT(0x1a, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "input");
+ }
+
+ if (HDA_PIN_GROUP(0x1b, group, pin_group, "black", n, "jack", 4)) /* Pin widget 0x1b */
+ {
+ /* Src 0x2=spdif */
+ if (HDA_PINSELECT(0x1b, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "spdif-out input");
+ HDA_OUTAMP(0x1b, group, "invol", 90);
+
+ /* Widget 0x02 (spdif) */
+ /* Src 0x1= */
+ /* Src 0x8=rec1-sel */
+ /* Src 0x9=rec2-sel */
+ }
+
+ if (HDA_PIN_GROUP(0x1c, group, pin_group, "red", n, "jack", 4)) /* Pin widget 0x1c */
+ {
+ /* Src 0x24=dock-mix */
+ if (HDA_PINSELECT(0x1c, ctl, group, "mode", -1))
+ HDA_CHOICES(ctl, "dock-mix-out input");
+ HDA_OUTMUTE(0x1c, group, "inmute", UNMUTE);
+ }
+ }
+ /* Handle ADC widgets */
+ {
+ int n, group, rec_group;
+
+ n=0;
+
+ HDA_GROUP(rec_group, top_group, "record");
+
+ if (HDA_ADC_GROUP(0x05, group, rec_group, "int-mic", n, "record", 4)) /* ADC widget 0x05 */
+ {
+ /* Src 0x17=int-mic */
+ HDA_INAMP(0x05, 0, group, "int-mic", 90); /* From widget 0x17 */
+ }
+
+ if (HDA_ADC_GROUP(0x06, group, rec_group, "int-mic", n, "record", 4)) /* ADC widget 0x06 */
+ {
+ /* Src 0x18=int-mic */
+ HDA_INAMP(0x06, 0, group, "int-mic", 90); /* From widget 0x18 */
+ }
+
+ if (HDA_ADC_GROUP(0x08, group, rec_group, "rec1-sel", n, "record", 4)) /* ADC widget 0x08 */
+ {
+ /* Src 0xc=rec1-sel */
+ }
+
+ if (HDA_ADC_GROUP(0x09, group, rec_group, "rec2-sel", n, "record", 4)) /* ADC widget 0x09 */
+ {
+ /* Src 0xd=rec2-sel */
+ }
+ }
+ /* Handle misc widgets */
+ {
+ int n, group, misc_group;
+
+ n=0;
+
+ HDA_GROUP(misc_group, top_group, "misc");
+
+ if (HDA_MISC_GROUP(0x03, group, misc_group, "headphone", n, "misc", 8)) /* Misc widget 0x03 */
+ {
+ HDA_OUTAMP(0x03, group, "-", 90);
+ }
+
+ if (HDA_MISC_GROUP(0x04, group, misc_group, "front", n, "misc", 8)) /* Misc widget 0x04 */
+ {
+ HDA_OUTAMP(0x04, group, "-", 90);
+ }
+
+ if (HDA_MISC_GROUP(0x07, group, misc_group, "headphone-mix", n, "misc", 8)) /* Misc widget 0x07 */
+ {
+ /* Src 0x22=headphone-sel */
+ /* Src 0x21=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP(amp_group, group, "mute");
+ HDA_INMUTE(0x07, 0, amp_group, "headphone-sel", UNMUTE); /* From widget 0x22 */
+ HDA_INMUTE(0x07, 1, amp_group, "input-mix", UNMUTE); /* From widget 0x21 */
+ }
+
+ /* Widget 0x22 (headphone-sel) */
+ /* Src 0x3=headphone */
+ /* Src 0x4=front */
+ if (HDA_SELECT(0x22, "src", ctl, group, 1 /* Select front */))
+ {
+ HDA_CHOICES(ctl, "headphone front");
+ }
+
+ /* Widget 0x21 (input-mix) */
+ /* Src 0x20=input-mix */
+ HDA_OUTAMP(0x21, group, "-", 90);
+
+ /* Widget 0x20 (input-mix) */
+ /* Src 0x14=red */
+ /* Src 0x96= */
+ /* Src 0x1a=int-internal */
+ /* Src 0x25=dock-mix */
+ HDA_INAMP(0x20, 0, group, "red", 90); /* From widget 0x14 */
+ HDA_INAMP(0x20, 1, group, "", 90); /* From widget 0x96 */
+ HDA_INAMP(0x20, 2, group, "int-internal", 90); /* From widget 0x1a */
+ HDA_INAMP(0x20, 3, group, "dock-mix", 90); /* From widget 0x25 */
+ }
+
+ if (HDA_MISC_GROUP(0x0a, group, misc_group, "lineout-mix", n, "misc", 8)) /* Misc widget 0x0a */
+ {
+ /* Src 0x4=front */
+ /* Src 0x21=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP(amp_group, group, "mute");
+ HDA_INMUTE(0x0a, 0, amp_group, "front", UNMUTE); /* From widget 0x04 */
+ HDA_INMUTE(0x0a, 1, amp_group, "input-mix", UNMUTE); /* From widget 0x21 */
+ }
+ }
+
+ if (HDA_MISC_GROUP(0x0b, group, misc_group, "aux-mix", n, "misc", 8)) /* Misc widget 0x0b */
+ {
+ /* Src 0xf=aux-sel */
+ /* Src 0x21=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP(amp_group, group, "mute");
+ HDA_INMUTE(0x0b, 0, amp_group, "aux-sel", UNMUTE); /* From widget 0x0f */
+ HDA_INMUTE(0x0b, 1, amp_group, "input-mix", UNMUTE); /* From widget 0x21 */
+ }
+
+ /* Widget 0x0f (aux-sel) */
+ /* Src 0x3=headphone */
+ /* Src 0x4=front */
+ if (HDA_SELECT(0x0f, "src", ctl, group, -1))
+ {
+ HDA_CHOICES(ctl, "headphone front");
+ }
+ }
+
+ if (HDA_MISC_GROUP(0x0c, group, misc_group, "rec1-sel", n, "misc", 8)) /* Misc widget 0x0c */
+ {
+ /* Src 0x14=mic */
+ /* Src 0x96= */
+ /* Src 0x20=input-mix */
+ /* Src 0x25=dock-mix */
+ if (HDA_SELECT(0x0c, "src", ctl, group, -1))
+ {
+ HDA_CHOICES(ctl, "mic unknown input-mix dock-mix");
+ }
+ HDA_OUTAMP(0x0c, group, "-", 90);
+
+ /* Widget 0x25 (dock-mix) */
+ /* Src 0x1c=red */
+ HDA_OUTAMP(0x25, group, "-", 90);
+ }
+
+ if (HDA_MISC_GROUP(0x0d, group, misc_group, "rec2-sel", n, "misc", 8)) /* Misc widget 0x0d */
+ {
+ /* Src 0x14=mic */
+ /* Src 0x96= */
+ /* Src 0x20=input-mix */
+ /* Src 0x25=dock-mix */
+ if (HDA_SELECT(0x0d, "src", ctl, group, -1))
+ {
+ HDA_CHOICES(ctl, "mic unknown input-mix dock-mix");
+ }
+ HDA_OUTAMP(0x0d, group, "-", 90);
+ }
+
+ if (HDA_MISC_GROUP(0x10, group, misc_group, "beep", n, "misc", 8)) /* Misc widget 0x10 */
+ {
+ HDA_OUTAMP(0x10, group, "-", 90);
+ }
+
+ if (HDA_MISC_GROUP(0x1e, group, misc_group, "mono-mix", n, "misc", 8)) /* Misc widget 0x1e */
+ {
+ /* Src 0xe=mono-sel */
+ /* Src 0x21=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP(amp_group, group, "mute");
+ HDA_INMUTE(0x1e, 0, amp_group, "mono-sel", UNMUTE); /* From widget 0x0e */
+ HDA_INMUTE(0x1e, 1, amp_group, "input-mix", UNMUTE); /* From widget 0x21 */
+ }
+ }
+
+ if (HDA_MISC_GROUP(0x24, group, misc_group, "dock-mix", n, "misc", 8)) /* Misc widget 0x24 */
+ {
+ /* Src 0x23=dock-sel */
+ /* Src 0x21=input-mix */
+ {
+ int amp_group;
+
+ HDA_GROUP(amp_group, group, "mute");
+ HDA_INMUTE(0x24, 0, amp_group, "dock-sel", UNMUTE); /* From widget 0x23 */
+ HDA_INMUTE(0x24, 1, amp_group, "input-mix", UNMUTE); /* From widget 0x21 */
+ }
+ }
+ }
+ return 0;
+}
diff --git a/kernel/drv/oss_hdaudio/hdaudio_vaio_vgn.c b/kernel/drv/oss_hdaudio/hdaudio_vaio_vgn.c
new file mode 100644
index 0000000..2472672
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/hdaudio_vaio_vgn.c
@@ -0,0 +1,144 @@
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+/* Codec index is 0 */
+/* Codec vendor 0804:73dc */
+/* HD codec revision 1.0 (2.1) (0x00100201) */
+/* Subsystem ID 104d2200 */
+/* Default amplifier caps: in=80050f00, out=80027f7f */
+#include "oss_hdaudio_cfg.h"
+#include "hdaudio.h"
+#include "hdaudio_codec.h"
+#include "hdaudio_dedicated.h"
+
+int
+hdaudio_vaio_vgn_mixer_init (int dev, hdaudio_mixer_t * mixer, int cad,
+ int top_group)
+{
+ int ctl = 0;
+
+ DDB (cmn_err (CE_CONT, "hdaudio_vaio_vgn_mixer_init got called.\n"));
+
+ HDA_OUTAMP_F (0x05, top_group, "speaker", 90, MIXF_MAINVOL);
+ /* We sync the volume of the headphone DAC to the speaker DAC */
+#if 1
+ HDA_OUTAMP_F (0x02, top_group, "headphone", 90, MIXF_MAINVOL);
+#endif
+
+
+ HDA_SETSELECT (0x0f, 0); /* Int speaker mode */
+ HDA_SETSELECT (0x14, 1); /* Int mic mode */
+
+ /* Handle PIN widgets */
+ {
+ int n, group, pin_group;
+
+ n = 0;
+
+ HDA_GROUP (pin_group, top_group, "jack");
+
+ if (HDA_PIN_GROUP (0x0a, group, pin_group, "headphone", n, "jack", 4)) /* Pin widget 0x0a */
+ {
+ /* Src 0x2=pcm */
+ if (HDA_PINSELECT (0x0a, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "pcm-out input");
+ HDA_OUTMUTE (0x0a, group, "mute", UNMUTE);
+ }
+
+ if (HDA_PIN_GROUP (0x0b, group, pin_group, "black", n, "jack", 4)) /* Pin widget 0x0b */
+ {
+ /* Src 0x4=pcm */
+ if (HDA_PINSELECT (0x0b, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "pcm-out input");
+ HDA_OUTMUTE (0x0b, group, "mute", UNMUTE);
+
+ /* Widget 0x04 (pcm) */
+ HDA_OUTAMP (0x04, group, "-", 90);
+ }
+
+ if (HDA_PIN_GROUP (0x0c, group, pin_group, "black", n, "jack", 4)) /* Pin widget 0x0c */
+ {
+ /* Src 0x3=pcm */
+ if (HDA_PINSELECT (0x0c, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "pcm-out input");
+ HDA_OUTMUTE (0x0c, group, "mute", UNMUTE);
+
+ /* Widget 0x03 (pcm) */
+ HDA_OUTAMP (0x03, group, "-", 90);
+ }
+
+ if (HDA_PIN_GROUP (0x0d, group, pin_group, "red", n, "jack", 4)) /* Pin widget 0x0d */
+ {
+ /* Src 0x2=pcm */
+ if (HDA_PINSELECT (0x0d, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "pcm-out input");
+ HDA_OUTMUTE (0x0d, group, "mute", UNMUTE);
+ }
+
+ if (HDA_PIN_GROUP (0x0e, group, pin_group, "black", n, "jack", 4)) /* Pin widget 0x0e */
+ {
+ if (HDA_PINSELECT (0x0e, ctl, group, "mode", -1))
+ HDA_CHOICES (ctl, "input");
+ }
+ }
+ /* Handle ADC widgets */
+ {
+ int n, group, rec_group;
+
+ n = 0;
+
+ HDA_GROUP (rec_group, top_group, "record");
+
+ if (HDA_ADC_GROUP (0x06, group, rec_group, "rec1", n, "record", 4)) /* ADC widget 0x06 */
+ {
+ /* Src 0x7=rec */
+
+ /* Widget 0x07 (rec) */
+ /* Src 0xe=black */
+ HDA_INAMP_F (0x07, 0, group, "black", 80, MIXF_RECVOL); /* From widget 0x0e */
+ }
+
+ if (HDA_ADC_GROUP (0x08, group, rec_group, "rec", n, "record", 8)) /* ADC widget 0x08 */
+ {
+ /* Src 0x9=rec */
+
+ /* Widget 0x09 (rec) */
+ /* Src 0x15=rec */
+ HDA_INAMP_F (0x09, 0, group, "rec", 80, MIXF_RECVOL); /* From widget 0x15 */
+
+ /* Widget 0x15 (rec) */
+ /* Src 0xa=black */
+ /* Src 0xd=red */
+ /* Src 0x14=int-mic */
+ /* Src 0x2=pcm */
+ if (HDA_SELECT (0x15, "src", ctl, group, -1))
+ {
+ HDA_CHOICES (ctl, "headphone mic int-mic pcm");
+ }
+ HDA_OUTAMP (0x15, group, "micboost", 0);
+ }
+
+ if (HDA_ADC_GROUP (0x12, group, rec_group, "spdifin", n, "record", 4)) /* ADC widget 0x12 */
+ {
+ /* Src 0x13=speaker */
+ }
+ }
+ /* Handle misc widgets */
+ {
+#if 0
+ if (HDA_MISC_GROUP (0x16, group, misc_group, "beep", n, "misc", 8)) /* Misc widget 0x16 */
+ {
+ HDA_OUTAMP (0x16, group, "-", 90);
+ }
+#endif
+ }
+ return 0;
+}
diff --git a/kernel/drv/oss_hdaudio/oss_hdaudio.c b/kernel/drv/oss_hdaudio/oss_hdaudio.c
new file mode 100644
index 0000000..6492bec
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/oss_hdaudio.c
@@ -0,0 +1,1996 @@
+/*
+ * Purpose: The High Definition Audio (HDA/Azalia) driver.
+ */
+/*
+ *
+ * This file is part of Open Sound System.
+ *
+ * Copyright (C) 4Front Technologies 1996-2008.
+ *
+ * This this source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ */
+
+#include "oss_hdaudio_cfg.h"
+#include "oss_pci.h"
+#include "hdaudio.h"
+#include "hdaudio_codec.h"
+#include "spdif.h"
+
+#define CORB_DELAY 10
+#define CORB_LOOPS 1000
+
+#define INTEL_VENDOR_ID 0x8086
+#define INTEL_DEVICE_ICH6 0x2668
+#define INTEL_DEVICE_ICH7 0x27d8
+#define INTEL_DEVICE_ESB2 0x269a
+#define INTEL_DEVICE_ICH8 0x284b
+#define INTEL_DEVICE_ICH9 0x293f
+#define INTEL_DEVICE_ICH10 0x3a3e
+#define INTEL_DEVICE_ICH10_B 0x3a6e
+#define INTEL_DEVICE_CPT 0x1c20
+#define INTEL_DEVICE_PCH 0x3b56
+#define INTEL_DEVICE_PCH2 0x3b57
+#define INTEL_DEVICE_SCH 0x811b
+#define INTEL_DEVICE_P35 0x293e
+
+#define NVIDIA_VENDOR_ID 0x10de
+#define NVIDIA_DEVICE_MCP51 0x026c
+#define NVIDIA_DEVICE_MCP55 0x0371
+#define NVIDIA_DEVICE_MCP61 0x03e4
+#define NVIDIA_DEVICE_MCP61A 0x03f0
+#define NVIDIA_DEVICE_MCP65 0x044a
+#define NVIDIA_DEVICE_MCP67 0x055c
+#define NVIDIA_DEVICE_MCP73 0x07fc
+#define NVIDIA_DEVICE_MCP78S 0x0774
+#define NVIDIA_DEVICE_MCP79 0x0ac0
+
+#define ATI_VENDOR_ID 0x1002
+#define ATI_DEVICE_SB450 0x437b
+#define ATI_DEVICE_SB600 0x4383
+
+#define VIA_VENDOR_ID 0x1106
+#define VIA_DEVICE_HDA 0x3288
+
+#define SIS_VENDOR_ID 0x1039
+#define SIS_DEVICE_HDA 0x7502
+
+#define ULI_VENDOR_ID 0x10b9
+#define ULI_DEVICE_HDA 0x5461
+
+#define CREATIVE_ID 0x1102
+#define CREATIVE_XFI_HDA 0x0009
+
+#define BDL_SIZE 32
+#define HDA_MAX_ENGINES 8
+#define MAX_OUTPUTS 8
+#define MAX_INPUTS 4
+
+typedef struct
+{
+ oss_uint64_t addr;
+ unsigned int len;
+ unsigned int ioc;
+} bdl_t;
+
+typedef struct hda_portc_t hda_portc_t;
+
+typedef struct
+{
+ int num;
+ int busy;
+ bdl_t *bdl;
+ oss_uint64_t bdl_phys;
+ oss_dma_handle_t bdl_dma_handle;
+ int bdl_size, bdl_max;
+ unsigned char *base;
+ unsigned int intrmask;
+ hda_portc_t *portc;
+} hda_engine_t;
+
+struct hda_portc_t
+{
+ int num;
+ int open_mode;
+ int speed, bits, channels;
+ int audio_enabled;
+ int trigger_bits;
+ int audiodev;
+ int port_type;
+#define PT_OUTPUT 0
+#define PT_INPUT 1
+ hdaudio_endpointinfo_t *endpoint;
+ hda_engine_t *engine;
+};
+
+typedef struct
+{
+ unsigned int response, resp_ex;
+} rirb_entry_t;
+
+typedef struct hda_devc_t
+{
+ oss_device_t *osdev;
+ oss_native_word base;
+ unsigned int membar_addr;
+ unsigned char *azbar;
+
+ char *chip_name;
+ unsigned int vendor_id, subvendor_id;
+
+ int irq;
+ oss_mutex_t mutex;
+ oss_mutex_t low_mutex;
+
+ /* CORB and RIRB */
+ int corbsize, rirbsize;
+ unsigned int *corb;
+ rirb_entry_t *rirb;
+ oss_uint64_t corb_phys, rirb_phys;
+ int rirb_rp;
+ unsigned int rirb_upper, rirb_lower;
+ volatile int rirb_empty;
+
+ oss_dma_handle_t corb_dma_handle;
+
+ /* Mixer */
+ unsigned short codecmask;
+ hdaudio_mixer_t *mixer;
+ int mixer_dev;
+ spdif_devc spdc;
+
+ /* Audio */
+ int first_dev;
+ hda_engine_t inengines[HDA_MAX_ENGINES];
+ hda_engine_t outengines[HDA_MAX_ENGINES];
+ int num_outengines, num_inengines;
+ hdaudio_endpointinfo_t *spdifout_endpoint;
+
+ hda_portc_t output_portc[MAX_OUTPUTS];
+ hda_portc_t input_portc[MAX_INPUTS];
+ int num_outputs, num_inputs;
+
+ int num_spdin, num_spdout;
+ int num_mdmin, num_mdmout;
+}
+hda_devc_t;
+
+static int
+rirb_intr (hda_devc_t * devc)
+{
+ int serviced = 0;
+ unsigned char rirbsts;
+ oss_native_word flags;
+
+ rirbsts = PCI_READB (devc->osdev, devc->azbar + HDA_RIRBSTS);
+ if (rirbsts != 0)
+ {
+ serviced = 1;
+
+ if (rirbsts & 0x01)
+ {
+ /* RIRB response interrupt */
+ int wp, rp;
+ unsigned int upper = 0, lower = 0;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ wp = PCI_READB (devc->osdev, devc->azbar + HDA_RIRBWP) & 0x00ff;
+ while (devc->rirb_rp != wp)
+ {
+ devc->rirb_rp++;
+ devc->rirb_rp %= devc->rirbsize;
+ rp = devc->rirb_rp;
+ upper = devc->rirb[rp].response;
+ lower = devc->rirb[rp].resp_ex;
+
+ if (lower & 0x10) /* Unsolicited response */
+ {
+ hda_codec_unsol (devc->mixer, upper, lower);
+ }
+ else if (devc->rirb_empty)
+ {
+ devc->rirb_upper = upper;
+ devc->rirb_lower = lower;
+ devc->rirb_empty--;
+ }
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ }
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_RIRBSTS, rirbsts);
+ }
+
+ return serviced;
+}
+
+static int
+hdaintr (oss_device_t * osdev)
+{
+ hda_devc_t *devc = (hda_devc_t *) osdev->devc;
+ unsigned int status;
+ int serviced = 0;
+ int i;
+
+ if (devc == NULL) /* Too bad */
+ return 1;
+
+ status = PCI_READL (devc->osdev, devc->azbar + HDA_INTSTS);
+ if (status != 0)
+ {
+ serviced = 1;
+
+ for (i = 0; i < devc->num_outengines; i++)
+ {
+ hda_engine_t *engine = &devc->outengines[i];
+
+ hda_portc_t *portc;
+ if (status & engine->intrmask)
+ {
+ PCI_WRITEB (devc->osdev, engine->base + 0x03, 0x1e);
+
+ portc = engine->portc;
+
+ if (portc != NULL && (portc->trigger_bits & PCM_ENABLE_OUTPUT))
+ oss_audio_outputintr (portc->audiodev, 1);
+ }
+ }
+
+ for (i = 0; i < devc->num_inengines; i++)
+ {
+ hda_engine_t *engine = &devc->inengines[i];
+
+ hda_portc_t *portc;
+ if (status & engine->intrmask)
+ {
+ PCI_WRITEB (devc->osdev, engine->base + 0x03, 0x1e);
+
+ portc = engine->portc;
+
+ if (portc != NULL && (portc->trigger_bits & PCM_ENABLE_INPUT))
+ oss_audio_inputintr (portc->audiodev, 0);
+ }
+ }
+ PCI_WRITEL (devc->osdev, devc->azbar + HDA_INTSTS, status); /* ACK */
+
+ if (status & (1 << 30)) /* Controller interrupt (RIRB) */
+ {
+ if (rirb_intr (devc))
+ serviced = 1;
+ }
+ }
+ return serviced;
+}
+
+static int
+do_corb_write (void *dc, unsigned int cad, unsigned int nid, unsigned int d,
+ unsigned int verb, unsigned int parm)
+{
+ unsigned int wp;
+ unsigned int tmp;
+ oss_native_word flags;
+ hda_devc_t *devc = (hda_devc_t *) dc;
+
+ tmp = (cad << 28) | (d << 27) | (nid << 20) | (verb << 8) | (parm & 0xffff);
+ wp = PCI_READB (devc->osdev, devc->azbar + HDA_CORBWP) & 0x00ff;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ wp = (wp + 1) % devc->corbsize;
+
+ devc->corb[wp] = tmp;
+ devc->rirb_empty++;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ PCI_WRITEL (devc->osdev, devc->azbar + HDA_CORBWP, wp);
+
+ return 1;
+}
+
+static int
+do_corb_write_nomutex (void *dc, unsigned int cad, unsigned int nid, unsigned int d,
+ unsigned int verb, unsigned int parm)
+{
+ unsigned int wp;
+ unsigned int tmp;
+ hda_devc_t *devc = (hda_devc_t *) dc;
+
+ tmp = (cad << 28) | (d << 27) | (nid << 20) | (verb << 8) | (parm & 0xffff);
+ wp = PCI_READB (devc->osdev, devc->azbar + HDA_CORBWP) & 0x00ff;
+
+ wp = (wp + 1) % devc->corbsize;
+
+ devc->corb[wp] = tmp;
+ devc->rirb_empty++;
+ PCI_WRITEL (devc->osdev, devc->azbar + HDA_CORBWP, wp);
+
+ return 1;
+}
+
+static int
+do_corb_read (void *dc, unsigned int cad, unsigned int nid, unsigned int d,
+ unsigned int verb, unsigned int parm, unsigned int *upper,
+ unsigned int *lower)
+{
+ int tmout;
+ hda_devc_t *devc = (hda_devc_t *) dc;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ do_corb_write_nomutex (devc, cad, nid, d, verb, parm);
+
+ tmout = CORB_LOOPS;
+ while (devc->rirb_empty)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ if (--tmout < 0)
+ {
+ devc->rirb_rp = PCI_READB (devc->osdev, devc->azbar + HDA_RIRBWP);
+ devc->rirb_empty = 0;
+ return 0;
+ }
+ oss_udelay (CORB_DELAY);
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ }
+
+ *upper = devc->rirb_upper;
+ *lower = devc->rirb_lower;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return 1;
+}
+
+static int
+do_corb_read_poll (void *dc, unsigned int cad, unsigned int nid,
+ unsigned int d, unsigned int verb, unsigned int parm,
+ unsigned int *upper, unsigned int *lower)
+{
+ int tmout = 0;
+ hda_devc_t *devc = (hda_devc_t *) dc;
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ do_corb_write_nomutex (devc, cad, nid, d, verb, parm);
+
+ tmout = CORB_LOOPS;
+ while (devc->rirb_empty)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ rirb_intr (devc);
+
+ if (--tmout < 0)
+ {
+ devc->rirb_rp = PCI_READB (devc->osdev, devc->azbar + HDA_RIRBWP);
+ devc->rirb_empty = 0;
+ return 0;
+ }
+ oss_udelay (CORB_DELAY);
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ }
+
+ *upper = devc->rirb_upper;
+ *lower = devc->rirb_lower;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return 1;
+}
+
+#undef corb_read
+
+static int
+corb_read (void *dc, unsigned int cad, unsigned int nid, unsigned int d,
+ unsigned int verb, unsigned int parm, unsigned int *upper,
+ unsigned int *lower)
+{
+ hda_devc_t *devc = (hda_devc_t *) dc;
+
+/*
+ * Do three retries using different access methods
+ */
+ if (do_corb_read (dc, cad, nid, d, verb, parm, upper, lower))
+ return 1;
+
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_RIRBCTL, 0x02); /* Intr disable */
+
+ if (do_corb_read_poll (dc, cad, nid, d, verb, parm, upper, lower))
+ {
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_RIRBCTL, 0x03); /* Intr re-enable */
+ return 1;
+ }
+
+ if (do_corb_read_poll (dc, cad, nid, d, verb, parm, upper, lower))
+ {
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_RIRBCTL, 0x03); /* Intr re-enable */
+ return 1;
+ }
+
+#if 0
+ // TODO: Implement this
+ if (do_corb_read_single (dc, cad, nid, d, verb, parm, upper, lower))
+ {
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_RIRBCTL, 0x03); /* Intr re-enable */
+ return 1;
+ }
+#endif
+
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_RIRBCTL, 0x03); /* Intr re-enable */
+
+ cmn_err (CE_WARN,
+ "RIRB timeout (cad=%d, nid=%d, d=%d, verb=%03x, parm=%x)\n",
+ cad, nid, d, verb, parm);
+
+ return 0;
+}
+
+/*
+ * Audio routines
+ */
+
+static int
+hda_audio_set_rate (int dev, int arg)
+{
+ hda_portc_t *portc = audio_engines[dev]->portc;
+ adev_p adev = audio_engines[dev];
+
+ int best = -1, bestdiff = 10000000;
+ int i;
+
+ if (arg == 0)
+ return portc->speed;
+
+ for (i = 0; i < adev->nrates; i++)
+ {
+ int diff = arg - adev->rates[i];
+ if (diff < 0)
+ diff = -diff;
+
+ if (diff < bestdiff)
+ {
+ best = i;
+ bestdiff = diff;
+ }
+ }
+
+ if (best == -1)
+ {
+ cmn_err (CE_WARN, "No suitable rate found!\n");
+ return portc->speed = 48000; /* Some default */
+ }
+ portc->speed = adev->rates[best];
+ return portc->speed;
+}
+
+static short
+hda_audio_set_channels (int dev, short arg)
+{
+ hda_portc_t *portc = audio_engines[dev]->portc;
+ hda_devc_t *devc = audio_engines[dev]->devc;
+ adev_p adev = audio_engines[dev];
+ int i, n1, n2;
+
+ if (arg == 0)
+ {
+ return portc->channels;
+ }
+
+ if (arg < 1)
+ arg = 1;
+
+ if (arg > adev->max_channels)
+ {
+ arg = adev->max_channels;
+ }
+
+ if (arg != 1 && arg != 2 && arg != 4 && arg != 6 && arg != 8)
+ {
+ cmn_err (CE_NOTE, "Ignored request for odd number (%d) of channels\n", arg);
+ return portc->channels = arg & ~1;
+ }
+
+ if (arg < adev->min_channels)
+ arg = adev->min_channels;
+
+ /*
+ * Check if more output endpoints need to be allocated
+ */
+ n2 = (arg + 1) / 2;
+ n1 = (portc->channels + 1) / 2;
+ if (n1 < 1)
+ n1 = 1;
+
+ if (n2 > n1) /* Needs more stereo pairs */
+ {
+ oss_native_word flags;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ for (i = n1; i < n2; i++)
+ {
+ if (portc->endpoint[i].busy)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return portc->channels;
+ }
+ }
+
+ for (i = n1; i < n2; i++)
+ {
+ portc->endpoint[i].busy = 1;
+ portc->endpoint->borrow_count++;
+ }
+
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ }
+ else if (n2 < n1) /* Some stereo pairs can be released */
+ {
+ for (i = n2; i < n1; i++)
+ {
+ portc->endpoint[i].busy = 0;
+ portc->endpoint->borrow_count--;
+ }
+ }
+
+ return portc->channels = arg;
+}
+
+static unsigned int
+hda_audio_set_format (int dev, unsigned int arg)
+{
+ hda_portc_t *portc = audio_engines[dev]->portc;
+
+ if (arg == 0)
+ return portc->bits;
+
+ if (!(arg & audio_engines[dev]->oformat_mask))
+ return portc->bits;
+ portc->bits = arg;
+
+ return portc->bits;
+}
+
+static int
+do_corb_write_simple (void *dc, unsigned int v)
+{
+ unsigned int wp;
+ hda_devc_t *devc = (hda_devc_t *) dc;
+
+ wp = PCI_READB (devc->osdev, devc->azbar + HDA_CORBWP) & 0x00ff;
+
+ wp = (wp + 1) % devc->corbsize;
+
+ devc->corb[wp] = v;
+ devc->rirb_empty++;
+ PCI_WRITEL (devc->osdev, devc->azbar + HDA_CORBWP, wp);
+
+ return 1;
+}
+
+static int
+do_corb_read_simple (void *dc, unsigned int cmd, unsigned int *v)
+{
+ int tmout = 0;
+ hda_devc_t *devc = (hda_devc_t *) dc;
+
+ do_corb_write_simple (dc, cmd);
+
+ tmout = CORB_LOOPS;
+ while (devc->rirb_empty)
+ {
+ if (--tmout < 0)
+ {
+ cmn_err (CE_WARN, "RIRB timeout (cmd=%08x)\n", cmd);
+ devc->rirb_rp = PCI_READB (devc->osdev, devc->azbar + HDA_RIRBWP);
+ devc->rirb_empty = 0;
+ return 0;
+ }
+ oss_udelay (CORB_DELAY);
+ }
+
+ *v = devc->rirb_upper;
+
+ return 1;
+}
+
+static int
+hda_audio_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
+{
+ hda_devc_t *devc = audio_engines[dev]->devc;
+ hda_portc_t *portc = audio_engines[dev]->portc;
+
+ //if (hdaudio_snoopy)
+ switch (cmd)
+ {
+ case HDA_IOCTL_WRITE:
+#ifdef GET_PROCESS_UID
+ if (GET_PROCESS_UID () != 0) /* Not root */
+ return OSS_EINVAL;
+#endif
+ do_corb_write_simple (devc, *(unsigned int *) arg);
+ return 0;
+ break;
+
+ case HDA_IOCTL_READ:
+#ifdef GET_PROCESS_UID
+ if (GET_PROCESS_UID () != 0) /* Not root */
+ return OSS_EINVAL;
+#endif
+ if (!do_corb_read_simple
+ (devc, *(unsigned int *) arg, (unsigned int *) arg))
+ return OSS_EIO;
+
+ return 0;
+ break;
+
+ case HDA_IOCTL_NAME:
+#ifdef GET_PROCESS_UID
+ if (GET_PROCESS_UID () != 0) /* Not root */
+ return OSS_EINVAL;
+#endif
+ return hda_codec_getname (devc->mixer, (hda_name_t *) arg);
+ break;
+
+ case HDA_IOCTL_WIDGET:
+#ifdef GET_PROCESS_UID
+ if (GET_PROCESS_UID () != 0) /* Not root */
+ return OSS_EINVAL;
+#endif
+ return hda_codec_getwidget (devc->mixer, (hda_widget_info_t *) arg);
+ break;
+
+ }
+ return hdaudio_codec_audio_ioctl (devc->mixer, portc->endpoint, cmd, arg);
+}
+
+static void hda_audio_trigger (int dev, int state);
+
+static void
+hda_audio_reset (int dev)
+{
+ hda_audio_trigger (dev, 0);
+}
+
+/*ARGSUSED*/
+static int
+hda_audio_open (int dev, int mode, int openflags)
+{
+ hda_portc_t *portc = audio_engines[dev]->portc;
+ hda_devc_t *devc = audio_engines[dev]->devc;
+ oss_native_word flags;
+ hda_engine_t *engines, *engine = NULL;
+ int i, n;
+ unsigned int tmp;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ if (portc->open_mode || portc->endpoint->busy)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return OSS_EBUSY;
+ }
+
+ if (portc->port_type == PT_INPUT)
+ {
+ engines = devc->inengines;
+ n = devc->num_inengines;
+ }
+ else
+ {
+ engines = devc->outengines;
+ n = devc->num_outengines;
+ }
+
+ for (i = 0; i < n && engine == NULL; i++)
+ {
+ if (!engines[i].busy)
+ engine = &engines[i];
+ }
+
+ if (engine == NULL)
+ {
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ cmn_err (CE_WARN, "No free DMA engines.\nn");
+ return OSS_EBUSY;
+ }
+
+ portc->open_mode = mode;
+ portc->audio_enabled &= ~mode;
+ portc->endpoint->busy = 1;
+ portc->endpoint->borrow_count = 1;
+
+ portc->engine = engine;
+ engine->portc = portc;
+ portc->engine->busy = 1;
+
+ portc->endpoint->stream_number = portc->endpoint->default_stream_number;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+/*
+ * Reset the DMA engine and wait for the reset to complete
+ */
+ tmp = PCI_READL (devc->osdev, engine->base);
+ PCI_WRITEL (devc->osdev, engine->base, tmp | 0x01); /* Stream reset */
+
+ for (i = 0; i < 1000; i++)
+ {
+ if (PCI_READL (devc->osdev, engine->base) & 0x01)
+ break;
+ oss_udelay (1000);
+ }
+
+ /* Unreset and wait */
+
+ tmp = PCI_READL (devc->osdev, engine->base);
+ PCI_WRITEL (devc->osdev, engine->base, tmp & ~0x01); /* Release reset */
+
+ for (i = 0; i < 1000; i++)
+ {
+ if (!(PCI_READL (devc->osdev, engine->base) & 0x01))
+ break;
+ oss_udelay (1000);
+ }
+
+ return 0;
+}
+
+static void
+hda_audio_close (int dev, int mode)
+{
+ hda_portc_t *portc = audio_engines[dev]->portc;
+ hda_devc_t *devc = audio_engines[dev]->devc;
+ oss_native_word flags;
+ int i;
+
+ if (!portc->open_mode)
+ {
+ cmn_err (CE_WARN, "Bad close %d\n", dev);
+ return;
+ }
+
+ hda_audio_reset (dev);
+ hdaudio_codec_reset_endpoint (devc->mixer, portc->endpoint,
+ portc->channels);
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ portc->audio_enabled &= ~mode;
+ portc->engine->busy = 0;
+ portc->engine->portc = NULL;
+ portc->engine = NULL;
+ portc->endpoint->busy = 0;
+ portc->endpoint->stream_number = portc->endpoint->default_stream_number;
+
+ for (i = 1; i < portc->endpoint->borrow_count; i++)
+ {
+ portc->endpoint[i].busy = 0;
+ }
+
+ portc->endpoint->borrow_count = 1;
+ portc->open_mode = 0;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+/*ARGSUSED*/
+static void
+hda_audio_output_block (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+ hda_portc_t *portc = audio_engines[dev]->portc;
+
+ portc->audio_enabled |= PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+}
+
+/*ARGSUSED*/
+static void
+hda_audio_start_input (int dev, oss_native_word buf, int count,
+ int fragsize, int intrflag)
+{
+ hda_portc_t *portc = audio_engines[dev]->portc;
+
+ portc->audio_enabled |= PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+}
+
+static void
+hda_audio_trigger (int dev, int state)
+{
+ hda_devc_t *devc = audio_engines[dev]->devc;
+ hda_portc_t *portc = audio_engines[dev]->portc;
+ hda_engine_t *engine = portc->engine;
+ oss_native_word flags;
+ unsigned char tmp, intr;
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+
+ tmp = PCI_READB (devc->osdev, engine->base + 0x00);
+ intr = PCI_READB (devc->osdev, devc->azbar + HDA_INTCTL);
+ if (portc->open_mode & OPEN_WRITE)
+ {
+ if (state & PCM_ENABLE_OUTPUT)
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) &&
+ !(portc->trigger_bits & PCM_ENABLE_OUTPUT))
+ {
+ portc->trigger_bits |= PCM_ENABLE_OUTPUT;
+ tmp |= 0x1e; /* DMA and Stream Interrupt enable */
+ intr |= engine->intrmask;
+ PCI_WRITEB (devc->osdev, engine->base + 0x00, tmp);
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_INTCTL, intr);
+ }
+ }
+ else
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_OUTPUT) &&
+ (portc->trigger_bits & PCM_ENABLE_OUTPUT))
+ {
+ portc->audio_enabled &= ~PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+ tmp &= ~0x1e; /* Run-off & intr disable */
+ intr &= ~engine->intrmask;
+ /* Stop DMA and Stream Int */
+ PCI_WRITEB (devc->osdev, engine->base + 0x00, tmp);
+ /* Ack pending ints */
+ PCI_WRITEB (devc->osdev, engine->base + 0x03, 0x1c);
+ /* Disable Interrupts */
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_INTCTL, intr);
+ }
+ }
+ }
+
+ if (portc->open_mode & OPEN_READ)
+ {
+ if (state & PCM_ENABLE_INPUT)
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_INPUT) &&
+ !(portc->trigger_bits & PCM_ENABLE_INPUT))
+ {
+ portc->trigger_bits |= PCM_ENABLE_INPUT;
+ tmp |= 0x1e; /* dma & intr enable */
+ intr |= engine->intrmask;
+ PCI_WRITEB (devc->osdev, engine->base + 0x00, tmp);
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_INTCTL, intr);
+ }
+ }
+ else
+ {
+ if ((portc->audio_enabled & PCM_ENABLE_INPUT) &&
+ (portc->trigger_bits & PCM_ENABLE_INPUT))
+ {
+ portc->audio_enabled &= ~PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+ tmp &= ~0x1e; /* Run-off & intr disable */
+ intr &= ~engine->intrmask;
+ /* Stop DMA and Stream Int */
+ PCI_WRITEB (devc->osdev, engine->base + 0x00, tmp);
+ /* Ack pending ints */
+ PCI_WRITEB (devc->osdev, engine->base + 0x03, 0x1c);
+ /* Disable Interrupts */
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_INTCTL, intr);
+ }
+ }
+ }
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+}
+
+static void
+init_bdl (hda_devc_t * devc, hda_engine_t * engine, dmap_p dmap)
+{
+ int i;
+ bdl_t *bdl = engine->bdl;
+
+ PCI_WRITEL (devc->osdev, engine->base + HDA_SD_BDLPL, 0);
+ PCI_WRITEL (devc->osdev, engine->base + HDA_SD_BDLPU, 0);
+ for (i = 0; i < dmap->nfrags; i++)
+ {
+ bdl[i].addr = dmap->dmabuf_phys + (i * dmap->fragment_size);
+ bdl[i].len = dmap->fragment_size;
+ bdl[i].ioc = 0x01;
+ }
+}
+
+static int
+setup_audio_engine (hda_devc_t * devc, hda_engine_t * engine, hda_portc_t * portc,
+ dmap_t * dmap)
+{
+ unsigned int tmp, tmout;
+
+/* make sure the run bit is zero for SD */
+ PCI_WRITEB (devc->osdev, engine->base + HDA_SD_CTL,
+ PCI_READB (devc->osdev, engine->base + HDA_SD_CTL) & ~0x2);
+/*
+ * First reset the engine.
+ */
+
+ tmp = PCI_READB (devc->osdev, engine->base + HDA_SD_CTL);
+ PCI_WRITEB (devc->osdev, engine->base + HDA_SD_CTL, tmp | 0x01); /* Reset */
+ oss_udelay (1000);
+
+ tmout = 300;
+ while (!(PCI_READB (devc->osdev, engine->base + HDA_SD_CTL) & 0x01)
+ && --tmout)
+ oss_udelay (1000);
+
+ PCI_WRITEB (devc->osdev, engine->base + HDA_SD_CTL, tmp & ~0x01); /* Release reset */
+ oss_udelay (1000);
+ tmout = 300;
+ while ((PCI_READB (devc->osdev, engine->base + HDA_SD_CTL) & 0x01)
+ && --tmout)
+ oss_udelay (1000);
+
+/*
+ * Set the engine tag number field
+ */
+ tmp = PCI_READL (devc->osdev, engine->base + HDA_SD_CTL);
+ tmp &= ~(0xf << 20);
+ tmp |= portc->endpoint->stream_number << 20;
+ PCI_WRITEL (devc->osdev, engine->base + HDA_SD_CTL, tmp);
+
+/* program buffer size and fragments in the engine */
+
+ PCI_WRITEL (devc->osdev, engine->base + HDA_SD_CBL, dmap->bytes_in_use);
+ PCI_WRITEW (devc->osdev, engine->base + HDA_SD_LVI, dmap->nfrags - 1);
+
+/* setup the BDL base address */
+ tmp = engine->bdl_phys;
+ PCI_WRITEL (devc->osdev, engine->base + HDA_SD_BDLPL, tmp & 0xffffffff);
+ tmp = engine->bdl_phys >> 32;
+ PCI_WRITEL (devc->osdev, engine->base + HDA_SD_BDLPU, tmp & 0xffffffff);
+ PCI_WRITEB (devc->osdev, engine->base + HDA_SD_STS, 0x1c); /* mask out ints */
+
+/*
+ * Next the sample rate and format setup
+ */
+ if (hdaudio_codec_setup_endpoint
+ (devc->mixer, portc->endpoint, portc->speed, portc->channels,
+ portc->bits, portc->endpoint->stream_number, &tmp) < 0)
+ {
+ cmn_err (CE_WARN, "Codec setup failed\n");
+ return OSS_EIO;
+ }
+
+ PCI_WRITEW (devc->osdev, engine->base + HDA_SD_FORMAT, tmp);
+ tmp = PCI_READW (devc->osdev, engine->base + HDA_SD_FORMAT);
+
+ return 0;
+}
+
+/*ARGSUSED*/
+static int
+hda_audio_prepare_for_input (int dev, int bsize, int bcount)
+{
+ hda_devc_t *devc = audio_engines[dev]->devc;
+ hda_portc_t *portc = audio_engines[dev]->portc;
+ dmap_t *dmap = audio_engines[dev]->dmap_in;
+ oss_native_word flags;
+ hda_engine_t *engine;
+
+ if (portc->port_type != PT_INPUT)
+ return OSS_ENOTSUP;
+
+ engine = portc->engine;
+
+ if (dmap->nfrags > engine->bdl_max) /* Overflow protection */
+ {
+ dmap->nfrags = engine->bdl_max;
+ dmap->bytes_in_use = dmap->nfrags * dmap->fragment_size;
+ }
+
+ init_bdl (devc, engine, dmap);
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ portc->audio_enabled &= ~PCM_ENABLE_INPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_INPUT;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+ return setup_audio_engine (devc, engine, portc, dmap);
+}
+
+/*ARGSUSED*/
+static int
+hda_audio_prepare_for_output (int dev, int bsize, int bcount)
+{
+ hda_devc_t *devc = audio_engines[dev]->devc;
+ hda_portc_t *portc = audio_engines[dev]->portc;
+ hda_engine_t *engine;
+
+ dmap_t *dmap = audio_engines[dev]->dmap_out;
+ oss_native_word flags;
+
+ if (portc->port_type != PT_OUTPUT)
+ return OSS_ENOTSUP;
+
+ engine = portc->engine;
+
+ if (dmap->nfrags > engine->bdl_max) /* Overflow protection */
+ {
+ dmap->nfrags = engine->bdl_max;
+ dmap->bytes_in_use = dmap->nfrags * dmap->fragment_size;
+ }
+
+ init_bdl (devc, engine, dmap);
+
+ MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
+ portc->audio_enabled &= ~PCM_ENABLE_OUTPUT;
+ portc->trigger_bits &= ~PCM_ENABLE_OUTPUT;
+ MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
+
+ return setup_audio_engine (devc, engine, portc, dmap);
+}
+
+/*ARGSUSED*/
+static int
+hda_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
+{
+ hda_portc_t *portc = audio_engines[dev]->portc;
+#ifdef sun
+ // PCI_READL under solaris needs devc->osdev
+ hda_devc_t *devc = audio_engines[dev]->devc;
+#endif
+ hda_engine_t *engine;
+ unsigned int ptr;
+
+ engine = portc->engine;
+ ptr =
+ PCI_READL (devc->osdev, engine->base + HDA_SD_LPIB) % dmap->bytes_in_use;
+
+ return ptr;
+}
+
+static const audiodrv_t hda_audio_driver = {
+ hda_audio_open,
+ hda_audio_close,
+ hda_audio_output_block,
+ hda_audio_start_input,
+ hda_audio_ioctl,
+ hda_audio_prepare_for_input,
+ hda_audio_prepare_for_output,
+ hda_audio_reset,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ hda_audio_trigger,
+ hda_audio_set_rate,
+ hda_audio_set_format,
+ hda_audio_set_channels,
+ NULL,
+ NULL,
+ NULL, /* hda_check_input, */
+ NULL, /* hda_check_output, */
+ NULL, /* hda_alloc_buffer */
+ NULL, /* hda_free_buffer */
+ NULL,
+ NULL,
+ hda_get_buffer_pointer
+};
+
+static int
+reset_controller (hda_devc_t * devc)
+{
+ unsigned int tmp, tmout;
+
+ /*reset the controller by writing a 0*/
+ tmp = PCI_READL (devc->osdev, devc->azbar + HDA_GCTL);
+ tmp &= ~CRST;
+ PCI_WRITEL (devc->osdev, devc->azbar + HDA_GCTL, tmp);
+
+ /*wait until the controller writes a 0 to indicate reset is done or until 50ms have passed*/
+ tmout = 50;
+ while ((PCI_READL (devc->osdev, devc->azbar + HDA_GCTL) & CRST) && --tmout)
+ oss_udelay (1000);
+
+ oss_udelay (1000);
+
+ /*bring the controller out of reset by writing a 1*/
+ tmp = PCI_READL (devc->osdev, devc->azbar + HDA_GCTL);
+ tmp |= CRST;
+ PCI_WRITEL (devc->osdev, devc->azbar + HDA_GCTL, tmp);
+
+ /*wait until the controller writes a 1 to indicate it is ready is or until 50ms have passed*/
+ tmout = 50;
+ while (!(PCI_READL (devc->osdev, devc->azbar + HDA_GCTL) & CRST) && --tmout)
+ oss_udelay (1000);
+
+ oss_udelay (1000);
+
+ /*if the controller is not ready now, abort*/
+ if (!(PCI_READL (devc->osdev, devc->azbar + HDA_GCTL)))
+ {
+ cmn_err (CE_WARN, "Controller not ready\n");
+ return 0;
+ }
+
+ if (!devc->codecmask)
+ {
+ devc->codecmask = PCI_READW (devc->osdev, devc->azbar + HDA_STATESTS);
+ DDB (cmn_err (CE_CONT, "Codec mask %x\n", devc->codecmask));
+ }
+
+ return 1;
+}
+
+static int
+setup_controller (hda_devc_t * devc)
+{
+ unsigned int tmp, tmout;
+ oss_native_word phaddr;
+
+/*
+ * Allocate the CORB and RIRB buffers.
+ */
+ tmp = PCI_READB (devc->osdev, devc->azbar + HDA_CORBSIZE) & 0x03;
+
+ switch (tmp)
+ {
+ case 0:
+ devc->corbsize = 2;
+ break;
+ case 1:
+ devc->corbsize = 16;
+ break;
+ case 2:
+ devc->corbsize = 256;
+ break;
+ default:
+ cmn_err (CE_WARN, "Bad CORB size\n");
+ return 0;
+ }
+
+ tmp = PCI_READB (devc->osdev, devc->azbar + HDA_RIRBSIZE) & 0x03;
+
+ switch (tmp)
+ {
+ case 0:
+ devc->rirbsize = 2;
+ break;
+ case 1:
+ devc->rirbsize = 16;
+ break;
+ case 2:
+ devc->rirbsize = 256;
+ break;
+ default:
+ cmn_err (CE_WARN, "Bad CORB size\n");
+ return 0;
+ }
+
+ if ((devc->corb =
+ CONTIG_MALLOC (devc->osdev, 4096, MEMLIMIT_32BITS, &phaddr, devc->corb_dma_handle)) == NULL)
+ {
+ cmn_err (CE_WARN, "Out of memory (CORB)\n");
+ return 0;
+ }
+
+ devc->corb_phys = phaddr;
+
+ devc->rirb = (rirb_entry_t *) (devc->corb + 512); /* 512 dwords = 2048 bytes */
+ devc->rirb_phys = devc->corb_phys + 2048;
+
+/*
+ * Initialize CORB registers
+ */
+
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_CORBCTL, 0); /* Stop */
+
+ tmout = 0;
+
+ while (tmout++ < 500
+ && (PCI_READB (devc->osdev, devc->azbar + HDA_CORBCTL) & 0x02));
+
+ if (PCI_READB (devc->osdev, devc->azbar + HDA_CORBCTL) & 0x02)
+ {
+ cmn_err (CE_WARN, "CORB didn't stop\n");
+ }
+
+ tmp = devc->corb_phys & 0xffffffff;
+ PCI_WRITEL (devc->osdev, devc->azbar + HDA_CORBLBASE, tmp);
+ tmp = (devc->corb_phys >> 32) & 0xffffffff;
+ PCI_WRITEL (devc->osdev, devc->azbar + HDA_CORBUBASE, tmp);
+
+ PCI_WRITEW (devc->osdev, devc->azbar + HDA_CORBWP, 0x0); /* Reset to 0 */
+ PCI_WRITEW (devc->osdev, devc->azbar + HDA_CORBRP, 0x8000); /* Reset to 0 */
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_CORBCTL, 0x02); /* Start */
+
+/*
+ * Initialize RIRB registers
+ */
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_RIRBCTL, 0); /* Stop */
+
+ tmout = 0;
+
+ while (tmout++ < 500
+ && (PCI_READB (devc->osdev, devc->azbar + HDA_RIRBCTL) & 0x02));
+
+ if (PCI_READB (devc->osdev, devc->azbar + HDA_RIRBCTL) & 0x02)
+ {
+ cmn_err (CE_WARN, "RIRB didn't stop\n");
+ }
+
+ tmp = devc->rirb_phys & 0xffffffff;
+ PCI_WRITEL (devc->osdev, devc->azbar + HDA_RIRBLBASE, tmp);
+ tmp = (devc->rirb_phys >> 32) & 0xffffffff;
+ PCI_WRITEL (devc->osdev, devc->azbar + HDA_RIRBUBASE, tmp);
+ devc->rirb_rp = 0;
+
+ PCI_WRITEW (devc->osdev, devc->azbar + HDA_RIRBWP, 0x8000); /* Reset to 0 */
+ PCI_WRITEW (devc->osdev, devc->azbar + HDA_RINTCNT, 1); /* reset hw write ptr */
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_RIRBCTL, 0x03); /* Start & Intr enable */
+
+ return 1;
+}
+
+static int
+setup_engines (hda_devc_t * devc)
+{
+ int i, p;
+ unsigned int gcap;
+ unsigned int num_inputs, num_outputs;
+ oss_native_word phaddr;
+ gcap = PCI_READW (devc->osdev, devc->azbar + HDA_GCAP);
+
+ num_outputs = (gcap >> 12) & 0x0f;
+ num_inputs = (gcap >> 8) & 0x0f;
+
+ /* Init output engines */
+ p = num_inputs; /* Output engines are located after the input ones */
+
+ if (num_outputs > HDA_MAX_ENGINES)
+ {
+ cmn_err (CE_WARN,
+ "Using only %d out of %d available output engines\n",
+ HDA_MAX_ENGINES, num_outputs);
+ num_outputs = HDA_MAX_ENGINES;
+ }
+
+ if (num_inputs > HDA_MAX_ENGINES)
+ {
+ cmn_err (CE_WARN,
+ "Using only %d out of %d available input engines\n",
+ HDA_MAX_ENGINES, num_inputs);
+ num_inputs = HDA_MAX_ENGINES;
+ }
+
+ for (i = 0; i < num_outputs; i++)
+ {
+ hda_engine_t *engine = &devc->outengines[i];
+
+ engine->bdl =
+ CONTIG_MALLOC (devc->osdev, 4096, MEMLIMIT_32BITS, &phaddr, engine->bdl_dma_handle);
+ if (engine->bdl == NULL)
+ {
+ cmn_err (CE_WARN, "Out of memory\n");
+ return 0;
+ }
+ engine->bdl_max = 4096 / sizeof (bdl_t);
+ engine->bdl_phys = phaddr;
+ engine->base = devc->azbar + 0x80 + (p * 0x20);
+ engine->num = i;
+ engine->intrmask = (1 << p);
+ engine->portc = NULL;
+ p++;
+ devc->num_outengines = i + 1;
+ }
+
+ /* Init input engines */
+ p = 0;
+ for (i = 0; i < num_inputs; i++)
+ {
+ hda_engine_t *engine = &devc->inengines[i];
+
+ engine->bdl =
+ CONTIG_MALLOC (devc->osdev, 4096, MEMLIMIT_32BITS, &phaddr, engine->bdl_dma_handle);
+ if (engine->bdl == NULL)
+ {
+ cmn_err (CE_WARN, "Out of memory\n");
+ return 0;
+ }
+ engine->bdl_max = 4096 / sizeof (bdl_t);
+ engine->bdl_phys = phaddr;
+ engine->base = devc->azbar + 0x80 + (p * 0x20);
+ engine->num = i;
+ engine->intrmask = (1 << p);
+ engine->portc = NULL;
+ p++;
+ devc->num_inengines = i + 1;
+ }
+ return 1;
+}
+
+/*ARGSUSED*/
+static void
+init_adev_caps (hda_devc_t * devc, adev_p adev, hdaudio_endpointinfo_t * ep)
+{
+ int i;
+
+ adev->oformat_mask = ep->fmt_mask;
+ adev->iformat_mask = ep->fmt_mask;
+
+ adev->min_rate = 500000;
+ adev->max_rate = 0;
+
+ adev->nrates = ep->nrates;
+ if (adev->nrates > OSS_MAX_SAMPLE_RATES)
+ {
+ cmn_err(CE_NOTE, "Too many supported sample rates (%d)\n", adev->nrates);
+ adev->nrates = OSS_MAX_SAMPLE_RATES;
+ }
+
+ for (i = 0; i < adev->nrates; i++)
+ {
+ int r = ep->rates[i];
+
+ adev->rates[i] = r;
+
+ if (adev->min_rate > r)
+ adev->min_rate = r;
+ if (adev->max_rate < r)
+ adev->max_rate = r;
+ }
+}
+
+/*
+ * S/PDIF lowlevel driver
+ */
+/*ARGSUSED*/
+static int
+hdaudio_reprogram_spdif (void *_devc, void *_portc,
+ oss_digital_control * ctl, unsigned int mask)
+{
+ unsigned short cbits = 0;
+ hda_devc_t *devc = _devc;
+ oss_native_word flags;
+ hdaudio_endpointinfo_t *endpoint = devc->spdifout_endpoint;
+
+ if (endpoint == NULL)
+ return OSS_EIO;
+
+ MUTEX_ENTER_IRQDISABLE (devc->low_mutex, flags);
+
+ cbits = 0x01; /* Digital interface enabled */
+
+ cbits |= (1 << 2); /* VCFG */
+
+ if (ctl->out_vbit == VBIT_ON)
+ cbits |= (1 << 1); /* Turn on the V bit */
+
+ if (ctl->cbitout[0] & 0x02) /* Audio/Data */
+ cbits |= (1 << 5); /* /AUDIO */
+
+ if (ctl->cbitout[0] & 0x01) /* Pro */
+ cbits |= (1 << 6);
+ else
+ {
+
+ if (ctl->cbitout[0] & 0x04) /* Copyright */
+ cbits |= (1 << 4); /* COPY */
+
+ if (ctl->cbitout[1] & 0x80) /* Generation level */
+ cbits |= (1 << 7); /* L */
+
+ if (ctl->emphasis_type & 1) /* Pre-emphasis */
+ cbits |= (1 << 3); /* PRE */
+ }
+ do_corb_write (devc, endpoint->cad, endpoint->base_wid, 0,
+ SET_SPDIF_CONTROL1, cbits);
+
+ cbits = ctl->cbitout[1] & 0x7f; /* Category code */
+ do_corb_write (devc, endpoint->cad, endpoint->base_wid, 0,
+ SET_SPDIF_CONTROL2, cbits);
+
+ MUTEX_EXIT_IRQRESTORE (devc->low_mutex, flags);
+ return 0;
+}
+
+spdif_driver_t hdaudio_spdif_driver = {
+/* reprogram_device: */ hdaudio_reprogram_spdif,
+};
+
+static int
+spdif_mixer_init (int dev)
+{
+ hda_devc_t *devc = mixer_devs[dev]->hw_devc;
+ int err;
+
+ if ((err = oss_spdif_mix_init (&devc->spdc)) < 0)
+ return err;
+
+ return 0;
+}
+
+static void
+install_outputdevs (hda_devc_t * devc)
+{
+ int i, n, pass, audio_dev, output_num=0;
+ char tmp_name[64];
+ hdaudio_endpointinfo_t *endpoints;
+
+ if ((n =
+ hdaudio_mixer_get_outendpoints (devc->mixer, &endpoints,
+ sizeof (*endpoints))) < 0)
+ return;
+
+#if 0
+ // This check will be done later.
+ if (n > MAX_OUTPUTS)
+ {
+ cmn_err (CE_WARN,
+ "Only %d out of %d output devices can be installed\n",
+ MAX_OUTPUTS, n);
+ n = MAX_OUTPUTS;
+ }
+#endif
+
+/*
+ * Install the output devices in two passes. First install the analog
+ * endpoints and then the digital one(s).
+ */
+ for (pass = 0; pass < 3; pass++)
+ for (i = 0; i < n; i++)
+ {
+ adev_p adev;
+ hda_portc_t *portc = &devc->output_portc[i];
+ unsigned int formats = AFMT_S16_LE;
+ int opts = ADEV_AUTOMODE | ADEV_NOINPUT;
+ char *devfile_name = "";
+ char devname[16];
+
+/* Skip endpoints that are not physically connected on the motherboard. */
+ if (endpoints[i].skip)
+ continue;
+
+ if (output_num >= MAX_OUTPUTS)
+ {
+ cmn_err(CE_CONT, "Too many output endpoints. Endpoint %d ignored.\n", i);
+ continue;
+ }
+
+ switch (pass)
+ {
+ case 0: /* Pick analog and non-modem ones */
+ if (endpoints[i].is_digital || endpoints[i].is_modem)
+ continue;
+ break;
+
+ case 1: /* Pick digital one(s) */
+ if (!endpoints[i].is_digital)
+ continue;
+ break;
+
+ case 2: /* Pick modem one(s) */
+ if (!endpoints[i].is_modem)
+ continue;
+ }
+
+ if (endpoints[i].is_digital)
+ {
+ opts |= ADEV_SPECIAL;
+ sprintf (devname, "spdout%d", devc->num_spdout++);
+ devfile_name = devname;
+ }
+ if (endpoints[i].is_modem)
+ {
+ opts |= ADEV_SPECIAL;
+ sprintf (devname, "mdmout%d", devc->num_mdmout++);
+ devfile_name = devname;
+ }
+
+ // sprintf (tmp_name, "%s %s", devc->chip_name, endpoints[i].name);
+ sprintf (tmp_name, "HD Audio play %s", endpoints[i].name);
+
+ if ((audio_dev = oss_install_audiodev_with_devname (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ tmp_name,
+ &hda_audio_driver,
+ sizeof (audiodrv_t),
+ opts, formats, devc, -1,
+ devfile_name)) < 0)
+ {
+ return;
+ }
+
+ if (output_num == 0)
+ devc->first_dev = audio_dev;
+
+ adev = audio_engines[audio_dev];
+ devc->num_outputs = i+1;
+
+ adev->devc = devc;
+ adev->portc = portc;
+ adev->rate_source = devc->first_dev;
+ adev->mixer_dev = devc->mixer_dev;
+ adev->min_rate = 8000;
+ adev->max_rate = 192000;
+ adev->min_channels = 2;
+
+ if (output_num == 0)
+ adev->max_channels = 8;
+ else
+ adev->max_channels = 2;
+
+ if (endpoints[i].is_modem)
+ {
+ adev->min_channels = 1;
+ adev->max_channels = 1;
+ adev->caps |= PCM_CAP_MODEM;
+ }
+
+ if (adev->max_channels > 4)
+ adev->dmabuf_alloc_flags |= DMABUF_LARGE | DMABUF_QUIET;
+ adev->min_block = 128; /* Hardware limitation */
+ adev->min_fragments = 4; /* Vmix doesn't work without this */
+ portc->num = output_num;
+ portc->open_mode = 0;
+ portc->audio_enabled = 0;
+ portc->audiodev = audio_dev;
+ portc->port_type = PT_OUTPUT;
+ portc->endpoint = &endpoints[i];
+ init_adev_caps (devc, adev, portc->endpoint);
+ portc->engine = NULL;
+
+ if (portc->endpoint->is_digital)
+ {
+ int err;
+
+ devc->spdifout_endpoint = portc->endpoint;
+
+/*
+ * To be precise the right place for the spdc field might be portc instead of
+ * devc. In this way it's possible to have multiple S/PDIF output DACs connected
+ * to the same HDA controller. OTOH having it in devc saves space. Multiple
+ * oss_spdif_install() calls on the same spdc structure doesn't cause any
+ * problems.
+ *
+ * If spdc is moved to portc then care must be taken that oss_spdif_uninstall()
+ * is called for all all output_portc instances.
+ */
+ if ((err = oss_spdif_install (&devc->spdc, devc->osdev,
+ &hdaudio_spdif_driver,
+ sizeof (spdif_driver_t), devc, NULL,
+ devc->mixer_dev, SPDF_OUT,
+ DIG_PASSTHROUGH | DIG_EXACT |
+ DIG_CBITOUT_LIMITED | DIG_VBITOUT |
+ DIG_PRO | DIG_CONSUMER)) != 0)
+ {
+ cmn_err (CE_WARN,
+ "S/PDIF driver install failed. Error %d\n", err);
+ }
+ else
+ {
+ hdaudio_mixer_set_initfunc (devc->mixer, spdif_mixer_init);
+ }
+ }
+ output_num++;
+ }
+}
+
+static void
+install_inputdevs (hda_devc_t * devc)
+{
+ int i, n, audio_dev;
+ char tmp_name[64];
+ hdaudio_endpointinfo_t *endpoints;
+ char devname[16], *devfile_name = "";
+
+ if ((n =
+ hdaudio_mixer_get_inendpoints (devc->mixer, &endpoints,
+ sizeof (*endpoints))) < 0)
+ return;
+
+ if (n > MAX_INPUTS)
+ {
+ cmn_err (CE_WARN,
+ "Only %d out of %d input devices can be installed\n",
+ MAX_INPUTS, n);
+ n = MAX_INPUTS;
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ adev_p adev;
+ hda_portc_t *portc = &devc->input_portc[i];
+ unsigned int formats = AFMT_S16_LE;
+ int opts = ADEV_AUTOMODE | ADEV_NOOUTPUT;
+
+/* Skip endpoints that are not physically connected on the motherboard. */
+ if (endpoints[i].skip)
+ continue;
+
+ if (endpoints[i].is_digital)
+ {
+ opts |= ADEV_SPECIAL;
+ sprintf (devname, "spdin%d", devc->num_spdin++);
+ devfile_name = devname;
+ }
+ if (endpoints[i].is_modem)
+ {
+ opts |= ADEV_SPECIAL;
+ sprintf (devname, "mdmin%d", devc->num_mdmin++);
+ devfile_name = devname;
+ }
+
+ //sprintf (tmp_name, "%s %s", devc->chip_name, endpoints[i].name);
+ sprintf (tmp_name, "HD Audio rec %s", endpoints[i].name);
+
+ if ((audio_dev = oss_install_audiodev_with_devname (OSS_AUDIO_DRIVER_VERSION,
+ devc->osdev,
+ devc->osdev,
+ tmp_name,
+ &hda_audio_driver,
+ sizeof (audiodrv_t),
+ opts, formats, devc, -1,
+ devfile_name)) < 0)
+ {
+ return;
+ }
+
+ if (i == 0 && devc->first_dev == -1)
+ devc->first_dev = audio_dev;
+
+ adev = audio_engines[audio_dev];
+ devc->num_inputs = i+1;
+
+ adev->devc = devc;
+ adev->portc = portc;
+ adev->rate_source = devc->first_dev;
+ adev->mixer_dev = devc->mixer_dev;
+ adev->min_rate = 8000;
+ adev->max_rate = 192000;
+ adev->min_channels = 2;
+ adev->max_channels = 2;
+
+ if (endpoints[i].is_modem)
+ {
+ adev->min_channels = 1;
+ adev->max_channels = 1;
+ adev->caps |= PCM_CAP_MODEM;
+ }
+
+ adev->min_block = 128; /* Hardware limitation */
+ adev->min_fragments = 4; /* Vmix doesn't work without this */
+ portc->num = i;
+ portc->open_mode = 0;
+ portc->audio_enabled = 0;
+ portc->audiodev = audio_dev;
+ portc->port_type = PT_INPUT;
+ portc->endpoint = &endpoints[i];
+ portc->engine = NULL;
+ init_adev_caps (devc, adev, portc->endpoint);
+ }
+}
+
+static void
+activate_vmix (hda_devc_t * devc)
+{
+#ifdef CONFIG_OSS_VMIX
+/*
+ * Attach vmix engines to all outputs and inputs.
+ */
+
+ if (devc->num_outputs > 0)
+ {
+ if (devc->num_inputs < 1)
+ vmix_attach_audiodev(devc->osdev, devc->output_portc[0].audiodev, -1, 0);
+ else
+ vmix_attach_audiodev(devc->osdev, devc->output_portc[0].audiodev, devc->input_portc[0].audiodev, 0);
+ };
+#endif
+}
+
+static int
+init_HDA (hda_devc_t * devc)
+{
+ unsigned int gcap;
+ unsigned int tmp;
+
+ /* Reset controller */
+
+ if (!reset_controller (devc))
+ return 0;
+
+ PCI_WRITEL (devc->osdev, devc->azbar + HDA_INTCTL, PCI_READL (devc->osdev, devc->azbar + HDA_INTCTL) | 0xc0000000); /* Intr enable */
+
+ /*
+ * Set CORB and RIRB sizes to 256, 16 or 2 entries.
+ *
+ */
+ tmp = (PCI_READB (devc->osdev, devc->azbar + HDA_CORBSIZE) >> 4) & 0x07;
+ if (tmp & 0x4) /* 256 entries */
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_CORBSIZE, 0x2);
+ else if (tmp & 0x2) /* 16 entries */
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_CORBSIZE, 0x1);
+ else /* Assume that 2 entries is supported */
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_CORBSIZE, 0x0);
+
+ tmp = (PCI_READB (devc->osdev, devc->azbar + HDA_RIRBSIZE) >> 4) & 0x07;
+ if (tmp & 0x4) /* 256 entries */
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_RIRBSIZE, 0x2);
+ else if (tmp & 0x2) /* 16 entries */
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_RIRBSIZE, 0x1);
+ else /* Assume that 2 entries is supported */
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_RIRBSIZE, 0x0);
+
+ /* setup the CORB/RIRB structs */
+ if (!setup_controller (devc))
+ return 0;
+
+ /* setup the engine structs */
+ if (!setup_engines (devc))
+ return 0;
+
+ if (!devc->codecmask)
+ {
+ cmn_err (CE_WARN, "No codecs found after reset\n");
+ return 0;
+ }
+ else
+ devc->mixer = hdaudio_mixer_create (devc->chip_name, devc, devc->osdev,
+ do_corb_write, corb_read,
+ devc->codecmask, devc->vendor_id,
+ devc->subvendor_id);
+ if (devc->mixer == NULL)
+ return 0;
+
+ devc->mixer_dev = hdaudio_mixer_get_mixdev (devc->mixer);
+
+ gcap = PCI_READW (devc->osdev, devc->azbar + HDA_GCAP);
+ DDB (cmn_err (CE_CONT, " GCAP register content 0x%x\n", gcap));
+
+ if (((gcap >> 3) & 0x0f) > 0)
+ cmn_err (CE_WARN, "Bidirectional engines not supported\n");
+ if (((gcap >> 1) & 0x03) > 0)
+ cmn_err (CE_WARN, "Multiple SDOs not supported\n");
+
+ install_outputdevs (devc);
+ install_inputdevs (devc);
+ activate_vmix (devc);
+
+ return 1;
+}
+
+
+int
+oss_hdaudio_attach (oss_device_t * osdev)
+{
+ unsigned char pci_irq_line, pci_revision, btmp;
+ unsigned short pci_command, vendor, device, wtmp;
+ unsigned short subvendor, subdevice;
+ hda_devc_t *devc;
+ static int already_attached = 0;
+ int err;
+ unsigned short devctl;
+
+ DDB (cmn_err (CE_CONT, "oss_hdaudio_attach entered\n"));
+
+ if (already_attached)
+ {
+ cmn_err (CE_WARN, "oss_hdaudio_attach: Already attached\n");
+ return 0;
+ }
+ already_attached = 1;
+
+ pci_read_config_word (osdev, PCI_VENDOR_ID, &vendor);
+ pci_read_config_word (osdev, PCI_DEVICE_ID, &device);
+
+#if 0
+ // This check is not necessary because the kernel has already checked
+ // the vendor&device ID
+
+ if ((vendor != INTEL_VENDOR_ID && vendor != NVIDIA_VENDOR_ID &&
+ vendor != ATI_VENDOR_ID && vendor != SIS_VENDOR_ID &&
+ vendor != VIA_VENDOR_ID && vendor != ULI_VENDOR_ID) ||
+ (device != INTEL_DEVICE_ICH6 && device != INTEL_DEVICE_ICH7 &&
+ device != INTEL_DEVICE_ESB2 && device != INTEL_DEVICE_ICH8 &&
+ device != INTEL_DEVICE_ICH9 && device != INTEL_DEVICE_P35 &&
+ device != INTEL_DEVICE_ICH10 && device != INTEL_DEVICE_ICH10_B &&
+ device != INTEL_DEVICE_PCH &&
+ device != NVIDIA_DEVICE_MCP51 && device != NVIDIA_DEVICE_MCP55 &&
+ device != NVIDIA_DEVICE_MCP61 && device != NVIDIA_DEVICE_MCP61A &&
+ device != NVIDIA_DEVICE_MCP65 && device != NVIDIA_DEVICE_MCP67 &&
+ device != NVIDIA_DEVICE_MCP73 && device != NVIDIA_DEVICE_MCP78S &&
+ device != NVIDIA_DEVICE_MCP79 &&
+ device != VIA_DEVICE_HDA &&
+ device != SIS_DEVICE_HDA &&
+ device != ULI_DEVICE_HDA &&
+ device != ATI_DEVICE_SB450 && device != ATI_DEVICE_SB600))
+
+ {
+ return 0;
+ }
+#endif
+
+ pci_read_config_byte (osdev, PCI_REVISION_ID, &pci_revision);
+ pci_read_config_word (osdev, PCI_COMMAND, &pci_command);
+ pci_read_config_irq (osdev, PCI_INTERRUPT_LINE, &pci_irq_line);
+ pci_read_config_word (osdev, PCI_SUBSYSTEM_VENDOR_ID, &subvendor);
+ pci_read_config_word (osdev, PCI_SUBSYSTEM_ID, &subdevice);
+
+ if ((devc = PMALLOC (osdev, sizeof (*devc))) == NULL)
+ {
+ cmn_err (CE_WARN, "Out of memory\n");
+ return 0;
+ }
+
+ devc->osdev = osdev;
+ osdev->devc = devc;
+ devc->first_dev = -1;
+
+ osdev->hw_info = PMALLOC (osdev, HWINFO_SIZE); /* Text buffer for additional device info */
+ memset (osdev->hw_info, 0, HWINFO_SIZE);
+
+ devc->vendor_id = (vendor << 16) | device;
+ devc->subvendor_id = (subvendor << 16) | subdevice;
+
+ oss_pci_byteswap (osdev, 1);
+
+ switch (device)
+ {
+ case INTEL_DEVICE_SCH:
+ pci_read_config_word (osdev, 0x78, &devctl);
+ DDB (cmn_err (CE_CONT, " DEVC register content 0x%04x\n", devctl);)
+ pci_write_config_word (osdev, 0x78, (devctl & (~0x0800)) );
+ DDB (pci_read_config_word (osdev, 0x78, &devctl);)
+ DDB (cmn_err (CE_CONT, " DEVC register content (after clearing DEVC.NSNPEN) 0x%04x\n", devctl);)
+ /* continue is intentional */
+ case INTEL_DEVICE_ICH6:
+ case INTEL_DEVICE_ICH7:
+ case INTEL_DEVICE_ESB2:
+ case INTEL_DEVICE_ICH8:
+ case INTEL_DEVICE_ICH9:
+ case INTEL_DEVICE_P35:
+ case INTEL_DEVICE_ICH10:
+ case INTEL_DEVICE_ICH10_B:
+ case INTEL_DEVICE_PCH:
+ case INTEL_DEVICE_PCH2:
+ case INTEL_DEVICE_CPT:
+ devc->chip_name = "Intel HD Audio";
+ break;
+
+ case NVIDIA_DEVICE_MCP51:
+ case NVIDIA_DEVICE_MCP55:
+ case NVIDIA_DEVICE_MCP61:
+ case NVIDIA_DEVICE_MCP61A:
+ case NVIDIA_DEVICE_MCP65:
+ case NVIDIA_DEVICE_MCP67:
+ case NVIDIA_DEVICE_MCP73:
+ case NVIDIA_DEVICE_MCP78S:
+ case NVIDIA_DEVICE_MCP79:
+ devc->chip_name = "nVidia HD Audio";
+ pci_read_config_byte (osdev, 0x4e, &btmp);
+ pci_write_config_byte (osdev, 0x4e, (btmp & 0xf0) | 0x0f);
+ pci_read_config_byte (osdev, 0x4d, &btmp);
+ pci_write_config_byte (osdev, 0x4d, (btmp & 0xfe) | 0x01);
+ pci_read_config_byte (osdev, 0x4c, &btmp);
+ pci_write_config_byte (osdev, 0x4c, (btmp & 0xfe) | 0x01);
+ break;
+
+ case ATI_DEVICE_SB450:
+ case ATI_DEVICE_SB600:
+ devc->chip_name = "ATI HD Audio";
+ pci_read_config_byte (osdev, 0x42, &btmp);
+ pci_write_config_byte (osdev, 0x42, (btmp & 0xf8) | 0x2);
+ break;
+
+ case VIA_DEVICE_HDA:
+ devc->chip_name = "VIA HD Audio";
+ break;
+
+ case SIS_DEVICE_HDA:
+ devc->chip_name = "SiS HD Audio";
+ break;
+
+ case ULI_DEVICE_HDA:
+ devc->chip_name = "ULI HD Audio";
+ pci_read_config_word (osdev, 0x40, &wtmp);
+ pci_write_config_word (osdev, 0x40, wtmp | 0x10);
+ pci_write_config_dword (osdev, PCI_MEM_BASE_ADDRESS_1, 0);
+ break;
+
+ case CREATIVE_XFI_HDA:
+ devc->chip_name = "Creative Labs XFi XTreme Audio";
+ break;
+
+ default:
+ devc->chip_name = "High definition audio device";
+ }
+
+ pci_read_config_dword (osdev, PCI_MEM_BASE_ADDRESS_0, &devc->membar_addr);
+
+ devc->membar_addr &= ~7;
+
+ /* get virtual address */
+ devc->azbar =
+ (void *) MAP_PCI_MEM (devc->osdev, 0, devc->membar_addr, 16 * 1024);
+
+ /*verify interrupt*/
+ if (pci_irq_line == 0)
+ {
+ cmn_err (CE_WARN, "IRQ not assigned by BIOS.\n");
+ return 0;
+ }
+
+ devc->irq = pci_irq_line;
+
+ /* activate the device */
+ pci_command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+ pci_write_config_word (osdev, PCI_COMMAND, pci_command);
+
+
+ MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV);
+ MUTEX_INIT (devc->osdev, devc->low_mutex, MH_DRV + 1);
+
+ oss_register_device (osdev, devc->chip_name);
+
+ if ((err = oss_register_interrupts (devc->osdev, 0, hdaintr, NULL)) < 0)
+ {
+ cmn_err (CE_WARN, "Can't register interrupt handler, err=%d\n", err);
+ return 0;
+ }
+
+ devc->base = devc->membar_addr;
+
+ /* Setup the TCSEL register. Don't do this with ATI chipsets. */
+ if (vendor != ATI_VENDOR_ID)
+ {
+ pci_read_config_byte (osdev, 0x44, &btmp);
+ pci_write_config_byte (osdev, 0x44, btmp & 0xf8);
+ }
+
+ err = init_HDA (devc);
+ return err;
+}
+
+int
+oss_hdaudio_detach (oss_device_t * osdev)
+{
+ hda_devc_t *devc = (hda_devc_t *) osdev->devc;
+ int j;
+
+ if (oss_disable_device (osdev) < 0)
+ return 0;
+
+ PCI_WRITEL (devc->osdev, devc->azbar + HDA_INTSTS, 0xc0000000); /* ack pending ints */
+
+ PCI_WRITEL (devc->osdev, devc->azbar + HDA_INTCTL, 0); /* Intr disable */
+ PCI_WRITEL (devc->osdev, devc->azbar + HDA_STATESTS, 0x7); /* Intr disable */
+ PCI_WRITEL (devc->osdev, devc->azbar + HDA_RIRBSTS, 0x5); /* Intr disable */
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_RIRBCTL, 0); /* Stop */
+ PCI_WRITEB (devc->osdev, devc->azbar + HDA_CORBCTL, 0); /* Stop */
+
+ oss_unregister_interrupts (devc->osdev);
+
+ MUTEX_CLEANUP (devc->mutex);
+ MUTEX_CLEANUP (devc->low_mutex);
+
+ if (devc->corb != NULL)
+ CONTIG_FREE (devc->osdev, devc->corb, 4096, devc->corb_dma_handle);
+
+ devc->corb = NULL;
+
+ for (j = 0; j < devc->num_outengines; j++)
+ {
+ hda_engine_t *engine = &devc->outengines[j];
+
+ if (engine->bdl == NULL)
+ continue;
+ CONTIG_FREE (devc->osdev, engine->bdl, 4096, engine->bdl_dma_handle);
+ }
+
+ for (j = 0; j < devc->num_inengines; j++)
+ {
+ hda_engine_t *engine = &devc->inengines[j];
+
+ if (engine->bdl == NULL)
+ continue;
+ CONTIG_FREE (devc->osdev, engine->bdl, 4096, engine->bdl_dma_handle);
+ }
+
+ if (devc->membar_addr != 0)
+ {
+ UNMAP_PCI_MEM (devc->osdev, 0, devc->membar_addr, devc->azbar,
+ 16 * 1024);
+ devc->membar_addr = 0;
+ }
+
+ oss_spdif_uninstall (&devc->spdc);
+
+ oss_unregister_device (devc->osdev);
+ return 1;
+}
diff --git a/kernel/drv/oss_hdaudio/oss_hdaudio.man b/kernel/drv/oss_hdaudio/oss_hdaudio.man
new file mode 100644
index 0000000..e2bae7a
--- /dev/null
+++ b/kernel/drv/oss_hdaudio/oss_hdaudio.man
@@ -0,0 +1,77 @@
+NAME
+ oss_hdaudio - Intel High Definition Audio (AZALIA)
+
+DESCRIPTION
+ Open Sound System driver for Intels high definition audio known as
+ "Azalia". This driver supports Intel 915/925 chipsets with the
+ Realtek ALC880 and CMedia 9880 8 channel codecs.
+
+ The HDA driver supports:
+
+ o 8-96Khz Playback/Recording
+ o 8 or 16 or 32 bits
+ o 2, 4, 6 or 8 channel audio.
+ o SPDIF digital output and Input
+ o AC3 passthrough
+
+ HDAUDIO MIXER
+ The Intel HDA mixer is a new type of mixer that doesn't have
+ the normal volume controls found on AC97 or legacy SB devices.
+ The HDA mixer presents a concept of Jacks and you can configure
+ any jack to be either an output or an input jack.
+
+ Some motherboards may not correctly initialize the jacks according
+ to their color and functionality but in general here's the
+ configuration that should generally be followed:
+
+ o Orange = Center/LFE o Blue = Line-in
+ o Black = Rear o Green = Front
+ o Grey = Side o Pink = Mic
+
+ Some Azalia codecs support front panel connectors and so if you see
+ fp-green and fp-pink connectors, then these are for front panel
+ speaker and mic/line-in Jacks.
+
+ There is a function selector for most of the analog audio jacks (for example
+ connector.pink.mode). This selector is used to control if the jack is used
+ as an input (microphone or line in) or output (front, rear, side, speaker,
+ etc).
+
+KNOWN PROBLEMS
+In general Azalia based systems (laptops/motherboards) would require a custom
+driver to work properly. Due to enormous number of different systems it is not
+possible to develop such custom drivers for all systems. A generic driver is
+used for systems that don't have dedicated drivers.
+
+Unfortunately the mixer and control panel interface (see ossmix(1))
+for "generic" systems is very cryptic and difficult to
+understand. To solve problems with volumes or signal routing you need to
+start ossxmix(1) and change the controls one at time until you get the desired
+effect.
+
+OPTIONS
+o hdaudio_jacksense enables jack sensing mode when the hdaudio driver is
+ loaded. In this mode all I/O pin's that are not
+ in use will be disabled as well as the mixer controls
+ that are related with them. In this way the
+ mixer/control panel will become more intuitive.
+ However OSS will need to be restarted with soundoff;
+ soundon every time new inputs or outputs are attached
+ to the audio jacks. Default : 0.
+
+ NOTE! hdaudio_jacksense=1 works only in some systems.
+ Many laptops and motherboards don't support jack
+ sensing.
+
+o hdaudio_noskip Disable skipping unconnected jack. All mixer controls
+ will be shown, even for disabled I/O pins.
+ Can get values 0-7. 1-7 is a bitmask, where every bit
+ masks a different check. Bit 3 (= value 4) overrides
+ jacksense check too.
+ Default: 0 - unconnected jacks are skipped.
+FILES
+ CONFIGFILEPATH/oss_hdaudio.conf Device configuration file
+
+AUTHOR
+ 4Front Technologies
+