diff options
Diffstat (limited to 'kernel/drv/oss_hdaudio')
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, >, &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, >, &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 + |